diff --git a/.dialyzer-ignore b/.dialyzer-ignore index 84162c2785e0..6dd625f3298f 100644 --- a/.dialyzer-ignore +++ b/.dialyzer-ignore @@ -6,8 +6,8 @@ lib/ethereum_jsonrpc/rolling_window.ex:171 lib/explorer/smart_contract/solidity/publisher_worker.ex:1 lib/explorer/smart_contract/vyper/publisher_worker.ex:1 -lib/explorer/smart_contract/solidity/publisher_worker.ex:6 -lib/explorer/smart_contract/vyper/publisher_worker.ex:6 +lib/explorer/smart_contract/solidity/publisher_worker.ex:8 +lib/explorer/smart_contract/vyper/publisher_worker.ex:8 lib/block_scout_web/router.ex:1 lib/block_scout_web/schema/types.ex:31 lib/phoenix/router.ex:324 diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml index 6bbec02ea1cb..18d436a17d06 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/config.yml @@ -12,6 +12,7 @@ on: - production-sokol-stg - production-rsk-stg - production-lukso-stg + - production-immutable-stg - staging-l2 pull_request: branches: @@ -47,7 +48,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_22-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps- @@ -105,7 +106,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_22-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -129,7 +130,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_22-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -152,7 +153,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_22-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -161,7 +162,7 @@ jobs: id: dialyzer-cache with: path: priv/plts - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-mixlockhash_20-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-mixlockhash_22-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-" @@ -192,7 +193,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_22-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -218,7 +219,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_22-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -247,7 +248,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_22-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -295,7 +296,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_22-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -341,7 +342,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_22-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -398,7 +399,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_22-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -452,7 +453,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_22-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -517,7 +518,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_22-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -581,7 +582,7 @@ jobs: path: | deps _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_20-${{ hashFiles('mix.lock') }} + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_22-${{ hashFiles('mix.lock') }} restore-keys: | ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" @@ -624,7 +625,7 @@ jobs: PGUSER: postgres ETHEREUM_JSONRPC_CASE: "EthereumJSONRPC.Case.Nethermind.Mox" ETHEREUM_JSONRPC_WEB_SOCKET_CASE: "EthereumJSONRPC.WebSocket.Case.Mox" - CHAIN_ID: "77" + CHAIN_ID: "10200" API_RATE_LIMIT_DISABLED: "true" ADMIN_PANEL_ENABLED: "true" ACCOUNT_ENABLED: "true" diff --git a/.github/workflows/publish-docker-image-every-push.yml b/.github/workflows/publish-docker-image-every-push.yml index a2205707247d..c748dfd25ea2 100644 --- a/.github/workflows/publish-docker-image-every-push.yml +++ b/.github/workflows/publish-docker-image-every-push.yml @@ -7,7 +7,7 @@ on: env: OTP_VERSION: '25.2.1' ELIXIR_VERSION: '1.14.3' - RELEASE_VERSION: 5.2.1 + RELEASE_VERSION: 5.2.2 jobs: push_to_registry: diff --git a/.github/workflows/publish-docker-image-for-core.yml b/.github/workflows/publish-docker-image-for-core.yml index d15ef3531a54..48470e040e3a 100644 --- a/.github/workflows/publish-docker-image-for-core.yml +++ b/.github/workflows/publish-docker-image-for-core.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 5.2.1 + RELEASE_VERSION: 5.2.2 DOCKER_CHAIN_NAME: poa steps: - name: Check out the repo @@ -41,12 +41,12 @@ jobs: context: . file: ./docker/Dockerfile push: true - tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:latest, blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-prerelease-${{ env.SHORT_SHA }} + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:latest, blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }} build-args: | CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_READ_API=false + API_V1_READ_METHODS_DISABLED=false DISABLE_WEBAPP=false - DISABLE_WRITE_API=false + API_V1_WRITE_METHODS_DISABLED=false CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= ADMIN_PANEL_ENABLED=false CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= diff --git a/.github/workflows/publish-docker-image-for-eth-goerli.yml b/.github/workflows/publish-docker-image-for-eth-goerli.yml index 0f1dfbb47991..95c8e9fa996f 100644 --- a/.github/workflows/publish-docker-image-for-eth-goerli.yml +++ b/.github/workflows/publish-docker-image-for-eth-goerli.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 5.2.1 + RELEASE_VERSION: 5.2.2 DOCKER_CHAIN_NAME: eth-goerli steps: - name: Check out the repo @@ -41,12 +41,12 @@ jobs: context: . file: ./docker/Dockerfile push: true - tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:latest, blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-prerelease-${{ env.SHORT_SHA }} + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:latest, blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }} build-args: | CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_READ_API=false + API_V1_READ_METHODS_DISABLED=false DISABLE_WEBAPP=false - DISABLE_WRITE_API=false + API_V1_WRITE_METHODS_DISABLED=false CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= ADMIN_PANEL_ENABLED=false CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= diff --git a/.github/workflows/publish-docker-image-for-eth.yml b/.github/workflows/publish-docker-image-for-eth.yml index 058b5f77d9a5..237eec76e5cd 100644 --- a/.github/workflows/publish-docker-image-for-eth.yml +++ b/.github/workflows/publish-docker-image-for-eth.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 5.2.1 + RELEASE_VERSION: 5.2.2 DOCKER_CHAIN_NAME: mainnet steps: - name: Check out the repo @@ -41,12 +41,12 @@ jobs: context: . file: ./docker/Dockerfile push: true - tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-prerelease-${{ env.SHORT_SHA }}-experimental + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-experimental build-args: | CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_READ_API=false + API_V1_READ_METHODS_DISABLED=false DISABLE_WEBAPP=false - DISABLE_WRITE_API=false + API_V1_WRITE_METHODS_DISABLED=false CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=false ADMIN_PANEL_ENABLED=false CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= diff --git a/.github/workflows/publish-docker-image-for-immutable.yml b/.github/workflows/publish-docker-image-for-immutable.yml new file mode 100644 index 000000000000..92eff232935f --- /dev/null +++ b/.github/workflows/publish-docker-image-for-immutable.yml @@ -0,0 +1,63 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Publish Docker image for specific chain branches + +on: + push: + branches: + - production-immutable-stg +env: + OTP_VERSION: '24.3.4.1' + ELIXIR_VERSION: '1.13.4' +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: 5.2.2 + DOCKER_CHAIN_NAME: immutable + steps: + - name: Check out the repo + uses: actions/checkout@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Log in to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v4 + with: + images: blockscout/blockscout + + - name: Add SHORT_SHA env property with commit short sha + run: echo "SHORT_SHA=`echo ${GITHUB_SHA} | cut -c1-8`" >> $GITHUB_ENV + + - name: Build and push Docker image + uses: docker/build-push-action@v3 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:latest, blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }} + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} \ No newline at end of file diff --git a/.github/workflows/publish-docker-image-for-l2-staging.yml b/.github/workflows/publish-docker-image-for-l2-staging.yml index c903fe819c44..a95c63ad9e66 100644 --- a/.github/workflows/publish-docker-image-for-l2-staging.yml +++ b/.github/workflows/publish-docker-image-for-l2-staging.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 5.2.1 + RELEASE_VERSION: 5.2.2 DOCKER_CHAIN_NAME: optimism-l2-advanced steps: - name: Check out the repo @@ -41,12 +41,12 @@ jobs: context: . file: ./docker/Dockerfile push: true - tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:latest, blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-prerelease-${{ env.SHORT_SHA }} + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:latest, blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }} build-args: | CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_READ_API=false + API_V1_READ_METHODS_DISABLED=false DISABLE_WEBAPP=false - DISABLE_WRITE_API=false + API_V1_WRITE_METHODS_DISABLED=false CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= ADMIN_PANEL_ENABLED=false CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= diff --git a/.github/workflows/publish-docker-image-for-lukso.yml b/.github/workflows/publish-docker-image-for-lukso.yml index 544f0729d5cc..8279c009f00f 100644 --- a/.github/workflows/publish-docker-image-for-lukso.yml +++ b/.github/workflows/publish-docker-image-for-lukso.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 5.2.1 + RELEASE_VERSION: 5.2.2 DOCKER_CHAIN_NAME: lukso steps: - name: Check out the repo @@ -41,12 +41,12 @@ jobs: context: . file: ./docker/Dockerfile push: true - tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:latest, blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-prerelease-${{ env.SHORT_SHA }} + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:latest, blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }} build-args: | CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_READ_API=false + API_V1_READ_METHODS_DISABLED=false DISABLE_WEBAPP=false - DISABLE_WRITE_API=false + API_V1_WRITE_METHODS_DISABLED=false CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= ADMIN_PANEL_ENABLED=false CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= diff --git a/.github/workflows/publish-docker-image-for-optimism.yml b/.github/workflows/publish-docker-image-for-optimism.yml index e1406ee5a24f..7c7bd2f11e64 100644 --- a/.github/workflows/publish-docker-image-for-optimism.yml +++ b/.github/workflows/publish-docker-image-for-optimism.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 5.2.1 + RELEASE_VERSION: 5.2.2 DOCKER_CHAIN_NAME: optimism steps: - name: Check out the repo @@ -44,9 +44,9 @@ jobs: tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:latest, blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }} build-args: | CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_READ_API=false + API_V1_READ_METHODS_DISABLED=false DISABLE_WEBAPP=false - DISABLE_WRITE_API=false + API_V1_WRITE_METHODS_DISABLED=false CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= ADMIN_PANEL_ENABLED=false CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= diff --git a/.github/workflows/publish-docker-image-for-rsk.yml b/.github/workflows/publish-docker-image-for-rsk.yml index df49d64ccac0..5eb97cf0de37 100644 --- a/.github/workflows/publish-docker-image-for-rsk.yml +++ b/.github/workflows/publish-docker-image-for-rsk.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 5.2.1 + RELEASE_VERSION: 5.2.2 DOCKER_CHAIN_NAME: rsk steps: - name: Check out the repo @@ -41,12 +41,12 @@ jobs: context: . file: ./docker/Dockerfile push: true - tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:latest, blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-prerelease-${{ env.SHORT_SHA }} + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:latest, blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }} build-args: | CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_READ_API=false + API_V1_READ_METHODS_DISABLED=false DISABLE_WEBAPP=false - DISABLE_WRITE_API=false + API_V1_WRITE_METHODS_DISABLED=false CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= ADMIN_PANEL_ENABLED=false CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= diff --git a/.github/workflows/publish-docker-image-for-xdai.yml b/.github/workflows/publish-docker-image-for-xdai.yml index 9f3ecd4a772e..37295fedd354 100644 --- a/.github/workflows/publish-docker-image-for-xdai.yml +++ b/.github/workflows/publish-docker-image-for-xdai.yml @@ -17,7 +17,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 5.2.1 + RELEASE_VERSION: 5.2.2 DOCKER_CHAIN_NAME: xdai steps: - name: Check out the repo @@ -50,12 +50,12 @@ jobs: context: . file: ./docker/Dockerfile push: true - tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:latest, blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-prerelease-${{ env.SHORT_SHA }} + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:latest, blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }} build-args: | CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_READ_API=false + API_V1_READ_METHODS_DISABLED=false DISABLE_WEBAPP=false - DISABLE_WRITE_API=false + API_V1_WRITE_METHODS_DISABLED=false CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= ADMIN_PANEL_ENABLED=false CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= diff --git a/.github/workflows/publish-docker-image-release.yml b/.github/workflows/publish-docker-image-release.yml index 689f3d7ec858..918abd13067d 100644 --- a/.github/workflows/publish-docker-image-release.yml +++ b/.github/workflows/publish-docker-image-release.yml @@ -18,7 +18,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 5.2.1 + RELEASE_VERSION: 5.2.2 steps: - name: Check out the repo uses: actions/checkout@v3 @@ -82,6 +82,7 @@ jobs: production-xdai-stg production-polygon-supernets-stg production-rsk-stg + production-immutable-stg steps: - uses: actions/checkout@v2 - name: Set Git config diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000000..505db0421db5 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,5 @@ +repos: + - repo: https://github.com/gitleaks/gitleaks + rev: v8.17.0 + hooks: + - id: gitleaks \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e63f5eec164..f31d821ced65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,15 +4,105 @@ ### Features +- [#8181](https://github.com/blockscout/blockscout/pull/8181) - Insert current token balances placeholders along with historical +- [#8210](https://github.com/blockscout/blockscout/pull/8210) - Drop address foreign keys +- [#8292](https://github.com/blockscout/blockscout/pull/8292) - Add ETHEREUM_JSONRPC_WAIT_PER_TIMEOUT env var + +### Fixes + +- [#8293](https://github.com/blockscout/blockscout/pull/8293) - Add ETHEREUM_JSONRPC_TRACE_URL for Geth in docker-compose.yml +- [#8240](https://github.com/blockscout/blockscout/pull/8240) - Refactor and fix paging params in API v2 +- [#8242](https://github.com/blockscout/blockscout/pull/8242) - Fixing visualizer service CORS issue when running docker-compose + +### Chore + +- [#8281](https://github.com/blockscout/blockscout/pull/8281) - Planned removal of duplicate API endpoints: for CSV export and GraphQL + +
+ Dependencies version bumps + +
+ +## 5.2.2-beta + +### Features + +- [#8218](https://github.com/blockscout/blockscout/pull/8218) - Add `/api/v2/search/quick` method +- [#8202](https://github.com/blockscout/blockscout/pull/8202) - Add `/api/v2/addresses/:address_hash/tabs-counters` endpoint +- [#8156](https://github.com/blockscout/blockscout/pull/8156) - Add `is_verified_via_admin_panel` property to tokens table +- [#8165](https://github.com/blockscout/blockscout/pull/8165), [#8201](https://github.com/blockscout/blockscout/pull/8201) - Add broadcast of updated address_current_token_balances +- [#7952](https://github.com/blockscout/blockscout/pull/7952) - Add parsing constructor arguments for sourcify contracts +- [#6190](https://github.com/blockscout/blockscout/pull/6190) - Add EIP-1559 support to gas price oracle +- [#7977](https://github.com/blockscout/blockscout/pull/7977) - GraphQL: extend schema with new field for existing objects +- [#8158](https://github.com/blockscout/blockscout/pull/8158), [#8164](https://github.com/blockscout/blockscout/pull/8164) - Include unfetched balances in TokenBalanceOnDemand fetcher + ### Fixes +- [#8233](https://github.com/blockscout/blockscout/pull/8233) - Fix API v2 broken tx response +- [#8147](https://github.com/blockscout/blockscout/pull/8147) - Switch sourcify tests from POA Sokol to Gnosis Chiado +- [#8145](https://github.com/blockscout/blockscout/pull/8145) - Handle negative holders count in API v2 - [#8040](https://github.com/blockscout/blockscout/pull/8040) - Resolve issue with Docker image for Mac M1/M2 +- [#8060](https://github.com/blockscout/blockscout/pull/8060) - Fix eth_getLogs API endpoint +- [#8082](https://github.com/blockscout/blockscout/pull/8082), [#8088](https://github.com/blockscout/blockscout/pull/8088) - Fix Rootstock charts API +- [#7992](https://github.com/blockscout/blockscout/pull/7992) - Fix missing range insert +- [#8022](https://github.com/blockscout/blockscout/pull/8022) - Don't add reorg block number to missing blocks ### Chore +- [#8222](https://github.com/blockscout/blockscout/pull/8222) - docker-compose for new UI with external backend +- [#8177](https://github.com/blockscout/blockscout/pull/8177) - Refactor address counter functions +- [#8183](https://github.com/blockscout/blockscout/pull/8183) - Update frontend envs in order to pass their validation +- [#8167](https://github.com/blockscout/blockscout/pull/8167) - Manage concurrency for Token and TokenBalance fetcher +- [#8179](https://github.com/blockscout/blockscout/pull/8179) - Enhance nginx config +- [#8146](https://github.com/blockscout/blockscout/pull/8146) - Add method_id to write methods in API v2 response +- [#8105](https://github.com/blockscout/blockscout/pull/8105) - Extend API v1 with endpoints used by new UI +- [#8104](https://github.com/blockscout/blockscout/pull/8104) - remove "TODO" from API v2 response +- [#8100](https://github.com/blockscout/blockscout/pull/8100), [#8103](https://github.com/blockscout/blockscout/pull/8103) - Extend docker-compose configs with new config when front is running externally +- [#8012](https://github.com/blockscout/blockscout/pull/8012) - API v2 smart-contract verification extended logging +
Dependencies version bumps +- [#7980](https://github.com/blockscout/blockscout/pull/7980) - Bump solc from 0.8.20 to 0.8.21 in /apps/explorer +- [#7986](https://github.com/blockscout/blockscout/pull/7986) - Bump sass from 1.63.6 to 1.64.0 in /apps/block_scout_web/assets +- [#8030](https://github.com/blockscout/blockscout/pull/8030) - Bump sweetalert2 from 11.7.18 to 11.7.20 in /apps/block_scout_web/assets +- [#8029](https://github.com/blockscout/blockscout/pull/8029) - Bump viewerjs from 1.11.3 to 1.11.4 in /apps/block_scout_web/assets +- [#8028](https://github.com/blockscout/blockscout/pull/8028) - Bump sass from 1.64.0 to 1.64.1 in /apps/block_scout_web/assets +- [#8026](https://github.com/blockscout/blockscout/pull/8026) - Bump dataloader from 1.0.10 to 1.0.11 +- [#8036](https://github.com/blockscout/blockscout/pull/8036) - Bump ex_cldr_numbers from 2.31.1 to 2.31.3 +- [#8027](https://github.com/blockscout/blockscout/pull/8027) - Bump absinthe from 1.7.4 to 1.7.5 +- [#8035](https://github.com/blockscout/blockscout/pull/8035) - Bump wallaby from 0.30.4 to 0.30.5 +- [#8038](https://github.com/blockscout/blockscout/pull/8038) - Bump chart.js from 4.3.0 to 4.3.1 in /apps/block_scout_web/assets +- [#8047](https://github.com/blockscout/blockscout/pull/8047) - Bump chart.js from 4.3.1 to 4.3.2 in /apps/block_scout_web/assets +- [#8000](https://github.com/blockscout/blockscout/pull/8000) - Bump postcss from 8.4.26 to 8.4.27 in /apps/block_scout_web/assets +- [#8052](https://github.com/blockscout/blockscout/pull/8052) - Bump @amplitude/analytics-browser from 2.1.2 to 2.1.3 in /apps/block_scout_web/assets +- [#8054](https://github.com/blockscout/blockscout/pull/8054) - Bump jest-environment-jsdom from 29.6.1 to 29.6.2 in /apps/block_scout_web/assets +- [#8063](https://github.com/blockscout/blockscout/pull/8063) - Bump eslint from 8.45.0 to 8.46.0 in /apps/block_scout_web/assets +- [#8066](https://github.com/blockscout/blockscout/pull/8066) - Bump ex_json_schema from 0.9.3 to 0.10.1 +- [#8064](https://github.com/blockscout/blockscout/pull/8064) - Bump core-js from 3.31.1 to 3.32.0 in /apps/block_scout_web/assets +- [#8053](https://github.com/blockscout/blockscout/pull/8053) - Bump jest from 29.6.1 to 29.6.2 in /apps/block_scout_web/assets +- [#8065](https://github.com/blockscout/blockscout/pull/8065) - Bump eslint-plugin-import from 2.27.5 to 2.28.0 in /apps/block_scout_web/assets +- [#8092](https://github.com/blockscout/blockscout/pull/8092) - Bump exvcr from 0.14.1 to 0.14.2 +- [#8091](https://github.com/blockscout/blockscout/pull/8091) - Bump sass from 1.64.1 to 1.64.2 in /apps/block_scout_web/assets +- [#8114](https://github.com/blockscout/blockscout/pull/8114) - Bump ex_doc from 0.30.3 to 0.30.4 +- [#8115](https://github.com/blockscout/blockscout/pull/8115) - Bump chart.js from 4.3.2 to 4.3.3 in /apps/block_scout_web/assets +- [#8116](https://github.com/blockscout/blockscout/pull/8116) - Bump @fortawesome/fontawesome-free from 6.4.0 to 6.4.2 in /apps/block_scout_web/assets +- [#8142](https://github.com/blockscout/blockscout/pull/8142) - Bump sobelow from 0.12.2 to 0.13.0 +- [#8141](https://github.com/blockscout/blockscout/pull/8141) - Bump @babel/core from 7.22.9 to 7.22.10 in /apps/block_scout_web/assets +- [#8140](https://github.com/blockscout/blockscout/pull/8140) - Bump @babel/preset-env from 7.22.9 to 7.22.10 in /apps/block_scout_web/assets +- [#8160](https://github.com/blockscout/blockscout/pull/8160) - Bump exvcr from 0.14.2 to 0.14.3 +- [#8159](https://github.com/blockscout/blockscout/pull/8159) - Bump luxon from 3.3.0 to 3.4.0 in /apps/block_scout_web/assets +- [#8169](https://github.com/blockscout/blockscout/pull/8169) - Bump sass from 1.64.2 to 1.65.1 in /apps/block_scout_web/assets +- [#8170](https://github.com/blockscout/blockscout/pull/8170) - Bump sweetalert2 from 11.7.20 to 11.7.22 in /apps/block_scout_web/assets +- [#8188](https://github.com/blockscout/blockscout/pull/8188) - Bump eslint from 8.46.0 to 8.47.0 in /apps/block_scout_web/assets +- [#8204](https://github.com/blockscout/blockscout/pull/8204) - Bump ex_doc from 0.30.4 to 0.30.5 +- [#8207](https://github.com/blockscout/blockscout/pull/8207) - Bump wallaby from 0.30.5 to 0.30.6 +- [#8212](https://github.com/blockscout/blockscout/pull/8212) - Bump sweetalert2 from 11.7.22 to 11.7.23 in /apps/block_scout_web/assets +- [#8203](https://github.com/blockscout/blockscout/pull/8203) - Bump autoprefixer from 10.4.14 to 10.4.15 in /apps/block_scout_web/assets +- [#8214](https://github.com/blockscout/blockscout/pull/8214) - Bump @amplitude/analytics-browser from 2.1.3 to 2.2.0 in /apps/block_scout_web/assets +- [#8225](https://github.com/blockscout/blockscout/pull/8225) - Bump postcss from 8.4.27 to 8.4.28 in /apps/block_scout_web/assets +- [#8224](https://github.com/blockscout/blockscout/pull/8224) - Bump gettext from 0.22.3 to 0.23.1 +
## 5.2.1-beta @@ -36,6 +126,7 @@ ### Fixes +- [#8187](https://github.com/blockscout/blockscout/pull/8187) - API v1 500 error convert to 404, if requested path is incorrect - [#7852](https://github.com/blockscout/blockscout/pull/7852) - Token balances refactoring & fixes - [#7872](https://github.com/blockscout/blockscout/pull/7872) - Fix pending gas price in pending tx - [#7875](https://github.com/blockscout/blockscout/pull/7875) - Fix twin compiler version @@ -88,7 +179,7 @@ - [#7867](https://github.com/blockscout/blockscout/pull/7867) - Bump postcss from 8.4.24 to 8.4.25 in /apps/block_scout_web/assets - [#7871](https://github.com/blockscout/blockscout/pull/7871) - Bump @babel/core from 7.22.6 to 7.22.8 in /apps/block_scout_web/assets - [#7868](https://github.com/blockscout/blockscout/pull/7868) - Bump jest-environment-jsdom from 29.6.0 to 29.6.1 in /apps/block_scout_web/assets -- [#7866](https://github.com/blockscout/blockscout/pull/7866) - Bump @babel/preset-env from 7.22.6 to 7.22.7 in /apps/block_scout_web/assets +- [#7866](https://github.com/blockscout/blockscout/pull/7866) - Bump @babel/preset-env from 7.22.6 to 7.22.7 in /apps/block_scout_web/assets - [#7869](https://github.com/blockscout/blockscout/pull/7869) - Bump core-js from 3.31.0 to 3.31.1 in /apps/block_scout_web/assets - [#7884](https://github.com/blockscout/blockscout/pull/7884) - Bump ecto from 3.10.2 to 3.10.3 - [#7882](https://github.com/blockscout/blockscout/pull/7882) - Bump jason from 1.4.0 to 1.4.1 @@ -215,7 +306,7 @@ - [#7702](https://github.com/blockscout/blockscout/pull/7702) - Bump @amplitude/analytics-browser from 1.10.8 to 1.11.0 in /apps/block_scout_web/assets - [#7708](https://github.com/blockscout/blockscout/pull/7708) - Bump phoenix_pubsub from 2.1.2 to 2.1.3 - [#7707](https://github.com/blockscout/blockscout/pull/7707) - Bump @amplitude/analytics-browser from 1.11.0 to 2.0.0 in /apps/block_scout_web/assets -- [#7706](https://github.com/blockscout/blockscout/pull/7706) - Bump webpack from 5.86.0 to 5.87.0 in /apps/block_scout_web/assets +- [#7706](https://github.com/blockscout/blockscout/pull/7706) - Bump webpack from 5.86.0 to 5.87.0 in /apps/block_scout_web/assets - [#7705](https://github.com/blockscout/blockscout/pull/7705) - Bump sass from 1.63.3 to 1.63.4 in /apps/block_scout_web/assets - [#7714](https://github.com/blockscout/blockscout/pull/7714) - Bump ex_cldr_units from 3.16.1 to 3.16.2 - [#7748](https://github.com/blockscout/blockscout/pull/7748) - Bump mock from 0.3.7 to 0.3.8 diff --git a/README.md b/README.md index 316e101047f5..0a59692f9704 100644 --- a/README.md +++ b/README.md @@ -8,24 +8,21 @@ -BlockScout provides a comprehensive, easy-to-use interface for users to view, confirm, and inspect transactions on EVM (Ethereum Virtual Machine) blockchains. This includes the POA Network, Gnosis Chain, Ethereum Classic and other **Ethereum testnets, private networks and sidechains**. +BlockScout provides a comprehensive, easy-to-use interface for users to view, confirm, and inspect transactions on EVM (Ethereum Virtual Machine) blockchains. This includes Ethereum Mainnet, Ethereum Classic, Optimism, Gnosis Chain and many other **Ethereum testnets, private networks, L2s and sidechains**. See our [project documentation](https://docs.blockscout.com/) for detailed information and setup instructions. -For questions, comments and feature requests see the [discussions section](https://github.com/blockscout/blockscout/discussions). +For questions, comments and feature requests see the [discussions section](https://github.com/blockscout/blockscout/discussions) or via [Discord](https://discord.com/invite/blockscout). ## About BlockScout -BlockScout is an Elixir application that allows users to search transactions, view accounts and balances, and verify smart contracts on the Ethereum network including all forks and sidechains. +BlockScout allows users to search transactions, view accounts and balances, verify and interact with smart contracts and view and interact with applications on the Ethereum network including many forks, sidechains, L2s and testnets. -Currently available full-featured block explorers (Etherscan, Etherchain, Blockchair) are closed systems which are not independently verifiable. As Ethereum sidechains continue to proliferate in both private and public settings, transparent, open-source tools are needed to analyze and validate transactions. +Blockscout is an open-source alternative to centralized, closed source block explorers such as Etherscan, Etherchain and others. As Ethereum sidechains and L2s continue to proliferate in both private and public settings, transparent, open-source tools are needed to analyze and validate all transactions. ## Supported Projects -BlockScout supports a number of projects. Hosted instances include POA Network, Gnosis Chain, Ethereum Classic, Sokol & Kovan testnets, and other EVM chains. - -- [List of hosted mainnets, testnets, and additional chains using BlockScout](https://docs.blockscout.com/for-projects/supported-projects) -- [Hosted instance versions](https://docs.blockscout.com/about/use-cases/hosted-blockscout) +BlockScout currently supports several hundred chains and rollups throughout the greater blockchain ecosystem. Ethereum, Cosmos, Polkadot, Avalanche, Near and many others include Blockscout integrations. [A comprehensive list is available here](https://docs.blockscout.com/about/projects). If your project is not listed, please submit a PR or [contact the team in Discord](https://discord.com/invite/blockscout). ## Getting Started diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index a521866da3aa..421a0861ea94 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -7,17 +7,17 @@ "name": "blockscout", "license": "GPL-3.0", "dependencies": { - "@amplitude/analytics-browser": "^2.1.2", - "@fortawesome/fontawesome-free": "^6.4.0", + "@amplitude/analytics-browser": "^2.2.0", + "@fortawesome/fontawesome-free": "^6.4.2", "@tarekraafat/autocomplete.js": "^10.2.7", "@walletconnect/web3-provider": "^1.8.0", "assert": "^2.0.0", "bignumber.js": "^9.1.1", "bootstrap": "^4.6.0", - "chart.js": "^4.3.2", + "chart.js": "^4.3.3", "chartjs-adapter-luxon": "^1.3.1", "clipboard": "^2.0.11", - "core-js": "^3.31.1", + "core-js": "^3.32.1", "crypto-browserify": "^3.12.0", "dropzone": "^5.9.3", "eth-net-props": "^1.0.41", @@ -44,7 +44,7 @@ "lodash.omit": "^4.5.0", "lodash.rangeright": "^4.2.0", "lodash.reduce": "^4.6.0", - "luxon": "^3.3.0", + "luxon": "^3.4.1", "malihu-custom-scrollbar-plugin": "3.1.5", "mixpanel-browser": "^2.47.0", "moment": "^2.29.4", @@ -61,7 +61,7 @@ "redux": "^4.2.1", "stream-browserify": "^3.0.0", "stream-http": "^3.1.1", - "sweetalert2": "^11.7.20", + "sweetalert2": "^11.7.27", "urijs": "^1.19.11", "url": "^0.11.1", "util": "^0.12.5", @@ -71,25 +71,25 @@ "xss": "^1.0.14" }, "devDependencies": { - "@babel/core": "^7.22.9", - "@babel/preset-env": "^7.22.9", - "autoprefixer": "^10.4.14", + "@babel/core": "^7.22.10", + "@babel/preset-env": "^7.22.10", + "autoprefixer": "^10.4.15", "babel-loader": "^9.1.3", "copy-webpack-plugin": "^11.0.0", "css-loader": "^5.2.7", "css-minimizer-webpack-plugin": "^5.0.1", - "eslint": "^8.45.0", + "eslint": "^8.47.0", "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.27.5", + "eslint-plugin-import": "^2.28.1", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^6.1.1", "file-loader": "^6.2.0", - "jest": "^29.6.1", - "jest-environment-jsdom": "^29.6.1", + "jest": "^29.6.3", + "jest-environment-jsdom": "^29.6.3", "mini-css-extract-plugin": "^2.7.6", - "postcss": "^8.4.26", + "postcss": "^8.4.28", "postcss-loader": "^7.3.3", - "sass": "^1.64.1", + "sass": "^1.66.1", "sass-loader": "^13.3.2", "style-loader": "^3.3.3", "webpack": "^5.88.2", @@ -116,15 +116,15 @@ } }, "node_modules/@amplitude/analytics-browser": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.1.2.tgz", - "integrity": "sha512-1XoWJmLgGrszxupz61yCjen53bq7+kJN2gedu+kSrq/Ze9ow5R5QI0YfLyuzi1FHchzmSVWd2PheXOVYC4K/Cw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.2.0.tgz", + "integrity": "sha512-gCRnvXT5FDmaXbrmDVdVLbm7ubYgpQX2dRnA3R8uPfqw3YGXszueVxTPKYuMf0fdQ3+1N9RcPmyEycpywN9vKg==", "dependencies": { "@amplitude/analytics-client-common": "^2.0.4", "@amplitude/analytics-core": "^2.0.3", "@amplitude/analytics-types": "^2.1.1", - "@amplitude/plugin-page-view-tracking-browser": "^2.0.4", - "@amplitude/plugin-web-attribution-browser": "^2.0.4", + "@amplitude/plugin-page-view-tracking-browser": "^2.0.6", + "@amplitude/plugin-web-attribution-browser": "^2.0.6", "tslib": "^2.4.1" } }, @@ -145,9 +145,9 @@ } }, "node_modules/@amplitude/analytics-client-common/node_modules/tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" }, "node_modules/@amplitude/analytics-connector": { "version": "1.4.8", @@ -164,9 +164,9 @@ } }, "node_modules/@amplitude/analytics-core/node_modules/tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" }, "node_modules/@amplitude/analytics-types": { "version": "2.1.1", @@ -174,9 +174,9 @@ "integrity": "sha512-H3vebPR9onRdp0WzAZmI/4qmAE903uLOd2ZfMeHsVc1zaFTTCk46SoCuV4IrlF+VILrDw9Fy6gC9yl5N2PZcJQ==" }, "node_modules/@amplitude/plugin-page-view-tracking-browser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.0.4.tgz", - "integrity": "sha512-XQlgydCmfUb+mkXjD8NoOU80eO/578m2AupRcV0dPKwk+VN5D7D1s+xXYM6Bg7l1HSJx2mt0Qwa5xsdSCFzF2g==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.0.6.tgz", + "integrity": "sha512-V8ef24E+KYINQf1o9MQnkmhFmQHGD5lb6mlTH8jGD2PdUe/KDXdO4S4rpBewxcrAfUH+iEqEanbVnTWpg6iPnw==", "dependencies": { "@amplitude/analytics-client-common": "^2.0.4", "@amplitude/analytics-types": "^2.1.1", @@ -184,14 +184,14 @@ } }, "node_modules/@amplitude/plugin-page-view-tracking-browser/node_modules/tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" }, "node_modules/@amplitude/plugin-web-attribution-browser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-attribution-browser/-/plugin-web-attribution-browser-2.0.4.tgz", - "integrity": "sha512-y/VQjDH7tnevRlKO9IWzELF9L87MwiR+y7WCRxR5/wRGKDxKS5Ol33vHYWe1RaOzkC/7wO0px7pZGRZkj/X+1Q==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-attribution-browser/-/plugin-web-attribution-browser-2.0.6.tgz", + "integrity": "sha512-+WQuhHJn7ZsYaTMe6iPE081b6y2oe9m+W/eZW+Lyf99Vt3rntDVYZIgZVwwds0s2SsUBcSvYFu1JgS65NfxWNQ==", "dependencies": { "@amplitude/analytics-client-common": "^2.0.4", "@amplitude/analytics-core": "^2.0.3", @@ -200,9 +200,9 @@ } }, "node_modules/@amplitude/plugin-web-attribution-browser/node_modules/tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" }, "node_modules/@ampproject/remapping": { "version": "2.2.0", @@ -229,11 +229,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", + "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", "dependencies": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.22.10", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" @@ -248,20 +249,20 @@ } }, "node_modules/@babel/core": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", - "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.10.tgz", + "integrity": "sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", + "@babel/helper-compilation-targets": "^7.22.10", "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", + "@babel/helpers": "^7.22.10", + "@babel/parser": "^7.22.10", "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", + "@babel/traverse": "^7.22.10", + "@babel/types": "^7.22.10", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -277,11 +278,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", - "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", + "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", "dependencies": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.22.10", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -314,9 +315,9 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", - "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", + "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", "dependencies": { "@babel/compat-data": "^7.22.9", "@babel/helper-validator-option": "^7.22.5", @@ -326,9 +327,6 @@ }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { @@ -493,15 +491,14 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz", - "integrity": "sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz", + "integrity": "sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-wrap-function": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-wrap-function": "^7.22.9" }, "engines": { "node": ">=6.9.0" @@ -586,40 +583,39 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz", - "integrity": "sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.10.tgz", + "integrity": "sha512-OnMhjWjuGYtdoO3FmsEFWvBStBAe2QOgwOLsLNDjN+aaiMD8InJk1/O3HSD8lkqTjCgg5YI34Tz15KNNA3p+nQ==", "dev": true, "dependencies": { "@babel/helper-function-name": "^7.22.5", "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/types": "^7.22.10" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", - "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.10.tgz", + "integrity": "sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==", "dependencies": { "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.6", - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.22.10", + "@babel/types": "^7.22.10" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", + "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", "dependencies": { "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -627,9 +623,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", - "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", + "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", "bin": { "parser": "bin/babel-parser.js" }, @@ -681,22 +677,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -990,14 +970,14 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz", - "integrity": "sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.10.tgz", + "integrity": "sha512-eueE8lvKVzq5wIObKK/7dvoeKJ+xc6TvRn6aysIjS6pSCeLy7S/eVi7pEQknZqyqvzaNKdDtem8nUNTBgDVR2g==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.9", "@babel/plugin-syntax-async-generators": "^7.8.4" }, "engines": { @@ -1040,9 +1020,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz", - "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.10.tgz", + "integrity": "sha512-1+kVpGAOOI1Albt6Vse7c8pHzcZQdQKW+wJH+g8mCaszOdDVwRXa/slHPqIw+oJAJANTKDMuM2cBdV0Dg618Vg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1127,9 +1107,9 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz", - "integrity": "sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.10.tgz", + "integrity": "sha512-dPJrL0VOyxqLM9sritNbMSGx/teueHF/htMKrPT7DNxccXxRDPYqlgPFFdr8u+F+qUZOkZoXue/6rL5O5GduEw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1496,9 +1476,9 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz", - "integrity": "sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.10.tgz", + "integrity": "sha512-MMkQqZAZ+MGj+jGTG3OTuhKeBpNcO+0oCEbrGNEaOmiEn+1MzRyQlYsruGiU8RTK3zV6XwrVJTmwiDOyYK6J9g==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1577,13 +1557,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz", - "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz", + "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.1" + "regenerator-transform": "^0.15.2" }, "engines": { "node": ">=6.9.0" @@ -1715,9 +1695,9 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz", - "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz", + "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1778,13 +1758,13 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.9.tgz", - "integrity": "sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.10.tgz", + "integrity": "sha512-riHpLb1drNkpLlocmSyEg4oYJIQFeXAK/d7rI6mbD0XsvoTOOweXDmQPG/ErxsEhWk3rl3Q/3F6RFQlVFS8m0A==", "dev": true, "dependencies": { "@babel/compat-data": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.10", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.22.5", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", @@ -1809,15 +1789,15 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.22.7", + "@babel/plugin-transform-async-generator-functions": "^7.22.10", "@babel/plugin-transform-async-to-generator": "^7.22.5", "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.22.10", "@babel/plugin-transform-class-properties": "^7.22.5", "@babel/plugin-transform-class-static-block": "^7.22.5", "@babel/plugin-transform-classes": "^7.22.6", "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.10", "@babel/plugin-transform-dotall-regex": "^7.22.5", "@babel/plugin-transform-duplicate-keys": "^7.22.5", "@babel/plugin-transform-dynamic-import": "^7.22.5", @@ -1840,27 +1820,27 @@ "@babel/plugin-transform-object-rest-spread": "^7.22.5", "@babel/plugin-transform-object-super": "^7.22.5", "@babel/plugin-transform-optional-catch-binding": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.6", + "@babel/plugin-transform-optional-chaining": "^7.22.10", "@babel/plugin-transform-parameters": "^7.22.5", "@babel/plugin-transform-private-methods": "^7.22.5", "@babel/plugin-transform-private-property-in-object": "^7.22.5", "@babel/plugin-transform-property-literals": "^7.22.5", - "@babel/plugin-transform-regenerator": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.10", "@babel/plugin-transform-reserved-words": "^7.22.5", "@babel/plugin-transform-shorthand-properties": "^7.22.5", "@babel/plugin-transform-spread": "^7.22.5", "@babel/plugin-transform-sticky-regex": "^7.22.5", "@babel/plugin-transform-template-literals": "^7.22.5", "@babel/plugin-transform-typeof-symbol": "^7.22.5", - "@babel/plugin-transform-unicode-escapes": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.10", "@babel/plugin-transform-unicode-property-regex": "^7.22.5", "@babel/plugin-transform-unicode-regex": "^7.22.5", "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.4", - "babel-plugin-polyfill-corejs3": "^0.8.2", - "babel-plugin-polyfill-regenerator": "^0.5.1", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "@babel/types": "^7.22.10", + "babel-plugin-polyfill-corejs2": "^0.4.5", + "babel-plugin-polyfill-corejs3": "^0.8.3", + "babel-plugin-polyfill-regenerator": "^0.5.2", "core-js-compat": "^3.31.0", "semver": "^6.3.1" }, @@ -1872,9 +1852,9 @@ } }, "node_modules/@babel/preset-env/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.1.tgz", - "integrity": "sha512-kX4oXixDxG197yhX+J3Wp+NpL2wuCFjWQAr6yX2jtCnflK9ulMI51ULFGIrWiX1jGfvAxdHp+XQCcP2bZGPs9A==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", + "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", @@ -1884,49 +1864,47 @@ "resolve": "^1.14.2" }, "peerDependencies": { - "@babel/core": "^7.4.0-0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.4.tgz", - "integrity": "sha512-9WeK9snM1BfxB38goUEv2FLnA6ja07UMfazFHzCXUb3NyDZAwfXvQiURQ6guTTMeHcOsdknULm1PDhs4uWtKyA==", + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", + "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", "dev": true, "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.1", - "@nicolo-ribaudo/semver-v6": "^6.3.3" + "@babel/helper-define-polyfill-provider": "^0.4.2", + "semver": "^6.3.1" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.1.tgz", - "integrity": "sha512-L8OyySuI6OSQ5hFy9O+7zFjyr4WhAfRjLIOkhQGYl+emwJkd/S4XXT1JpfrgR1jrQ1NcGiOh+yAdGlF8pnC3Jw==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", + "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.1" + "@babel/helper-define-polyfill-provider": "^0.4.2" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/regjsgen": { @@ -1960,18 +1938,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.22.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", - "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz", + "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==", "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.7", + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-function-name": "^7.22.5", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/types": "^7.22.5", + "@babel/parser": "^7.22.10", + "@babel/types": "^7.22.10", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1980,9 +1958,9 @@ } }, "node_modules/@babel/types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", - "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", + "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", "dependencies": { "@babel/helper-string-parser": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.5", @@ -2046,18 +2024,18 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz", - "integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -2084,9 +2062,9 @@ "dev": true }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -2123,9 +2101,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", + "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2524,9 +2502,9 @@ } }, "node_modules/@fortawesome/fontawesome-free": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz", - "integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.2.tgz", + "integrity": "sha512-m5cPn3e2+FDCOgi1mz0RexTUvvQibBebOUlUlW0+YrMjDTPkiJ6VTKukA1GRsvRw+12KyJndNjj0O4AgTxm2Pg==", "hasInstallScript": true, "engines": { "node": ">=6" @@ -2600,16 +2578,16 @@ } }, "node_modules/@jest/console": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.1.tgz", - "integrity": "sha512-Aj772AYgwTSr5w8qnyoJ0eDYvN6bMsH3ORH1ivMotrInHLKdUz6BDlaEXHdM6kODaBIkNIyQGzsMvRdOv7VG7Q==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.3.tgz", + "integrity": "sha512-ukZbHAdDH4ktZIOKvWs1juAXhiVAdvCyM8zv4S/7Ii3vJSDvMW5k+wOVGMQmHLHUFw3Ko63ZQNy7NI6PSlsD5w==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", "slash": "^3.0.0" }, "engines": { @@ -2687,37 +2665,37 @@ } }, "node_modules/@jest/core": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.1.tgz", - "integrity": "sha512-CcowHypRSm5oYQ1obz1wfvkjZZ2qoQlrKKvlfPwh5jUXVU12TWr2qMeH8chLMuTFzHh5a1g2yaqlqDICbr+ukQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.3.tgz", + "integrity": "sha512-skV1XrfNxfagmjRUrk2FyN5/2YwIzdWVVBa/orUfbLvQUANXxERq2pTvY0I+FinWHjDKB2HRmpveUiph4X0TJw==", "dev": true, "dependencies": { - "@jest/console": "^29.6.1", - "@jest/reporters": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.3", + "@jest/reporters": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/transform": "^29.6.3", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.6.1", - "jest-haste-map": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.1", - "jest-resolve-dependencies": "^29.6.1", - "jest-runner": "^29.6.1", - "jest-runtime": "^29.6.1", - "jest-snapshot": "^29.6.1", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", - "jest-watcher": "^29.6.1", + "jest-changed-files": "^29.6.3", + "jest-config": "^29.6.3", + "jest-haste-map": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.3", + "jest-resolve-dependencies": "^29.6.3", + "jest-runner": "^29.6.3", + "jest-runtime": "^29.6.3", + "jest-snapshot": "^29.6.3", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "jest-watcher": "^29.6.3", "micromatch": "^4.0.4", - "pretty-format": "^29.6.1", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -2804,88 +2782,88 @@ } }, "node_modules/@jest/environment": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.1.tgz", - "integrity": "sha512-RMMXx4ws+Gbvw3DfLSuo2cfQlK7IwGbpuEWXCqyYDcqYTI+9Ju3a5hDnXaxjNsa6uKh9PQF2v+qg+RLe63tz5A==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.3.tgz", + "integrity": "sha512-u/u3cCztYCfgBiGHsamqP5x+XvucftOGPbf5RJQxfpeC1y4AL8pCjKvPDA3oCmdhZYPgk5AE0VOD/flweR69WA==", "dev": true, "dependencies": { - "@jest/fake-timers": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/fake-timers": "^29.6.3", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.1" + "jest-mock": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.1.tgz", - "integrity": "sha512-N5xlPrAYaRNyFgVf2s9Uyyvr795jnB6rObuPx4QFvNJz8aAjpZUDfO4bh5G/xuplMID8PrnuF1+SfSyDxhsgYg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.3.tgz", + "integrity": "sha512-Ic08XbI2jlg6rECy+CGwk/8NDa6VE7UmIG6++9OTPAMnQmNGY28hu69Nf629CWv6T7YMODLbONxDFKdmQeI9FA==", "dev": true, "dependencies": { - "expect": "^29.6.1", - "jest-snapshot": "^29.6.1" + "expect": "^29.6.3", + "jest-snapshot": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.1.tgz", - "integrity": "sha512-o319vIf5pEMx0LmzSxxkYYxo4wrRLKHq9dP1yJU7FoPTB0LfAKSz8SWD6D/6U3v/O52t9cF5t+MeJiRsfk7zMw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.3.tgz", + "integrity": "sha512-nvOEW4YoqRKD9HBJ9OJ6przvIvP9qilp5nAn1462P5ZlL/MM9SgPEZFyjTGPfs7QkocdUsJa6KjHhyRn4ueItA==", "dev": true, "dependencies": { - "jest-get-type": "^29.4.3" + "jest-get-type": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/fake-timers": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.1.tgz", - "integrity": "sha512-RdgHgbXyosCDMVYmj7lLpUwXA4c69vcNzhrt69dJJdf8azUrpRh3ckFCaTPNjsEeRi27Cig0oKDGxy5j7hOgHg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.3.tgz", + "integrity": "sha512-pa1wmqvbj6eX0nMvOM2VDAWvJOI5A/Mk3l8O7n7EsAh71sMZblaKO9iT4GjIj0LwwK3CP/Jp1ypEV0x3m89RvA==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.6.1", - "jest-mock": "^29.6.1", - "jest-util": "^29.6.1" + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.1.tgz", - "integrity": "sha512-2VjpaGy78JY9n9370H8zGRCFbYVWwjY6RdDMhoJHa1sYfwe6XM/azGN0SjY8kk7BOZApIejQ1BFPyH7FPG0w3A==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.3.tgz", + "integrity": "sha512-RB+uI+CZMHntzlnOPlll5x/jgRff3LEPl/td/jzMXiIgR0iIhKq9qm1HLU+EC52NuoVy/1swit/sDGjVn4bc6A==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.1", - "@jest/expect": "^29.6.1", - "@jest/types": "^29.6.1", - "jest-mock": "^29.6.1" + "@jest/environment": "^29.6.3", + "@jest/expect": "^29.6.3", + "@jest/types": "^29.6.3", + "jest-mock": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.1.tgz", - "integrity": "sha512-9zuaI9QKr9JnoZtFQlw4GREQbxgmNYXU6QuWtmuODvk5nvPUeBYapVR/VYMyi2WSx3jXTLJTJji8rN6+Cm4+FA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.3.tgz", + "integrity": "sha512-kGz59zMi0GkVjD2CJeYWG9k6cvj7eBqt9aDAqo2rcCLRTYlvQ62Gu/n+tOmJMBHGjzeijjuCENjzTyYBgrtLUw==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/transform": "^29.6.3", + "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", @@ -2894,13 +2872,13 @@ "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1", - "jest-worker": "^29.6.1", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.3", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -2977,13 +2955,13 @@ } }, "node_modules/@jest/reporters/node_modules/jest-worker": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.1.tgz", - "integrity": "sha512-U+Wrbca7S8ZAxAe9L6nb6g8kPdia5hj32Puu5iOqBCMTMWFHXuK6dOV2IFrpedbTV8fjMFLdWNttQTBL6u2MRA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.3.tgz", + "integrity": "sha512-wacANXecZ/GbQakpf2CClrqrlwsYYDSXFd4fIGdL+dXpM2GWoJ+6bhQ7vR3TKi3+gkSfBkjy1/khH/WrYS4Q6g==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.6.1", + "jest-util": "^29.6.3", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -3019,9 +2997,9 @@ } }, "node_modules/@jest/schemas": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz", - "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "dependencies": { "@sinclair/typebox": "^0.27.8" @@ -3031,9 +3009,9 @@ } }, "node_modules/@jest/source-map": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.0.tgz", - "integrity": "sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", @@ -3045,13 +3023,13 @@ } }, "node_modules/@jest/test-result": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.1.tgz", - "integrity": "sha512-Ynr13ZRcpX6INak0TPUukU8GWRfm/vAytE3JbJNGAvINySWYdfE7dGZMbk36oVuK4CigpbhMn8eg1dixZ7ZJOw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.3.tgz", + "integrity": "sha512-k7ZZaNvOSMBHPZYiy0kuiaFoyansR5QnTwDux1EjK3kD5iWpRVyJIJ0RAIV39SThafchuW59vra7F8mdy5Hfgw==", "dev": true, "dependencies": { - "@jest/console": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.3", + "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" }, @@ -3060,14 +3038,14 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.1.tgz", - "integrity": "sha512-oBkC36PCDf/wb6dWeQIhaviU0l5u6VCsXa119yqdUosYAt7/FbQU2M2UoziO3igj/HBDEgp57ONQ3fm0v9uyyg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.3.tgz", + "integrity": "sha512-/SmijaAU2TY9ComFGIYa6Z+fmKqQMnqs2Nmwb0P/Z/tROdZ7M0iruES1EaaU9PBf8o9uED5xzaJ3YPFEIcDgAg==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.1", + "@jest/test-result": "^29.6.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", + "jest-haste-map": "^29.6.3", "slash": "^3.0.0" }, "engines": { @@ -3075,22 +3053,22 @@ } }, "node_modules/@jest/transform": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.1.tgz", - "integrity": "sha512-URnTneIU3ZjRSaf906cvf6Hpox3hIeJXRnz3VDSw5/X93gR8ycdfSIEy19FlVx8NFmpN7fe3Gb1xF+NjXaQLWg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.3.tgz", + "integrity": "sha512-dPIc3DsvMZ/S8ut4L2ViCj265mKO0owB0wfzBv2oGzL9pQ+iRvJewHqLBmsGb7XFb5UotWIEtvY5A/lnylaIoQ==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.1", + "jest-haste-map": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -3177,12 +3155,12 @@ } }, "node_modules/@jest/types": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.1.tgz", - "integrity": "sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.0", + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -3326,15 +3304,6 @@ "resolved": "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz", "integrity": "sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==" }, - "node_modules/@nicolo-ribaudo/semver-v6": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz", - "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3612,12 +3581,6 @@ "@types/node": "*" } }, - "node_modules/@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true - }, "node_modules/@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -4337,6 +4300,19 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -4361,6 +4337,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.2.tgz", + "integrity": "sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array.prototype.flat": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", @@ -4397,6 +4392,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", + "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -4488,9 +4503,9 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "node_modules/autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "version": "10.4.15", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.15.tgz", + "integrity": "sha512-KCuPB8ZCIqFdA4HwKXsvz7j6gvSDNhDP7WnUjBleRkKjPdvCmHFuQ77ocavI8FT6NdvlBnE2UFr2H4Mycn8Vew==", "dev": true, "funding": [ { @@ -4500,11 +4515,15 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001520", "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -4545,15 +4564,15 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "node_modules/babel-jest": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.1.tgz", - "integrity": "sha512-qu+3bdPEQC6KZSPz+4Fyjbga5OODNcp49j6GKzG1EKbkfyJBxEYGVUmVGpwCSeGouG52R4EgYMLb6p9YeEEQ4A==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.3.tgz", + "integrity": "sha512-1Ne93zZZEy5XmTa4Q+W5+zxBrDpExX8E3iy+xJJ+24ewlfo/T3qHfQJCzi/MMVFmBQDNxtRR/Gfd2dwb/0yrQw==", "dev": true, "dependencies": { - "@jest/transform": "^29.6.1", + "@jest/transform": "^29.6.3", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", + "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" @@ -4668,10 +4687,26 @@ "node": ">=8" } }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/babel-plugin-jest-hoist": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", - "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "dependencies": { "@babel/template": "^7.3.3", @@ -4697,22 +4732,22 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.2.tgz", - "integrity": "sha512-Cid+Jv1BrY9ReW9lIfNlNpsI53N+FN7gE+f73zLAUbr9C52W4gKLWSByx47pfDJsEysojKArqOtOKZSVIIUTuQ==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz", + "integrity": "sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.1", + "@babel/helper-define-polyfill-provider": "^0.4.2", "core-js-compat": "^3.31.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-corejs3/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.1.tgz", - "integrity": "sha512-kX4oXixDxG197yhX+J3Wp+NpL2wuCFjWQAr6yX2jtCnflK9ulMI51ULFGIrWiX1jGfvAxdHp+XQCcP2bZGPs9A==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", + "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", @@ -4722,7 +4757,7 @@ "resolve": "^1.14.2" }, "peerDependencies": { - "@babel/core": "^7.4.0-0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { @@ -4779,12 +4814,12 @@ } }, "node_modules/babel-preset-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", - "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "dependencies": { - "babel-plugin-jest-hoist": "^29.5.0", + "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "engines": { @@ -5068,9 +5103,9 @@ ] }, "node_modules/browserslist": { - "version": "4.21.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", - "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", "funding": [ { "type": "opencollective", @@ -5086,9 +5121,9 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001503", - "electron-to-chromium": "^1.4.431", - "node-releases": "^2.0.12", + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", "update-browserslist-db": "^1.0.11" }, "bin": { @@ -5338,9 +5373,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001512", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001512.tgz", - "integrity": "sha512-2S9nK0G/mE+jasCUsMPlARhRCts1ebcp2Ji8Y8PWi4NDE1iRdLCnEPHkEfeBrGC45L4isBx5ur3IQ6yTE2mRZw==", + "version": "1.0.30001520", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001520.tgz", + "integrity": "sha512-tahF5O9EiiTzwTUqAeFjIZbn4Dnqxzz7ktrgGlMYNLH43Ul26IgTMH/zvL3DG0lZxBYnlT04axvInszUsZULdA==", "funding": [ { "type": "opencollective", @@ -5400,9 +5435,9 @@ } }, "node_modules/chart.js": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.2.tgz", - "integrity": "sha512-pvQNyFOY1QmbmIr8oDORL16/FFivfxj8V26VFpFilMo4cNvkV5WXLJetDio365pd9gKUHGdirUTbqJfw8tr+Dg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.3.tgz", + "integrity": "sha512-aTk7pBw+x6sQYhon/NR3ikfUJuym/LdgpTlgZRe2PaEhjUMKBKyNaFCMVRAyTEWYFNO7qRu7iQVqOw/OqzxZxQ==", "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -5778,9 +5813,9 @@ } }, "node_modules/core-js": { - "version": "3.31.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.31.1.tgz", - "integrity": "sha512-2sKLtfq1eFST7l7v62zaqXacPc7uG8ZAya8ogijLhTtaKNcpzpB4TMoTw2Si+8GYKRwFPMMtUT0263QFWFfqyQ==", + "version": "3.32.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.1.tgz", + "integrity": "sha512-lqufgNn9NLnESg5mQeYsxQP5w7wrViSj0jr/kv6ECQiByzQkrn1MKvV0L3acttpDqfQrHLwr2KCMgX5b8X+lyQ==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -6408,10 +6443,18 @@ } }, "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } }, "node_modules/deep-eql": { "version": "3.0.1", @@ -6456,9 +6499,9 @@ } }, "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", "dependencies": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" @@ -6524,9 +6567,9 @@ } }, "node_modules/diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -6668,9 +6711,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.450", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.450.tgz", - "integrity": "sha512-BLG5HxSELlrMx7dJ2s+8SFlsCtJp37Zpk2VAxyC6CZtbc+9AJeZHfYHbrlSgdXp6saQ8StMqOTEDaBKgA7u1sw==" + "version": "1.4.490", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.490.tgz", + "integrity": "sha512-6s7NVJz+sATdYnIwhdshx/N/9O6rvMxmhVoDSDFdj6iA45gHR8EQje70+RYsF4GeB+k0IeNSBnP7yG9ZXJFr7A==" }, "node_modules/elliptic": { "version": "6.5.4", @@ -6815,18 +6858,19 @@ } }, "node_modules/es-abstract": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", - "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", + "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", "dev": true, "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.1", "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.1", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", @@ -6834,8 +6878,8 @@ "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.4", - "is-array-buffer": "^3.0.1", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", @@ -6843,16 +6887,21 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.10", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", + "object-inspect": "^1.12.3", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", + "regexp.prototype.flags": "^1.5.0", + "safe-array-concat": "^1.0.0", "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", "string.prototype.trimend": "^1.0.6", "string.prototype.trimstart": "^1.0.6", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" + "which-typed-array": "^1.1.10" }, "engines": { "node": ">= 0.4" @@ -7054,27 +7103,27 @@ } }, "node_modules/eslint": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz", - "integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", + "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "^8.47.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -7157,9 +7206,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", "dev": true, "dependencies": { "debug": "^3.2.7" @@ -7203,26 +7252,28 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz", + "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==", "dev": true, "dependencies": { "array-includes": "^3.1.6", + "array.prototype.findlastindex": "^1.2.2", "array.prototype.flat": "^1.3.1", "array.prototype.flatmap": "^1.3.1", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", + "eslint-module-utils": "^2.8.0", "has": "^1.0.3", - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "is-glob": "^4.0.3", "minimatch": "^3.1.2", + "object.fromentries": "^2.0.6", + "object.groupby": "^1.0.0", "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" + "semver": "^6.3.1", + "tsconfig-paths": "^3.14.2" }, "engines": { "node": ">=4" @@ -7412,9 +7463,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -7491,9 +7542,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -7631,9 +7682,9 @@ } }, "node_modules/espree": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", - "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { "acorn": "^8.9.0", @@ -8275,17 +8326,16 @@ } }, "node_modules/expect": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.1.tgz", - "integrity": "sha512-XEdDLonERCU1n9uR56/Stx9OqojaLAQtZf9PrCHH9Hl8YXiEIka3H4NXJ3NOIBmQJTg7+j7buh34PMHfJujc8g==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.3.tgz", + "integrity": "sha512-x1vY4LlEMWUYVZQrFi4ZANXFwqYbJ/JNQspLVvzhW2BNY28aNcXMQH6imBbt+RBf5sVRTodYHXtSP/TLEU0Dxw==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.6.1", - "@types/node": "*", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1" + "@jest/expect-utils": "^29.6.3", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -8834,12 +8884,13 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3" }, "funding": { @@ -9111,7 +9162,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -9462,12 +9512,12 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internal-slot": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", - "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.0", "has": "^1.0.3", "side-channel": "^1.0.4" }, @@ -9508,13 +9558,13 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", - "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.0", "is-typed-array": "^1.1.10" }, "funding": { @@ -9579,9 +9629,9 @@ } }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "dependencies": { "has": "^1.0.3" }, @@ -9897,33 +9947,48 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", + "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", "dev": true, "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "semver": "^7.5.4" }, "engines": { - "node": ">=8" + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/istanbul-lib-report/node_modules/has-flag": { @@ -9962,9 +10027,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -9975,15 +10040,15 @@ } }, "node_modules/jest": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.1.tgz", - "integrity": "sha512-Nirw5B4nn69rVUZtemCQhwxOBhm0nsp3hmtF4rzCeWD7BkjAXRIji7xWQfnTNbz9g0aVsBX6aZK3n+23LM6uDw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.3.tgz", + "integrity": "sha512-alueLuoPCDNHFcFGmgETR4KpQ+0ff3qVaiJwxQM4B5sC0CvXcgg4PEi7xrDkxuItDmdz/FVc7SSit4KEu8GRvw==", "dev": true, "dependencies": { - "@jest/core": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/core": "^29.6.3", + "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.6.1" + "jest-cli": "^29.6.3" }, "bin": { "jest": "bin/jest.js" @@ -10001,12 +10066,13 @@ } }, "node_modules/jest-changed-files": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", - "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", + "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", "dev": true, "dependencies": { "execa": "^5.0.0", + "jest-util": "^29.6.3", "p-limit": "^3.1.0" }, "engines": { @@ -10014,28 +10080,28 @@ } }, "node_modules/jest-circus": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.1.tgz", - "integrity": "sha512-tPbYLEiBU4MYAL2XoZme/bgfUeotpDBd81lgHLCbDZZFaGmECk0b+/xejPFtmiBP87GgP/y4jplcRpbH+fgCzQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.3.tgz", + "integrity": "sha512-p0R5YqZEMnOpHqHLWRSjm2z/0p6RNsrNE/GRRT3eli8QGOAozj6Ys/3Tv+Ej+IfltJoSPwcQ6/hOCRkNlxLLCw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.1", - "@jest/expect": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.3", + "@jest/expect": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", - "dedent": "^0.7.0", + "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.1", - "jest-matcher-utils": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-runtime": "^29.6.1", - "jest-snapshot": "^29.6.1", - "jest-util": "^29.6.1", + "jest-each": "^29.6.3", + "jest-matcher-utils": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-runtime": "^29.6.3", + "jest-snapshot": "^29.6.3", + "jest-util": "^29.6.3", "p-limit": "^3.1.0", - "pretty-format": "^29.6.1", + "pretty-format": "^29.6.3", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -10115,21 +10181,21 @@ } }, "node_modules/jest-cli": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.1.tgz", - "integrity": "sha512-607dSgTA4ODIN6go9w6xY3EYkyPFGicx51a69H7yfvt7lN53xNswEVLovq+E77VsTRi5fWprLH0yl4DJgE8Ing==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.3.tgz", + "integrity": "sha512-KuPdXUPXQIf0t6DvmG8MV4QyhcjR1a6ruKl3YL7aGn/AQ8JkROwFkWzEpDIpt11Qy188dHbRm8WjwMsV/4nmnQ==", "dev": true, "dependencies": { - "@jest/core": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/core": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.6.1", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", + "jest-config": "^29.6.3", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "prompts": "^2.0.1", "yargs": "^17.3.1" }, @@ -10219,31 +10285,31 @@ } }, "node_modules/jest-config": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.1.tgz", - "integrity": "sha512-XdjYV2fy2xYixUiV2Wc54t3Z4oxYPAELUzWnV6+mcbq0rh742X2p52pii5A3oeRzYjLnQxCsZmp0qpI6klE2cQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.3.tgz", + "integrity": "sha512-nb9bOq2aEqogbyL4F9mLkAeQGAgNt7Uz6U59YtQDIxFPiL7Ejgq0YIrp78oyEHD6H4CIV/k7mFrK7eFDzUJ69w==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.1", - "@jest/types": "^29.6.1", - "babel-jest": "^29.6.1", + "@jest/test-sequencer": "^29.6.3", + "@jest/types": "^29.6.3", + "babel-jest": "^29.6.3", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.1", - "jest-environment-node": "^29.6.1", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.1", - "jest-runner": "^29.6.1", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", + "jest-circus": "^29.6.3", + "jest-environment-node": "^29.6.3", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.3", + "jest-runner": "^29.6.3", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.6.1", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -10334,15 +10400,15 @@ } }, "node_modules/jest-diff": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.1.tgz", - "integrity": "sha512-FsNCvinvl8oVxpNLttNQX7FAq7vR+gMDGj90tiP7siWw1UdakWUGqrylpsYrpvj908IYckm5Y0Q7azNAozU1Kg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.3.tgz", + "integrity": "sha512-3sw+AdWnwH9sSNohMRKA7JiYUJSRr/WS6+sEFfBuhxU5V5GlEVKfvUn8JuMHE0wqKowemR1C2aHy8VtXbaV8dQ==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.1" + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -10419,9 +10485,9 @@ } }, "node_modules/jest-docblock": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", - "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", + "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", "dev": true, "dependencies": { "detect-newline": "^3.0.0" @@ -10431,16 +10497,16 @@ } }, "node_modules/jest-each": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.1.tgz", - "integrity": "sha512-n5eoj5eiTHpKQCAVcNTT7DRqeUmJ01hsAL0Q1SMiBHcBcvTKDELixQOGMCpqhbIuTcfC4kMfSnpmDqRgRJcLNQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", + "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.6.1", - "pretty-format": "^29.6.1" + "jest-get-type": "^29.6.3", + "jest-util": "^29.6.3", + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -10517,18 +10583,18 @@ } }, "node_modules/jest-environment-jsdom": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.6.1.tgz", - "integrity": "sha512-PoY+yLaHzVRhVEjcVKSfJ7wXmJW4UqPYNhR05h7u/TK0ouf6DmRNZFBL/Z00zgQMyWGMBXn69/FmOvhEJu8cIw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.6.3.tgz", + "integrity": "sha512-nMJz/i27Moit9bv8Z323/13Melj4FEQH93yRu7GnilvBmPBMH4EGEkEfBTJXYuubyzhMO7w/VHzljIDV+Q/SeQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.1", - "@jest/fake-timers": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.3", + "@jest/fake-timers": "^29.6.3", + "@jest/types": "^29.6.3", "@types/jsdom": "^20.0.0", "@types/node": "*", - "jest-mock": "^29.6.1", - "jest-util": "^29.6.1", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3", "jsdom": "^20.0.0" }, "engines": { @@ -10544,46 +10610,46 @@ } }, "node_modules/jest-environment-node": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.1.tgz", - "integrity": "sha512-ZNIfAiE+foBog24W+2caIldl4Irh8Lx1PUhg/GZ0odM1d/h2qORAsejiFc7zb+SEmYPn1yDZzEDSU5PmDkmVLQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.3.tgz", + "integrity": "sha512-PKl7upfPJXMYbWpD+60o4HP86KvFO2c9dZ+Zr6wUzsG5xcPx/65o3ArNgHW5M0RFvLYdW4/aieR4JSooD0a2ew==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.1", - "@jest/fake-timers": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.3", + "@jest/fake-timers": "^29.6.3", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.1", - "jest-util": "^29.6.1" + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-haste-map": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.1.tgz", - "integrity": "sha512-0m7f9PZXxOCk1gRACiVgX85knUKPKLPg4oRCjLoqIm9brTHXaorMA0JpmtmVkQiT8nmXyIVoZd/nnH1cfC33ig==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.3.tgz", + "integrity": "sha512-GecR5YavfjkhOytEFHAeI6aWWG3f/cOKNB1YJvj/B76xAmeVjy4zJUYobGF030cRmKaO1FBw3V8CZZ6KVh9ZSw==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.1", - "jest-worker": "^29.6.1", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.3", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -10604,13 +10670,13 @@ } }, "node_modules/jest-haste-map/node_modules/jest-worker": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.1.tgz", - "integrity": "sha512-U+Wrbca7S8ZAxAe9L6nb6g8kPdia5hj32Puu5iOqBCMTMWFHXuK6dOV2IFrpedbTV8fjMFLdWNttQTBL6u2MRA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.3.tgz", + "integrity": "sha512-wacANXecZ/GbQakpf2CClrqrlwsYYDSXFd4fIGdL+dXpM2GWoJ+6bhQ7vR3TKi3+gkSfBkjy1/khH/WrYS4Q6g==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.6.1", + "jest-util": "^29.6.3", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -10634,28 +10700,28 @@ } }, "node_modules/jest-leak-detector": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.1.tgz", - "integrity": "sha512-OrxMNyZirpOEwkF3UHnIkAiZbtkBWiye+hhBweCHkVbCgyEy71Mwbb5zgeTNYWJBi1qgDVfPC1IwO9dVEeTLwQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", + "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", "dev": true, "dependencies": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.1" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.1.tgz", - "integrity": "sha512-SLaztw9d2mfQQKHmJXKM0HCbl2PPVld/t9Xa6P9sgiExijviSp7TnZZpw2Fpt+OI3nwUO/slJbOfzfUMKKC5QA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.3.tgz", + "integrity": "sha512-6ZrMYINZdwduSt5Xu18/n49O1IgXdjsfG7NEZaQws9k69eTKWKcVbJBw/MZsjOZe2sSyJFmuzh8042XWwl54Zg==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.6.1", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.1" + "jest-diff": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -10732,18 +10798,18 @@ } }, "node_modules/jest-message-util": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.1.tgz", - "integrity": "sha512-KoAW2zAmNSd3Gk88uJ56qXUWbFk787QKmjjJVOjtGFmmGSZgDBrlIL4AfQw1xyMYPNVD7dNInfIbur9B2rd/wQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", + "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.6.1", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -10822,14 +10888,14 @@ } }, "node_modules/jest-mock": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.1.tgz", - "integrity": "sha512-brovyV9HBkjXAEdRooaTQK42n8usKoSRR3gihzUpYeV/vwqgSoNfrksO7UfSACnPmxasO/8TmHM3w9Hp3G1dgw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", + "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.6.1" + "jest-util": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -10853,26 +10919,26 @@ } }, "node_modules/jest-regex-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", - "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-resolve": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.1.tgz", - "integrity": "sha512-AeRkyS8g37UyJiP9w3mmI/VXU/q8l/IH52vj/cDAyScDcemRbSBhfX/NMYIGilQgSVwsjxrCHf3XJu4f+lxCMg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.3.tgz", + "integrity": "sha512-WMXwxhvzDeA/J+9jz1i8ZKGmbw/n+s988EiUvRI4egM+eTn31Hb5v10Re3slG3/qxntkBt2/6GkQVDGu6Bwyhw==", "dev": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", + "jest-haste-map": "^29.6.3", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -10882,13 +10948,13 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.1.tgz", - "integrity": "sha512-BbFvxLXtcldaFOhNMXmHRWx1nXQO5LoXiKSGQcA1LxxirYceZT6ch8KTE1bK3X31TNG/JbkI7OkS/ABexVahiw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.3.tgz", + "integrity": "sha512-iah5nhSPTwtUV7yzpTc9xGg8gP3Ch2VNsuFMsKoCkNCrQSbFtx5KRPemmPJ32AUhTSDqJXB6djPN6zAaUGV53g==", "dev": true, "dependencies": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.6.1" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -10965,30 +11031,30 @@ } }, "node_modules/jest-runner": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.1.tgz", - "integrity": "sha512-tw0wb2Q9yhjAQ2w8rHRDxteryyIck7gIzQE4Reu3JuOBpGp96xWgF0nY8MDdejzrLCZKDcp8JlZrBN/EtkQvPQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.3.tgz", + "integrity": "sha512-E4zsMhQnjhirFPhDTJgoLMWUrVCDij/KGzWlbslDHGuO8Hl2pVUfOiygMzVZtZq+BzmlqwEr7LYmW+WFLlmX8w==", "dev": true, "dependencies": { - "@jest/console": "^29.6.1", - "@jest/environment": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.3", + "@jest/environment": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/transform": "^29.6.3", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.6.1", - "jest-haste-map": "^29.6.1", - "jest-leak-detector": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-resolve": "^29.6.1", - "jest-runtime": "^29.6.1", - "jest-util": "^29.6.1", - "jest-watcher": "^29.6.1", - "jest-worker": "^29.6.1", + "jest-docblock": "^29.6.3", + "jest-environment-node": "^29.6.3", + "jest-haste-map": "^29.6.3", + "jest-leak-detector": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-resolve": "^29.6.3", + "jest-runtime": "^29.6.3", + "jest-util": "^29.6.3", + "jest-watcher": "^29.6.3", + "jest-worker": "^29.6.3", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -11055,13 +11121,13 @@ } }, "node_modules/jest-runner/node_modules/jest-worker": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.1.tgz", - "integrity": "sha512-U+Wrbca7S8ZAxAe9L6nb6g8kPdia5hj32Puu5iOqBCMTMWFHXuK6dOV2IFrpedbTV8fjMFLdWNttQTBL6u2MRA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.3.tgz", + "integrity": "sha512-wacANXecZ/GbQakpf2CClrqrlwsYYDSXFd4fIGdL+dXpM2GWoJ+6bhQ7vR3TKi3+gkSfBkjy1/khH/WrYS4Q6g==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.6.1", + "jest-util": "^29.6.3", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -11107,31 +11173,31 @@ } }, "node_modules/jest-runtime": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.1.tgz", - "integrity": "sha512-D6/AYOA+Lhs5e5il8+5pSLemjtJezUr+8zx+Sn8xlmOux3XOqx4d8l/2udBea8CRPqqrzhsKUsN/gBDE/IcaPQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.6.1", - "@jest/fake-timers": "^29.6.1", - "@jest/globals": "^29.6.1", - "@jest/source-map": "^29.6.0", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.3.tgz", + "integrity": "sha512-VM0Z3a9xaqizGpEKwCOIhImkrINYzxgwk8oQAvrmAiXX8LNrJrRjyva30RkuRY0ETAotHLlUcd2moviCA1hgsQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.3", + "@jest/fake-timers": "^29.6.3", + "@jest/globals": "^29.6.3", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/transform": "^29.6.3", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-mock": "^29.6.1", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.1", - "jest-snapshot": "^29.6.1", - "jest-util": "^29.6.1", + "jest-haste-map": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.3", + "jest-snapshot": "^29.6.3", + "jest-util": "^29.6.3", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -11210,9 +11276,9 @@ } }, "node_modules/jest-snapshot": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.1.tgz", - "integrity": "sha512-G4UQE1QQ6OaCgfY+A0uR1W2AY0tGXUPQpoUClhWHq1Xdnx1H6JOrC2nH5lqnOEqaDgbHFgIwZ7bNq24HpB180A==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.3.tgz", + "integrity": "sha512-66Iu7H1ojiveQMGFnKecHIZPPPBjZwfQEnF6wxqpxGf57sV3YSUtAb5/sTKM5TPa3OndyxZp1wxHFbmgVhc53w==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -11220,21 +11286,20 @@ "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/prettier": "^2.1.5", + "@jest/expect-utils": "^29.6.3", + "@jest/transform": "^29.6.3", + "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.6.1", + "expect": "^29.6.3", "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.1", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1", + "jest-diff": "^29.6.3", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", "natural-compare": "^1.4.0", - "pretty-format": "^29.6.1", + "pretty-format": "^29.6.3", "semver": "^7.5.3" }, "engines": { @@ -11300,9 +11365,9 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -11327,12 +11392,12 @@ } }, "node_modules/jest-util": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.1.tgz", - "integrity": "sha512-NRFCcjc+/uO3ijUVyNOQJluf8PtGCe/W6cix36+M3cTFgiYqFOOW5MgN4JOOcvbUhcKTYVd1CvHz/LWi8d16Mg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", + "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -11414,17 +11479,17 @@ } }, "node_modules/jest-validate": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.1.tgz", - "integrity": "sha512-r3Ds69/0KCN4vx4sYAbGL1EVpZ7MSS0vLmd3gV78O+NAx3PDQQukRU5hNHPXlyqCgFY8XUk7EuTMLugh0KzahA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", + "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", + "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^29.6.1" + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -11513,18 +11578,18 @@ } }, "node_modules/jest-watcher": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.1.tgz", - "integrity": "sha512-d4wpjWTS7HEZPaaj8m36QiaP856JthRZkrgcIY/7ISoUWPIillrXM23WPboZVLbiwZBt4/qn2Jke84Sla6JhFA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.3.tgz", + "integrity": "sha512-NgpFjZ2U2MKusjidbi4Oiu7tfs+nrgdIxIEVROvH1cFmOei9Uj25lwkMsakqLnH/s0nEcvxO1ck77FiRlcnpZg==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/test-result": "^29.6.3", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.6.1", + "jest-util": "^29.6.3", "string-length": "^4.0.1" }, "engines": { @@ -12271,28 +12336,43 @@ "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=" }, "node_modules/luxon": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", - "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.1.tgz", + "integrity": "sha512-2USspxOCXWGIKHwuQ9XElxPPYrDOJHDQ5DQ870CoD+CxJbBnRDIBCfhioUJJjct7BKOy80Ia8cVstIcIMb/0+Q==", "engines": { "node": ">=12" } }, "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "dependencies": { - "semver": "^6.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -12806,9 +12886,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", - "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==" + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -12960,6 +13040,35 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.fromentries": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.0.tgz", + "integrity": "sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.21.2", + "get-intrinsic": "^1.2.1" + } + }, "node_modules/object.values": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", @@ -13419,9 +13528,9 @@ } }, "node_modules/postcss": { - "version": "8.4.26", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.26.tgz", - "integrity": "sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==", + "version": "8.4.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz", + "integrity": "sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==", "dev": true, "funding": [ { @@ -13994,12 +14103,12 @@ } }, "node_modules/pretty-format": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.1.tgz", - "integrity": "sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", + "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.0", + "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -14516,23 +14625,23 @@ "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, "node_modules/regenerator-transform": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", - "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dev": true, "dependencies": { "@babel/runtime": "^7.8.4" } }, "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" @@ -14663,11 +14772,11 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.3.tgz", + "integrity": "sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==", "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.12.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -14814,6 +14923,30 @@ "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" }, + "node_modules/safe-array-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", + "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -14848,9 +14981,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sass": { - "version": "1.64.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.64.1.tgz", - "integrity": "sha512-16rRACSOFEE8VN7SCgBu1MpYCyN7urj9At898tyzdXFhC+a+yOX5dXwAR7L8/IdPJ1NB8OYoXmD55DM30B2kEQ==", + "version": "1.66.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.66.1.tgz", + "integrity": "sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -15423,6 +15556,23 @@ "node": ">=8" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/string.prototype.trimend": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", @@ -15702,9 +15852,9 @@ } }, "node_modules/sweetalert2": { - "version": "11.7.20", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.20.tgz", - "integrity": "sha512-GdU1TkiLpGGC0mcPV8bKmS7G0MR7caxambPkEU8zyepRSNR9EaEvIjNhX5QNkL0VFVzHbI3l12NtuEklkJ0D4Q==", + "version": "11.7.27", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.27.tgz", + "integrity": "sha512-QbRXGQn1sb7HEhzA/K2xtWIwQHh/qkSbb1w6jYcTql2xy17876lTREEt1D4X6Q0x2wHtfUjKJ+Cb8IVkRoq7DQ==", "funding": { "type": "individual", "url": "https://github.com/sponsors/limonte" @@ -15962,13 +16112,13 @@ } }, "node_modules/tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", - "json5": "^1.0.1", + "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } @@ -16064,6 +16214,57 @@ "node": ">= 0.6" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typed-array-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", @@ -17122,16 +17323,15 @@ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" }, "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -17404,15 +17604,15 @@ "dev": true }, "@amplitude/analytics-browser": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.1.2.tgz", - "integrity": "sha512-1XoWJmLgGrszxupz61yCjen53bq7+kJN2gedu+kSrq/Ze9ow5R5QI0YfLyuzi1FHchzmSVWd2PheXOVYC4K/Cw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.2.0.tgz", + "integrity": "sha512-gCRnvXT5FDmaXbrmDVdVLbm7ubYgpQX2dRnA3R8uPfqw3YGXszueVxTPKYuMf0fdQ3+1N9RcPmyEycpywN9vKg==", "requires": { "@amplitude/analytics-client-common": "^2.0.4", "@amplitude/analytics-core": "^2.0.3", "@amplitude/analytics-types": "^2.1.1", - "@amplitude/plugin-page-view-tracking-browser": "^2.0.4", - "@amplitude/plugin-web-attribution-browser": "^2.0.4", + "@amplitude/plugin-page-view-tracking-browser": "^2.0.6", + "@amplitude/plugin-web-attribution-browser": "^2.0.6", "tslib": "^2.4.1" }, "dependencies": { @@ -17435,9 +17635,9 @@ }, "dependencies": { "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" } } }, @@ -17456,9 +17656,9 @@ }, "dependencies": { "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" } } }, @@ -17468,9 +17668,9 @@ "integrity": "sha512-H3vebPR9onRdp0WzAZmI/4qmAE903uLOd2ZfMeHsVc1zaFTTCk46SoCuV4IrlF+VILrDw9Fy6gC9yl5N2PZcJQ==" }, "@amplitude/plugin-page-view-tracking-browser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.0.4.tgz", - "integrity": "sha512-XQlgydCmfUb+mkXjD8NoOU80eO/578m2AupRcV0dPKwk+VN5D7D1s+xXYM6Bg7l1HSJx2mt0Qwa5xsdSCFzF2g==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.0.6.tgz", + "integrity": "sha512-V8ef24E+KYINQf1o9MQnkmhFmQHGD5lb6mlTH8jGD2PdUe/KDXdO4S4rpBewxcrAfUH+iEqEanbVnTWpg6iPnw==", "requires": { "@amplitude/analytics-client-common": "^2.0.4", "@amplitude/analytics-types": "^2.1.1", @@ -17478,16 +17678,16 @@ }, "dependencies": { "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" } } }, "@amplitude/plugin-web-attribution-browser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-attribution-browser/-/plugin-web-attribution-browser-2.0.4.tgz", - "integrity": "sha512-y/VQjDH7tnevRlKO9IWzELF9L87MwiR+y7WCRxR5/wRGKDxKS5Ol33vHYWe1RaOzkC/7wO0px7pZGRZkj/X+1Q==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-attribution-browser/-/plugin-web-attribution-browser-2.0.6.tgz", + "integrity": "sha512-+WQuhHJn7ZsYaTMe6iPE081b6y2oe9m+W/eZW+Lyf99Vt3rntDVYZIgZVwwds0s2SsUBcSvYFu1JgS65NfxWNQ==", "requires": { "@amplitude/analytics-client-common": "^2.0.4", "@amplitude/analytics-core": "^2.0.3", @@ -17496,9 +17696,9 @@ }, "dependencies": { "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" } } }, @@ -17523,11 +17723,12 @@ } }, "@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", + "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", "requires": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.22.10", + "chalk": "^2.4.2" } }, "@babel/compat-data": { @@ -17536,20 +17737,20 @@ "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==" }, "@babel/core": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", - "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.10.tgz", + "integrity": "sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==", "requires": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", + "@babel/helper-compilation-targets": "^7.22.10", "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", + "@babel/helpers": "^7.22.10", + "@babel/parser": "^7.22.10", "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", + "@babel/traverse": "^7.22.10", + "@babel/types": "^7.22.10", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -17558,11 +17759,11 @@ } }, "@babel/generator": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", - "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", + "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", "requires": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.22.10", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -17586,9 +17787,9 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", - "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", + "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", "requires": { "@babel/compat-data": "^7.22.9", "@babel/helper-validator-option": "^7.22.5", @@ -17719,15 +17920,14 @@ "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==" }, "@babel/helper-remap-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz", - "integrity": "sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz", + "integrity": "sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-wrap-function": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-wrap-function": "^7.22.9" } }, "@babel/helper-replace-supers": { @@ -17785,41 +17985,40 @@ "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==" }, "@babel/helper-wrap-function": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz", - "integrity": "sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.10.tgz", + "integrity": "sha512-OnMhjWjuGYtdoO3FmsEFWvBStBAe2QOgwOLsLNDjN+aaiMD8InJk1/O3HSD8lkqTjCgg5YI34Tz15KNNA3p+nQ==", "dev": true, "requires": { "@babel/helper-function-name": "^7.22.5", "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/types": "^7.22.10" } }, "@babel/helpers": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", - "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.10.tgz", + "integrity": "sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==", "requires": { "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.6", - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.22.10", + "@babel/types": "^7.22.10" } }, "@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", + "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", "requires": { "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", - "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==" + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", + "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.22.5", @@ -17848,16 +18047,6 @@ "dev": true, "requires": {} }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, "@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -18058,14 +18247,14 @@ } }, "@babel/plugin-transform-async-generator-functions": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz", - "integrity": "sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.10.tgz", + "integrity": "sha512-eueE8lvKVzq5wIObKK/7dvoeKJ+xc6TvRn6aysIjS6pSCeLy7S/eVi7pEQknZqyqvzaNKdDtem8nUNTBgDVR2g==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.9", "@babel/plugin-syntax-async-generators": "^7.8.4" } }, @@ -18090,9 +18279,9 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz", - "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.10.tgz", + "integrity": "sha512-1+kVpGAOOI1Albt6Vse7c8pHzcZQdQKW+wJH+g8mCaszOdDVwRXa/slHPqIw+oJAJANTKDMuM2cBdV0Dg618Vg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" @@ -18147,9 +18336,9 @@ } }, "@babel/plugin-transform-destructuring": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz", - "integrity": "sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.10.tgz", + "integrity": "sha512-dPJrL0VOyxqLM9sritNbMSGx/teueHF/htMKrPT7DNxccXxRDPYqlgPFFdr8u+F+qUZOkZoXue/6rL5O5GduEw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" @@ -18378,9 +18567,9 @@ } }, "@babel/plugin-transform-optional-chaining": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz", - "integrity": "sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.10.tgz", + "integrity": "sha512-MMkQqZAZ+MGj+jGTG3OTuhKeBpNcO+0oCEbrGNEaOmiEn+1MzRyQlYsruGiU8RTK3zV6XwrVJTmwiDOyYK6J9g==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", @@ -18429,13 +18618,13 @@ } }, "@babel/plugin-transform-regenerator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz", - "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz", + "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.1" + "regenerator-transform": "^0.15.2" } }, "@babel/plugin-transform-reserved-words": { @@ -18518,9 +18707,9 @@ } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz", - "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz", + "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" @@ -18557,13 +18746,13 @@ } }, "@babel/preset-env": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.9.tgz", - "integrity": "sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.10.tgz", + "integrity": "sha512-riHpLb1drNkpLlocmSyEg4oYJIQFeXAK/d7rI6mbD0XsvoTOOweXDmQPG/ErxsEhWk3rl3Q/3F6RFQlVFS8m0A==", "dev": true, "requires": { "@babel/compat-data": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.10", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.22.5", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", @@ -18588,15 +18777,15 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.22.7", + "@babel/plugin-transform-async-generator-functions": "^7.22.10", "@babel/plugin-transform-async-to-generator": "^7.22.5", "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.22.10", "@babel/plugin-transform-class-properties": "^7.22.5", "@babel/plugin-transform-class-static-block": "^7.22.5", "@babel/plugin-transform-classes": "^7.22.6", "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.10", "@babel/plugin-transform-dotall-regex": "^7.22.5", "@babel/plugin-transform-duplicate-keys": "^7.22.5", "@babel/plugin-transform-dynamic-import": "^7.22.5", @@ -18619,35 +18808,35 @@ "@babel/plugin-transform-object-rest-spread": "^7.22.5", "@babel/plugin-transform-object-super": "^7.22.5", "@babel/plugin-transform-optional-catch-binding": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.6", + "@babel/plugin-transform-optional-chaining": "^7.22.10", "@babel/plugin-transform-parameters": "^7.22.5", "@babel/plugin-transform-private-methods": "^7.22.5", "@babel/plugin-transform-private-property-in-object": "^7.22.5", "@babel/plugin-transform-property-literals": "^7.22.5", - "@babel/plugin-transform-regenerator": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.10", "@babel/plugin-transform-reserved-words": "^7.22.5", "@babel/plugin-transform-shorthand-properties": "^7.22.5", "@babel/plugin-transform-spread": "^7.22.5", "@babel/plugin-transform-sticky-regex": "^7.22.5", "@babel/plugin-transform-template-literals": "^7.22.5", "@babel/plugin-transform-typeof-symbol": "^7.22.5", - "@babel/plugin-transform-unicode-escapes": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.10", "@babel/plugin-transform-unicode-property-regex": "^7.22.5", "@babel/plugin-transform-unicode-regex": "^7.22.5", "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.4", - "babel-plugin-polyfill-corejs3": "^0.8.2", - "babel-plugin-polyfill-regenerator": "^0.5.1", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "@babel/types": "^7.22.10", + "babel-plugin-polyfill-corejs2": "^0.4.5", + "babel-plugin-polyfill-corejs3": "^0.8.3", + "babel-plugin-polyfill-regenerator": "^0.5.2", "core-js-compat": "^3.31.0", "semver": "^6.3.1" }, "dependencies": { "@babel/helper-define-polyfill-provider": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.1.tgz", - "integrity": "sha512-kX4oXixDxG197yhX+J3Wp+NpL2wuCFjWQAr6yX2jtCnflK9ulMI51ULFGIrWiX1jGfvAxdHp+XQCcP2bZGPs9A==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", + "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", "dev": true, "requires": { "@babel/helper-compilation-targets": "^7.22.6", @@ -18658,36 +18847,34 @@ } }, "babel-plugin-polyfill-corejs2": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.4.tgz", - "integrity": "sha512-9WeK9snM1BfxB38goUEv2FLnA6ja07UMfazFHzCXUb3NyDZAwfXvQiURQ6guTTMeHcOsdknULm1PDhs4uWtKyA==", + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", + "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", "dev": true, "requires": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.1", - "@nicolo-ribaudo/semver-v6": "^6.3.3" + "@babel/helper-define-polyfill-provider": "^0.4.2", + "semver": "^6.3.1" } }, "babel-plugin-polyfill-regenerator": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.1.tgz", - "integrity": "sha512-L8OyySuI6OSQ5hFy9O+7zFjyr4WhAfRjLIOkhQGYl+emwJkd/S4XXT1JpfrgR1jrQ1NcGiOh+yAdGlF8pnC3Jw==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", + "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.1" + "@babel/helper-define-polyfill-provider": "^0.4.2" } } } }, "@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", "@babel/types": "^7.4.4", "esutils": "^2.0.2" } @@ -18717,26 +18904,26 @@ } }, "@babel/traverse": { - "version": "7.22.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", - "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz", + "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==", "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.7", + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-function-name": "^7.22.5", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/types": "^7.22.5", + "@babel/parser": "^7.22.10", + "@babel/types": "^7.22.10", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", - "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", + "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", "requires": { "@babel/helper-string-parser": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.5", @@ -18788,15 +18975,15 @@ } }, "@eslint-community/regexpp": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz", - "integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true }, "@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -18817,9 +19004,9 @@ "dev": true }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -18843,9 +19030,9 @@ } }, "@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", + "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", "dev": true }, "@ethereumjs/common": { @@ -19061,9 +19248,9 @@ } }, "@fortawesome/fontawesome-free": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz", - "integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ==" + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.2.tgz", + "integrity": "sha512-m5cPn3e2+FDCOgi1mz0RexTUvvQibBebOUlUlW0+YrMjDTPkiJ6VTKukA1GRsvRw+12KyJndNjj0O4AgTxm2Pg==" }, "@humanwhocodes/config-array": { "version": "0.11.10", @@ -19116,16 +19303,16 @@ "dev": true }, "@jest/console": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.1.tgz", - "integrity": "sha512-Aj772AYgwTSr5w8qnyoJ0eDYvN6bMsH3ORH1ivMotrInHLKdUz6BDlaEXHdM6kODaBIkNIyQGzsMvRdOv7VG7Q==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.3.tgz", + "integrity": "sha512-ukZbHAdDH4ktZIOKvWs1juAXhiVAdvCyM8zv4S/7Ii3vJSDvMW5k+wOVGMQmHLHUFw3Ko63ZQNy7NI6PSlsD5w==", "dev": true, "requires": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", "slash": "^3.0.0" }, "dependencies": { @@ -19181,37 +19368,37 @@ } }, "@jest/core": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.1.tgz", - "integrity": "sha512-CcowHypRSm5oYQ1obz1wfvkjZZ2qoQlrKKvlfPwh5jUXVU12TWr2qMeH8chLMuTFzHh5a1g2yaqlqDICbr+ukQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.3.tgz", + "integrity": "sha512-skV1XrfNxfagmjRUrk2FyN5/2YwIzdWVVBa/orUfbLvQUANXxERq2pTvY0I+FinWHjDKB2HRmpveUiph4X0TJw==", "dev": true, "requires": { - "@jest/console": "^29.6.1", - "@jest/reporters": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.3", + "@jest/reporters": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/transform": "^29.6.3", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.6.1", - "jest-haste-map": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.1", - "jest-resolve-dependencies": "^29.6.1", - "jest-runner": "^29.6.1", - "jest-runtime": "^29.6.1", - "jest-snapshot": "^29.6.1", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", - "jest-watcher": "^29.6.1", + "jest-changed-files": "^29.6.3", + "jest-config": "^29.6.3", + "jest-haste-map": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.3", + "jest-resolve-dependencies": "^29.6.3", + "jest-runner": "^29.6.3", + "jest-runtime": "^29.6.3", + "jest-snapshot": "^29.6.3", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "jest-watcher": "^29.6.3", "micromatch": "^4.0.4", - "pretty-format": "^29.6.1", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -19268,73 +19455,73 @@ } }, "@jest/environment": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.1.tgz", - "integrity": "sha512-RMMXx4ws+Gbvw3DfLSuo2cfQlK7IwGbpuEWXCqyYDcqYTI+9Ju3a5hDnXaxjNsa6uKh9PQF2v+qg+RLe63tz5A==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.3.tgz", + "integrity": "sha512-u/u3cCztYCfgBiGHsamqP5x+XvucftOGPbf5RJQxfpeC1y4AL8pCjKvPDA3oCmdhZYPgk5AE0VOD/flweR69WA==", "dev": true, "requires": { - "@jest/fake-timers": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/fake-timers": "^29.6.3", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.1" + "jest-mock": "^29.6.3" } }, "@jest/expect": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.1.tgz", - "integrity": "sha512-N5xlPrAYaRNyFgVf2s9Uyyvr795jnB6rObuPx4QFvNJz8aAjpZUDfO4bh5G/xuplMID8PrnuF1+SfSyDxhsgYg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.3.tgz", + "integrity": "sha512-Ic08XbI2jlg6rECy+CGwk/8NDa6VE7UmIG6++9OTPAMnQmNGY28hu69Nf629CWv6T7YMODLbONxDFKdmQeI9FA==", "dev": true, "requires": { - "expect": "^29.6.1", - "jest-snapshot": "^29.6.1" + "expect": "^29.6.3", + "jest-snapshot": "^29.6.3" } }, "@jest/expect-utils": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.1.tgz", - "integrity": "sha512-o319vIf5pEMx0LmzSxxkYYxo4wrRLKHq9dP1yJU7FoPTB0LfAKSz8SWD6D/6U3v/O52t9cF5t+MeJiRsfk7zMw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.3.tgz", + "integrity": "sha512-nvOEW4YoqRKD9HBJ9OJ6przvIvP9qilp5nAn1462P5ZlL/MM9SgPEZFyjTGPfs7QkocdUsJa6KjHhyRn4ueItA==", "dev": true, "requires": { - "jest-get-type": "^29.4.3" + "jest-get-type": "^29.6.3" } }, "@jest/fake-timers": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.1.tgz", - "integrity": "sha512-RdgHgbXyosCDMVYmj7lLpUwXA4c69vcNzhrt69dJJdf8azUrpRh3ckFCaTPNjsEeRi27Cig0oKDGxy5j7hOgHg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.3.tgz", + "integrity": "sha512-pa1wmqvbj6eX0nMvOM2VDAWvJOI5A/Mk3l8O7n7EsAh71sMZblaKO9iT4GjIj0LwwK3CP/Jp1ypEV0x3m89RvA==", "dev": true, "requires": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.6.1", - "jest-mock": "^29.6.1", - "jest-util": "^29.6.1" + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" } }, "@jest/globals": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.1.tgz", - "integrity": "sha512-2VjpaGy78JY9n9370H8zGRCFbYVWwjY6RdDMhoJHa1sYfwe6XM/azGN0SjY8kk7BOZApIejQ1BFPyH7FPG0w3A==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.3.tgz", + "integrity": "sha512-RB+uI+CZMHntzlnOPlll5x/jgRff3LEPl/td/jzMXiIgR0iIhKq9qm1HLU+EC52NuoVy/1swit/sDGjVn4bc6A==", "dev": true, "requires": { - "@jest/environment": "^29.6.1", - "@jest/expect": "^29.6.1", - "@jest/types": "^29.6.1", - "jest-mock": "^29.6.1" + "@jest/environment": "^29.6.3", + "@jest/expect": "^29.6.3", + "@jest/types": "^29.6.3", + "jest-mock": "^29.6.3" } }, "@jest/reporters": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.1.tgz", - "integrity": "sha512-9zuaI9QKr9JnoZtFQlw4GREQbxgmNYXU6QuWtmuODvk5nvPUeBYapVR/VYMyi2WSx3jXTLJTJji8rN6+Cm4+FA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.3.tgz", + "integrity": "sha512-kGz59zMi0GkVjD2CJeYWG9k6cvj7eBqt9aDAqo2rcCLRTYlvQ62Gu/n+tOmJMBHGjzeijjuCENjzTyYBgrtLUw==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/transform": "^29.6.3", + "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", @@ -19343,13 +19530,13 @@ "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1", - "jest-worker": "^29.6.1", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.3", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -19397,13 +19584,13 @@ "dev": true }, "jest-worker": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.1.tgz", - "integrity": "sha512-U+Wrbca7S8ZAxAe9L6nb6g8kPdia5hj32Puu5iOqBCMTMWFHXuK6dOV2IFrpedbTV8fjMFLdWNttQTBL6u2MRA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.3.tgz", + "integrity": "sha512-wacANXecZ/GbQakpf2CClrqrlwsYYDSXFd4fIGdL+dXpM2GWoJ+6bhQ7vR3TKi3+gkSfBkjy1/khH/WrYS4Q6g==", "dev": true, "requires": { "@types/node": "*", - "jest-util": "^29.6.1", + "jest-util": "^29.6.3", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -19431,18 +19618,18 @@ } }, "@jest/schemas": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz", - "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "requires": { "@sinclair/typebox": "^0.27.8" } }, "@jest/source-map": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.0.tgz", - "integrity": "sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "requires": { "@jridgewell/trace-mapping": "^0.3.18", @@ -19451,46 +19638,46 @@ } }, "@jest/test-result": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.1.tgz", - "integrity": "sha512-Ynr13ZRcpX6INak0TPUukU8GWRfm/vAytE3JbJNGAvINySWYdfE7dGZMbk36oVuK4CigpbhMn8eg1dixZ7ZJOw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.3.tgz", + "integrity": "sha512-k7ZZaNvOSMBHPZYiy0kuiaFoyansR5QnTwDux1EjK3kD5iWpRVyJIJ0RAIV39SThafchuW59vra7F8mdy5Hfgw==", "dev": true, "requires": { - "@jest/console": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.3", + "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/test-sequencer": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.1.tgz", - "integrity": "sha512-oBkC36PCDf/wb6dWeQIhaviU0l5u6VCsXa119yqdUosYAt7/FbQU2M2UoziO3igj/HBDEgp57ONQ3fm0v9uyyg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.3.tgz", + "integrity": "sha512-/SmijaAU2TY9ComFGIYa6Z+fmKqQMnqs2Nmwb0P/Z/tROdZ7M0iruES1EaaU9PBf8o9uED5xzaJ3YPFEIcDgAg==", "dev": true, "requires": { - "@jest/test-result": "^29.6.1", + "@jest/test-result": "^29.6.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", + "jest-haste-map": "^29.6.3", "slash": "^3.0.0" } }, "@jest/transform": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.1.tgz", - "integrity": "sha512-URnTneIU3ZjRSaf906cvf6Hpox3hIeJXRnz3VDSw5/X93gR8ycdfSIEy19FlVx8NFmpN7fe3Gb1xF+NjXaQLWg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.3.tgz", + "integrity": "sha512-dPIc3DsvMZ/S8ut4L2ViCj265mKO0owB0wfzBv2oGzL9pQ+iRvJewHqLBmsGb7XFb5UotWIEtvY5A/lnylaIoQ==", "dev": true, "requires": { "@babel/core": "^7.11.6", - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.1", + "jest-haste-map": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -19555,12 +19742,12 @@ } }, "@jest/types": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.1.tgz", - "integrity": "sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "requires": { - "@jest/schemas": "^29.6.0", + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -19673,12 +19860,6 @@ "resolved": "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz", "integrity": "sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==" }, - "@nicolo-ribaudo/semver-v6": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz", - "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==", - "dev": true - }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -19921,12 +20102,6 @@ "@types/node": "*" } }, - "@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true - }, "@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -20553,6 +20728,16 @@ "sprintf-js": "~1.0.2" } }, + "array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + } + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -20571,6 +20756,19 @@ "is-string": "^1.0.7" } }, + "array.prototype.findlastindex": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.2.tgz", + "integrity": "sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.1.3" + } + }, "array.prototype.flat": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", @@ -20595,6 +20793,20 @@ "es-shim-unscopables": "^1.0.0" } }, + "arraybuffer.prototype.slice": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", + "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + } + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -20684,13 +20896,13 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "version": "10.4.15", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.15.tgz", + "integrity": "sha512-KCuPB8ZCIqFdA4HwKXsvz7j6gvSDNhDP7WnUjBleRkKjPdvCmHFuQ77ocavI8FT6NdvlBnE2UFr2H4Mycn8Vew==", "dev": true, "requires": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001520", "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -20713,15 +20925,15 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "babel-jest": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.1.tgz", - "integrity": "sha512-qu+3bdPEQC6KZSPz+4Fyjbga5OODNcp49j6GKzG1EKbkfyJBxEYGVUmVGpwCSeGouG52R4EgYMLb6p9YeEEQ4A==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.3.tgz", + "integrity": "sha512-1Ne93zZZEy5XmTa4Q+W5+zxBrDpExX8E3iy+xJJ+24ewlfo/T3qHfQJCzi/MMVFmBQDNxtRR/Gfd2dwb/0yrQw==", "dev": true, "requires": { - "@jest/transform": "^29.6.1", + "@jest/transform": "^29.6.3", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", + "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" @@ -20799,12 +21011,27 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" + }, + "dependencies": { + "istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + } } }, "babel-plugin-jest-hoist": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", - "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "requires": { "@babel/template": "^7.3.3", @@ -20824,19 +21051,19 @@ } }, "babel-plugin-polyfill-corejs3": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.2.tgz", - "integrity": "sha512-Cid+Jv1BrY9ReW9lIfNlNpsI53N+FN7gE+f73zLAUbr9C52W4gKLWSByx47pfDJsEysojKArqOtOKZSVIIUTuQ==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz", + "integrity": "sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.1", + "@babel/helper-define-polyfill-provider": "^0.4.2", "core-js-compat": "^3.31.0" }, "dependencies": { "@babel/helper-define-polyfill-provider": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.1.tgz", - "integrity": "sha512-kX4oXixDxG197yhX+J3Wp+NpL2wuCFjWQAr6yX2jtCnflK9ulMI51ULFGIrWiX1jGfvAxdHp+XQCcP2bZGPs9A==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", + "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", "dev": true, "requires": { "@babel/helper-compilation-targets": "^7.22.6", @@ -20893,12 +21120,12 @@ } }, "babel-preset-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", - "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "requires": { - "babel-plugin-jest-hoist": "^29.5.0", + "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" } }, @@ -21120,13 +21347,13 @@ } }, "browserslist": { - "version": "4.21.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", - "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", "requires": { - "caniuse-lite": "^1.0.30001503", - "electron-to-chromium": "^1.4.431", - "node-releases": "^2.0.12", + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", "update-browserslist-db": "^1.0.11" } }, @@ -21317,9 +21544,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001512", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001512.tgz", - "integrity": "sha512-2S9nK0G/mE+jasCUsMPlARhRCts1ebcp2Ji8Y8PWi4NDE1iRdLCnEPHkEfeBrGC45L4isBx5ur3IQ6yTE2mRZw==" + "version": "1.0.30001520", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001520.tgz", + "integrity": "sha512-tahF5O9EiiTzwTUqAeFjIZbn4Dnqxzz7ktrgGlMYNLH43Ul26IgTMH/zvL3DG0lZxBYnlT04axvInszUsZULdA==" }, "caseless": { "version": "0.12.0", @@ -21356,9 +21583,9 @@ "dev": true }, "chart.js": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.2.tgz", - "integrity": "sha512-pvQNyFOY1QmbmIr8oDORL16/FFivfxj8V26VFpFilMo4cNvkV5WXLJetDio365pd9gKUHGdirUTbqJfw8tr+Dg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.3.tgz", + "integrity": "sha512-aTk7pBw+x6sQYhon/NR3ikfUJuym/LdgpTlgZRe2PaEhjUMKBKyNaFCMVRAyTEWYFNO7qRu7iQVqOw/OqzxZxQ==", "requires": { "@kurkle/color": "^0.3.0" } @@ -21653,9 +21880,9 @@ } }, "core-js": { - "version": "3.31.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.31.1.tgz", - "integrity": "sha512-2sKLtfq1eFST7l7v62zaqXacPc7uG8ZAya8ogijLhTtaKNcpzpB4TMoTw2Si+8GYKRwFPMMtUT0263QFWFfqyQ==" + "version": "3.32.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.1.tgz", + "integrity": "sha512-lqufgNn9NLnESg5mQeYsxQP5w7wrViSj0jr/kv6ECQiByzQkrn1MKvV0L3acttpDqfQrHLwr2KCMgX5b8X+lyQ==" }, "core-js-compat": { "version": "3.31.0", @@ -22109,10 +22336,11 @@ } }, "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "requires": {} }, "deep-eql": { "version": "3.0.1", @@ -22148,9 +22376,9 @@ } }, "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", "requires": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" @@ -22197,9 +22425,9 @@ "dev": true }, "diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true }, "diffie-hellman": { @@ -22313,9 +22541,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron-to-chromium": { - "version": "1.4.450", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.450.tgz", - "integrity": "sha512-BLG5HxSELlrMx7dJ2s+8SFlsCtJp37Zpk2VAxyC6CZtbc+9AJeZHfYHbrlSgdXp6saQ8StMqOTEDaBKgA7u1sw==" + "version": "1.4.490", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.490.tgz", + "integrity": "sha512-6s7NVJz+sATdYnIwhdshx/N/9O6rvMxmhVoDSDFdj6iA45gHR8EQje70+RYsF4GeB+k0IeNSBnP7yG9ZXJFr7A==" }, "elliptic": { "version": "6.5.4", @@ -22431,18 +22659,19 @@ } }, "es-abstract": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", - "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", + "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", "dev": true, "requires": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.1", "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.1", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", @@ -22450,8 +22679,8 @@ "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.4", - "is-array-buffer": "^3.0.1", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", @@ -22459,16 +22688,21 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.10", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", + "object-inspect": "^1.12.3", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", + "regexp.prototype.flags": "^1.5.0", + "safe-array-concat": "^1.0.0", "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", "string.prototype.trimend": "^1.0.6", "string.prototype.trimstart": "^1.0.6", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" + "which-typed-array": "^1.1.10" } }, "es-module-lexer": { @@ -22623,27 +22857,27 @@ } }, "eslint": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz", - "integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", + "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "^8.47.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -22714,9 +22948,9 @@ "dev": true }, "eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -22834,9 +23068,9 @@ } }, "eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", "dev": true, "requires": { "debug": "^3.2.7" @@ -22865,26 +23099,28 @@ } }, "eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz", + "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==", "dev": true, "requires": { "array-includes": "^3.1.6", + "array.prototype.findlastindex": "^1.2.2", "array.prototype.flat": "^1.3.1", "array.prototype.flatmap": "^1.3.1", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", + "eslint-module-utils": "^2.8.0", "has": "^1.0.3", - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "is-glob": "^4.0.3", "minimatch": "^3.1.2", + "object.fromentries": "^2.0.6", + "object.groupby": "^1.0.0", "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" + "semver": "^6.3.1", + "tsconfig-paths": "^3.14.2" }, "dependencies": { "debug": { @@ -23014,15 +23250,15 @@ } }, "eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "espree": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", - "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { "acorn": "^8.9.0", @@ -23625,17 +23861,16 @@ "dev": true }, "expect": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.1.tgz", - "integrity": "sha512-XEdDLonERCU1n9uR56/Stx9OqojaLAQtZf9PrCHH9Hl8YXiEIka3H4NXJ3NOIBmQJTg7+j7buh34PMHfJujc8g==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.3.tgz", + "integrity": "sha512-x1vY4LlEMWUYVZQrFi4ZANXFwqYbJ/JNQspLVvzhW2BNY28aNcXMQH6imBbt+RBf5sVRTodYHXtSP/TLEU0Dxw==", "dev": true, "requires": { - "@jest/expect-utils": "^29.6.1", - "@types/node": "*", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1" + "@jest/expect-utils": "^29.6.3", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3" } }, "express": { @@ -24069,12 +24304,13 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" }, "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3" } }, @@ -24271,8 +24507,7 @@ "has-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" }, "has-symbols": { "version": "1.0.3", @@ -24523,12 +24758,12 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "internal-slot": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", - "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "dev": true, "requires": { - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.0", "has": "^1.0.3", "side-channel": "^1.0.4" } @@ -24554,13 +24789,13 @@ } }, "is-array-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", - "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", "dev": true, "requires": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.0", "is-typed-array": "^1.1.10" } }, @@ -24604,9 +24839,9 @@ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" }, "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "requires": { "has": "^1.0.3" } @@ -24819,26 +25054,37 @@ "dev": true }, "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", + "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", "dev": true, "requires": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "requires": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "dependencies": { @@ -24871,9 +25117,9 @@ } }, "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -24881,50 +25127,51 @@ } }, "jest": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.1.tgz", - "integrity": "sha512-Nirw5B4nn69rVUZtemCQhwxOBhm0nsp3hmtF4rzCeWD7BkjAXRIji7xWQfnTNbz9g0aVsBX6aZK3n+23LM6uDw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.3.tgz", + "integrity": "sha512-alueLuoPCDNHFcFGmgETR4KpQ+0ff3qVaiJwxQM4B5sC0CvXcgg4PEi7xrDkxuItDmdz/FVc7SSit4KEu8GRvw==", "dev": true, "requires": { - "@jest/core": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/core": "^29.6.3", + "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.6.1" + "jest-cli": "^29.6.3" } }, "jest-changed-files": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", - "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", + "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", "dev": true, "requires": { "execa": "^5.0.0", + "jest-util": "^29.6.3", "p-limit": "^3.1.0" } }, "jest-circus": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.1.tgz", - "integrity": "sha512-tPbYLEiBU4MYAL2XoZme/bgfUeotpDBd81lgHLCbDZZFaGmECk0b+/xejPFtmiBP87GgP/y4jplcRpbH+fgCzQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.3.tgz", + "integrity": "sha512-p0R5YqZEMnOpHqHLWRSjm2z/0p6RNsrNE/GRRT3eli8QGOAozj6Ys/3Tv+Ej+IfltJoSPwcQ6/hOCRkNlxLLCw==", "dev": true, "requires": { - "@jest/environment": "^29.6.1", - "@jest/expect": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.3", + "@jest/expect": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", - "dedent": "^0.7.0", + "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.1", - "jest-matcher-utils": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-runtime": "^29.6.1", - "jest-snapshot": "^29.6.1", - "jest-util": "^29.6.1", + "jest-each": "^29.6.3", + "jest-matcher-utils": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-runtime": "^29.6.3", + "jest-snapshot": "^29.6.3", + "jest-util": "^29.6.3", "p-limit": "^3.1.0", - "pretty-format": "^29.6.1", + "pretty-format": "^29.6.3", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -24982,21 +25229,21 @@ } }, "jest-cli": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.1.tgz", - "integrity": "sha512-607dSgTA4ODIN6go9w6xY3EYkyPFGicx51a69H7yfvt7lN53xNswEVLovq+E77VsTRi5fWprLH0yl4DJgE8Ing==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.3.tgz", + "integrity": "sha512-KuPdXUPXQIf0t6DvmG8MV4QyhcjR1a6ruKl3YL7aGn/AQ8JkROwFkWzEpDIpt11Qy188dHbRm8WjwMsV/4nmnQ==", "dev": true, "requires": { - "@jest/core": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/core": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.6.1", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", + "jest-config": "^29.6.3", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "prompts": "^2.0.1", "yargs": "^17.3.1" }, @@ -25053,31 +25300,31 @@ } }, "jest-config": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.1.tgz", - "integrity": "sha512-XdjYV2fy2xYixUiV2Wc54t3Z4oxYPAELUzWnV6+mcbq0rh742X2p52pii5A3oeRzYjLnQxCsZmp0qpI6klE2cQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.3.tgz", + "integrity": "sha512-nb9bOq2aEqogbyL4F9mLkAeQGAgNt7Uz6U59YtQDIxFPiL7Ejgq0YIrp78oyEHD6H4CIV/k7mFrK7eFDzUJ69w==", "dev": true, "requires": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.1", - "@jest/types": "^29.6.1", - "babel-jest": "^29.6.1", + "@jest/test-sequencer": "^29.6.3", + "@jest/types": "^29.6.3", + "babel-jest": "^29.6.3", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.1", - "jest-environment-node": "^29.6.1", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.1", - "jest-runner": "^29.6.1", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", + "jest-circus": "^29.6.3", + "jest-environment-node": "^29.6.3", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.3", + "jest-runner": "^29.6.3", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.6.1", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -25134,15 +25381,15 @@ } }, "jest-diff": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.1.tgz", - "integrity": "sha512-FsNCvinvl8oVxpNLttNQX7FAq7vR+gMDGj90tiP7siWw1UdakWUGqrylpsYrpvj908IYckm5Y0Q7azNAozU1Kg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.3.tgz", + "integrity": "sha512-3sw+AdWnwH9sSNohMRKA7JiYUJSRr/WS6+sEFfBuhxU5V5GlEVKfvUn8JuMHE0wqKowemR1C2aHy8VtXbaV8dQ==", "dev": true, "requires": { "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.1" + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" }, "dependencies": { "ansi-styles": { @@ -25197,25 +25444,25 @@ } }, "jest-docblock": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", - "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", + "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", "dev": true, "requires": { "detect-newline": "^3.0.0" } }, "jest-each": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.1.tgz", - "integrity": "sha512-n5eoj5eiTHpKQCAVcNTT7DRqeUmJ01hsAL0Q1SMiBHcBcvTKDELixQOGMCpqhbIuTcfC4kMfSnpmDqRgRJcLNQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", + "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", "dev": true, "requires": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.6.1", - "pretty-format": "^29.6.1" + "jest-get-type": "^29.6.3", + "jest-util": "^29.6.3", + "pretty-format": "^29.6.3" }, "dependencies": { "ansi-styles": { @@ -25270,57 +25517,57 @@ } }, "jest-environment-jsdom": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.6.1.tgz", - "integrity": "sha512-PoY+yLaHzVRhVEjcVKSfJ7wXmJW4UqPYNhR05h7u/TK0ouf6DmRNZFBL/Z00zgQMyWGMBXn69/FmOvhEJu8cIw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.6.3.tgz", + "integrity": "sha512-nMJz/i27Moit9bv8Z323/13Melj4FEQH93yRu7GnilvBmPBMH4EGEkEfBTJXYuubyzhMO7w/VHzljIDV+Q/SeQ==", "dev": true, "requires": { - "@jest/environment": "^29.6.1", - "@jest/fake-timers": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.3", + "@jest/fake-timers": "^29.6.3", + "@jest/types": "^29.6.3", "@types/jsdom": "^20.0.0", "@types/node": "*", - "jest-mock": "^29.6.1", - "jest-util": "^29.6.1", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3", "jsdom": "^20.0.0" } }, "jest-environment-node": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.1.tgz", - "integrity": "sha512-ZNIfAiE+foBog24W+2caIldl4Irh8Lx1PUhg/GZ0odM1d/h2qORAsejiFc7zb+SEmYPn1yDZzEDSU5PmDkmVLQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.3.tgz", + "integrity": "sha512-PKl7upfPJXMYbWpD+60o4HP86KvFO2c9dZ+Zr6wUzsG5xcPx/65o3ArNgHW5M0RFvLYdW4/aieR4JSooD0a2ew==", "dev": true, "requires": { - "@jest/environment": "^29.6.1", - "@jest/fake-timers": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.3", + "@jest/fake-timers": "^29.6.3", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.1", - "jest-util": "^29.6.1" + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" } }, "jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true }, "jest-haste-map": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.1.tgz", - "integrity": "sha512-0m7f9PZXxOCk1gRACiVgX85knUKPKLPg4oRCjLoqIm9brTHXaorMA0JpmtmVkQiT8nmXyIVoZd/nnH1cfC33ig==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.3.tgz", + "integrity": "sha512-GecR5YavfjkhOytEFHAeI6aWWG3f/cOKNB1YJvj/B76xAmeVjy4zJUYobGF030cRmKaO1FBw3V8CZZ6KVh9ZSw==", "dev": true, "requires": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.3.2", "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.1", - "jest-worker": "^29.6.1", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.3", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -25332,13 +25579,13 @@ "dev": true }, "jest-worker": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.1.tgz", - "integrity": "sha512-U+Wrbca7S8ZAxAe9L6nb6g8kPdia5hj32Puu5iOqBCMTMWFHXuK6dOV2IFrpedbTV8fjMFLdWNttQTBL6u2MRA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.3.tgz", + "integrity": "sha512-wacANXecZ/GbQakpf2CClrqrlwsYYDSXFd4fIGdL+dXpM2GWoJ+6bhQ7vR3TKi3+gkSfBkjy1/khH/WrYS4Q6g==", "dev": true, "requires": { "@types/node": "*", - "jest-util": "^29.6.1", + "jest-util": "^29.6.3", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" } @@ -25355,25 +25602,25 @@ } }, "jest-leak-detector": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.1.tgz", - "integrity": "sha512-OrxMNyZirpOEwkF3UHnIkAiZbtkBWiye+hhBweCHkVbCgyEy71Mwbb5zgeTNYWJBi1qgDVfPC1IwO9dVEeTLwQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", + "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", "dev": true, "requires": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.1" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" } }, "jest-matcher-utils": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.1.tgz", - "integrity": "sha512-SLaztw9d2mfQQKHmJXKM0HCbl2PPVld/t9Xa6P9sgiExijviSp7TnZZpw2Fpt+OI3nwUO/slJbOfzfUMKKC5QA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.3.tgz", + "integrity": "sha512-6ZrMYINZdwduSt5Xu18/n49O1IgXdjsfG7NEZaQws9k69eTKWKcVbJBw/MZsjOZe2sSyJFmuzh8042XWwl54Zg==", "dev": true, "requires": { "chalk": "^4.0.0", - "jest-diff": "^29.6.1", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.1" + "jest-diff": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" }, "dependencies": { "ansi-styles": { @@ -25428,18 +25675,18 @@ } }, "jest-message-util": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.1.tgz", - "integrity": "sha512-KoAW2zAmNSd3Gk88uJ56qXUWbFk787QKmjjJVOjtGFmmGSZgDBrlIL4AfQw1xyMYPNVD7dNInfIbur9B2rd/wQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", + "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.6.1", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -25496,14 +25743,14 @@ } }, "jest-mock": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.1.tgz", - "integrity": "sha512-brovyV9HBkjXAEdRooaTQK42n8usKoSRR3gihzUpYeV/vwqgSoNfrksO7UfSACnPmxasO/8TmHM3w9Hp3G1dgw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", + "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", "dev": true, "requires": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.6.1" + "jest-util": "^29.6.3" } }, "jest-pnp-resolver": { @@ -25514,23 +25761,23 @@ "requires": {} }, "jest-regex-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", - "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true }, "jest-resolve": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.1.tgz", - "integrity": "sha512-AeRkyS8g37UyJiP9w3mmI/VXU/q8l/IH52vj/cDAyScDcemRbSBhfX/NMYIGilQgSVwsjxrCHf3XJu4f+lxCMg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.3.tgz", + "integrity": "sha512-WMXwxhvzDeA/J+9jz1i8ZKGmbw/n+s988EiUvRI4egM+eTn31Hb5v10Re3slG3/qxntkBt2/6GkQVDGu6Bwyhw==", "dev": true, "requires": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", + "jest-haste-map": "^29.6.3", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.1", - "jest-validate": "^29.6.1", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -25588,40 +25835,40 @@ } }, "jest-resolve-dependencies": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.1.tgz", - "integrity": "sha512-BbFvxLXtcldaFOhNMXmHRWx1nXQO5LoXiKSGQcA1LxxirYceZT6ch8KTE1bK3X31TNG/JbkI7OkS/ABexVahiw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.3.tgz", + "integrity": "sha512-iah5nhSPTwtUV7yzpTc9xGg8gP3Ch2VNsuFMsKoCkNCrQSbFtx5KRPemmPJ32AUhTSDqJXB6djPN6zAaUGV53g==", "dev": true, "requires": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.6.1" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.6.3" } }, "jest-runner": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.1.tgz", - "integrity": "sha512-tw0wb2Q9yhjAQ2w8rHRDxteryyIck7gIzQE4Reu3JuOBpGp96xWgF0nY8MDdejzrLCZKDcp8JlZrBN/EtkQvPQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.3.tgz", + "integrity": "sha512-E4zsMhQnjhirFPhDTJgoLMWUrVCDij/KGzWlbslDHGuO8Hl2pVUfOiygMzVZtZq+BzmlqwEr7LYmW+WFLlmX8w==", "dev": true, "requires": { - "@jest/console": "^29.6.1", - "@jest/environment": "^29.6.1", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.3", + "@jest/environment": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/transform": "^29.6.3", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.6.1", - "jest-haste-map": "^29.6.1", - "jest-leak-detector": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-resolve": "^29.6.1", - "jest-runtime": "^29.6.1", - "jest-util": "^29.6.1", - "jest-watcher": "^29.6.1", - "jest-worker": "^29.6.1", + "jest-docblock": "^29.6.3", + "jest-environment-node": "^29.6.3", + "jest-haste-map": "^29.6.3", + "jest-leak-detector": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-resolve": "^29.6.3", + "jest-runtime": "^29.6.3", + "jest-util": "^29.6.3", + "jest-watcher": "^29.6.3", + "jest-worker": "^29.6.3", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -25667,13 +25914,13 @@ "dev": true }, "jest-worker": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.1.tgz", - "integrity": "sha512-U+Wrbca7S8ZAxAe9L6nb6g8kPdia5hj32Puu5iOqBCMTMWFHXuK6dOV2IFrpedbTV8fjMFLdWNttQTBL6u2MRA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.3.tgz", + "integrity": "sha512-wacANXecZ/GbQakpf2CClrqrlwsYYDSXFd4fIGdL+dXpM2GWoJ+6bhQ7vR3TKi3+gkSfBkjy1/khH/WrYS4Q6g==", "dev": true, "requires": { "@types/node": "*", - "jest-util": "^29.6.1", + "jest-util": "^29.6.3", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -25711,31 +25958,31 @@ } }, "jest-runtime": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.1.tgz", - "integrity": "sha512-D6/AYOA+Lhs5e5il8+5pSLemjtJezUr+8zx+Sn8xlmOux3XOqx4d8l/2udBea8CRPqqrzhsKUsN/gBDE/IcaPQ==", - "dev": true, - "requires": { - "@jest/environment": "^29.6.1", - "@jest/fake-timers": "^29.6.1", - "@jest/globals": "^29.6.1", - "@jest/source-map": "^29.6.0", - "@jest/test-result": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.3.tgz", + "integrity": "sha512-VM0Z3a9xaqizGpEKwCOIhImkrINYzxgwk8oQAvrmAiXX8LNrJrRjyva30RkuRY0ETAotHLlUcd2moviCA1hgsQ==", + "dev": true, + "requires": { + "@jest/environment": "^29.6.3", + "@jest/fake-timers": "^29.6.3", + "@jest/globals": "^29.6.3", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/transform": "^29.6.3", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-mock": "^29.6.1", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.1", - "jest-snapshot": "^29.6.1", - "jest-util": "^29.6.1", + "jest-haste-map": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.3", + "jest-snapshot": "^29.6.3", + "jest-util": "^29.6.3", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -25792,9 +26039,9 @@ } }, "jest-snapshot": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.1.tgz", - "integrity": "sha512-G4UQE1QQ6OaCgfY+A0uR1W2AY0tGXUPQpoUClhWHq1Xdnx1H6JOrC2nH5lqnOEqaDgbHFgIwZ7bNq24HpB180A==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.3.tgz", + "integrity": "sha512-66Iu7H1ojiveQMGFnKecHIZPPPBjZwfQEnF6wxqpxGf57sV3YSUtAb5/sTKM5TPa3OndyxZp1wxHFbmgVhc53w==", "dev": true, "requires": { "@babel/core": "^7.11.6", @@ -25802,21 +26049,20 @@ "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.6.1", - "@jest/transform": "^29.6.1", - "@jest/types": "^29.6.1", - "@types/prettier": "^2.1.5", + "@jest/expect-utils": "^29.6.3", + "@jest/transform": "^29.6.3", + "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.6.1", + "expect": "^29.6.3", "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.1", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.1", - "jest-message-util": "^29.6.1", - "jest-util": "^29.6.1", + "jest-diff": "^29.6.3", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", "natural-compare": "^1.4.0", - "pretty-format": "^29.6.1", + "pretty-format": "^29.6.3", "semver": "^7.5.3" }, "dependencies": { @@ -25861,9 +26107,9 @@ "dev": true }, "semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -25881,12 +26127,12 @@ } }, "jest-util": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.1.tgz", - "integrity": "sha512-NRFCcjc+/uO3ijUVyNOQJluf8PtGCe/W6cix36+M3cTFgiYqFOOW5MgN4JOOcvbUhcKTYVd1CvHz/LWi8d16Mg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", + "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", "dev": true, "requires": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -25946,17 +26192,17 @@ } }, "jest-validate": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.1.tgz", - "integrity": "sha512-r3Ds69/0KCN4vx4sYAbGL1EVpZ7MSS0vLmd3gV78O+NAx3PDQQukRU5hNHPXlyqCgFY8XUk7EuTMLugh0KzahA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", + "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", "dev": true, "requires": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", + "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^29.6.1" + "pretty-format": "^29.6.3" }, "dependencies": { "ansi-styles": { @@ -26017,18 +26263,18 @@ } }, "jest-watcher": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.1.tgz", - "integrity": "sha512-d4wpjWTS7HEZPaaj8m36QiaP856JthRZkrgcIY/7ISoUWPIillrXM23WPboZVLbiwZBt4/qn2Jke84Sla6JhFA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.3.tgz", + "integrity": "sha512-NgpFjZ2U2MKusjidbi4Oiu7tfs+nrgdIxIEVROvH1cFmOei9Uj25lwkMsakqLnH/s0nEcvxO1ck77FiRlcnpZg==", "dev": true, "requires": { - "@jest/test-result": "^29.6.1", - "@jest/types": "^29.6.1", + "@jest/test-result": "^29.6.3", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.6.1", + "jest-util": "^29.6.3", "string-length": "^4.0.1" }, "dependencies": { @@ -26657,17 +26903,28 @@ "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=" }, "luxon": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", - "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==" + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.1.tgz", + "integrity": "sha512-2USspxOCXWGIKHwuQ9XElxPPYrDOJHDQ5DQ870CoD+CxJbBnRDIBCfhioUJJjct7BKOy80Ia8cVstIcIMb/0+Q==" }, "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "requires": { - "semver": "^6.0.0" + "semver": "^7.5.3" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "makeerror": { @@ -27099,9 +27356,9 @@ "dev": true }, "node-releases": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", - "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==" + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" }, "normalize-path": { "version": "3.0.0", @@ -27206,6 +27463,29 @@ "object-keys": "^1.1.1" } }, + "object.fromentries": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "object.groupby": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.0.tgz", + "integrity": "sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.21.2", + "get-intrinsic": "^1.2.1" + } + }, "object.values": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", @@ -27533,9 +27813,9 @@ "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" }, "postcss": { - "version": "8.4.26", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.26.tgz", - "integrity": "sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==", + "version": "8.4.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz", + "integrity": "sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==", "dev": true, "requires": { "nanoid": "^3.3.6", @@ -27883,12 +28163,12 @@ "dev": true }, "pretty-format": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.1.tgz", - "integrity": "sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", + "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", "dev": true, "requires": { - "@jest/schemas": "^29.6.0", + "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -28291,23 +28571,23 @@ "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, "regenerator-transform": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", - "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dev": true, "requires": { "@babel/runtime": "^7.8.4" } }, "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" } }, "regexpp": { @@ -28408,11 +28688,11 @@ "dev": true }, "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.3.tgz", + "integrity": "sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==", "requires": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.12.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -28512,6 +28792,26 @@ "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" }, + "safe-array-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", + "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -28542,9 +28842,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { - "version": "1.64.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.64.1.tgz", - "integrity": "sha512-16rRACSOFEE8VN7SCgBu1MpYCyN7urj9At898tyzdXFhC+a+yOX5dXwAR7L8/IdPJ1NB8OYoXmD55DM30B2kEQ==", + "version": "1.66.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.66.1.tgz", + "integrity": "sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", @@ -28972,6 +29272,17 @@ "strip-ansi": "^6.0.1" } }, + "string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, "string.prototype.trimend": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", @@ -29161,9 +29472,9 @@ } }, "sweetalert2": { - "version": "11.7.20", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.20.tgz", - "integrity": "sha512-GdU1TkiLpGGC0mcPV8bKmS7G0MR7caxambPkEU8zyepRSNR9EaEvIjNhX5QNkL0VFVzHbI3l12NtuEklkJ0D4Q==" + "version": "11.7.27", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.27.tgz", + "integrity": "sha512-QbRXGQn1sb7HEhzA/K2xtWIwQHh/qkSbb1w6jYcTql2xy17876lTREEt1D4X6Q0x2wHtfUjKJ+Cb8IVkRoq7DQ==" }, "symbol-tree": { "version": "3.2.4", @@ -29344,13 +29655,13 @@ } }, "tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", "dev": true, "requires": { "@types/json5": "^0.0.29", - "json5": "^1.0.1", + "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" }, @@ -29424,6 +29735,42 @@ "mime-types": "~2.1.24" } }, + "typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + } + }, "typed-array-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", @@ -30279,16 +30626,15 @@ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" }, "which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", "requires": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "has-tostringtag": "^1.0.0" } }, "wildcard": { diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 53436e8d65dc..030c75b07638 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -19,17 +19,17 @@ "eslint": "eslint js/**" }, "dependencies": { - "@fortawesome/fontawesome-free": "^6.4.0", - "@amplitude/analytics-browser": "^2.1.2", + "@fortawesome/fontawesome-free": "^6.4.2", + "@amplitude/analytics-browser": "^2.2.0", "@tarekraafat/autocomplete.js": "^10.2.7", "@walletconnect/web3-provider": "^1.8.0", "assert": "^2.0.0", "bignumber.js": "^9.1.1", "bootstrap": "^4.6.0", - "chart.js": "^4.3.2", + "chart.js": "^4.3.3", "chartjs-adapter-luxon": "^1.3.1", "clipboard": "^2.0.11", - "core-js": "^3.31.1", + "core-js": "^3.32.1", "crypto-browserify": "^3.12.0", "dropzone": "^5.9.3", "eth-net-props": "^1.0.41", @@ -56,7 +56,7 @@ "lodash.omit": "^4.5.0", "lodash.rangeright": "^4.2.0", "lodash.reduce": "^4.6.0", - "luxon": "^3.3.0", + "luxon": "^3.4.1", "malihu-custom-scrollbar-plugin": "3.1.5", "mixpanel-browser": "^2.47.0", "moment": "^2.29.4", @@ -73,7 +73,7 @@ "redux": "^4.2.1", "stream-browserify": "^3.0.0", "stream-http": "^3.1.1", - "sweetalert2": "^11.7.20", + "sweetalert2": "^11.7.27", "urijs": "^1.19.11", "url": "^0.11.1", "util": "^0.12.5", @@ -83,25 +83,25 @@ "xss": "^1.0.14" }, "devDependencies": { - "@babel/core": "^7.22.9", - "@babel/preset-env": "^7.22.9", - "autoprefixer": "^10.4.14", + "@babel/core": "^7.22.10", + "@babel/preset-env": "^7.22.10", + "autoprefixer": "^10.4.15", "babel-loader": "^9.1.3", "copy-webpack-plugin": "^11.0.0", "css-loader": "^5.2.7", "css-minimizer-webpack-plugin": "^5.0.1", - "eslint": "^8.45.0", + "eslint": "^8.47.0", "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.27.5", + "eslint-plugin-import": "^2.28.1", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^6.1.1", "file-loader": "^6.2.0", - "jest": "^29.6.1", - "jest-environment-jsdom": "^29.6.1", + "jest": "^29.6.3", + "jest-environment-jsdom": "^29.6.3", "mini-css-extract-plugin": "^2.7.6", - "postcss": "^8.4.26", + "postcss": "^8.4.28", "postcss-loader": "^7.3.3", - "sass": "^1.64.1", + "sass": "^1.66.1", "sass-loader": "^13.3.2", "style-loader": "^3.3.3", "webpack": "^5.88.2", diff --git a/apps/block_scout_web/lib/block_scout_web/api_router.ex b/apps/block_scout_web/lib/block_scout_web/api_router.ex index 12245a9d9bd5..c422faed4620 100644 --- a/apps/block_scout_web/lib/block_scout_web/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/api_router.ex @@ -13,7 +13,7 @@ defmodule BlockScoutWeb.ApiRouter do Router for API """ use BlockScoutWeb, :router - alias BlockScoutWeb.{APIKeyV2Router, SmartContractsApiV2Router} + alias BlockScoutWeb.{AddressTransactionController, APIKeyV2Router, SmartContractsApiV2Router} alias BlockScoutWeb.Plug.{CheckAccountAPI, CheckApiV2, RateLimit} forward("/v2/smart-contracts", SmartContractsApiV2Router) @@ -124,6 +124,7 @@ defmodule BlockScoutWeb.ApiRouter do scope "/search" do get("/", V2.SearchController, :search) get("/check-redirect", V2.SearchController, :check_redirect) + get("/quick", V2.SearchController, :quick_search) end scope "/config" do @@ -134,12 +135,12 @@ defmodule BlockScoutWeb.ApiRouter do scope "/transactions" do get("/", V2.TransactionController, :transactions) get("/watchlist", V2.TransactionController, :watchlist_transactions) - get("/:transaction_hash", V2.TransactionController, :transaction) - get("/:transaction_hash/token-transfers", V2.TransactionController, :token_transfers) - get("/:transaction_hash/internal-transactions", V2.TransactionController, :internal_transactions) - get("/:transaction_hash/logs", V2.TransactionController, :logs) - get("/:transaction_hash/raw-trace", V2.TransactionController, :raw_trace) - get("/:transaction_hash/state-changes", V2.TransactionController, :state_changes) + get("/:transaction_hash_param", V2.TransactionController, :transaction) + get("/:transaction_hash_param/token-transfers", V2.TransactionController, :token_transfers) + get("/:transaction_hash_param/internal-transactions", V2.TransactionController, :internal_transactions) + get("/:transaction_hash_param/logs", V2.TransactionController, :logs) + get("/:transaction_hash_param/raw-trace", V2.TransactionController, :raw_trace) + get("/:transaction_hash_param/state-changes", V2.TransactionController, :state_changes) end scope "/blocks" do @@ -151,31 +152,32 @@ defmodule BlockScoutWeb.ApiRouter do scope "/addresses" do get("/", V2.AddressController, :addresses_list) - get("/:address_hash", V2.AddressController, :address) - get("/:address_hash/counters", V2.AddressController, :counters) - get("/:address_hash/token-balances", V2.AddressController, :token_balances) - get("/:address_hash/tokens", V2.AddressController, :tokens) - get("/:address_hash/transactions", V2.AddressController, :transactions) - get("/:address_hash/token-transfers", V2.AddressController, :token_transfers) - get("/:address_hash/internal-transactions", V2.AddressController, :internal_transactions) - get("/:address_hash/logs", V2.AddressController, :logs) - get("/:address_hash/blocks-validated", V2.AddressController, :blocks_validated) - get("/:address_hash/coin-balance-history", V2.AddressController, :coin_balance_history) - get("/:address_hash/coin-balance-history-by-day", V2.AddressController, :coin_balance_history_by_day) - get("/:address_hash/withdrawals", V2.AddressController, :withdrawals) + get("/:address_hash_param", V2.AddressController, :address) + get("/:address_hash_param/tabs-counters", V2.AddressController, :tabs_counters) + get("/:address_hash_param/counters", V2.AddressController, :counters) + get("/:address_hash_param/token-balances", V2.AddressController, :token_balances) + get("/:address_hash_param/tokens", V2.AddressController, :tokens) + get("/:address_hash_param/transactions", V2.AddressController, :transactions) + get("/:address_hash_param/token-transfers", V2.AddressController, :token_transfers) + get("/:address_hash_param/internal-transactions", V2.AddressController, :internal_transactions) + get("/:address_hash_param/logs", V2.AddressController, :logs) + get("/:address_hash_param/blocks-validated", V2.AddressController, :blocks_validated) + get("/:address_hash_param/coin-balance-history", V2.AddressController, :coin_balance_history) + get("/:address_hash_param/coin-balance-history-by-day", V2.AddressController, :coin_balance_history_by_day) + get("/:address_hash_param/withdrawals", V2.AddressController, :withdrawals) end scope "/tokens" do get("/", V2.TokenController, :tokens_list) - get("/:address_hash", V2.TokenController, :token) - get("/:address_hash/counters", V2.TokenController, :counters) - get("/:address_hash/transfers", V2.TokenController, :transfers) - get("/:address_hash/holders", V2.TokenController, :holders) - get("/:address_hash/instances", V2.TokenController, :instances) - get("/:address_hash/instances/:token_id", V2.TokenController, :instance) - get("/:address_hash/instances/:token_id/transfers", V2.TokenController, :transfers_by_instance) - get("/:address_hash/instances/:token_id/holders", V2.TokenController, :holders_by_instance) - get("/:address_hash/instances/:token_id/transfers-count", V2.TokenController, :transfers_count_by_instance) + get("/:address_hash_param", V2.TokenController, :token) + get("/:address_hash_param/counters", V2.TokenController, :counters) + get("/:address_hash_param/transfers", V2.TokenController, :transfers) + get("/:address_hash_param/holders", V2.TokenController, :holders) + get("/:address_hash_param/instances", V2.TokenController, :instances) + get("/:address_hash_param/instances/:token_id", V2.TokenController, :instance) + get("/:address_hash_param/instances/:token_id/transfers", V2.TokenController, :transfers_by_instance) + get("/:address_hash_param/instances/:token_id/holders", V2.TokenController, :holders_by_instance) + get("/:address_hash_param/instances/:token_id/transfers-count", V2.TokenController, :transfers_count_by_instance) end scope "/main-page" do @@ -209,6 +211,22 @@ defmodule BlockScoutWeb.ApiRouter do # leave the same endpoint in v1 in order to keep backward compatibility get("/search", SearchController, :search) + @max_complexity 200 + + forward("/graphql", Absinthe.Plug, + schema: BlockScoutWeb.Schema, + analyze_complexity: true, + max_complexity: @max_complexity + ) + + get("/transactions-csv", AddressTransactionController, :transactions_csv) + + get("/token-transfers-csv", AddressTransactionController, :token_transfers_csv) + + get("/internal-transactions-csv", AddressTransactionController, :internal_transactions_csv) + + get("/logs-csv", AddressTransactionController, :logs_csv) + scope "/health" do get("/", HealthController, :health) get("/liveness", HealthController, :liveness) diff --git a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex index ba808cc16f2b..35a5ea606c07 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex @@ -28,11 +28,13 @@ defmodule BlockScoutWeb.AddressChannel do "transaction", "verification_result", "token_transfer", - "pending_transaction" + "pending_transaction", + "address_current_token_balances" ]) {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") @burn_address_hash burn_address_hash + @current_token_balances_limit 50 def join("addresses:" <> address_hash, _params, socket) do {:ok, %{}, assign(socket, :address_hash, address_hash)} @@ -225,6 +227,34 @@ defmodule BlockScoutWeb.AddressChannel do def handle_out("pending_transaction", data, socket), do: handle_transaction(data, socket, "transaction") + def handle_out( + "address_current_token_balances", + %{address_current_token_balances: address_current_token_balances}, + %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket + ) do + push_current_token_balances(socket, address_current_token_balances, "erc_20", "ERC-20") + push_current_token_balances(socket, address_current_token_balances, "erc_721", "ERC-721") + push_current_token_balances(socket, address_current_token_balances, "erc_1155", "ERC-1155") + + {:noreply, socket} + end + + def handle_out("address_current_token_balances", _, socket) do + {:noreply, socket} + end + + defp push_current_token_balances(socket, address_current_token_balances, event_postfix, token_type) do + filtered_ctbs = address_current_token_balances |> Enum.filter(fn ctb -> ctb.token_type == token_type end) + + push(socket, "updated_token_balances_" <> event_postfix, %{ + token_balances: + AddressViewAPI.render("token_balances.json", %{ + token_balances: Enum.take(filtered_ctbs, @current_token_balances_limit) + }), + overflow: Enum.count(filtered_ctbs) > @current_token_balances_limit + }) + end + def push_current_coin_balance( %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket, block_number, diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex index 9e37382e12a7..c352cb8e783b 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex @@ -15,6 +15,7 @@ defmodule BlockScoutWeb.AddressController do } alias Explorer.{Chain, Market} + alias Explorer.Chain.Address.Counters alias Explorer.Chain.Wei alias Indexer.Fetcher.CoinBalanceOnDemand alias Phoenix.View @@ -82,7 +83,7 @@ defmodule BlockScoutWeb.AddressController do render(conn, "index.html", current_path: Controller.current_full_path(conn), - address_count: Chain.address_estimated_count(), + address_count: Counters.address_estimated_count(), total_supply: total_supply ) end @@ -146,7 +147,7 @@ defmodule BlockScoutWeb.AddressController do def address_counters(conn, %{"id" => address_hash_string}) do with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), {:ok, address} <- Chain.hash_to_address(address_hash) do - {validation_count} = Chain.address_counters(address) + {validation_count} = Counters.address_counters(address) transactions_from_db = address.transactions_count || 0 token_transfers_from_db = address.token_transfers_count || 0 diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_token_balance_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_balance_controller.ex index 2013c7bcaad5..5899458fd887 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_token_balance_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_balance_controller.ex @@ -9,12 +9,8 @@ defmodule BlockScoutWeb.AddressTokenBalanceController do def index(conn, %{"address_id" => address_hash_string} = params) do with true <- ajax?(conn), {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string) do - token_balances = - address_hash - |> Chain.fetch_last_token_balances() - Task.start_link(fn -> - TokenBalanceOnDemand.trigger_fetch(address_hash, token_balances) + TokenBalanceOnDemand.trigger_fetch(address_hash) end) case AccessHelper.restricted_access?(address_hash_string, params) do @@ -24,7 +20,7 @@ defmodule BlockScoutWeb.AddressTokenBalanceController do |> put_layout(false) |> render("_token_balances.html", address_hash: Address.checksum(address_hash), - token_balances: token_balances, + token_balances: Chain.fetch_last_token_balances(address_hash), conn: conn ) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex index 53a5ec3add25..b27554eed39b 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex @@ -29,7 +29,7 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslator do end def call(%Conn{params: %{"module" => module, "action" => action}} = conn, translations) do - with true <- valid_api_request_path(conn), + with {:valid_api_request, true} <- {:valid_api_request, valid_api_request_path(conn)}, {:ok, {controller, write_actions}} <- translate_module(translations, module), {:ok, action} <- translate_action(action), true <- action_accessed?(action, write_actions), @@ -58,6 +58,13 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslator do :rate_limit_reached -> AccessHelper.handle_rate_limit_deny(conn) + {:valid_api_request, false} -> + conn + |> put_status(404) + |> put_view(RPCView) + |> Controller.render(:error, error: "Not found") + |> halt() + _ -> conn |> put_status(500) @@ -119,7 +126,8 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslator do end defp valid_api_request_path(conn) do - if conn.request_path == "/api" || conn.request_path == "/api/v1" do + if conn.request_path == "/api" || conn.request_path == "/api/" || conn.request_path == "/api/v1" || + conn.request_path == "/api/v1/" do true else false diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index c8062039c870..160fd28cf60d 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -17,6 +17,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do alias BlockScoutWeb.AccessHelper alias BlockScoutWeb.API.V2.{BlockView, TransactionView, WithdrawalView} alias Explorer.{Chain, Market} + alias Explorer.Chain.Address.Counters alias Indexer.Fetcher.{CoinBalanceOnDemand, TokenBalanceOnDemand} @transaction_necessity_by_association [ @@ -57,7 +58,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do action_fallback(BlockScoutWeb.API.V2.FallbackController) - def address(conn, %{"address_hash" => address_hash_string} = params) do + def address(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, address}} <- {:not_found, Chain.hash_to_address(address_hash, @address_options)} do @@ -69,11 +70,11 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end - def counters(conn, %{"address_hash" => address_hash_string} = params) do + def counters(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, address}} <- {:not_found, Chain.hash_to_address(address_hash, @api_true, false)} do - {validation_count} = Chain.address_counters(address, @api_true) + {validation_count} = Counters.address_counters(address, @api_true) transactions_from_db = address.transactions_count || 0 token_transfers_from_db = address.token_transfers_count || 0 @@ -88,7 +89,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end - def token_balances(conn, %{"address_hash" => address_hash_string} = params) do + def token_balances(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, _address}} <- {:not_found, Chain.hash_to_address(address_hash, @api_true, false)} do @@ -97,7 +98,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do |> Chain.fetch_last_token_balances(@api_true) Task.start_link(fn -> - TokenBalanceOnDemand.trigger_fetch(address_hash, token_balances) + TokenBalanceOnDemand.trigger_fetch(address_hash) end) conn @@ -106,7 +107,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end - def transactions(conn, %{"address_hash" => address_hash_string} = params) do + def transactions(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, _address}} <- {:not_found, Chain.hash_to_address(address_hash, @api_true, false)} do @@ -118,8 +119,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do results_plus_one = Chain.address_to_transactions_without_rewards(address_hash, options, false) {transactions, next_page} = split_list_by_page(results_plus_one) - next_page_params = - next_page |> next_page_params(transactions, params) |> delete_parameters_from_next_page_params() + next_page_params = next_page |> next_page_params(transactions, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -130,7 +130,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do def token_transfers( conn, - %{"address_hash" => address_hash_string, "token" => token_address_hash_string} = params + %{"address_hash_param" => address_hash_string, "token" => token_address_hash_string} = params ) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:format, {:ok, token_address_hash}} <- {:format, Chain.string_to_address_hash(token_address_hash_string)}, @@ -166,8 +166,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do next_page_params = next_page - |> token_transfers_next_page_params(token_transfers, params) - |> delete_parameters_from_next_page_params() + |> token_transfers_next_page_params(token_transfers, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -176,7 +175,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end - def token_transfers(conn, %{"address_hash" => address_hash_string} = params) do + def token_transfers(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, _address}} <- {:not_found, Chain.hash_to_address(address_hash, @api_true, false)} do @@ -198,8 +197,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do next_page_params = next_page - |> token_transfers_next_page_params(token_transfers, params) - |> delete_parameters_from_next_page_params() + |> token_transfers_next_page_params(token_transfers, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -208,7 +206,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end - def internal_transactions(conn, %{"address_hash" => address_hash_string} = params) do + def internal_transactions(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, _address}} <- {:not_found, Chain.hash_to_address(address_hash, @api_true, false)} do @@ -231,7 +229,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do {internal_transactions, next_page} = split_list_by_page(results_plus_one) next_page_params = - next_page |> next_page_params(internal_transactions, params) |> delete_parameters_from_next_page_params() + next_page |> next_page_params(internal_transactions, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -243,7 +241,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end - def logs(conn, %{"address_hash" => address_hash_string, "topic" => topic} = params) do + def logs(conn, %{"address_hash_param" => address_hash_string, "topic" => topic} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, _address}} <- {:not_found, Chain.hash_to_address(address_hash, @api_true, false)} do @@ -257,7 +255,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do {logs, next_page} = split_list_by_page(results_plus_one) - next_page_params = next_page |> next_page_params(logs, params) |> delete_parameters_from_next_page_params() + next_page_params = next_page |> next_page_params(logs, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -266,7 +264,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end - def logs(conn, %{"address_hash" => address_hash_string} = params) do + def logs(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, _address}} <- {:not_found, Chain.hash_to_address(address_hash, @api_true, false)} do @@ -276,7 +274,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do {logs, next_page} = split_list_by_page(results_plus_one) - next_page_params = next_page |> next_page_params(logs, params) |> delete_parameters_from_next_page_params() + next_page_params = next_page |> next_page_params(logs, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -285,7 +283,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end - def blocks_validated(conn, %{"address_hash" => address_hash_string} = params) do + def blocks_validated(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, _address}} <- {:not_found, Chain.hash_to_address(address_hash, @api_true, false)} do @@ -304,7 +302,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do results_plus_one = Chain.get_blocks_validated_by_address(full_options, address_hash) {blocks, next_page} = split_list_by_page(results_plus_one) - next_page_params = next_page |> next_page_params(blocks, params) |> delete_parameters_from_next_page_params() + next_page_params = next_page |> next_page_params(blocks, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -313,7 +311,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end - def coin_balance_history(conn, %{"address_hash" => address_hash_string} = params) do + def coin_balance_history(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, address}} <- {:not_found, Chain.hash_to_address(address_hash, @api_true, false)} do @@ -323,8 +321,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do {coin_balances, next_page} = split_list_by_page(results_plus_one) - next_page_params = - next_page |> next_page_params(coin_balances, params) |> delete_parameters_from_next_page_params() + next_page_params = next_page |> next_page_params(coin_balances, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -332,7 +329,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end - def coin_balance_history_by_day(conn, %{"address_hash" => address_hash_string} = params) do + def coin_balance_history_by_day(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, _address}} <- {:not_found, Chain.hash_to_address(address_hash, @api_true, false)} do @@ -346,7 +343,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end - def tokens(conn, %{"address_hash" => address_hash_string} = params) do + def tokens(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, _address}} <- {:not_found, Chain.hash_to_address(address_hash, @api_true, false)} do @@ -354,20 +351,18 @@ defmodule BlockScoutWeb.API.V2.AddressController do address_hash |> Chain.fetch_paginated_last_token_balances( params - |> delete_parameters_from_next_page_params() |> paging_options() |> Keyword.merge(token_transfers_types_options(params)) |> Keyword.merge(@api_true) ) Task.start_link(fn -> - TokenBalanceOnDemand.trigger_fetch(address_hash, results_plus_one) + TokenBalanceOnDemand.trigger_fetch(address_hash) end) {tokens, next_page} = split_list_by_page(results_plus_one) - next_page_params = - next_page |> next_page_params(tokens, params, true) |> delete_parameters_from_next_page_params() + next_page_params = next_page |> next_page_params(tokens, delete_parameters_from_next_page_params(params), true) conn |> put_status(200) @@ -375,7 +370,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end - def withdrawals(conn, %{"address_hash" => address_hash_string} = params) do + def withdrawals(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, _address}} <- {:not_found, Chain.hash_to_address(address_hash, @api_true, false)} do @@ -383,7 +378,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do withdrawals_plus_one = address_hash |> Chain.address_hash_to_withdrawals(options) {withdrawals, next_page} = split_list_by_page(withdrawals_plus_one) - next_page_params = next_page |> next_page_params(withdrawals, params) |> delete_parameters_from_next_page_params() + next_page_params = next_page |> next_page_params(withdrawals, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -414,4 +409,26 @@ defmodule BlockScoutWeb.API.V2.AddressController do total_supply: total_supply }) end + + def tabs_counters(conn, %{"address_hash_param" => address_hash_string} = params) do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), + {:not_found, {:ok, _address}} <- {:not_found, Chain.hash_to_address(address_hash, @api_true, false)} do + {validations, transactions, token_transfers, token_balances, logs, withdrawals, internal_txs, coin_balances} = + Counters.address_limited_counters(address_hash_string, @api_true) + + conn + |> put_status(200) + |> json(%{ + validations_count: validations, + transactions_count: transactions, + token_transfers_count: token_transfers, + token_balances_count: token_balances, + logs_count: logs, + withdrawals_count: withdrawals, + internal_txs_count: internal_txs, + coin_balances_count: coin_balances + }) + end + end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex index 865127d756a0..a680d7616d40 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex @@ -77,7 +77,7 @@ defmodule BlockScoutWeb.API.V2.BlockController do {blocks, next_page} = split_list_by_page(blocks_plus_one) - next_page_params = next_page |> next_page_params(blocks, params) |> delete_parameters_from_next_page_params() + next_page_params = next_page |> next_page_params(blocks, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -98,8 +98,7 @@ defmodule BlockScoutWeb.API.V2.BlockController do next_page_params = next_page - |> next_page_params(transactions, params) - |> delete_parameters_from_next_page_params() + |> next_page_params(transactions, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -118,7 +117,7 @@ defmodule BlockScoutWeb.API.V2.BlockController do withdrawals_plus_one = Chain.block_to_withdrawals(block.hash, full_options) {withdrawals, next_page} = split_list_by_page(withdrawals_plus_one) - next_page_params = next_page |> next_page_params(withdrawals, params) |> delete_parameters_from_next_page_params() + next_page_params = next_page |> next_page_params(withdrawals, delete_parameters_from_next_page_params(params)) conn |> put_status(200) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex index 78ef0980d2c5..79cad2ebffed 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex @@ -1,134 +1,235 @@ defmodule BlockScoutWeb.API.V2.FallbackController do use Phoenix.Controller + require Logger + alias BlockScoutWeb.API.V2.ApiView - def call(conn, {:format, _}) do + @verification_failed "API v2 smart-contract verification failed" + @invalid_parameters "Invalid parameter(s)" + @invalid_address_hash "Invalid address hash" + @invalid_hash "Invalid hash" + @invalid_number "Invalid number" + @invalid_url "Invalid URL" + @not_found "Not found" + @contract_interaction_disabled "Contract interaction disabled" + @restricted_access "Restricted access" + @already_verified "Already verified" + @json_not_found "JSON files not found" + @error_while_reading_json "Error while reading JSON file" + @error_in_libraries "Libraries are not valid JSON map" + @block_lost_consensus "Block lost consensus" + @invalid_captcha_resp "Invalid reCAPTCHA response" + @unauthorized "Unauthorized" + @not_configured_api_key "API key not configured on the server" + @wrong_api_key "Wrong API key" + + def call(conn, {:format, _params}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@invalid_parameters}"] + end) + conn |> put_status(:unprocessable_entity) |> put_view(ApiView) - |> render(:message, %{message: "Invalid parameter(s)"}) + |> render(:message, %{message: @invalid_parameters}) end def call(conn, {:format_address, _}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@invalid_address_hash}"] + end) + conn |> put_status(:unprocessable_entity) |> put_view(ApiView) - |> render(:message, %{message: "Invalid address hash"}) + |> render(:message, %{message: @invalid_address_hash}) end def call(conn, {:format_url, _}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@invalid_url}"] + end) + conn |> put_status(:unprocessable_entity) |> put_view(ApiView) - |> render(:message, %{message: "Invalid URL"}) + |> render(:message, %{message: @invalid_url}) end def call(conn, {:not_found, _, :empty_items_with_next_page_params}) do + Logger.error(fn -> + ["#{@verification_failed}: :empty_items_with_next_page_params"] + end) + conn |> json(%{"items" => [], "next_page_params" => nil}) end def call(conn, {:not_found, _}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@not_found}"] + end) + conn |> put_status(:not_found) |> put_view(ApiView) - |> render(:message, %{message: "Not found"}) + |> render(:message, %{message: @not_found}) end def call(conn, {:contract_interaction_disabled, _}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@contract_interaction_disabled}"] + end) + conn |> put_status(:forbidden) |> put_view(ApiView) - |> render(:message, %{message: "Contract interaction disabled"}) + |> render(:message, %{message: @contract_interaction_disabled}) end def call(conn, {:error, {:invalid, :hash}}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@invalid_hash}"] + end) + conn |> put_status(:unprocessable_entity) |> put_view(ApiView) - |> render(:message, %{message: "Invalid hash"}) + |> render(:message, %{message: @invalid_hash}) end def call(conn, {:error, {:invalid, :number}}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@invalid_number}"] + end) + conn |> put_status(:unprocessable_entity) |> put_view(ApiView) - |> render(:message, %{message: "Invalid number"}) + |> render(:message, %{message: @invalid_number}) end def call(conn, {:error, :not_found}) do + Logger.error(fn -> + ["#{@verification_failed}: :not_found"] + end) + conn |> call({:not_found, nil}) end def call(conn, {:restricted_access, true}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@restricted_access}"] + end) + conn |> put_status(:forbidden) |> put_view(ApiView) - |> render(:message, %{message: "Restricted access"}) + |> render(:message, %{message: @restricted_access}) end def call(conn, {:already_verified, true}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@already_verified}"] + end) + conn |> put_view(ApiView) - |> render(:message, %{message: "Already verified"}) + |> render(:message, %{message: @already_verified}) end def call(conn, {:no_json_file, _}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@json_not_found}"] + end) + conn |> put_view(ApiView) - |> render(:message, %{message: "JSON files not found"}) + |> render(:message, %{message: @json_not_found}) end def call(conn, {:file_error, _}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@error_while_reading_json}"] + end) + conn |> put_view(ApiView) - |> render(:message, %{message: "Error while reading JSON file"}) + |> render(:message, %{message: @error_while_reading_json}) end def call(conn, {:libs_format, _}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@error_in_libraries}"] + end) + conn |> put_view(ApiView) - |> render(:message, %{message: "Libraries are not valid JSON map"}) + |> render(:message, %{message: @error_in_libraries}) end def call(conn, {:lost_consensus, {:ok, block}}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@block_lost_consensus}"] + end) + conn |> put_status(:not_found) - |> json(%{message: "Block lost consensus", hash: to_string(block.hash)}) + |> json(%{message: @block_lost_consensus, hash: to_string(block.hash)}) end def call(conn, {:lost_consensus, {:error, :not_found}}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@block_lost_consensus}"] + end) + conn |> call({:not_found, nil}) end def call(conn, {:recaptcha, _}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@invalid_captcha_resp}"] + end) + conn |> put_status(:forbidden) |> put_view(ApiView) - |> render(:message, %{message: "Invalid reCAPTCHA response"}) + |> render(:message, %{message: @invalid_captcha_resp}) end def call(conn, {:auth, _}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@unauthorized}"] + end) + conn |> put_status(:unauthorized) |> put_view(ApiView) - |> render(:message, %{message: "Unauthorized"}) + |> render(:message, %{message: @unauthorized}) end def call(conn, {:sensitive_endpoints_api_key, _}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@not_configured_api_key}"] + end) + conn |> put_status(:forbidden) |> put_view(ApiView) - |> render(:message, %{message: "API key not configured on the server"}) + |> render(:message, %{message: @not_configured_api_key}) end def call(conn, {:api_key, _}) do + Logger.error(fn -> + ["#{@verification_failed}: #{@wrong_api_key}"] + end) + conn |> put_status(:unauthorized) |> put_view(ApiView) - |> render(:message, %{message: "Wrong API key"}) + |> render(:message, %{message: @wrong_api_key}) end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/import_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/import_controller.ex index 29175a1ad697..a89cbb323e74 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/import_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/import_controller.ex @@ -18,7 +18,7 @@ defmodule BlockScoutWeb.API.V2.ImportController do {:format_address, Chain.string_to_address_hash(token_address_hash_string)}, {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)}, {:format_url, true} <- {:format_url, valid_url?(icon_url)} do - case token |> Token.changeset(%{icon_url: icon_url}) |> Repo.update() do + case token |> Token.changeset(%{icon_url: icon_url, is_verified_via_admin_panel: true}) |> Repo.update() do {:ok, _} -> conn |> put_view(ApiView) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/search_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/search_controller.ex index e76b4408e2f0..abe0aca31486 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/search_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/search_controller.ex @@ -3,7 +3,10 @@ defmodule BlockScoutWeb.API.V2.SearchController do import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1, from_param: 1] - alias Explorer.Chain + alias Explorer.Chain.Search + alias Explorer.PagingOptions + + @api_true [api?: true] def search(conn, %{"q" => query} = params) do [paging_options: paging_options] = paging_options(params) @@ -11,7 +14,7 @@ defmodule BlockScoutWeb.API.V2.SearchController do search_results_plus_one = paging_options - |> Chain.joint_search(offset, query, api?: true) + |> Search.joint_search(offset, query, @api_true) {search_results, next_page} = split_list_by_page(search_results_plus_one) @@ -32,4 +35,12 @@ defmodule BlockScoutWeb.API.V2.SearchController do |> put_status(200) |> render(:search_results, %{result: result}) end + + def quick_search(conn, %{"q" => query}) do + search_results = Search.balanced_unpaginated_search(%PagingOptions{page_size: 50}, query, @api_true) + + conn + |> put_status(200) + |> render(:search_results, %{search_results: search_results}) + end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex index 8d016c404714..34ad68ca255c 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex @@ -82,7 +82,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do {:not_found, true} <- {:not_found, AddressView.check_custom_abi_for_having_write_functions(custom_abi)} do conn |> put_status(200) - |> json(Writer.filter_write_functions(custom_abi.abi)) + |> json(custom_abi.abi |> Writer.filter_write_functions() |> Reader.get_abi_with_method_id()) end end @@ -95,7 +95,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do {:not_found, false} <- {:not_found, is_nil(smart_contract)} do conn |> put_status(200) - |> json(Writer.write_functions(smart_contract)) + |> json(smart_contract |> Writer.write_functions() |> Reader.get_abi_with_method_id()) end end @@ -135,7 +135,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do conn |> put_status(200) - |> json(Writer.write_functions_proxy(implementation_address_hash_string, @api_true)) + |> json( + implementation_address_hash_string + |> Writer.write_functions_proxy(@api_true) + |> Reader.get_abi_with_method_id() + ) end end @@ -200,8 +204,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do next_page_params = next_page - |> next_page_params(smart_contracts, params) - |> delete_parameters_from_next_page_params() + |> next_page_params(smart_contracts, delete_parameters_from_next_page_params(params)) conn |> put_status(200) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex index 6ffbd0c6bbfe..098b05f7e9c8 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex @@ -4,6 +4,7 @@ defmodule BlockScoutWeb.API.V2.StatsController do alias BlockScoutWeb.API.V2.Helper alias BlockScoutWeb.Chain.MarketHistoryChartController alias Explorer.{Chain, Market} + alias Explorer.Chain.Address.Counters alias Explorer.Chain.Cache.Block, as: BlockCache alias Explorer.Chain.Cache.{GasPriceOracle, GasUsage} alias Explorer.Chain.Cache.Transaction, as: TransactionCache @@ -43,7 +44,7 @@ defmodule BlockScoutWeb.API.V2.StatsController do conn, %{ "total_blocks" => BlockCache.estimated_count() |> to_string(), - "total_addresses" => @api_true |> Chain.address_estimated_count() |> to_string(), + "total_addresses" => @api_true |> Counters.address_estimated_count() |> to_string(), "total_transactions" => TransactionCache.estimated_count() |> to_string(), "average_block_time" => AverageBlockTime.average_block_time() |> Duration.to_milliseconds(), "coin_price" => exchange_rate.usd_value, @@ -94,7 +95,7 @@ defmodule BlockScoutWeb.API.V2.StatsController do exchange_rate = Market.get_coin_exchange_rate() recent_market_history = Market.fetch_recent_history() - current_total_supply = available_supply(Chain.supply_for_days(), exchange_rate) + current_total_supply = MarketHistoryChartController.available_supply(Chain.supply_for_days(), exchange_rate) price_history_data = recent_market_history @@ -122,8 +123,4 @@ defmodule BlockScoutWeb.API.V2.StatsController do available_supply: current_total_supply }) end - - defp available_supply(:ok, exchange_rate), do: exchange_rate.available_supply || 0 - - defp available_supply({:ok, supply_for_days}, _exchange_rate), do: supply_for_days end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex index 647e38ab6283..1ec2a32a3e0b 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex @@ -23,7 +23,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do @api_true [api?: true] - def token(conn, %{"address_hash" => address_hash_string} = params) do + def token(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)} do @@ -35,7 +35,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do end end - def counters(conn, %{"address_hash" => address_hash_string} = params) do + def counters(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, true} <- {:not_found, Chain.token_from_address_hash_exists?(address_hash, @api_true)} do @@ -45,7 +45,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do end end - def transfers(conn, %{"address_hash" => address_hash_string} = params) do + def transfers(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, true} <- {:not_found, Chain.token_from_address_hash_exists?(address_hash, @api_true)} do @@ -61,8 +61,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do next_page_params = next_page - |> token_transfers_next_page_params(token_transfers, params) - |> delete_parameters_from_next_page_params() + |> token_transfers_next_page_params(token_transfers, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -71,7 +70,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do end end - def holders(conn, %{"address_hash" => address_hash_string} = params) do + def holders(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)} do @@ -80,8 +79,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do {token_balances, next_page} = split_list_by_page(results_plus_one) - next_page_params = - next_page |> next_page_params(token_balances, params) |> delete_parameters_from_next_page_params() + next_page_params = next_page |> next_page_params(token_balances, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -89,7 +87,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do end end - def instances(conn, %{"address_hash" => address_hash_string} = params) do + def instances(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)} do @@ -102,7 +100,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do {token_instances, next_page} = split_list_by_page(results_plus_one) next_page_params = - next_page |> unique_tokens_next_page(token_instances, params) |> delete_parameters_from_next_page_params() + next_page |> unique_tokens_next_page(token_instances, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -110,7 +108,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do end end - def instance(conn, %{"address_hash" => address_hash_string, "token_id" => token_id_str} = params) do + def instance(conn, %{"address_hash_param" => address_hash_string, "token_id" => token_id_str} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)}, @@ -131,7 +129,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do end end - def transfers_by_instance(conn, %{"address_hash" => address_hash_string, "token_id" => token_id_str} = params) do + def transfers_by_instance(conn, %{"address_hash_param" => address_hash_string, "token_id" => token_id_str} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)}, @@ -149,8 +147,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do next_page_params = next_page - |> token_transfers_next_page_params(token_transfers, params) - |> delete_parameters_from_next_page_params() + |> token_transfers_next_page_params(token_transfers, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -159,7 +156,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do end end - def holders_by_instance(conn, %{"address_hash" => address_hash_string, "token_id" => token_id_str} = params) do + def holders_by_instance(conn, %{"address_hash_param" => address_hash_string, "token_id" => token_id_str} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)}, @@ -178,8 +175,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do next_page_params = next_page - |> next_page_params(token_holders, params) - |> delete_parameters_from_next_page_params() + |> next_page_params(token_holders, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -187,7 +183,10 @@ defmodule BlockScoutWeb.API.V2.TokenController do end end - def transfers_count_by_instance(conn, %{"address_hash" => address_hash_string, "token_id" => token_id_str} = params) do + def transfers_count_by_instance( + conn, + %{"address_hash_param" => address_hash_string, "token_id" => token_id_str} = params + ) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)}, @@ -213,7 +212,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do {tokens, next_page} = filter |> Chain.list_top_tokens(options) |> split_list_by_page() - next_page_params = next_page |> next_page_params(tokens, params) |> delete_parameters_from_next_page_params() + next_page_params = next_page |> next_page_params(tokens, delete_parameters_from_next_page_params(params)) conn |> put_status(200) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex index 94d060f918ad..973fdc7094dd 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex @@ -62,7 +62,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do @api_true [api?: true] - def transaction(conn, %{"transaction_hash" => transaction_hash_string} = params) do + def transaction(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)}, {:not_found, {:ok, transaction}} <- {:not_found, @@ -97,14 +97,14 @@ defmodule BlockScoutWeb.API.V2.TransactionController do {transactions, next_page} = split_list_by_page(transactions_plus_one) - next_page_params = next_page |> next_page_params(transactions, params) |> delete_parameters_from_next_page_params() + next_page_params = next_page |> next_page_params(transactions, delete_parameters_from_next_page_params(params)) conn |> put_status(200) |> render(:transactions, %{transactions: transactions, next_page_params: next_page_params}) end - def raw_trace(conn, %{"transaction_hash" => transaction_hash_string} = params) do + def raw_trace(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)}, {:not_found, {:ok, transaction}} <- {:not_found, Chain.hash_to_transaction(transaction_hash, @api_true)}, @@ -133,7 +133,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do end end - def token_transfers(conn, %{"transaction_hash" => transaction_hash_string} = params) do + def token_transfers(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)}, {:not_found, {:ok, transaction}} <- {:not_found, Chain.hash_to_transaction(transaction_hash, @api_true)}, @@ -157,8 +157,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do next_page_params = next_page - |> token_transfers_next_page_params(token_transfers, params) - |> delete_parameters_from_next_page_params() + |> token_transfers_next_page_params(token_transfers, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -166,7 +165,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do end end - def internal_transactions(conn, %{"transaction_hash" => transaction_hash_string} = params) do + def internal_transactions(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)}, {:not_found, {:ok, transaction}} <- {:not_found, Chain.hash_to_transaction(transaction_hash, @api_true)}, @@ -183,8 +182,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do next_page_params = next_page - |> next_page_params(internal_transactions, params) - |> delete_parameters_from_next_page_params() + |> next_page_params(internal_transactions, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -195,7 +193,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do end end - def logs(conn, %{"transaction_hash" => transaction_hash_string} = params) do + def logs(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)}, {:not_found, {:ok, transaction}} <- {:not_found, Chain.hash_to_transaction(transaction_hash, @api_true)}, @@ -218,8 +216,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do next_page_params = next_page - |> next_page_params(logs, params) - |> delete_parameters_from_next_page_params() + |> next_page_params(logs, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -231,7 +228,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do end end - def state_changes(conn, %{"transaction_hash" => transaction_hash_string} = params) do + def state_changes(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)}, {:not_found, {:ok, transaction}} <- {:not_found, @@ -249,8 +246,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do next_page_params = next_page - |> next_page_params(state_changes, params) - |> delete_parameters_from_next_page_params() + |> next_page_params(state_changes, delete_parameters_from_next_page_params(params)) conn |> put_status(200) @@ -271,8 +267,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do {transactions, next_page} = split_list_by_page(transactions_plus_one) - next_page_params = - next_page |> next_page_params(transactions, params) |> delete_parameters_from_next_page_params() + next_page_params = next_page |> next_page_params(transactions, delete_parameters_from_next_page_params(params)) conn |> put_status(200) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex index 49c98c5153ee..b2bbb07d3189 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex @@ -3,6 +3,8 @@ defmodule BlockScoutWeb.API.V2.VerificationController do import Explorer.SmartContract.Solidity.Verifier, only: [parse_boolean: 1] + require Logger + alias BlockScoutWeb.AccessHelper alias BlockScoutWeb.API.V2.ApiView alias Explorer.Chain @@ -14,6 +16,7 @@ defmodule BlockScoutWeb.API.V2.VerificationController do action_fallback(BlockScoutWeb.API.V2.FallbackController) @api_true [api?: true] + @sc_verification_started "Smart-contract verification started" def config(conn, _params) do solidity_compiler_versions = CompilerVersion.fetch_version_list(:solc) @@ -46,6 +49,8 @@ defmodule BlockScoutWeb.API.V2.VerificationController do %{"address_hash" => address_hash_string, "compiler_version" => compiler_version, "source_code" => source_code} = params ) do + Logger.info("API v2 smart-contract #{address_hash_string} verification via flattened file") + with :validated <- validate_address(params) do verification_params = %{ @@ -65,11 +70,12 @@ defmodule BlockScoutWeb.API.V2.VerificationController do |> Map.put("external_libraries", Map.get(params, "libraries", %{})) |> Map.put("is_yul", Map.get(params, "is_yul_contract", false)) + log_sc_verification_started(address_hash_string) Que.add(SolidityPublisherWorker, {"flattened_api_v2", verification_params}) conn |> put_view(ApiView) - |> render(:message, %{message: "Verification started"}) + |> render(:message, %{message: @sc_verification_started}) end end @@ -77,6 +83,8 @@ defmodule BlockScoutWeb.API.V2.VerificationController do conn, %{"address_hash" => address_hash_string, "files" => _files, "compiler_version" => compiler_version} = params ) do + Logger.info("API v2 smart-contract #{address_hash_string} verification via standard json input") + with {:json_input, json_input} <- validate_params_standard_json_input(params) do verification_params = %{ @@ -87,15 +95,18 @@ defmodule BlockScoutWeb.API.V2.VerificationController do |> Map.put("constructor_arguments", Map.get(params, "constructor_args", "")) |> Map.put("name", Map.get(params, "contract_name", "")) + log_sc_verification_started(address_hash_string) Que.add(SolidityPublisherWorker, {"json_api_v2", verification_params, json_input}) conn |> put_view(ApiView) - |> render(:message, %{message: "Verification started"}) + |> render(:message, %{message: @sc_verification_started}) end end def verification_via_sourcify(conn, %{"address_hash" => address_hash_string, "files" => files} = params) do + Logger.info("API v2 smart-contract #{address_hash_string} verification via Sourcify") + with {:not_found, true} <- {:not_found, Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled]}, :validated <- validate_address(params), @@ -105,6 +116,8 @@ defmodule BlockScoutWeb.API.V2.VerificationController do files_content <- PublishHelper.read_files(files_array) do chosen_contract = params["chosen_contract_index"] + log_sc_verification_started(address_hash_string) + Que.add( SolidityPublisherWorker, {"sourcify_api_v2", String.downcase(address_hash_string), files_content, conn, chosen_contract} @@ -112,7 +125,7 @@ defmodule BlockScoutWeb.API.V2.VerificationController do conn |> put_view(ApiView) - |> render(:message, %{message: "Verification started"}) + |> render(:message, %{message: @sc_verification_started}) end end @@ -120,6 +133,8 @@ defmodule BlockScoutWeb.API.V2.VerificationController do conn, %{"address_hash" => address_hash_string, "compiler_version" => compiler_version, "files" => files} = params ) do + Logger.info("API v2 smart-contract #{address_hash_string} verification via multipart") + with :verifier_enabled <- check_microservice(), :validated <- validate_address(params), libraries <- Map.get(params, "libraries", "{}"), @@ -142,11 +157,12 @@ defmodule BlockScoutWeb.API.V2.VerificationController do |> PublishHelper.prepare_files_array() |> PublishHelper.read_files() + log_sc_verification_started(address_hash_string) Que.add(SolidityPublisherWorker, {"multipart_api_v2", verification_params, files_array}) conn |> put_view(ApiView) - |> render(:message, %{message: "Verification started"}) + |> render(:message, %{message: @sc_verification_started}) end end @@ -166,11 +182,12 @@ defmodule BlockScoutWeb.API.V2.VerificationController do |> Map.put("name", Map.get(params, "contract_name", "Vyper_contract")) |> Map.put("evm_version", Map.get(params, "evm_version")) + log_sc_verification_started(address_hash_string) Que.add(VyperPublisherWorker, {"vyper_flattened", verification_params}) conn |> put_view(ApiView) - |> render(:message, %{message: "Verification started"}) + |> render(:message, %{message: @sc_verification_started}) end end @@ -178,6 +195,8 @@ defmodule BlockScoutWeb.API.V2.VerificationController do conn, %{"address_hash" => address_hash_string, "compiler_version" => compiler_version, "files" => files} = params ) do + Logger.info("API v2 vyper smart-contract #{address_hash_string} verification") + with :verifier_enabled <- check_microservice(), :validated <- validate_address(params) do interfaces = parse_interfaces(params["interfaces"]) @@ -195,11 +214,12 @@ defmodule BlockScoutWeb.API.V2.VerificationController do |> PublishHelper.prepare_files_array() |> PublishHelper.read_files() + log_sc_verification_started(address_hash_string) Que.add(VyperPublisherWorker, {"vyper_multipart", verification_params, files_array}) conn |> put_view(ApiView) - |> render(:message, %{message: "Verification started"}) + |> render(:message, %{message: @sc_verification_started}) end end @@ -207,6 +227,8 @@ defmodule BlockScoutWeb.API.V2.VerificationController do conn, %{"address_hash" => address_hash_string, "files" => _files, "compiler_version" => compiler_version} = params ) do + Logger.info("API v2 vyper smart-contract #{address_hash_string} verification via standard json input") + with :verifier_enabled <- check_microservice(), {:json_input, json_input} <- validate_params_standard_json_input(params) do verification_params = %{ @@ -215,11 +237,12 @@ defmodule BlockScoutWeb.API.V2.VerificationController do "input" => json_input } + log_sc_verification_started(address_hash_string) Que.add(VyperPublisherWorker, {"vyper_standard_json", verification_params}) conn |> put_view(ApiView) - |> render(:message, %{message: "Verification started"}) + |> render(:message, %{message: @sc_verification_started}) end end @@ -269,4 +292,8 @@ defmodule BlockScoutWeb.API.V2.VerificationController do :verifier_enabled end end + + defp log_sc_verification_started(address_hash_string) do + Logger.info("API v2 smart-contract #{address_hash_string} verification request sent to the microservice") + end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/withdrawal_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/withdrawal_controller.ex index 396e66712a54..fc26823e5211 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/withdrawal_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/withdrawal_controller.ex @@ -16,7 +16,7 @@ defmodule BlockScoutWeb.API.V2.WithdrawalController do withdrawals_plus_one = Chain.list_withdrawals(full_options) {withdrawals, next_page} = split_list_by_page(withdrawals_plus_one) - next_page_params = next_page |> next_page_params(withdrawals, params) |> delete_parameters_from_next_page_params() + next_page_params = next_page |> next_page_params(withdrawals, delete_parameters_from_next_page_params(params)) conn |> put_status(200) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex index 5ab997f4a91e..a9816adcbd04 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex @@ -29,16 +29,22 @@ defmodule BlockScoutWeb.Chain.MarketHistoryChartController do end end - defp available_supply(:ok, exchange_rate) do - to_string(exchange_rate.available_supply || 0) - end + def available_supply(:ok, exchange_rate), do: exchange_rate.available_supply || 0 - defp available_supply({:ok, supply_for_days}, _exchange_rate) do + def available_supply({:ok, supply_for_days}, _exchange_rate) do supply_for_days |> Jason.encode() |> case do - {:ok, data} -> data - _ -> [] + {:ok, _data} -> + current_date = + supply_for_days + |> Map.keys() + |> Enum.max(Date) + + Map.get(supply_for_days, current_date) + + _ -> + nil end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex index afa8143329b3..eabc844c7d01 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex @@ -6,10 +6,12 @@ defmodule BlockScoutWeb.ChainController do alias BlockScoutWeb.API.V2.Helper alias BlockScoutWeb.{ChainView, Controller} alias Explorer.{Chain, PagingOptions, Repo} + alias Explorer.Chain.Address.Counters alias Explorer.Chain.{Address, Block, Transaction} alias Explorer.Chain.Cache.Block, as: BlockCache alias Explorer.Chain.Cache.GasUsage alias Explorer.Chain.Cache.Transaction, as: TransactionCache + alias Explorer.Chain.Search alias Explorer.Chain.Supply.RSK alias Explorer.Counters.AverageBlockTime alias Explorer.Market @@ -19,7 +21,7 @@ defmodule BlockScoutWeb.ChainController do transaction_estimated_count = TransactionCache.estimated_count() total_gas_usage = GasUsage.total() block_count = BlockCache.estimated_count() - address_count = Chain.address_estimated_count() + address_count = Counters.address_estimated_count() market_cap_calculation = case Application.get_env(:explorer, :supply) do @@ -90,7 +92,7 @@ defmodule BlockScoutWeb.ChainController do results = paging_options - |> Chain.joint_search(offset, term) + |> Search.joint_search(offset, term) encoded_results = results diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/search_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/search_controller.ex index b2f639a6e3a9..937c4b2603a6 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/search_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/search_controller.ex @@ -4,7 +4,7 @@ defmodule BlockScoutWeb.SearchController do import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] alias BlockScoutWeb.{Controller, SearchView} - alias Explorer.Chain + alias Explorer.Chain.Search alias Phoenix.View def search_results(conn, %{"q" => query, "type" => "JSON"} = params) do @@ -13,7 +13,7 @@ defmodule BlockScoutWeb.SearchController do search_results_plus_one = paging_options - |> Chain.joint_search(offset, query) + |> Search.joint_search(offset, query) {search_results, next_page} = split_list_by_page(search_results_plus_one) diff --git a/apps/block_scout_web/lib/block_scout_web/notifier.ex b/apps/block_scout_web/lib/block_scout_web/notifier.ex index fc69ceb3e27d..a82202c4acdc 100644 --- a/apps/block_scout_web/lib/block_scout_web/notifier.ex +++ b/apps/block_scout_web/lib/block_scout_web/notifier.ex @@ -3,6 +3,8 @@ defmodule BlockScoutWeb.Notifier do Responds to events by sending appropriate channel updates to front-end. """ + require Logger + alias Absinthe.Subscription alias BlockScoutWeb.API.V2, as: API_V2 @@ -17,6 +19,7 @@ defmodule BlockScoutWeb.Notifier do } alias Explorer.{Chain, Market, Repo} + alias Explorer.Chain.Address.Counters alias Explorer.Chain.{Address, InternalTransaction, Transaction} alias Explorer.Chain.Supply.RSK alias Explorer.Chain.Transaction.History.TransactionStats @@ -27,7 +30,7 @@ defmodule BlockScoutWeb.Notifier do @check_broadcast_sequence_period 500 def handle_event({:chain_event, :addresses, type, addresses}) when type in [:realtime, :on_demand] do - Endpoint.broadcast("addresses:new_address", "count", %{count: Chain.address_estimated_count()}) + Endpoint.broadcast("addresses:new_address", "count", %{count: Counters.address_estimated_count()}) addresses |> Stream.reject(fn %Address{fetched_coin_balance: fetched_coin_balance} -> is_nil(fetched_coin_balance) end) @@ -44,14 +47,11 @@ defmodule BlockScoutWeb.Notifier do Enum.each(address_token_balances, &broadcast_address_token_balance/1) end - def handle_event({:chain_event, :address_current_token_balances, type, address_current_token_balances}) - when type in [:realtime, :on_demand] do - Enum.each(address_current_token_balances, &broadcast_address_token_balance/1) - end - def handle_event( {:chain_event, :contract_verification_result, :on_demand, {address_hash, contract_verification_result}} ) do + log_broadcast_verification_results_for_address(address_hash) + Endpoint.broadcast( "addresses:#{address_hash}", "verification_result", @@ -64,6 +64,7 @@ defmodule BlockScoutWeb.Notifier do def handle_event( {:chain_event, :contract_verification_result, :on_demand, {address_hash, contract_verification_result, conn}} ) do + log_broadcast_verification_results_for_address(address_hash) %{view: view, compiler: compiler} = select_contract_type_and_form_view(conn.params) contract_verification_result = @@ -222,10 +223,20 @@ defmodule BlockScoutWeb.Notifier do end def handle_event({:chain_event, :smart_contract_was_verified, :on_demand, [address_hash]}) do + log_broadcast_smart_contract_was_verified(address_hash) Endpoint.broadcast("addresses:#{to_string(address_hash)}", "smart_contract_was_verified", %{}) end - def handle_event(_), do: nil + def handle_event({:chain_event, :address_current_token_balances, :on_demand, address_current_token_balances}) do + Endpoint.broadcast("addresses:#{address_current_token_balances.address_hash}", "address_current_token_balances", %{ + address_current_token_balances: address_current_token_balances.address_current_token_balances + }) + end + + def handle_event(event) do + Logger.warning("Unknown broadcasted event #{inspect(event)}.") + nil + end def fetch_compiler_version(compiler) do case CompilerVersion.fetch_versions(compiler) do @@ -479,4 +490,12 @@ defmodule BlockScoutWeb.Notifier do Endpoint.broadcast("addresses:#{address_hash}", event, %{map_key => elements}) end end + + defp log_broadcast_verification_results_for_address(address_hash) do + Logger.info("Broadcast smart-contract #{address_hash} verification results") + end + + defp log_broadcast_smart_contract_was_verified(address_hash) do + Logger.info("Broadcast smart-contract #{address_hash} was verified") + end end diff --git a/apps/block_scout_web/lib/block_scout_web/paging_helper.ex b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex index bcbbc9b0a274..860a8f7d4a15 100644 --- a/apps/block_scout_web/lib/block_scout_web/paging_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex @@ -129,12 +129,11 @@ defmodule BlockScoutWeb.PagingHelper do params |> Map.drop([ "block_hash_or_number", - "transaction_hash", - "address_hash", + "transaction_hash_param", + "address_hash_param", "type", "method", "filter", - "token_address_hash", "q", "sort", "order" diff --git a/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex b/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex index 7c0a05969912..47c9289f8301 100644 --- a/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex +++ b/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex @@ -26,6 +26,7 @@ defmodule BlockScoutWeb.RealtimeEventHandler do Subscriber.to(:transactions, :realtime) Subscriber.to(:addresses, :on_demand) Subscriber.to(:address_coin_balances, :on_demand) + Subscriber.to(:address_current_token_balances, :on_demand) Subscriber.to(:address_token_balances, :on_demand) Subscriber.to(:contract_verification_result, :on_demand) Subscriber.to(:token_total_supply, :on_demand) diff --git a/apps/block_scout_web/lib/block_scout_web/router.ex b/apps/block_scout_web/lib/block_scout_web/router.ex index 8b7421b2773b..ff9d5afcc609 100644 --- a/apps/block_scout_web/lib/block_scout_web/router.ex +++ b/apps/block_scout_web/lib/block_scout_web/router.ex @@ -28,12 +28,6 @@ defmodule BlockScoutWeb.Router do # Needs to be 200 to support the schema introspection for graphiql @max_complexity 200 - forward("/graphql", Absinthe.Plug, - schema: BlockScoutWeb.Schema, - analyze_complexity: true, - max_complexity: @max_complexity - ) - forward("/graphiql", Absinthe.Plug.GraphiQL, schema: BlockScoutWeb.Schema, interface: :advanced, diff --git a/apps/block_scout_web/lib/block_scout_web/schema/types.ex b/apps/block_scout_web/lib/block_scout_web/schema/types.ex index 426d9909f59b..99f47a29163b 100644 --- a/apps/block_scout_web/lib/block_scout_web/schema/types.ex +++ b/apps/block_scout_web/lib/block_scout_web/schema/types.ex @@ -22,10 +22,14 @@ defmodule BlockScoutWeb.Schema.Types do A stored representation of a Web3 address. """ object :address do - field(:hash, :address_hash) field(:fetched_coin_balance, :wei) field(:fetched_coin_balance_block_number, :integer) + field(:hash, :address_hash) field(:contract_code, :data) + field(:nonce, :integer) + field(:gas_used, :integer) + field(:transactions_count, :integer) + field(:token_transfers_count, :integer) field :smart_contract, :smart_contract do resolve(dataloader(:db, :smart_contract)) @@ -36,16 +40,7 @@ defmodule BlockScoutWeb.Schema.Types do arg(:order, type: :sort_order, default_value: :desc) resolve(&Transaction.get_by/3) - complexity(fn - %{first: first}, child_complexity -> - first * child_complexity - - %{last: last}, child_complexity -> - last * child_complexity - - %{}, _child_complexity -> - 0 - end) + complexity(fn params, child_complexity -> process_complexity(params, child_complexity) end) end end @@ -55,18 +50,20 @@ defmodule BlockScoutWeb.Schema.Types do structure that they form is called a "blockchain". """ object :block do - field(:hash, :full_hash) field(:consensus, :boolean) field(:difficulty, :decimal) field(:gas_limit, :decimal) field(:gas_used, :decimal) + field(:hash, :full_hash) + field(:miner_hash, :address_hash) field(:nonce, :nonce_hash) field(:number, :integer) + field(:parent_hash, :full_hash) field(:size, :integer) field(:timestamp, :datetime) field(:total_difficulty, :decimal) - field(:miner_hash, :address_hash) - field(:parent_hash, :full_hash) + field(:base_fee_per_gas, :wei) + field(:is_empty, :boolean) end @desc """ @@ -85,12 +82,14 @@ defmodule BlockScoutWeb.Schema.Types do field(:trace_address, :json) field(:type, :type) field(:value, :wei) - field(:block_number, :integer) - field(:transaction_index, :integer) field(:created_contract_address_hash, :address_hash) field(:from_address_hash, :address_hash) field(:to_address_hash, :address_hash) field(:transaction_hash, :full_hash) + field(:block_number, :integer) + field(:transaction_index, :integer) + field(:block_hash, :full_hash) + field(:block_index, :integer) end @desc """ @@ -108,6 +107,19 @@ defmodule BlockScoutWeb.Schema.Types do field(:contract_source_code, :string) field(:abi, :json) field(:address_hash, :address_hash) + field(:constructor_arguments, :string) + field(:optimization_runs, :integer) + field(:evm_version, :string) + field(:external_libraries, :json) + field(:verified_via_sourcify, :boolean) + field(:partially_verified, :boolean) + field(:file_path, :string) + field(:is_vyper_contract, :boolean) + field(:is_changed_bytecode, :boolean) + field(:implementation_name, :string) + field(:implementation_address_hash, :address_hash) + field(:compiler_settings, :json) + field(:verified_via_eth_bytecode_db, :boolean) end @desc """ @@ -130,13 +142,12 @@ defmodule BlockScoutWeb.Schema.Types do Models a Web3 transaction. """ node object(:transaction, id_fetcher: &transaction_id_fetcher/2) do - field(:hash, :full_hash) - field(:block_number, :integer) field(:cumulative_gas_used, :decimal) field(:error, :string) field(:gas, :decimal) field(:gas_price, :wei) field(:gas_used, :decimal) + field(:hash, :full_hash) field(:index, :integer) field(:input, :string) field(:nonce, :nonce_hash) @@ -145,24 +156,23 @@ defmodule BlockScoutWeb.Schema.Types do field(:status, :status) field(:v, :decimal) field(:value, :wei) + field(:block_hash, :full_hash) + field(:block_number, :integer) field(:from_address_hash, :address_hash) field(:to_address_hash, :address_hash) field(:created_contract_address_hash, :address_hash) + field(:earliest_processing_start, :datetime) + field(:revert_reason, :string) + field(:max_priority_fee_per_gas, :wei) + field(:max_fee_per_gas, :wei) + field(:type, :integer) + field(:has_error_in_internal_txs, :boolean) connection field(:internal_transactions, node_type: :internal_transaction) do arg(:count, :integer) resolve(&InternalTransaction.get_by/3) - complexity(fn - %{first: first}, child_complexity -> - first * child_complexity - - %{last: last}, child_complexity -> - last * child_complexity - - %{}, _child_complexity -> - 0 - end) + complexity(fn params, child_complexity -> process_complexity(params, child_complexity) end) end end @@ -175,4 +185,17 @@ defmodule BlockScoutWeb.Schema.Types do def internal_transaction_id_fetcher(%{transaction_hash: transaction_hash, index: index}, _) do Jason.encode!(%{transaction_hash: to_string(transaction_hash), index: index}) end + + defp process_complexity(params, child_complexity) do + case params do + %{first: first} -> + first * child_complexity + + %{last: last} -> + last * child_complexity + + %{} -> + 0 + end + end end diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex index de29c7e7fd9d..a4060c9485d9 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex @@ -8,7 +8,7 @@ class: "card-tab #{tab_status("transactions", @conn.request_path)}", to: AccessHelper.get_path(@conn, :address_transaction_path, :index, @address.hash) ) %> - <%= if Chain.check_if_token_transfers_at_address(@address.hash) do %> + <%= if Counters.check_if_token_transfers_at_address(@address.hash) do %> <%= link( gettext("Token Transfers"), class: "card-tab #{tab_status("token-transfers", @conn.request_path)}", @@ -16,7 +16,7 @@ to: AccessHelper.get_path(@conn, :address_token_transfers_path, :index, @address.hash) ) %> <% end %> - <%= if Chain.check_if_tokens_at_address(@address.hash) do %> + <%= if Counters.check_if_tokens_at_address(@address.hash) do %> <%= link( gettext("Tokens"), class: "card-tab #{tab_status("tokens", @conn.request_path)}", @@ -24,7 +24,7 @@ "data-test": "tokens_tab_link" ) %> <% end %> - <%= if Chain.check_if_withdrawals_at_address(@address.hash) do %> + <%= if Counters.check_if_withdrawals_at_address(@address.hash) do %> <%= link( gettext("Withdrawals"), class: "card-tab #{tab_status("withdrawals", @conn.request_path)}", @@ -44,14 +44,14 @@ "data-test": "coin_balance_tab_link", to: AccessHelper.get_path(@conn, :address_coin_balance_path, :index, @address.hash) ) %> - <%= if Chain.check_if_logs_at_address(@address.hash) do %> + <%= if Counters.check_if_logs_at_address(@address.hash) do %> <%= link( gettext("Logs"), class: "card-tab #{tab_status("logs", @conn.request_path)}", to: AccessHelper.get_path(@conn, :address_logs_path, :index, @address.hash) ) %> <% end %> - <%= if Chain.check_if_validated_blocks_at_address(@address.hash) do %> + <%= if Counters.check_if_validated_blocks_at_address(@address.hash) do %> <%= link( gettext("Blocks Validated"), class: "card-tab #{tab_status("validations", @conn.request_path)}", diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex index 658acfd417b4..77c96739cb5d 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex @@ -43,7 +43,7 @@ <% end %> <%= if smart_contract_verified || (!smart_contract_verified && metadata_for_verification) do %> <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: metadata_for_verification %> - <%= if @address.smart_contract.partially_verified && smart_contract_verified do %> + <%= if @address.smart_contract.verified_via_sourcify && @address.smart_contract.partially_verified && smart_contract_verified do %>
<%= gettext("This contract has been partially verified via Sourcify.") %> <% else %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/csv_export/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/csv_export/index.html.eex index f0484d83d28e..979926300cf6 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/csv_export/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/csv_export/index.html.eex @@ -27,7 +27,7 @@ id="export-csv-button" class="button button-primary" style="padding: 10px 25px;" - data-link=<%= address_transaction_path(@conn, type_download_path(@type)) %> + data-link=<%= BlockScoutWebController.full_path("/api/v1/#{type_download_path(@type)}") %> data-address-hash=<%= address_checksum(@address_hash_string) %> data-type=<%= @type %> ><%= gettext("Download") %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/page_not_found/index.json.eex b/apps/block_scout_web/lib/block_scout_web/templates/page_not_found/index.json.eex new file mode 100644 index 000000000000..98b8502d5f4f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/page_not_found/index.json.eex @@ -0,0 +1 @@ +Page not found \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex b/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex index a4a7fc66bebb..c25dbbf5392e 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex @@ -192,23 +192,21 @@ defmodule BlockScoutWeb.ABIEncodedValueView do end defp base_value_json(_, {:dynamic, value}) do - hex(value) + hex_for_json(value) end defp base_value_json(:address, value) do - hex(value) - end - - defp base_value_json(:address_text, value) do - hex(value) + hex_for_json(value) end defp base_value_json(:bytes, value) do - hex(value) + hex_for_json(value) end defp base_value_json(_, value), do: to_string(value) defp hex("0x" <> value), do: "0x" <> value defp hex(value), do: "0x" <> Base.encode16(value, case: :lower) + + defp hex_for_json(value), do: "0x" <> Base.encode16(value, case: :lower) end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex index 3f60d4c7eadc..41ebf80976d6 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex @@ -6,6 +6,7 @@ defmodule BlockScoutWeb.AddressView do alias BlockScoutWeb.{AccessHelper, LayoutView} alias Explorer.Account.CustomABI alias Explorer.{Chain, CustomContractsHelper, Repo} + alias Explorer.Chain.Address.Counters alias Explorer.Chain.{Address, Hash, InternalTransaction, Log, SmartContract, Token, TokenTransfer, Transaction, Wei} alias Explorer.Chain.Block.Reward alias Explorer.ExchangeRates.Token, as: TokenExchangeRate diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex index 4f1601f23481..9cdf88abb2f9 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex @@ -7,6 +7,7 @@ defmodule BlockScoutWeb.API.V2.AddressView do alias BlockScoutWeb.API.V2.{ApiView, Helper, TokenView} alias BlockScoutWeb.API.V2.Helper alias Explorer.{Chain, Market} + alias Explorer.Chain.Address.Counters alias Explorer.Chain.{Address, SmartContract} @api_true [api?: true] @@ -102,12 +103,12 @@ defmodule BlockScoutWeb.API.V2.AddressView do "has_methods_read_proxy" => is_proxy, "has_methods_write_proxy" => AddressView.smart_contract_with_write_functions?(address) && is_proxy, "has_decompiled_code" => AddressView.has_decompiled_code?(address), - "has_validated_blocks" => Chain.check_if_validated_blocks_at_address(address.hash, @api_true), - "has_logs" => Chain.check_if_logs_at_address(address.hash, @api_true), - "has_tokens" => Chain.check_if_tokens_at_address(address.hash, @api_true), - "has_token_transfers" => Chain.check_if_token_transfers_at_address(address.hash, @api_true), + "has_validated_blocks" => Counters.check_if_validated_blocks_at_address(address.hash, @api_true), + "has_logs" => Counters.check_if_logs_at_address(address.hash, @api_true), + "has_tokens" => Counters.check_if_tokens_at_address(address.hash, @api_true), + "has_token_transfers" => Counters.check_if_token_transfers_at_address(address.hash, @api_true), "watchlist_address_id" => Chain.select_watchlist_address_id(get_watchlist_id(conn), address.hash), - "has_beacon_chain_withdrawals" => Chain.check_if_withdrawals_at_address(address.hash, @api_true) + "has_beacon_chain_withdrawals" => Counters.check_if_withdrawals_at_address(address.hash, @api_true) }) end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex index 9c022d364e52..8da05d3b686a 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex @@ -50,9 +50,9 @@ defmodule BlockScoutWeb.API.V2.BlockView do "base_fee_per_gas" => block.base_fee_per_gas, "burnt_fees" => burned_fee, "priority_fee" => priority_fee, - "extra_data" => "TODO", + # "extra_data" => "TODO", "uncles_hashes" => prepare_uncles(block.uncle_relations), - "state_root" => "TODO", + # "state_root" => "TODO", "rewards" => prepare_rewards(block.rewards, block, single_block?), "gas_target_percentage" => gas_target(block), "gas_used_percentage" => gas_used_percentage(block), diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex index 7b4f45cbb2da..3663e2f96e80 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex @@ -2,12 +2,16 @@ defmodule BlockScoutWeb.API.V2.SearchView do use BlockScoutWeb, :view alias BlockScoutWeb.Endpoint - alias Explorer.Chain.{Address, Block, Transaction} + alias Explorer.Chain.{Address, Block, Hash, Transaction} def render("search_results.json", %{search_results: search_results, next_page_params: next_page_params}) do %{"items" => Enum.map(search_results, &prepare_search_result/1), "next_page_params" => next_page_params} end + def render("search_results.json", %{search_results: search_results}) do + Enum.map(search_results, &prepare_search_result/1) + end + def render("search_results.json", %{result: {:ok, result}}) do Map.merge(%{"redirect" => true}, redirect_search_results(result)) end @@ -30,7 +34,8 @@ defmodule BlockScoutWeb.API.V2.SearchView do "exchange_rate" => search_result.exchange_rate && to_string(search_result.exchange_rate), "total_supply" => search_result.total_supply, "circulating_market_cap" => - search_result.circulating_market_cap && to_string(search_result.circulating_market_cap) + search_result.circulating_market_cap && to_string(search_result.circulating_market_cap), + "is_verified_via_admin_panel" => search_result.is_verified_via_admin_panel } end @@ -68,6 +73,7 @@ defmodule BlockScoutWeb.API.V2.SearchView do } end + defp hash_to_string(%Hash{bytes: bytes}), do: hash_to_string(bytes) defp hash_to_string(hash), do: "0x" <> Base.encode16(hash, case: :lower) defp redirect_search_results(%Address{} = item) do diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex index fe60782eb14b..7f983ed84b56 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex @@ -13,7 +13,7 @@ defmodule BlockScoutWeb.API.V2.TokenView do "name" => token.name, "decimals" => token.decimals, "type" => token.type, - "holders" => token.holder_count && to_string(token.holder_count), + "holders" => prepare_holders_count(token.holder_count), "exchange_rate" => exchange_rate(token), "total_supply" => token.total_supply, "icon_url" => token.icon_url, @@ -80,4 +80,8 @@ defmodule BlockScoutWeb.API.V2.TokenView do "is_unique" => is_unique } end + + defp prepare_holders_count(nil), do: nil + defp prepare_holders_count(count) when count < 0, do: prepare_holders_count(0) + defp prepare_holders_count(count), do: to_string(count) end diff --git a/apps/block_scout_web/lib/block_scout_web/views/csv_export.ex b/apps/block_scout_web/lib/block_scout_web/views/csv_export.ex index 34596956e45a..a076e1d00afe 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/csv_export.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/csv_export.ex @@ -1,6 +1,7 @@ defmodule BlockScoutWeb.CsvExportView do use BlockScoutWeb, :view + alias BlockScoutWeb.Controller, as: BlockScoutWebController alias Explorer.Chain alias Explorer.Chain.Address alias Explorer.Chain.CSVExport.Helper @@ -15,14 +16,10 @@ defmodule BlockScoutWeb.CsvExportView do end end + defp type_download_path(nil), do: "" + defp type_download_path(type) do - case type do - "internal-transactions" -> :internal_transactions_csv - "transactions" -> :transactions_csv - "token-transfers" -> :token_transfers_csv - "logs" -> :logs_csv - _ -> "" - end + type <> "-csv" end defp address_checksum(address_hash_string) do diff --git a/apps/block_scout_web/lib/block_scout_web/web_router.ex b/apps/block_scout_web/lib/block_scout_web/web_router.ex index 4b3f7a20ae74..3f3dbed7d10e 100644 --- a/apps/block_scout_web/lib/block_scout_web/web_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/web_router.ex @@ -493,16 +493,8 @@ defmodule BlockScoutWeb.WebRouter do get("/csv-export", CsvExportController, :index) - get("/transactions-csv", AddressTransactionController, :transactions_csv) - get("/token-autocomplete", ChainController, :token_autocomplete) - get("/token-transfers-csv", AddressTransactionController, :token_transfers_csv) - - get("/internal-transactions-csv", AddressTransactionController, :internal_transactions_csv) - - get("/logs-csv", AddressTransactionController, :logs_csv) - get("/chain-blocks", ChainController, :chain_blocks, as: :chain_blocks) get("/token-counters", Tokens.TokenController, :token_counters) diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs index 03a852bd85b2..01efe54cadbf 100644 --- a/apps/block_scout_web/mix.exs +++ b/apps/block_scout_web/mix.exs @@ -23,7 +23,7 @@ defmodule BlockScoutWeb.Mixfile do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "5.2.1" + version: "5.2.2" ] end @@ -83,7 +83,7 @@ defmodule BlockScoutWeb.Mixfile do # HTML CSS selectors for Phoenix controller tests {:floki, "~> 0.31"}, {:flow, "~> 1.2"}, - {:gettext, "~> 0.22.0"}, + {:gettext, "~> 0.23.1"}, {:hammer, "~> 6.0"}, {:httpoison, "~> 2.0"}, {:indexer, in_umbrella: true, runtime: false}, @@ -128,7 +128,7 @@ defmodule BlockScoutWeb.Mixfile do {:wallaby, "~> 0.30", only: :test, runtime: false}, # `:cowboy` `~> 2.0` and Phoenix 1.4 compatibility {:websocket_client, git: "https://github.com/blockscout/websocket_client.git", branch: "master", override: true}, - {:ex_json_schema, "~> 0.9.1"}, + {:ex_json_schema, "~> 0.10.1"}, {:ueberauth, "~> 0.7"}, {:ueberauth_auth0, "~> 2.0"}, {:bureaucrat, "~> 0.2.9", only: :test} diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index 9e448f7f82a7..930e2348eca3 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -265,7 +265,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:20 #: lib/block_scout_web/templates/transaction_state/index.html.eex:34 #: lib/block_scout_web/templates/verified_contracts/index.html.eex:60 -#: lib/block_scout_web/views/address_view.ex:107 +#: lib/block_scout_web/views/address_view.ex:108 #, elixir-autogen, elixir-format msgid "Address" msgstr "" @@ -556,7 +556,7 @@ msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:56 #: lib/block_scout_web/templates/address/overview.html.eex:275 #: lib/block_scout_web/templates/address_validation/index.html.eex:11 -#: lib/block_scout_web/views/address_view.ex:384 +#: lib/block_scout_web/views/address_view.ex:385 #, elixir-autogen, elixir-format msgid "Blocks Validated" msgstr "" @@ -656,13 +656,13 @@ msgstr "" #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:187 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:126 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:149 -#: lib/block_scout_web/views/address_view.ex:377 +#: lib/block_scout_web/views/address_view.ex:378 #, elixir-autogen, elixir-format msgid "Code" msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:42 -#: lib/block_scout_web/views/address_view.ex:383 +#: lib/block_scout_web/views/address_view.ex:384 #, elixir-autogen, elixir-format msgid "Coin Balance History" msgstr "" @@ -771,14 +771,14 @@ msgstr "" #: lib/block_scout_web/templates/account/custom_abi/form.html.eex:18 #: lib/block_scout_web/templates/account/custom_abi/index.html.eex:29 #: lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_address_field.html.eex:3 -#: lib/block_scout_web/views/address_view.ex:105 +#: lib/block_scout_web/views/address_view.ex:106 #, elixir-autogen, elixir-format msgid "Contract Address" msgstr "" #: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:16 -#: lib/block_scout_web/views/address_view.ex:45 -#: lib/block_scout_web/views/address_view.ex:79 +#: lib/block_scout_web/views/address_view.ex:46 +#: lib/block_scout_web/views/address_view.ex:80 #, elixir-autogen, elixir-format msgid "Contract Address Pending" msgstr "" @@ -1084,7 +1084,7 @@ msgstr "" msgid "Decoded" msgstr "" -#: lib/block_scout_web/views/address_view.ex:378 +#: lib/block_scout_web/views/address_view.ex:379 #, elixir-autogen, elixir-format msgid "Decompiled Code" msgstr "" @@ -1601,7 +1601,7 @@ msgstr "" #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:17 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 -#: lib/block_scout_web/views/address_view.ex:374 +#: lib/block_scout_web/views/address_view.ex:375 #: lib/block_scout_web/views/transaction_view.ex:533 #, elixir-autogen, elixir-format msgid "Internal Transactions" @@ -1718,7 +1718,7 @@ msgstr "" #: lib/block_scout_web/templates/address_logs/index.html.eex:10 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8 -#: lib/block_scout_web/views/address_view.ex:385 +#: lib/block_scout_web/views/address_view.ex:386 #: lib/block_scout_web/views/transaction_view.ex:534 #, elixir-autogen, elixir-format msgid "Logs" @@ -1732,7 +1732,7 @@ msgstr "" #: lib/block_scout_web/templates/chain/show.html.eex:53 #: lib/block_scout_web/templates/layout/app.html.eex:50 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:85 -#: lib/block_scout_web/views/address_view.ex:145 +#: lib/block_scout_web/views/address_view.ex:146 #, elixir-autogen, elixir-format msgid "Market Cap" msgstr "" @@ -2208,7 +2208,7 @@ msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:89 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:27 -#: lib/block_scout_web/views/address_view.ex:379 +#: lib/block_scout_web/views/address_view.ex:380 #: lib/block_scout_web/views/tokens/overview_view.ex:41 #, elixir-autogen, elixir-format msgid "Read Contract" @@ -2216,7 +2216,7 @@ msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:96 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:41 -#: lib/block_scout_web/views/address_view.ex:380 +#: lib/block_scout_web/views/address_view.ex:381 #, elixir-autogen, elixir-format msgid "Read Proxy" msgstr "" @@ -2903,7 +2903,7 @@ msgstr "" #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:15 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:4 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 -#: lib/block_scout_web/views/address_view.ex:376 +#: lib/block_scout_web/views/address_view.ex:377 #: lib/block_scout_web/views/tokens/instance/overview_view.ex:114 #: lib/block_scout_web/views/tokens/overview_view.ex:39 #: lib/block_scout_web/views/transaction_view.ex:532 @@ -2927,7 +2927,7 @@ msgstr "" #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:13 #: lib/block_scout_web/templates/layout/_topnav.html.eex:84 #: lib/block_scout_web/templates/tokens/index.html.eex:10 -#: lib/block_scout_web/views/address_view.ex:373 +#: lib/block_scout_web/views/address_view.ex:374 #, elixir-autogen, elixir-format msgid "Tokens" msgstr "" @@ -3099,7 +3099,7 @@ msgstr "" #: lib/block_scout_web/templates/block/overview.html.eex:80 #: lib/block_scout_web/templates/chain/show.html.eex:214 #: lib/block_scout_web/templates/layout/_topnav.html.eex:49 -#: lib/block_scout_web/views/address_view.ex:375 +#: lib/block_scout_web/views/address_view.ex:376 #, elixir-autogen, elixir-format msgid "Transactions" msgstr "" @@ -3469,14 +3469,14 @@ msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:103 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:34 -#: lib/block_scout_web/views/address_view.ex:381 +#: lib/block_scout_web/views/address_view.ex:382 #, elixir-autogen, elixir-format msgid "Write Contract" msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:110 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:48 -#: lib/block_scout_web/views/address_view.ex:382 +#: lib/block_scout_web/views/address_view.ex:383 #, elixir-autogen, elixir-format msgid "Write Proxy" msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index d84b5aca859a..dd08664c4b1f 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -265,7 +265,7 @@ msgstr "" #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:20 #: lib/block_scout_web/templates/transaction_state/index.html.eex:34 #: lib/block_scout_web/templates/verified_contracts/index.html.eex:60 -#: lib/block_scout_web/views/address_view.ex:107 +#: lib/block_scout_web/views/address_view.ex:108 #, elixir-autogen, elixir-format msgid "Address" msgstr "" @@ -556,7 +556,7 @@ msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:56 #: lib/block_scout_web/templates/address/overview.html.eex:275 #: lib/block_scout_web/templates/address_validation/index.html.eex:11 -#: lib/block_scout_web/views/address_view.ex:384 +#: lib/block_scout_web/views/address_view.ex:385 #, elixir-autogen, elixir-format msgid "Blocks Validated" msgstr "" @@ -656,13 +656,13 @@ msgstr "" #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:187 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:126 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:149 -#: lib/block_scout_web/views/address_view.ex:377 +#: lib/block_scout_web/views/address_view.ex:378 #, elixir-autogen, elixir-format msgid "Code" msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:42 -#: lib/block_scout_web/views/address_view.ex:383 +#: lib/block_scout_web/views/address_view.ex:384 #, elixir-autogen, elixir-format msgid "Coin Balance History" msgstr "" @@ -771,14 +771,14 @@ msgstr "" #: lib/block_scout_web/templates/account/custom_abi/form.html.eex:18 #: lib/block_scout_web/templates/account/custom_abi/index.html.eex:29 #: lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_address_field.html.eex:3 -#: lib/block_scout_web/views/address_view.ex:105 +#: lib/block_scout_web/views/address_view.ex:106 #, elixir-autogen, elixir-format msgid "Contract Address" msgstr "" #: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:16 -#: lib/block_scout_web/views/address_view.ex:45 -#: lib/block_scout_web/views/address_view.ex:79 +#: lib/block_scout_web/views/address_view.ex:46 +#: lib/block_scout_web/views/address_view.ex:80 #, elixir-autogen, elixir-format msgid "Contract Address Pending" msgstr "" @@ -1084,7 +1084,7 @@ msgstr "" msgid "Decoded" msgstr "" -#: lib/block_scout_web/views/address_view.ex:378 +#: lib/block_scout_web/views/address_view.ex:379 #, elixir-autogen, elixir-format msgid "Decompiled Code" msgstr "" @@ -1601,7 +1601,7 @@ msgstr "" #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:17 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 -#: lib/block_scout_web/views/address_view.ex:374 +#: lib/block_scout_web/views/address_view.ex:375 #: lib/block_scout_web/views/transaction_view.ex:533 #, elixir-autogen, elixir-format msgid "Internal Transactions" @@ -1718,7 +1718,7 @@ msgstr "" #: lib/block_scout_web/templates/address_logs/index.html.eex:10 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8 -#: lib/block_scout_web/views/address_view.ex:385 +#: lib/block_scout_web/views/address_view.ex:386 #: lib/block_scout_web/views/transaction_view.ex:534 #, elixir-autogen, elixir-format msgid "Logs" @@ -1732,7 +1732,7 @@ msgstr "" #: lib/block_scout_web/templates/chain/show.html.eex:53 #: lib/block_scout_web/templates/layout/app.html.eex:50 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:85 -#: lib/block_scout_web/views/address_view.ex:145 +#: lib/block_scout_web/views/address_view.ex:146 #, elixir-autogen, elixir-format msgid "Market Cap" msgstr "" @@ -2208,7 +2208,7 @@ msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:89 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:27 -#: lib/block_scout_web/views/address_view.ex:379 +#: lib/block_scout_web/views/address_view.ex:380 #: lib/block_scout_web/views/tokens/overview_view.ex:41 #, elixir-autogen, elixir-format msgid "Read Contract" @@ -2216,7 +2216,7 @@ msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:96 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:41 -#: lib/block_scout_web/views/address_view.ex:380 +#: lib/block_scout_web/views/address_view.ex:381 #, elixir-autogen, elixir-format msgid "Read Proxy" msgstr "" @@ -2903,7 +2903,7 @@ msgstr "" #: lib/block_scout_web/templates/tokens/transfer/index.html.eex:15 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:4 #: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 -#: lib/block_scout_web/views/address_view.ex:376 +#: lib/block_scout_web/views/address_view.ex:377 #: lib/block_scout_web/views/tokens/instance/overview_view.ex:114 #: lib/block_scout_web/views/tokens/overview_view.ex:39 #: lib/block_scout_web/views/transaction_view.ex:532 @@ -2927,7 +2927,7 @@ msgstr "" #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:13 #: lib/block_scout_web/templates/layout/_topnav.html.eex:84 #: lib/block_scout_web/templates/tokens/index.html.eex:10 -#: lib/block_scout_web/views/address_view.ex:373 +#: lib/block_scout_web/views/address_view.ex:374 #, elixir-autogen, elixir-format msgid "Tokens" msgstr "" @@ -3099,7 +3099,7 @@ msgstr "" #: lib/block_scout_web/templates/block/overview.html.eex:80 #: lib/block_scout_web/templates/chain/show.html.eex:214 #: lib/block_scout_web/templates/layout/_topnav.html.eex:49 -#: lib/block_scout_web/views/address_view.ex:375 +#: lib/block_scout_web/views/address_view.ex:376 #, elixir-autogen, elixir-format msgid "Transactions" msgstr "" @@ -3469,14 +3469,14 @@ msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:103 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:34 -#: lib/block_scout_web/views/address_view.ex:381 +#: lib/block_scout_web/views/address_view.ex:382 #, elixir-autogen, elixir-format msgid "Write Contract" msgstr "" #: lib/block_scout_web/templates/address/_tabs.html.eex:110 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:48 -#: lib/block_scout_web/views/address_view.ex:382 +#: lib/block_scout_web/views/address_view.ex:383 #, elixir-autogen, elixir-format msgid "Write Proxy" msgstr "" diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs index 3e1434750870..ad86587530fc 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs @@ -176,7 +176,7 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) conn = - get(conn, "/token-transfers-csv", %{ + get(conn, "/api/v1/token-transfers-csv", %{ "address_id" => Address.checksum(address.hash), "from_period" => from_period, "to_period" => to_period @@ -203,7 +203,7 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) conn = - get(conn, "/token-transfers-csv", %{ + get(conn, "/api/v1/token-transfers-csv", %{ "address_id" => Address.checksum(address.hash), "from_period" => from_period, "to_period" => to_period, @@ -231,7 +231,7 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) conn = - get(conn, "/token-transfers-csv", %{ + get(conn, "/api/v1/token-transfers-csv", %{ "address_id" => Address.checksum(address.hash), "from_period" => from_period, "to_period" => to_period @@ -260,7 +260,7 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) conn = - get(conn, "/token-transfers-csv", %{ + get(conn, "/api/v1/token-transfers-csv", %{ "address_id" => Address.checksum(address.hash), "from_period" => from_period, "to_period" => to_period, @@ -290,7 +290,7 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) conn = - get(conn, "/transactions-csv", %{ + get(conn, "/api/v1/transactions-csv", %{ "address_id" => Address.checksum(address.hash), "from_period" => from_period, "to_period" => to_period, @@ -357,7 +357,7 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) conn = - get(conn, "/internal-transactions-csv", %{ + get(conn, "/api/v1/internal-transactions-csv", %{ "address_id" => Address.checksum(address.hash), "from_period" => from_period, "to_period" => to_period, @@ -418,7 +418,7 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) conn = - get(conn, "/logs-csv", %{ + get(conn, "/api/v1/logs-csv", %{ "address_id" => Address.checksum(address.hash), "from_period" => from_period, "to_period" => to_period, diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs index 0cd2b50bd390..752c393c4710 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs @@ -712,7 +712,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do params = %{ "module" => "contract", "action" => "verify_via_sourcify", - "addressHash" => "0x18d89C12e9463Be6343c35C9990361bA4C42AfC2" + "addressHash" => "0xf26594F585De4EB0Ae9De865d9053FEe02ac6eF1" } response = @@ -732,14 +732,14 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do _created_contract_address = insert( :address, - hash: "0x18d89C12e9463Be6343c35C9990361bA4C42AfC2", + hash: "0xf26594F585De4EB0Ae9De865d9053FEe02ac6eF1", contract_code: smart_contract_bytecode ) params = %{ "module" => "contract", "action" => "verify_via_sourcify", - "addressHash" => "0x18d89C12e9463Be6343c35C9990361bA4C42AfC2" + "addressHash" => "0xf26594F585De4EB0Ae9De865d9053FEe02ac6eF1" } get_implementation() diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs index 7073f491550a..adfba5247640 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do alias BlockScoutWeb.Models.UserFromAuth alias Explorer.{Chain, Repo} + alias Explorer.Chain.Address.Counters alias Explorer.Chain.{ Address, @@ -159,9 +160,9 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do insert(:block, miner: address) - Chain.transaction_count(address) - Chain.token_transfers_count(address) - Chain.gas_usage_count(address) + Counters.transaction_count(address) + Counters.token_transfers_count(address) + Counters.gas_usage_count(address) request = get(conn, "/api/v2/addresses/#{address.hash}/counters") diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/search_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/search_controller_test.exs index e9dda32b848e..145963c99d32 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/search_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/search_controller_test.exs @@ -135,6 +135,7 @@ defmodule BlockScoutWeb.API.V2.SearchControllerTest do assert item["exchange_rate"] == (token.fiat_value && to_string(token.fiat_value)) assert item["total_supply"] == to_string(token.total_supply) assert item["icon_url"] == token.icon_url + assert item["is_verified_via_admin_panel"] == token.is_verified_via_admin_panel end test "search transaction", %{conn: conn} do @@ -287,4 +288,49 @@ defmodule BlockScoutWeb.API.V2.SearchControllerTest do %{"redirect" => false, "type" => nil, "parameter" => nil} = json_response(request, 200) end end + + describe "/search/quick" do + test "check that all categories are in response list", %{conn: conn} do + name = "156000" + + tags = + for _ <- 0..50 do + insert(:address_to_tag, tag: build(:address_tag, display_name: name)) + end + + contracts = insert_list(50, :smart_contract, name: name) + tokens = insert_list(50, :token, name: name) + blocks = [insert(:block, number: name, consensus: false), insert(:block, number: name)] + + request = get(conn, "/api/v2/search/quick?q=#{name}") + assert response = json_response(request, 200) + assert Enum.count(response) == 50 + + assert response |> Enum.filter(fn x -> x["type"] == "label" end) |> Enum.map(fn x -> x["address"] end) == + tags |> Enum.reverse() |> Enum.take(16) |> Enum.map(fn tag -> Address.checksum(tag.address.hash) end) + + assert response |> Enum.filter(fn x -> x["type"] == "contract" end) |> Enum.map(fn x -> x["address"] end) == + contracts + |> Enum.reverse() + |> Enum.take(16) + |> Enum.map(fn contract -> Address.checksum(contract.address_hash) end) + + assert response |> Enum.filter(fn x -> x["type"] == "token" end) |> Enum.map(fn x -> x["address"] end) == + tokens + |> Enum.reverse() + |> Enum.sort_by(fn x -> x.is_verified_via_admin_panel end, :desc) + |> Enum.take(16) + |> Enum.map(fn token -> Address.checksum(token.contract_address_hash) end) + + block_hashes = response |> Enum.filter(fn x -> x["type"] == "block" end) |> Enum.map(fn x -> x["block_hash"] end) + + assert block_hashes == blocks |> Enum.reverse() |> Enum.map(fn block -> to_string(block.hash) end) || + block_hashes == blocks |> Enum.map(fn block -> to_string(block.hash) end) + end + + test "returns empty list and don't crash", %{conn: conn} do + request = get(conn, "/api/v2/search/quick?q=qwertyuioiuytrewertyuioiuytrertyuio") + assert [] = json_response(request, 200) + end + end end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs index 12dd8c8c5f4b..863345a9038a 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs @@ -1205,6 +1205,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert [ %{ + "method_id" => "49ba1b49", "type" => "function", "stateMutability" => "nonpayable", "outputs" => [], @@ -1271,7 +1272,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "stateMutability" => "nonpayable", "outputs" => [], "name" => "disableWhitelist", - "inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}] + "inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}], + "method_id" => "49ba1b49" } ] == response end @@ -1912,7 +1914,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "stateMutability" => "nonpayable", "outputs" => [], "name" => "disableWhitelist", - "inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}] + "inputs" => [%{"type" => "bool", "name" => "disable", "internalType" => "bool"}], + "method_id" => "49ba1b49" } ] == response end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs index fbed9880a6ec..05b7009487c4 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs @@ -390,6 +390,30 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do check_paginated_response(response, response_2nd_page, token_balances) end + + test "check pagination with the same values", %{conn: conn} do + token = insert(:token) + + token_balances = + for _ <- 0..50 do + insert( + :address_current_token_balance, + token_contract_address_hash: token.contract_address_hash, + value: 1000 + ) + end + |> Enum.sort_by(fn x -> x.address_hash end, :asc) + + request = get(conn, "/api/v2/tokens/#{token.contract_address.hash}/holders") + assert response = json_response(request, 200) + + request_2nd_page = + get(conn, "/api/v2/tokens/#{token.contract_address.hash}/holders", response["next_page_params"]) + + assert response_2nd_page = json_response(request_2nd_page, 200) + + check_paginated_response(response, response_2nd_page, token_balances) + end end describe "/tokens" do diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/verification_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/verification_controller_test.exs index 4ff1545377bd..bd471e8d771f 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/verification_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/verification_controller_test.exs @@ -75,7 +75,7 @@ defmodule BlockScoutWeb.API.V2.VerificationControllerTest do request = post(conn, "/api/v2/smart-contracts/#{contract_address.hash}/verification/via/flattened-code", params) - assert %{"message" => "Verification started"} = json_response(request, 200) + assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) assert_receive %Phoenix.Socket.Message{ payload: %{status: "success"}, @@ -118,7 +118,7 @@ defmodule BlockScoutWeb.API.V2.VerificationControllerTest do request = post(conn, "/api/v2/smart-contracts/#{contract_address.hash}/verification/via/flattened-code", params) - assert %{"message" => "Verification started"} = json_response(request, 200) + assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) assert_receive %Phoenix.Socket.Message{ payload: %{status: "error", errors: %{name: ["Wrong contract name, please try again."]}}, @@ -198,7 +198,7 @@ defmodule BlockScoutWeb.API.V2.VerificationControllerTest do body ) - assert %{"message" => "Verification started"} = json_response(request, 200) + assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) assert_receive %Phoenix.Socket.Message{ payload: %{status: "success"}, @@ -222,7 +222,7 @@ defmodule BlockScoutWeb.API.V2.VerificationControllerTest do end test "verify contract from sourcify repo", %{conn: conn} do - address = "0x18d89C12e9463Be6343c35C9990361bA4C42AfC2" + address = "0xf26594F585De4EB0Ae9De865d9053FEe02ac6eF1" _contract = insert(:address, hash: address, contract_code: "0x01") @@ -259,7 +259,7 @@ defmodule BlockScoutWeb.API.V2.VerificationControllerTest do body ) - assert %{"message" => "Verification started"} = json_response(request, 200) + assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) assert_receive %Phoenix.Socket.Message{ payload: %{status: "success"}, @@ -329,7 +329,7 @@ defmodule BlockScoutWeb.API.V2.VerificationControllerTest do request = post(conn, "/api/v2/smart-contracts/#{contract_address.hash}/verification/via/vyper-code", params) - assert %{"message" => "Verification started"} = json_response(request, 200) + assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) assert_receive %Phoenix.Socket.Message{ payload: %{status: "success"}, diff --git a/apps/block_scout_web/test/block_scout_web/schema/query/address_test.exs b/apps/block_scout_web/test/block_scout_web/schema/query/address_test.exs index ea1f5579f01c..f4526bc31454 100644 --- a/apps/block_scout_web/test/block_scout_web/schema/query/address_test.exs +++ b/apps/block_scout_web/test/block_scout_web/schema/query/address_test.exs @@ -18,7 +18,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do variables = %{"hash" => to_string(address.hash)} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -45,7 +45,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do variables = %{"hash" => to_string(address.hash)} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -78,7 +78,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do variables = %{"hash" => to_string(address.hash)} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -110,7 +110,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do variables = %{"hash" => to_string(address.hash)} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert %{"errors" => [error]} = json_response(conn, 200) assert error["message"] =~ ~s(Address not found.) @@ -127,7 +127,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do variables = %{} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert %{"errors" => [error]} = json_response(conn, 200) assert error["message"] == ~s(In argument "hash": Expected type "AddressHash!", found null.) @@ -144,7 +144,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do variables = %{"hash" => "someInvalidHash"} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert %{"errors" => [error]} = json_response(conn, 200) assert error["message"] =~ ~s(Argument "hash" has invalid value) @@ -193,7 +193,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do "first" => 1 } - conn = post(conn, "/graphql", query: query, variables: variables) + conn = post(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -251,7 +251,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do "first" => 1 } - conn = post(conn, "/graphql", query: query, variables: variables) + conn = post(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -304,7 +304,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do "first" => 3 } - conn = post(conn, "/graphql", query: query, variables: variables) + conn = post(conn, "/api/v1/graphql", query: query, variables: variables) %{ "data" => %{ @@ -366,7 +366,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do "first" => 3 } - conn = post(conn, "/graphql", query: query, variables: variables) + conn = post(conn, "/api/v1/graphql", query: query, variables: variables) %{ "data" => %{ @@ -410,7 +410,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do "first" => 67 } - conn = post(conn, "/graphql", query: query, variables: variables) + conn = post(conn, "/api/v1/graphql", query: query, variables: variables) assert %{"errors" => [error1, error2, error3]} = json_response(conn, 200) assert error1["message"] =~ ~s(Field transactions is too complex) @@ -465,7 +465,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do "count" => 9 } - conn = post(conn, "/graphql", query: query, variables: variables) + conn = post(conn, "/api/v1/graphql", query: query, variables: variables) %{ "data" => %{ @@ -525,7 +525,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do "first" => 3 } - conn = post(conn, "/graphql", query: query1, variables: variables1) + conn = post(conn, "/api/v1/graphql", query: query1, variables: variables1) %{"data" => %{"address" => %{"transactions" => page1}}} = json_response(conn, 200) @@ -564,7 +564,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do "after" => last_cursor_page1 } - conn = post(conn, "/graphql", query: query2, variables: variables2) + conn = post(conn, "/api/v1/graphql", query: query2, variables: variables2) %{"data" => %{"address" => %{"transactions" => page2}}} = json_response(conn, 200) @@ -583,7 +583,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressTest do "after" => last_cursor_page2 } - conn = post(conn, "/graphql", query: query2, variables: variables3) + conn = post(conn, "/api/v1/graphql", query: query2, variables: variables3) %{"data" => %{"address" => %{"transactions" => page3}}} = json_response(conn, 200) diff --git a/apps/block_scout_web/test/block_scout_web/schema/query/addresses_test.exs b/apps/block_scout_web/test/block_scout_web/schema/query/addresses_test.exs index f6145a4afc1d..e3ca744f4937 100644 --- a/apps/block_scout_web/test/block_scout_web/schema/query/addresses_test.exs +++ b/apps/block_scout_web/test/block_scout_web/schema/query/addresses_test.exs @@ -18,7 +18,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressesTest do variables = %{"hashes" => to_string(address.hash)} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -47,7 +47,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressesTest do variables = %{"hashes" => to_string(address.hash)} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -82,7 +82,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressesTest do variables = %{"hashes" => to_string(address.hash)} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -116,7 +116,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressesTest do variables = %{"hashes" => [to_string(address.hash)]} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert %{"errors" => [error]} = json_response(conn, 200) assert error["message"] =~ ~s(Addresses not found.) @@ -133,7 +133,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressesTest do variables = %{} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert %{"errors" => [error]} = json_response(conn, 200) assert error["message"] == ~s(In argument "hashes": Expected type "[AddressHash!]!", found null.) @@ -150,7 +150,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressesTest do variables = %{"hashes" => ["someInvalidHash"]} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert %{"errors" => [error]} = json_response(conn, 200) assert error["message"] =~ ~s(Argument "hashes" has invalid value) @@ -175,7 +175,7 @@ defmodule BlockScoutWeb.Schema.Query.AddressesTest do variables = %{"hashes" => hashes} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert %{"errors" => [error1, error2]} = json_response(conn, 200) assert error1["message"] =~ ~s(Field addresses is too complex) diff --git a/apps/block_scout_web/test/block_scout_web/schema/query/block_test.exs b/apps/block_scout_web/test/block_scout_web/schema/query/block_test.exs index d601e3dd1005..26c612b344ce 100644 --- a/apps/block_scout_web/test/block_scout_web/schema/query/block_test.exs +++ b/apps/block_scout_web/test/block_scout_web/schema/query/block_test.exs @@ -27,7 +27,7 @@ defmodule BlockScoutWeb.Schema.Query.BlockTest do variables = %{"number" => block.number} - conn = post(conn, "/graphql", query: query, variables: variables) + conn = post(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -63,7 +63,7 @@ defmodule BlockScoutWeb.Schema.Query.BlockTest do variables = %{"number" => non_existent_block_number} - conn = post(conn, "/graphql", query: query, variables: variables) + conn = post(conn, "/api/v1/graphql", query: query, variables: variables) assert %{"errors" => [error]} = json_response(conn, 200) assert error["message"] =~ ~s(Block number #{non_existent_block_number} was not found) @@ -80,7 +80,7 @@ defmodule BlockScoutWeb.Schema.Query.BlockTest do } """ - conn = get(conn, "/graphql", query: query) + conn = get(conn, "/api/v1/graphql", query: query) assert %{"errors" => [error]} = json_response(conn, 200) assert error["message"] == ~s(In argument "number": Expected type "Int!", found null.) @@ -99,7 +99,7 @@ defmodule BlockScoutWeb.Schema.Query.BlockTest do variables = %{"number" => "invalid"} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert %{"errors" => [error]} = json_response(conn, 200) assert error["message"] =~ ~s(Argument "number" has invalid value) diff --git a/apps/block_scout_web/test/block_scout_web/schema/query/node_test.exs b/apps/block_scout_web/test/block_scout_web/schema/query/node_test.exs index 4638e2f1c379..75e6aacd1ac6 100644 --- a/apps/block_scout_web/test/block_scout_web/schema/query/node_test.exs +++ b/apps/block_scout_web/test/block_scout_web/schema/query/node_test.exs @@ -20,7 +20,7 @@ defmodule BlockScoutWeb.Schema.Query.NodeTest do variables = %{"id" => id} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -50,7 +50,7 @@ defmodule BlockScoutWeb.Schema.Query.NodeTest do variables = %{"id" => id} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) %{"errors" => [error]} = json_response(conn, 200) @@ -88,7 +88,7 @@ defmodule BlockScoutWeb.Schema.Query.NodeTest do variables = %{"id" => id} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -132,7 +132,7 @@ defmodule BlockScoutWeb.Schema.Query.NodeTest do variables = %{"id" => id} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) %{"errors" => [error]} = json_response(conn, 200) @@ -163,7 +163,7 @@ defmodule BlockScoutWeb.Schema.Query.NodeTest do variables = %{"id" => id} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -199,7 +199,7 @@ defmodule BlockScoutWeb.Schema.Query.NodeTest do variables = %{"id" => id} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) %{"errors" => [error]} = json_response(conn, 200) diff --git a/apps/block_scout_web/test/block_scout_web/schema/query/token_transfers_test.exs b/apps/block_scout_web/test/block_scout_web/schema/query/token_transfers_test.exs index aef875fe3e16..ea20aa4f4ede 100644 --- a/apps/block_scout_web/test/block_scout_web/schema/query/token_transfers_test.exs +++ b/apps/block_scout_web/test/block_scout_web/schema/query/token_transfers_test.exs @@ -33,7 +33,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do "first" => 1 } - conn = post(conn, "/graphql", query: query, variables: variables) + conn = post(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -86,7 +86,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do "first" => 10 } - conn = post(conn, "/graphql", query: query, variables: variables) + conn = post(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -121,7 +121,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do response1 = conn - |> post("/graphql", query: query1, variables: variables1) + |> post("/api/v1/graphql", query: query1, variables: variables1) |> json_response(200) %{"errors" => [response1_error1, response1_error2]} = response1 @@ -150,7 +150,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do response2 = conn - |> post("/graphql", query: query2, variables: variables2) + |> post("/api/v1/graphql", query: query2, variables: variables2) |> json_response(200) %{"errors" => [response2_error1, response2_error2]} = response2 @@ -212,7 +212,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do [token_transfer] = conn - |> post("/graphql", query: query, variables: variables) + |> post("/api/v1/graphql", query: query, variables: variables) |> json_response(200) |> get_in(["data", "token_transfers", "edges"]) @@ -264,7 +264,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do "first" => 1 } - conn = post(conn, "/graphql", query: query1, variables: variables1) + conn = post(conn, "/api/v1/graphql", query: query1, variables: variables1) %{"data" => %{"token_transfers" => page1}} = json_response(conn, 200) @@ -300,7 +300,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do "after" => last_cursor_page1 } - conn = post(conn, "/graphql", query: query2, variables: variables2) + conn = post(conn, "/api/v1/graphql", query: query2, variables: variables2) %{"data" => %{"token_transfers" => page2}} = json_response(conn, 200) @@ -319,7 +319,7 @@ defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do "after" => last_cursor_page2 } - conn = post(conn, "/graphql", query: query2, variables: variables3) + conn = post(conn, "/api/v1/graphql", query: query2, variables: variables3) %{"data" => %{"token_transfers" => page3}} = json_response(conn, 200) diff --git a/apps/block_scout_web/test/block_scout_web/schema/query/transaction_test.exs b/apps/block_scout_web/test/block_scout_web/schema/query/transaction_test.exs index 66b31d676c72..6212ca89a788 100644 --- a/apps/block_scout_web/test/block_scout_web/schema/query/transaction_test.exs +++ b/apps/block_scout_web/test/block_scout_web/schema/query/transaction_test.exs @@ -37,7 +37,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do variables = %{"hash" => to_string(transaction.hash)} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -78,7 +78,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do variables = %{"hash" => to_string(transaction.hash)} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert %{"errors" => [error]} = json_response(conn, 200) assert error["message"] == "Transaction not found." @@ -93,7 +93,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do } """ - conn = get(conn, "/graphql", query: query) + conn = get(conn, "/api/v1/graphql", query: query) assert %{"errors" => [error]} = json_response(conn, 200) assert error["message"] == ~s(In argument "hash": Expected type "FullHash!", found null.) @@ -110,7 +110,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do variables = %{"hash" => "0x000"} - conn = get(conn, "/graphql", query: query, variables: variables) + conn = get(conn, "/api/v1/graphql", query: query, variables: variables) assert %{"errors" => [error]} = json_response(conn, 200) assert error["message"] =~ ~s(Argument "hash" has invalid value) @@ -180,7 +180,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do "first" => 1 } - conn = post(conn, "/graphql", query: query, variables: variables) + conn = post(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -247,7 +247,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do "first" => 1 } - conn = post(conn, "/graphql", query: query, variables: variables) + conn = post(conn, "/api/v1/graphql", query: query, variables: variables) assert json_response(conn, 200) == %{ "data" => %{ @@ -306,7 +306,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do response = conn - |> post("/graphql", query: query, variables: variables) + |> post("/api/v1/graphql", query: query, variables: variables) |> json_response(200) internal_transactions = get_in(response, ["data", "transaction", "internal_transactions", "edges"]) @@ -341,7 +341,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do response1 = conn - |> post("/graphql", query: query1, variables: variables1) + |> post("/api/v1/graphql", query: query1, variables: variables1) |> json_response(200) assert %{"errors" => [error1, error2, error3]} = response1 @@ -372,7 +372,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do response2 = conn - |> post("/graphql", query: query2, variables: variables2) + |> post("/api/v1/graphql", query: query2, variables: variables2) |> json_response(200) assert %{"errors" => [error1, error2, error3]} = response2 @@ -435,7 +435,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do [internal_transaction] = conn - |> post("/graphql", query: query, variables: variables) + |> post("/api/v1/graphql", query: query, variables: variables) |> json_response(200) |> get_in(["data", "transaction", "internal_transactions", "edges"]) @@ -479,7 +479,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do "first" => 2 } - conn = post(conn, "/graphql", query: query1, variables: variables1) + conn = post(conn, "/api/v1/graphql", query: query1, variables: variables1) %{"data" => %{"transaction" => %{"internal_transactions" => page1}}} = json_response(conn, 200) @@ -520,7 +520,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do page2 = conn - |> post("/graphql", query: query2, variables: variables2) + |> post("/api/v1/graphql", query: query2, variables: variables2) |> json_response(200) |> get_in(["data", "transaction", "internal_transactions"]) @@ -541,7 +541,7 @@ defmodule BlockScoutWeb.Schema.Query.TransactionTest do page3 = conn - |> post("/graphql", query: query2, variables: variables3) + |> post("/api/v1/graphql", query: query2, variables: variables3) |> json_response(200) |> get_in(["data", "transaction", "internal_transactions"]) diff --git a/apps/ethereum_jsonrpc/config/config.exs b/apps/ethereum_jsonrpc/config/config.exs index e578ffbea78a..503b7a3828ed 100644 --- a/apps/ethereum_jsonrpc/config/config.exs +++ b/apps/ethereum_jsonrpc/config/config.exs @@ -6,7 +6,6 @@ config :ethereum_jsonrpc, EthereumJSONRPC.RequestCoordinator, duration: :timer.minutes(1), table: EthereumJSONRPC.RequestCoordinator.TimeoutCounter ], - wait_per_timeout: :timer.seconds(20), max_jitter: :timer.seconds(2) # Add this configuration to add global RPC request throttling. diff --git a/apps/ethereum_jsonrpc/config/runtime/test.exs b/apps/ethereum_jsonrpc/config/runtime/test.exs index e2043f6c1435..081c952dfc9d 100644 --- a/apps/ethereum_jsonrpc/config/runtime/test.exs +++ b/apps/ethereum_jsonrpc/config/runtime/test.exs @@ -2,6 +2,8 @@ import Config alias EthereumJSONRPC.Variant +config :ethereum_jsonrpc, EthereumJSONRPC.RequestCoordinator, wait_per_timeout: 2 + variant = Variant.get() Code.require_file("#{variant}.exs", "#{__DIR__}/../../../explorer/config/test") diff --git a/apps/ethereum_jsonrpc/config/test.exs b/apps/ethereum_jsonrpc/config/test.exs index 0ed3de28b282..61e5f67398a0 100644 --- a/apps/ethereum_jsonrpc/config/test.exs +++ b/apps/ethereum_jsonrpc/config/test.exs @@ -6,7 +6,6 @@ config :ethereum_jsonrpc, EthereumJSONRPC.RequestCoordinator, duration: :timer.seconds(6), table: EthereumJSONRPC.RequestCoordinator.TimeoutCounter ], - wait_per_timeout: 2, max_jitter: 1, # This should not actually limit anything in tests, but it is here to enable the relevant code for testing throttle_rate_limit: 10_000, diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex index 2a6ebeeb397d..4a59a80e9c7d 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex @@ -275,6 +275,16 @@ defmodule EthereumJSONRPC do |> fetch_blocks_by_params(&Block.ByNumber.request/1, json_rpc_named_arguments) end + @doc """ + Fetches block by "t:tag/0". + """ + @spec fetch_block_by_tag(tag(), json_rpc_named_arguments) :: + {:ok, Blocks.t()} | {:error, reason :: :invalid_tag | :not_found | term()} + def fetch_block_by_tag(tag, json_rpc_named_arguments) when tag in ~w(earliest latest pending) do + [%{tag: tag}] + |> fetch_blocks_by_params(&Block.ByTag.request/1, json_rpc_named_arguments) + end + @doc """ Fetches uncle blocks by nephew hashes and indices. """ @@ -310,9 +320,8 @@ defmodule EthereumJSONRPC do @spec fetch_block_number_by_tag(tag(), json_rpc_named_arguments) :: {:ok, non_neg_integer()} | {:error, reason :: :invalid_tag | :not_found | term()} def fetch_block_number_by_tag(tag, json_rpc_named_arguments) when tag in ~w(earliest latest pending) do - %{id: 0, tag: tag} - |> Block.ByTag.request() - |> json_rpc(json_rpc_named_arguments) + tag + |> fetch_block_by_tag(json_rpc_named_arguments) |> Block.ByTag.number_from_result() end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block/by_tag.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block/by_tag.ex index fa4a9b83c990..d257f4e506c5 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block/by_tag.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block/by_tag.ex @@ -5,6 +5,7 @@ defmodule EthereumJSONRPC.Block.ByTag do """ import EthereumJSONRPC, only: [quantity_to_integer: 1] + alias EthereumJSONRPC.Blocks def request(%{id: id, tag: tag}) when is_binary(tag) do EthereumJSONRPC.request(%{id: id, method: "eth_getBlockByNumber", params: [tag, false]}) @@ -16,6 +17,10 @@ defmodule EthereumJSONRPC.Block.ByTag do {:ok, quantity_to_integer(quantity)} end + def number_from_result({:ok, %Blocks{blocks_params: []}}), do: {:error, :not_found} + + def number_from_result({:ok, %Blocks{blocks_params: [%{number: number}]}}), do: {:ok, number} + def number_from_result({:ok, nil}), do: {:error, :not_found} def number_from_result({:error, %{"code" => -32602}}), do: {:error, :invalid_tag} diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs index 752c6465f656..5c4003d17880 100644 --- a/apps/ethereum_jsonrpc/mix.exs +++ b/apps/ethereum_jsonrpc/mix.exs @@ -23,7 +23,7 @@ defmodule EthereumJsonrpc.MixProject do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "5.2.1" + version: "5.2.2" ] end diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/mox_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/mox_test.exs index 2ad5b8f52c20..f0c4fbaf2d65 100644 --- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/mox_test.exs +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/mox_test.exs @@ -21,8 +21,15 @@ defmodule EthereumJSONRPC.MoxTest do describe "fetch_block_number_by_tag/2" do test "with pending with null result", %{json_rpc_named_arguments: json_rpc_named_arguments} do - expect(EthereumJSONRPC.Mox, :json_rpc, fn _json, _options -> - {:ok, nil} + expect(EthereumJSONRPC.Mox, :json_rpc, fn [ + %{ + id: id, + method: "eth_getBlockByNumber", + params: ["pending", false] + } + ], + _options -> + {:ok, [%{id: id, result: nil}]} end) assert {:error, :not_found} = EthereumJSONRPC.fetch_block_number_by_tag("pending", json_rpc_named_arguments) diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs index bc77cabd5c6a..7299d48478fb 100644 --- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs @@ -2,6 +2,7 @@ defmodule EthereumJSONRPCTest do use EthereumJSONRPC.Case, async: true import EthereumJSONRPC.Case + import EthereumJSONRPC, only: [quantity_to_integer: 1] import Mox alias EthereumJSONRPC.{Blocks, FetchedBalances, FetchedBeneficiaries, FetchedCodes, Subscription} @@ -543,61 +544,92 @@ defmodule EthereumJSONRPCTest do end end - describe "fetch_block_number_by_tag" do - @tag capture_log: false - test "with earliest", %{json_rpc_named_arguments: json_rpc_named_arguments} do - if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do - expect(EthereumJSONRPC.Mox, :json_rpc, fn _json, _options -> - {:ok, %{"number" => "0x0"}} - end) - end + describe "fetch_block_by_tag/2" do + @supported_tags ~w(earliest latest pending) - log_bad_gateway( - fn -> EthereumJSONRPC.fetch_block_number_by_tag("earliest", json_rpc_named_arguments) end, - fn result -> - assert {:ok, 0} = result + @tag capture_log: false + test "with all supported tags", %{json_rpc_named_arguments: json_rpc_named_arguments} do + for tag <- @supported_tags do + if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do + expect(EthereumJSONRPC.Mox, :json_rpc, fn [ + %{ + id: id, + method: "eth_getBlockByNumber", + params: [^tag, false] + } + ], + _options -> + block_response(id, tag == "pending", "0x1") + end) end - ) - end - @tag capture_log: false - test "with latest", %{json_rpc_named_arguments: json_rpc_named_arguments} do - if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do - expect(EthereumJSONRPC.Mox, :json_rpc, fn _json, _options -> - {:ok, %{"number" => "0x1"}} - end) + log_bad_gateway( + fn -> EthereumJSONRPC.fetch_block_by_tag(tag, json_rpc_named_arguments) end, + fn result -> + {:ok, %Blocks{blocks_params: [_ | _], transactions_params: []}} = result + end + ) end + end - log_bad_gateway( - fn -> EthereumJSONRPC.fetch_block_number_by_tag("latest", json_rpc_named_arguments) end, - fn result -> - assert {:ok, number} = result - assert number > 0 - end - ) + test "unknown errors are returned", %{json_rpc_named_arguments: json_rpc_named_arguments} do + # Can't be faked reliably on real chain + moxed_json_rpc_named_arguments = Keyword.put(json_rpc_named_arguments, :transport, EthereumJSONRPC.Mox) + + unknown_error = %{"code" => 500, "message" => "Unknown error"} + + expect(EthereumJSONRPC.Mox, :json_rpc, fn _json, _options -> + {:error, unknown_error} + end) + + assert {:error, ^unknown_error} = EthereumJSONRPC.fetch_block_by_tag("latest", moxed_json_rpc_named_arguments) end + end - @tag capture_log: false - test "with pending", %{json_rpc_named_arguments: json_rpc_named_arguments} do - if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do - expect(EthereumJSONRPC.Mox, :json_rpc, fn _json, _options -> - {:ok, nil} - end) - end + describe "fetch_block_number_by_tag" do + @supported_tags %{"earliest" => "0x0", "latest" => "0x1", "pending" => nil} - log_bad_gateway( - fn -> EthereumJSONRPC.fetch_block_number_by_tag("pending", json_rpc_named_arguments) end, - fn - # Parity after https://github.com/paritytech/parity-ethereum/pull/8281 and anything spec-compliant - {:error, reason} -> - assert reason == :not_found - - # Parity before https://github.com/paritytech/parity-ethereum/pull/8281 - {:ok, number} -> - assert is_integer(number) - assert number > 0 + @tag capture_log: false + test "with all supported tags", %{json_rpc_named_arguments: json_rpc_named_arguments} do + for {tag, expected_result} <- @supported_tags do + if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do + expect(EthereumJSONRPC.Mox, :json_rpc, fn [ + %{ + id: id, + method: "eth_getBlockByNumber", + params: [^tag, false] + } + ], + _options -> + if tag == "pending" do + {:ok, [%{id: id, result: nil}]} + else + block_response(id, false, expected_result) + end + end) end - ) + + log_bad_gateway( + fn -> EthereumJSONRPC.fetch_block_number_by_tag(tag, json_rpc_named_arguments) end, + if tag == "pending" do + fn + # Parity after https://github.com/paritytech/parity-ethereum/pull/8281 and anything spec-compliant + {:error, reason} -> + assert reason == :not_found + + # Parity before https://github.com/paritytech/parity-ethereum/pull/8281 + {:ok, number} -> + assert is_integer(number) + assert number > 0 + end + else + fn result -> + integer_result = expected_result && quantity_to_integer(expected_result) + assert {:ok, ^integer_result} = result + end + end + ) + end end test "unknown errors are returned", %{json_rpc_named_arguments: json_rpc_named_arguments} do @@ -912,12 +944,43 @@ defmodule EthereumJSONRPCTest do :ok end end + + defp block_response(id, pending, block_number) do + block_hash = "0x29c850324e357f3c0c836d79860c5af55f7b651e5d7ee253c1af1b14908af49c" + transaction_hash = "0xa2e81bb56b55ba3dab2daf76501b50dfaad240cccb905dbf89d65c7a84a4a48e" + + {:ok, + [ + %{ + id: id, + result: %{ + "difficulty" => "0x0", + "gasLimit" => "0x0", + "gasUsed" => "0x0", + "hash" => if(pending, do: nil, else: block_hash), + "extraData" => "0x0", + "logsBloom" => "0x0", + "miner" => "0x0", + "number" => block_number, + "parentHash" => "0x0", + "receiptsRoot" => "0x0", + "size" => "0x0", + "sha3Uncles" => "0x0", + "stateRoot" => "0x0", + "timestamp" => "0x0", + "totalDifficulty" => "0x0", + "transactions" => [transaction_hash], + "transactionsRoot" => "0x0", + "uncles" => [] + } + } + ]} + end end defmodule EthereumJSONRPCSyncTest do use EthereumJSONRPC.Case, async: false - import EthereumJSONRPC.Case import Mox alias EthereumJSONRPC.FetchedBalances diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index dd4200d0f0c0..264ce9d67b1d 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -55,6 +55,7 @@ defmodule Explorer.Chain do InternalTransaction, Log, PendingBlockOperation, + Search, SmartContract, SmartContractAdditionalSource, Token, @@ -81,24 +82,15 @@ defmodule Explorer.Chain do } alias Explorer.Chain.Cache.Block, as: BlockCache - alias Explorer.Chain.Cache.Helper, as: CacheHelper alias Explorer.Chain.Cache.PendingBlockOperation, as: PendingBlockOperationCache alias Explorer.Chain.Fetcher.{CheckBytecodeMatchingOnDemand, LookUpSmartContractSourcesOnDemand} alias Explorer.Chain.Import.Runner alias Explorer.Chain.InternalTransaction.{CallType, Type} - alias Explorer.Counters.{ - AddressesCounter, - AddressesWithBalanceCounter, - AddressTokenTransfersCounter, - AddressTransactionsCounter, - AddressTransactionsGasUsageCounter - } - alias Explorer.Market.MarketHistoryCache alias Explorer.{PagingOptions, Repo} alias Explorer.SmartContract.Helper - alias Explorer.Tags.{AddressTag, AddressToTag} + alias Explorer.SmartContract.Solidity.Verifier alias Dataloader.Ecto, as: DataloaderEcto @@ -121,8 +113,6 @@ defmodule Explorer.Chain do "commit" => "f14fcbc8" } - @max_incoming_transactions_count 10_000 - @revert_msg_prefix_1 "Revert: " @revert_msg_prefix_2 "revert: " @revert_msg_prefix_3 "reverted " @@ -178,56 +168,7 @@ defmodule Explorer.Chain do @typep necessity_by_association_option :: {:necessity_by_association, necessity_by_association} @typep paging_options :: {:paging_options, PagingOptions.t()} @typep balance_by_day :: %{date: String.t(), value: Wei.t()} - @typep api? :: {:api?, true | false} - - @doc """ - Gets from the cache the count of `t:Explorer.Chain.Address.t/0`'s where the `fetched_coin_balance` is > 0 - """ - @spec count_addresses_with_balance_from_cache :: non_neg_integer() - def count_addresses_with_balance_from_cache do - AddressesWithBalanceCounter.fetch() - end - - @doc """ - Estimated count of `t:Explorer.Chain.Address.t/0`. - - Estimated count of addresses. - """ - @spec address_estimated_count() :: non_neg_integer() - def address_estimated_count(options \\ []) do - cached_value = AddressesCounter.fetch() - - if is_nil(cached_value) || cached_value == 0 do - count = CacheHelper.estimated_count_from("addresses", options) - - max(count, 0) - else - cached_value - end - end - - @doc """ - Counts the number of addresses with fetched coin balance > 0. - - This function should be used with caution. In larger databases, it may take a - while to have the return back. - """ - def count_addresses_with_balance do - Repo.one( - Address.count_with_fetched_coin_balance(), - timeout: :infinity - ) - end - - @doc """ - Counts the number of all addresses. - - This function should be used with caution. In larger databases, it may take a - while to have the return back. - """ - def count_addresses do - Repo.aggregate(Address, :count, timeout: :infinity) - end + @type api? :: {:api?, true | false} @doc """ `t:Explorer.Chain.InternalTransaction/0`s from the address with the given `hash`. @@ -326,25 +267,6 @@ defmodule Explorer.Chain do ) end - @doc """ - Get the total number of transactions sent by the address with the given hash according to the last block indexed. - - We have to increment +1 in the last nonce result because it works like an array position, the first - nonce has the value 0. When last nonce is nil, it considers that the given address has 0 transactions. - """ - @spec total_transactions_sent_by_address(Hash.Address.t()) :: non_neg_integer() - def total_transactions_sent_by_address(address_hash) do - last_nonce = - address_hash - |> Transaction.last_nonce_by_address_query() - |> Repo.one(timeout: :infinity) - - case last_nonce do - nil -> 0 - value -> value + 1 - end - end - @doc """ Fetches the transactions related to the address with the given hash, including transactions that only have the address in the `token_transfers` related table @@ -1027,53 +949,6 @@ defmodule Explorer.Chain do |> select_repo(options).exists?() end - @spec address_to_incoming_transaction_count(Hash.Address.t()) :: non_neg_integer() - def address_to_incoming_transaction_count(address_hash) do - to_address_query = - from( - transaction in Transaction, - where: transaction.to_address_hash == ^address_hash - ) - - Repo.aggregate(to_address_query, :count, :hash, timeout: :infinity) - end - - @spec address_hash_to_transaction_count(Hash.Address.t()) :: non_neg_integer() - def address_hash_to_transaction_count(address_hash) do - query = - from( - transaction in Transaction, - where: transaction.to_address_hash == ^address_hash or transaction.from_address_hash == ^address_hash - ) - - Repo.aggregate(query, :count, :hash, timeout: :infinity) - end - - @spec address_to_incoming_transaction_gas_usage(Hash.Address.t()) :: Decimal.t() | nil - def address_to_incoming_transaction_gas_usage(address_hash) do - to_address_query = - from( - transaction in Transaction, - where: transaction.to_address_hash == ^address_hash - ) - - Repo.aggregate(to_address_query, :sum, :gas_used, timeout: :infinity) - end - - @spec address_to_outcoming_transaction_gas_usage(Hash.Address.t()) :: Decimal.t() | nil - def address_to_outcoming_transaction_gas_usage(address_hash) do - to_address_query = - from( - transaction in Transaction, - where: transaction.from_address_hash == ^address_hash - ) - - Repo.aggregate(to_address_query, :sum, :gas_used, timeout: :infinity) - end - - @spec max_incoming_transactions_count() :: non_neg_integer() - def max_incoming_transactions_count, do: @max_incoming_transactions_count - @doc """ How many blocks have confirmed `block` based on the current `max_block_number` @@ -1472,328 +1347,6 @@ defmodule Explorer.Chain do end end - defp prepare_search_term(string) do - case Regex.scan(~r/[a-zA-Z0-9]+/, string) do - [_ | _] = words -> - term_final = - words - |> Enum.map_join(" & ", fn [word] -> word <> ":*" end) - - {:some, term_final} - - _ -> - :none - end - end - - def search_label_query(term) do - inner_query = - from(tag in AddressTag, - where: fragment("to_tsvector('english', ?) @@ to_tsquery(?)", tag.display_name, ^term), - select: tag - ) - - from(att in AddressToTag, - inner_join: at in subquery(inner_query), - on: att.tag_id == at.id, - left_join: smart_contract in SmartContract, - on: att.address_hash == smart_contract.address_hash, - select: %{ - address_hash: att.address_hash, - tx_hash: fragment("CAST(NULL AS bytea)"), - block_hash: fragment("CAST(NULL AS bytea)"), - type: "label", - name: at.display_name, - symbol: ^nil, - holder_count: ^nil, - inserted_at: att.inserted_at, - block_number: 0, - icon_url: nil, - token_type: nil, - timestamp: fragment("NULL::timestamp without time zone"), - verified: not is_nil(smart_contract), - exchange_rate: nil, - total_supply: nil, - circulating_market_cap: nil, - priority: 1 - } - ) - end - - defp search_token_query(term) do - from(token in Token, - left_join: smart_contract in SmartContract, - on: token.contract_address_hash == smart_contract.address_hash, - where: fragment("to_tsvector('english', ? || ' ' || ?) @@ to_tsquery(?)", token.symbol, token.name, ^term), - select: %{ - address_hash: token.contract_address_hash, - tx_hash: fragment("CAST(NULL AS bytea)"), - block_hash: fragment("CAST(NULL AS bytea)"), - type: "token", - name: token.name, - symbol: token.symbol, - holder_count: token.holder_count, - inserted_at: token.inserted_at, - block_number: 0, - icon_url: token.icon_url, - token_type: token.type, - timestamp: fragment("NULL::timestamp without time zone"), - verified: not is_nil(smart_contract), - exchange_rate: token.fiat_value, - total_supply: token.total_supply, - circulating_market_cap: token.circulating_market_cap, - priority: 0 - } - ) - end - - defp search_contract_query(term) do - from(smart_contract in SmartContract, - left_join: address in Address, - on: smart_contract.address_hash == address.hash, - where: fragment("to_tsvector('english', ?) @@ to_tsquery(?)", smart_contract.name, ^term), - select: %{ - address_hash: smart_contract.address_hash, - tx_hash: fragment("CAST(NULL AS bytea)"), - block_hash: fragment("CAST(NULL AS bytea)"), - type: "contract", - name: smart_contract.name, - symbol: ^nil, - holder_count: ^nil, - inserted_at: address.inserted_at, - block_number: 0, - icon_url: nil, - token_type: nil, - timestamp: fragment("NULL::timestamp without time zone"), - verified: true, - exchange_rate: nil, - total_supply: nil, - circulating_market_cap: nil, - priority: 0 - } - ) - end - - defp search_address_query(term) do - case Chain.string_to_address_hash(term) do - {:ok, address_hash} -> - from(address in Address, - left_join: - address_name in subquery( - from(name in Address.Name, - where: name.address_hash == ^address_hash, - order_by: [desc: name.primary], - limit: 1 - ) - ), - on: address.hash == address_name.address_hash, - where: address.hash == ^address_hash, - select: %{ - address_hash: address.hash, - tx_hash: fragment("CAST(NULL AS bytea)"), - block_hash: fragment("CAST(NULL AS bytea)"), - type: "address", - name: address_name.name, - symbol: ^nil, - holder_count: ^nil, - inserted_at: address.inserted_at, - block_number: 0, - icon_url: nil, - token_type: nil, - timestamp: fragment("NULL::timestamp without time zone"), - verified: address.verified, - exchange_rate: nil, - total_supply: nil, - circulating_market_cap: nil, - priority: 0 - } - ) - - _ -> - nil - end - end - - defp search_tx_query(term) do - case Chain.string_to_transaction_hash(term) do - {:ok, tx_hash} -> - from(transaction in Transaction, - left_join: block in Block, - on: transaction.block_hash == block.hash, - where: transaction.hash == ^tx_hash, - select: %{ - address_hash: fragment("CAST(NULL AS bytea)"), - tx_hash: transaction.hash, - block_hash: fragment("CAST(NULL AS bytea)"), - type: "transaction", - name: ^nil, - symbol: ^nil, - holder_count: ^nil, - inserted_at: transaction.inserted_at, - block_number: 0, - icon_url: nil, - token_type: nil, - timestamp: block.timestamp, - verified: nil, - exchange_rate: nil, - total_supply: nil, - circulating_market_cap: nil, - priority: 0 - } - ) - - _ -> - nil - end - end - - defp search_block_query(term) do - case Chain.string_to_block_hash(term) do - {:ok, block_hash} -> - from(block in Block, - where: block.hash == ^block_hash, - select: %{ - address_hash: fragment("CAST(NULL AS bytea)"), - tx_hash: fragment("CAST(NULL AS bytea)"), - block_hash: block.hash, - type: "block", - name: ^nil, - symbol: ^nil, - holder_count: ^nil, - inserted_at: block.inserted_at, - block_number: block.number, - icon_url: nil, - token_type: nil, - timestamp: block.timestamp, - verified: nil, - exchange_rate: nil, - total_supply: nil, - circulating_market_cap: nil, - priority: 0 - } - ) - - _ -> - case Integer.parse(term) do - {block_number, ""} -> - from(block in Block, - where: block.number == ^block_number, - select: %{ - address_hash: fragment("CAST(NULL AS bytea)"), - tx_hash: fragment("CAST(NULL AS bytea)"), - block_hash: block.hash, - type: "block", - name: ^nil, - symbol: ^nil, - holder_count: ^nil, - inserted_at: block.inserted_at, - block_number: block.number, - icon_url: nil, - token_type: nil, - timestamp: block.timestamp, - verified: nil, - exchange_rate: nil, - total_supply: nil, - circulating_market_cap: nil, - priority: 0 - } - ) - - _ -> - nil - end - end - end - - def joint_search(paging_options, offset, raw_string, options \\ []) do - string = String.trim(raw_string) - - case prepare_search_term(string) do - {:some, term} -> - tokens_query = search_token_query(term) - contracts_query = search_contract_query(term) - labels_query = search_label_query(term) - tx_query = search_tx_query(string) - address_query = search_address_query(string) - block_query = search_block_query(string) - - basic_query = - from( - tokens in subquery(tokens_query), - union: ^contracts_query, - union: ^labels_query - ) - - query = - cond do - address_query -> - basic_query - |> union(^address_query) - - tx_query -> - basic_query - |> union(^tx_query) - |> union(^block_query) - - block_query -> - basic_query - |> union(^block_query) - - true -> - basic_query - end - - ordered_query = - from(items in subquery(query), - order_by: [ - desc: items.priority, - desc_nulls_last: items.circulating_market_cap, - desc_nulls_last: items.exchange_rate, - desc_nulls_last: items.holder_count, - asc: items.name, - desc: items.inserted_at - ], - limit: ^paging_options.page_size, - offset: ^offset - ) - - paginated_ordered_query = - ordered_query - |> page_search_results(paging_options) - - search_results = select_repo(options).all(paginated_ordered_query) - - search_results - |> Enum.map(fn result -> - result - |> compose_result_checksummed_address_hash() - |> format_timestamp() - end) - - _ -> - [] - end - end - - defp compose_result_checksummed_address_hash(result) do - if result.address_hash do - result - |> Map.put(:address_hash, Address.checksum(result.address_hash)) - else - result - end - end - - # For some reasons timestamp for blocks and txs returns as ~N[2023-06-25 19:39:47.339493] - defp format_timestamp(result) do - if result.timestamp do - result - |> Map.put(:timestamp, DateTime.from_naive!(result.timestamp, "Etc/UTC")) - else - result - end - end - @doc """ Converts `t:Explorer.Chain.Address.t/0` `hash` to the `t:Explorer.Chain.Address.t/0` with that `hash`. @@ -1913,7 +1466,7 @@ defmodule Explorer.Chain do if smart_contract do CheckBytecodeMatchingOnDemand.trigger_check(address_result, smart_contract) LookUpSmartContractSourcesOnDemand.trigger_fetch(address_result, smart_contract) - address_result + check_and_update_constructor_args(address_result) else LookUpSmartContractSourcesOnDemand.trigger_fetch(address_result, nil) @@ -1936,6 +1489,36 @@ defmodule Explorer.Chain do end end + defp check_and_update_constructor_args( + %SmartContract{address_hash: address_hash, constructor_arguments: nil, verified_via_sourcify: true} = + smart_contract + ) do + if args = Verifier.parse_constructor_arguments_for_sourcify_contract(address_hash, smart_contract.abi) do + smart_contract |> SmartContract.changeset(%{constructor_arguments: args}) |> Repo.update() + %SmartContract{smart_contract | constructor_arguments: args} + else + smart_contract + end + end + + defp check_and_update_constructor_args( + %Address{ + hash: address_hash, + contract_code: deployed_bytecode, + smart_contract: %SmartContract{constructor_arguments: nil, verified_via_sourcify: true} = smart_contract + } = address + ) do + if args = + Verifier.parse_constructor_arguments_for_sourcify_contract(address_hash, smart_contract.abi, deployed_bytecode) do + smart_contract |> SmartContract.changeset(%{constructor_arguments: args}) |> Repo.update() + %Address{address | smart_contract: %SmartContract{smart_contract | constructor_arguments: args}} + else + address + end + end + + defp check_and_update_constructor_args(other), do: other + defp add_twin_info_to_contract(address_result, address_verified_twin_contract, _hash) when is_nil(address_verified_twin_contract), do: address_result @@ -2510,7 +2093,7 @@ defmodule Explorer.Chain do query = if filter && filter !== "" do - case prepare_search_term(filter) do + case Search.prepare_search_term(filter) do {:some, filter_term} -> base_query_with_paging |> where(fragment("to_tsvector('english', symbol || ' ' || name) @@ to_tsquery(?)", ^filter_term)) @@ -2574,56 +2157,6 @@ defmodule Explorer.Chain do |> select_repo(options).all() end - def check_if_validated_blocks_at_address(address_hash, options \\ []) do - select_repo(options).exists?(from(b in Block, where: b.miner_hash == ^address_hash)) - end - - def check_if_logs_at_address(address_hash, options \\ []) do - select_repo(options).exists?(from(l in Log, where: l.address_hash == ^address_hash)) - end - - def check_if_internal_transactions_at_address(address_hash) do - internal_transactions_exists_by_created_contract_address_hash = - Repo.exists?(from(it in InternalTransaction, where: it.created_contract_address_hash == ^address_hash)) - - internal_transactions_exists_by_from_address_hash = - Repo.exists?(from(it in InternalTransaction, where: it.from_address_hash == ^address_hash)) - - internal_transactions_exists_by_to_address_hash = - Repo.exists?(from(it in InternalTransaction, where: it.to_address_hash == ^address_hash)) - - internal_transactions_exists_by_created_contract_address_hash || internal_transactions_exists_by_from_address_hash || - internal_transactions_exists_by_to_address_hash - end - - def check_if_token_transfers_at_address(address_hash, options \\ []) do - token_transfers_exists_by_from_address_hash = - select_repo(options).exists?(from(tt in TokenTransfer, where: tt.from_address_hash == ^address_hash)) - - token_transfers_exists_by_to_address_hash = - select_repo(options).exists?(from(tt in TokenTransfer, where: tt.to_address_hash == ^address_hash)) - - token_transfers_exists_by_from_address_hash || - token_transfers_exists_by_to_address_hash - end - - def check_if_tokens_at_address(address_hash, options \\ []) do - select_repo(options).exists?( - from( - tb in CurrentTokenBalance, - where: tb.address_hash == ^address_hash, - where: tb.value > 0 - ) - ) - end - - @spec check_if_withdrawals_at_address(Hash.Address.t()) :: boolean() - def check_if_withdrawals_at_address(address_hash, options \\ []) do - address_hash - |> Withdrawal.address_hash_to_withdrawals_unordered_query() - |> select_repo(options).exists?() - end - @doc """ Counts all of the block validations and groups by the `miner_hash`. """ @@ -2640,53 +2173,6 @@ defmodule Explorer.Chain do Repo.stream_each(query, fun) end - @doc """ - Counts the number of `t:Explorer.Chain.Block.t/0` validated by the address with the given `hash`. - """ - @spec address_to_validation_count(Hash.Address.t(), [api?]) :: non_neg_integer() - def address_to_validation_count(hash, options) do - query = from(block in Block, where: block.miner_hash == ^hash, select: fragment("COUNT(*)")) - - select_repo(options).one(query) - end - - @spec address_to_transaction_count(Address.t()) :: non_neg_integer() - def address_to_transaction_count(address) do - address_hash_to_transaction_count(address.hash) - end - - @spec address_to_token_transfer_count(Address.t()) :: non_neg_integer() - def address_to_token_transfer_count(address) do - query = - from( - token_transfer in TokenTransfer, - where: token_transfer.to_address_hash == ^address.hash, - or_where: token_transfer.from_address_hash == ^address.hash - ) - - Repo.aggregate(query, :count, timeout: :infinity) - end - - @spec address_to_gas_usage_count(Address.t()) :: Decimal.t() | nil - def address_to_gas_usage_count(address) do - if contract?(address) do - incoming_transaction_gas_usage = address_to_incoming_transaction_gas_usage(address.hash) - - cond do - !incoming_transaction_gas_usage -> - address_to_outcoming_transaction_gas_usage(address.hash) - - Decimal.compare(incoming_transaction_gas_usage, 0) == :eq -> - address_to_outcoming_transaction_gas_usage(address.hash) - - true -> - incoming_transaction_gas_usage - end - else - address_to_outcoming_transaction_gas_usage(address.hash) - end - end - @doc """ Return the balance in usd corresponding to this token. Return nil if the fiat_value of the token is not present. """ @@ -2703,9 +2189,9 @@ defmodule Explorer.Chain do Decimal.mult(tokens, fiat_value) end - defp contract?(%{contract_code: nil}), do: false + def contract?(%{contract_code: nil}), do: false - defp contract?(%{contract_code: _}), do: true + def contract?(%{contract_code: _}), do: true @doc """ Returns a stream of unfetched `t:Explorer.Chain.Address.CoinBalance.t/0`. @@ -4310,7 +3796,7 @@ defmodule Explorer.Chain do verified_contract_twin_additional_sources = get_contract_additional_sources(verified_contract_twin, options) %{ - :verified_contract => verified_contract_twin, + :verified_contract => check_and_update_constructor_args(verified_contract_twin), :additional_sources => verified_contract_twin_additional_sources } else @@ -4772,37 +4258,6 @@ defmodule Explorer.Chain do where(query, [transaction], transaction.index < ^index) end - defp page_search_results(query, %PagingOptions{key: nil}), do: query - - defp page_search_results(query, %PagingOptions{ - key: {_address_hash, _tx_hash, _block_hash, holder_count, name, inserted_at, item_type} - }) - when holder_count in [nil, ""] do - where( - query, - [item], - (item.name > ^name and item.type == ^item_type) or - (item.name == ^name and item.inserted_at < ^inserted_at and - item.type == ^item_type) or - item.type != ^item_type - ) - end - - # credo:disable-for-next-line - defp page_search_results(query, %PagingOptions{ - key: {_address_hash, _tx_hash, _block_hash, holder_count, name, inserted_at, item_type} - }) do - where( - query, - [item], - (item.holder_count < ^holder_count and item.type == ^item_type) or - (item.holder_count == ^holder_count and item.name > ^name and item.type == ^item_type) or - (item.holder_count == ^holder_count and item.name == ^name and item.inserted_at < ^inserted_at and - item.type == ^item_type) or - item.type != ^item_type - ) - end - def page_token_balances(query, %PagingOptions{key: nil}), do: query def page_token_balances(query, %PagingOptions{key: {value, address_hash}}) do @@ -5295,6 +4750,13 @@ defmodule Explorer.Chain do end end + @spec fetch_last_token_balances_include_unfetched(Hash.Address.t(), [api?]) :: [] + def fetch_last_token_balances_include_unfetched(address_hash, options \\ []) do + address_hash + |> CurrentTokenBalance.last_token_balances_include_unfetched() + |> select_repo(options).all() + end + @spec fetch_last_token_balances(Hash.Address.t(), [api?]) :: [] def fetch_last_token_balances(address_hash, options \\ []) do address_hash @@ -5578,7 +5040,7 @@ defmodule Explorer.Chain do def count_token_holders_from_token_hash(contract_address_hash) do query = from(ctb in CurrentTokenBalance.token_holders_query_for_count(contract_address_hash), - select: fragment("COUNT(DISTINCT(address_hash))") + select: fragment("COUNT(DISTINCT(?))", ctb.address_hash) ) Repo.one!(query, timeout: :infinity) @@ -6650,55 +6112,6 @@ defmodule Explorer.Chain do NewContractsCounter.fetch(options) end - def address_counters(address, options \\ []) do - validation_count_task = - Task.async(fn -> - address_to_validation_count(address.hash, options) - end) - - Task.start_link(fn -> - transaction_count(address) - end) - - Task.start_link(fn -> - token_transfers_count(address) - end) - - Task.start_link(fn -> - gas_usage_count(address) - end) - - [ - validation_count_task - ] - |> Task.yield_many(:infinity) - |> Enum.map(fn {_task, res} -> - case res do - {:ok, result} -> - result - - {:exit, reason} -> - raise "Query fetching address counters terminated: #{inspect(reason)}" - - nil -> - raise "Query fetching address counters timed out." - end - end) - |> List.to_tuple() - end - - def transaction_count(address) do - AddressTransactionsCounter.fetch(address) - end - - def token_transfers_count(address) do - AddressTokenTransfersCounter.fetch(address) - end - - def gas_usage_count(address) do - AddressTransactionsGasUsageCounter.fetch(address) - end - def fetch_token_counters(address_hash, timeout) do total_token_transfers_task = Task.async(fn -> diff --git a/apps/explorer/lib/explorer/chain/address/coin_balance.ex b/apps/explorer/lib/explorer/chain/address/coin_balance.ex index bab4a62b6291..b2e5a46f263e 100644 --- a/apps/explorer/lib/explorer/chain/address/coin_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/coin_balance.ex @@ -155,7 +155,6 @@ defmodule Explorer.Chain.Address.CoinBalance do balance |> cast(params, @allowed_fields) |> validate_required(@required_fields) - |> foreign_key_constraint(:address_hash) |> unique_constraint(:block_number, name: :address_coin_balances_address_hash_block_number_index) end end diff --git a/apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex b/apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex index 0aab03e3619a..cc881d9c471d 100644 --- a/apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex +++ b/apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex @@ -69,7 +69,6 @@ defmodule Explorer.Chain.Address.CoinBalanceDaily do balance |> cast(params, @allowed_fields) |> validate_required(@required_fields) - |> foreign_key_constraint(:address_hash) |> unique_constraint(:day, name: :address_coin_balances_daily_address_hash_day_index) end end diff --git a/apps/explorer/lib/explorer/chain/address/counters.ex b/apps/explorer/lib/explorer/chain/address/counters.ex new file mode 100644 index 000000000000..a8d2b78b6d69 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/address/counters.ex @@ -0,0 +1,543 @@ +defmodule Explorer.Chain.Address.Counters do + @moduledoc """ + Functions related to Explorer.Chain.Address counters + """ + import Ecto.Query, only: [from: 2, limit: 2, select: 3, subquery: 1, union: 2, where: 3] + + import Explorer.Chain, + only: [select_repo: 1, wrapped_union_subquery: 1] + + alias Explorer.{Chain, Repo} + + alias Explorer.Counters.{ + AddressesCounter, + AddressesWithBalanceCounter, + AddressTokenTransfersCounter, + AddressTransactionsCounter, + AddressTransactionsGasUsageCounter + } + + alias Explorer.Chain.{ + Address, + Address.CoinBalance, + Address.CurrentTokenBalance, + Block, + Hash, + InternalTransaction, + Log, + TokenTransfer, + Transaction, + Withdrawal + } + + alias Explorer.Chain.Cache.Helper, as: CacheHelper + + require Logger + + defp address_hash_to_logs_query(address_hash) do + from(l in Log, where: l.address_hash == ^address_hash) + end + + defp address_hash_to_validated_blocks_query(address_hash) do + from(b in Block, where: b.miner_hash == ^address_hash) + end + + def check_if_validated_blocks_at_address(address_hash, options \\ []) do + select_repo(options).exists?(address_hash_to_validated_blocks_query(address_hash)) + end + + def check_if_logs_at_address(address_hash, options \\ []) do + select_repo(options).exists?(address_hash_to_logs_query(address_hash)) + end + + defp address_hash_to_coin_balances(address_hash) do + query = + from( + cb in CoinBalance, + where: cb.address_hash == ^address_hash, + where: not is_nil(cb.value), + select_merge: %{ + delta: fragment("? - coalesce(lead(?, 1) over (order by ? desc), 0)", cb.value, cb.value, cb.block_number) + } + ) + + from(balance in subquery(query), + where: balance.delta != 0 + ) + end + + def check_if_token_transfers_at_address(address_hash, options \\ []) do + select_repo(options).exists?(from(tt in TokenTransfer, where: tt.from_address_hash == ^address_hash)) || + select_repo(options).exists?(from(tt in TokenTransfer, where: tt.to_address_hash == ^address_hash)) + end + + def check_if_tokens_at_address(address_hash, options \\ []) do + select_repo(options).exists?(address_hash_to_token_balances_query(address_hash)) + end + + @spec check_if_withdrawals_at_address(Hash.Address.t()) :: boolean() + def check_if_withdrawals_at_address(address_hash, options \\ []) do + address_hash + |> Withdrawal.address_hash_to_withdrawals_unordered_query() + |> select_repo(options).exists?() + end + + @doc """ + Gets from the cache the count of `t:Explorer.Chain.Address.t/0`'s where the `fetched_coin_balance` is > 0 + """ + @spec count_addresses_with_balance_from_cache :: non_neg_integer() + def count_addresses_with_balance_from_cache do + AddressesWithBalanceCounter.fetch() + end + + @doc """ + Estimated count of `t:Explorer.Chain.Address.t/0`. + + Estimated count of addresses. + """ + @spec address_estimated_count() :: non_neg_integer() + def address_estimated_count(options \\ []) do + cached_value = AddressesCounter.fetch() + + if is_nil(cached_value) || cached_value == 0 do + count = CacheHelper.estimated_count_from("addresses", options) + + max(count, 0) + else + cached_value + end + end + + @doc """ + Counts the number of all addresses. + + This function should be used with caution. In larger databases, it may take a + while to have the return back. + """ + def count_addresses do + Repo.aggregate(Address, :count, timeout: :infinity) + end + + @doc """ + Get the total number of transactions sent by the address with the given hash according to the last block indexed. + + We have to increment +1 in the last nonce result because it works like an array position, the first + nonce has the value 0. When last nonce is nil, it considers that the given address has 0 transactions. + """ + @spec total_transactions_sent_by_address(Hash.Address.t()) :: non_neg_integer() + def total_transactions_sent_by_address(address_hash) do + last_nonce = + address_hash + |> Transaction.last_nonce_by_address_query() + |> Repo.one(timeout: :infinity) + + case last_nonce do + nil -> 0 + value -> value + 1 + end + end + + def address_hash_to_transaction_count_query(address_hash) do + from( + transaction in Transaction, + where: transaction.to_address_hash == ^address_hash or transaction.from_address_hash == ^address_hash + ) + end + + @spec address_hash_to_transaction_count(Hash.Address.t()) :: non_neg_integer() + def address_hash_to_transaction_count(address_hash) do + query = address_hash_to_transaction_count_query(address_hash) + + Repo.aggregate(query, :count, :hash, timeout: :infinity) + end + + @spec address_to_transaction_count(Address.t()) :: non_neg_integer() + def address_to_transaction_count(address) do + address_hash_to_transaction_count(address.hash) + end + + @doc """ + Counts the number of `t:Explorer.Chain.Block.t/0` validated by the address with the given `hash`. + """ + @spec address_to_validation_count(Hash.Address.t(), [Chain.api?()]) :: non_neg_integer() + def address_to_validation_count(hash, options) do + query = from(block in Block, where: block.miner_hash == ^hash, select: fragment("COUNT(*)")) + + select_repo(options).one(query) + end + + @doc """ + Counts the number of addresses with fetched coin balance > 0. + + This function should be used with caution. In larger databases, it may take a + while to have the return back. + """ + def count_addresses_with_balance do + Repo.one( + Address.count_with_fetched_coin_balance(), + timeout: :infinity + ) + end + + @spec address_to_incoming_transaction_count(Hash.Address.t()) :: non_neg_integer() + def address_to_incoming_transaction_count(address_hash) do + to_address_query = + from( + transaction in Transaction, + where: transaction.to_address_hash == ^address_hash + ) + + Repo.aggregate(to_address_query, :count, :hash, timeout: :infinity) + end + + @spec address_to_incoming_transaction_gas_usage(Hash.Address.t()) :: Decimal.t() | nil + def address_to_incoming_transaction_gas_usage(address_hash) do + to_address_query = + from( + transaction in Transaction, + where: transaction.to_address_hash == ^address_hash + ) + + Repo.aggregate(to_address_query, :sum, :gas_used, timeout: :infinity) + end + + @spec address_to_outcoming_transaction_gas_usage(Hash.Address.t()) :: Decimal.t() | nil + def address_to_outcoming_transaction_gas_usage(address_hash) do + to_address_query = + from( + transaction in Transaction, + where: transaction.from_address_hash == ^address_hash + ) + + Repo.aggregate(to_address_query, :sum, :gas_used, timeout: :infinity) + end + + def address_to_token_transfer_count_query(address_hash) do + from( + token_transfer in TokenTransfer, + where: token_transfer.to_address_hash == ^address_hash, + or_where: token_transfer.from_address_hash == ^address_hash + ) + end + + @spec address_to_token_transfer_count(Address.t()) :: non_neg_integer() + def address_to_token_transfer_count(address) do + query = address_to_token_transfer_count_query(address.hash) + + Repo.aggregate(query, :count, timeout: :infinity) + end + + def address_hash_to_token_balances_query(address_hash) do + from( + tb in CurrentTokenBalance, + where: tb.address_hash == ^address_hash, + where: tb.value > 0 + ) + end + + @spec address_to_gas_usage_count(Address.t()) :: Decimal.t() | nil + def address_to_gas_usage_count(address) do + if Chain.contract?(address) do + incoming_transaction_gas_usage = address_to_incoming_transaction_gas_usage(address.hash) + + cond do + !incoming_transaction_gas_usage -> + address_to_outcoming_transaction_gas_usage(address.hash) + + Decimal.compare(incoming_transaction_gas_usage, 0) == :eq -> + address_to_outcoming_transaction_gas_usage(address.hash) + + true -> + incoming_transaction_gas_usage + end + else + address_to_outcoming_transaction_gas_usage(address.hash) + end + end + + def address_counters(address, options \\ []) do + validation_count_task = + Task.async(fn -> + address_to_validation_count(address.hash, options) + end) + + Task.start_link(fn -> + transaction_count(address) + end) + + Task.start_link(fn -> + token_transfers_count(address) + end) + + Task.start_link(fn -> + gas_usage_count(address) + end) + + [ + validation_count_task + ] + |> Task.yield_many(:infinity) + |> Enum.map(fn {_task, res} -> + case res do + {:ok, result} -> + result + + {:exit, reason} -> + raise "Query fetching address counters terminated: #{inspect(reason)}" + + nil -> + raise "Query fetching address counters timed out." + end + end) + |> List.to_tuple() + end + + def transaction_count(address) do + AddressTransactionsCounter.fetch(address) + end + + def token_transfers_count(address) do + AddressTokenTransfersCounter.fetch(address) + end + + def gas_usage_count(address) do + AddressTransactionsGasUsageCounter.fetch(address) + end + + @counters_limit 51 + + def address_limited_counters(address_hash, options) do + start = Time.utc_now() + + validations_count_task = + Task.async(fn -> + result = + address_hash + |> address_hash_to_validated_blocks_query() + |> limit(@counters_limit) + |> select_repo(options).aggregate(:count) + + Logger.info( + "Time consumed for validations_count_task for #{address_hash} is #{Time.diff(Time.utc_now(), start, :millisecond)}ms" + ) + + result + end) + + transactions_from_count_task = + Task.async(fn -> + result = + Transaction + |> where([t], t.from_address_hash == ^address_hash) + |> Transaction.not_dropped_or_replaced_transactions() + |> select([t], t.hash) + |> limit(@counters_limit) + |> select_repo(options).all() + + Logger.info( + "Time consumed for transactions_from_count_task for #{address_hash} is #{Time.diff(Time.utc_now(), start, :millisecond)}ms" + ) + + result + end) + + transactions_to_count_task = + Task.async(fn -> + result = + Transaction + |> where([t], t.to_address_hash == ^address_hash) + |> Transaction.not_dropped_or_replaced_transactions() + |> select([t], t.hash) + |> limit(@counters_limit) + |> select_repo(options).all() + + Logger.info( + "Time consumed for transactions_to_count_task for #{address_hash} is #{Time.diff(Time.utc_now(), start, :millisecond)}ms" + ) + + result + end) + + transactions_created_contract_count_task = + Task.async(fn -> + result = + Transaction + |> where([t], t.created_contract_address_hash == ^address_hash) + |> Transaction.not_dropped_or_replaced_transactions() + |> select([t], t.hash) + |> limit(@counters_limit) + |> select_repo(options).all() + + Logger.info( + "Time consumed for transactions_created_contract_count_task for #{address_hash} is #{Time.diff(Time.utc_now(), start, :millisecond)}ms" + ) + + result + end) + + token_transfer_count_task = + Task.async(fn -> + result = + address_hash + |> address_to_token_transfer_count_query() + |> limit(@counters_limit) + |> select_repo(options).aggregate(:count) + + Logger.info( + "Time consumed for token_transfer_count_task for #{address_hash} is #{Time.diff(Time.utc_now(), start, :millisecond)}ms" + ) + + result + end) + + token_balances_count_task = + Task.async(fn -> + result = + address_hash + |> address_hash_to_token_balances_query() + |> limit(@counters_limit) + |> select_repo(options).aggregate(:count) + + Logger.info( + "Time consumed for token_balances_count_task for #{address_hash} is #{Time.diff(Time.utc_now(), start, :millisecond)}ms" + ) + + result + end) + + logs_count_task = + Task.async(fn -> + result = + address_hash + |> address_hash_to_logs_query() + |> limit(@counters_limit) + |> select_repo(options).aggregate(:count) + + Logger.info( + "Time consumed for logs_count_task for #{address_hash} is #{Time.diff(Time.utc_now(), start, :millisecond)}ms" + ) + + result + end) + + withdrawals_count_task = + Task.async(fn -> + result = + address_hash + |> Withdrawal.address_hash_to_withdrawals_unordered_query() + |> limit(@counters_limit) + |> select_repo(options).aggregate(:count) + + Logger.info( + "Time consumed for withdrawals_count_task for #{address_hash} is #{Time.diff(Time.utc_now(), start, :millisecond)}ms" + ) + + result + end) + + internal_txs_count_task = + Task.async(fn -> + query_to_address_hash_wrapped = + InternalTransaction + |> InternalTransaction.where_nonpending_block() + |> InternalTransaction.where_address_fields_match(address_hash, :to_address_hash) + |> InternalTransaction.where_is_different_from_parent_transaction() + |> limit(@counters_limit) + |> wrapped_union_subquery() + + query_from_address_hash_wrapped = + InternalTransaction + |> InternalTransaction.where_nonpending_block() + |> InternalTransaction.where_address_fields_match(address_hash, :from_address_hash) + |> InternalTransaction.where_is_different_from_parent_transaction() + |> limit(@counters_limit) + |> wrapped_union_subquery() + + query_created_contract_address_hash_wrapped = + InternalTransaction + |> InternalTransaction.where_nonpending_block() + |> InternalTransaction.where_address_fields_match(address_hash, :created_contract_address_hash) + |> InternalTransaction.where_is_different_from_parent_transaction() + |> limit(@counters_limit) + |> wrapped_union_subquery() + + result = + query_to_address_hash_wrapped + |> union(^query_from_address_hash_wrapped) + |> union(^query_created_contract_address_hash_wrapped) + |> wrapped_union_subquery() + |> InternalTransaction.where_is_different_from_parent_transaction() + |> limit(@counters_limit) + |> select_repo(options).aggregate(:count) + + Logger.info( + "Time consumed for internal_txs_count_task for #{address_hash} is #{Time.diff(Time.utc_now(), start, :millisecond)}ms" + ) + + result + end) + + coin_balances_count_task = + Task.async(fn -> + result = + address_hash + |> address_hash_to_coin_balances() + |> limit(@counters_limit) + |> select_repo(options).aggregate(:count) + + Logger.info( + "Time consumed for coin_balances_count_task for #{address_hash} is #{Time.diff(Time.utc_now(), start, :millisecond)}ms" + ) + + result + end) + + {validations, txs_from, txs_to, txs_contract, token_transfers, token_balances, logs, withdrawals, internal_txs, + coin_balances} = + [ + validations_count_task, + transactions_from_count_task, + transactions_to_count_task, + transactions_created_contract_count_task, + token_transfer_count_task, + token_balances_count_task, + logs_count_task, + withdrawals_count_task, + internal_txs_count_task, + coin_balances_count_task + ] + |> Task.yield_many(:timer.seconds(30)) + |> Enum.map(fn {_task, res} -> + case res do + {:ok, result} -> + result + + {:exit, reason} -> + Logger.warn(fn -> + [ + "Query fetching address counters terminated: #{inspect(reason)}" + ] + end) + + nil + + nil -> + Logger.warn(fn -> + [ + "Query fetching address counters timed out." + ] + end) + + nil + end + end) + |> List.to_tuple() + + {validations, + (sanitize_list(txs_from) ++ sanitize_list(txs_to) ++ sanitize_list(txs_contract)) |> Enum.dedup() |> Enum.count(), + token_transfers, token_balances, logs, withdrawals, internal_txs, coin_balances} + end + + defp sanitize_list(nil), do: [] + defp sanitize_list(other), do: other +end diff --git a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex index 4b4574598a72..88c4594a3a7b 100644 --- a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex @@ -74,7 +74,6 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do token_balance |> cast(attrs, @allowed_fields) |> validate_required(@required_fields) - |> foreign_key_constraint(:address_hash) |> foreign_key_constraint(:token_contract_address_hash) end @@ -158,6 +157,25 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do dynamic([ctb, t], ctb.value * t.fiat_value / fragment("10 ^ ?", t.decimals)) end + @doc """ + Builds an `t:Ecto.Query.t/0` to fetch the current token balances of the given address (include unfetched). + """ + def last_token_balances_include_unfetched(address_hash) do + fiat_balance = fiat_value_query() + + from( + ctb in __MODULE__, + where: ctb.address_hash == ^address_hash, + left_join: t in assoc(ctb, :token), + on: ctb.token_contract_address_hash == t.contract_address_hash, + preload: [token: t], + select: ctb, + select_merge: ^%{fiat_value: fiat_balance}, + order_by: ^[desc_nulls_last: fiat_balance], + order_by: [desc: ctb.value, desc: ctb.id] + ) + end + @doc """ Builds an `t:Ecto.Query.t/0` to fetch the current token balances of the given address. """ diff --git a/apps/explorer/lib/explorer/chain/address/token_balance.ex b/apps/explorer/lib/explorer/chain/address/token_balance.ex index bdf7d42293b9..24b8dfb1c856 100644 --- a/apps/explorer/lib/explorer/chain/address/token_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/token_balance.ex @@ -65,7 +65,6 @@ defmodule Explorer.Chain.Address.TokenBalance do token_balance |> cast(attrs, @allowed_fields) |> validate_required(@required_fields) - |> foreign_key_constraint(:address_hash) |> foreign_key_constraint(:token_contract_address_hash) |> unique_constraint(:block_number, name: :token_balances_address_hash_block_number_index) end diff --git a/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex b/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex index 4315bd7b5d1a..60111725e142 100644 --- a/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex +++ b/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex @@ -10,6 +10,8 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do from: 2 ] + alias EthereumJSONRPC.Blocks + alias Explorer.Chain.{ Block, Wei @@ -25,8 +27,17 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do ttl_check_interval: :timer.seconds(1), callback: &async_task_on_deletion(&1) + @doc """ + Get `safelow`, `average` and `fast` percentile of transactions gas prices among the last `num_of_blocks` blocks + """ + @spec get_average_gas_price(pos_integer(), pos_integer(), pos_integer(), pos_integer()) :: + {:error, any} | {:ok, %{String.t() => nil | float, String.t() => nil | float, String.t() => nil | float}} def get_average_gas_price(num_of_blocks, safelow_percentile, average_percentile, fast_percentile) do - latest_gas_price_query = + safelow_percentile_fraction = safelow_percentile / 100 + average_percentile_fraction = average_percentile / 100 + fast_percentile_fraction = fast_percentile / 100 + + fee_query = from( block in Block, left_join: transaction in assoc(block, :transactions), @@ -35,27 +46,48 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do where: transaction.gas_price > ^0, group_by: block.number, order_by: [desc: block.number], - select: min(transaction.gas_price), + select: %{ + slow_gas_price: + fragment( + "percentile_disc(?) within group ( order by ? )", + ^safelow_percentile_fraction, + transaction.gas_price + ), + average_gas_price: + fragment( + "percentile_disc(?) within group ( order by ? )", + ^average_percentile_fraction, + transaction.gas_price + ), + fast_gas_price: + fragment( + "percentile_disc(?) within group ( order by ? )", + ^fast_percentile_fraction, + transaction.gas_price + ), + slow: + fragment( + "percentile_disc(?) within group ( order by ? )", + ^safelow_percentile_fraction, + transaction.max_priority_fee_per_gas + ), + average: + fragment( + "percentile_disc(?) within group ( order by ? )", + ^average_percentile_fraction, + transaction.max_priority_fee_per_gas + ), + fast: + fragment( + "percentile_disc(?) within group ( order by ? )", + ^fast_percentile_fraction, + transaction.max_priority_fee_per_gas + ) + }, limit: ^num_of_blocks ) - latest_gas_prices = - latest_gas_price_query - |> Repo.all(timeout: :infinity) - - latest_ordered_gas_prices = - latest_gas_prices - |> Enum.map(fn %Wei{value: gas_price} -> Decimal.to_integer(gas_price) end) - - safelow_gas_price = gas_price_percentile_to_gwei(latest_ordered_gas_prices, safelow_percentile) - average_gas_price = gas_price_percentile_to_gwei(latest_ordered_gas_prices, average_percentile) - fast_gas_price = gas_price_percentile_to_gwei(latest_ordered_gas_prices, fast_percentile) - - gas_prices = %{ - "slow" => safelow_gas_price, - "average" => average_gas_price, - "fast" => fast_gas_price - } + gas_prices = fee_query |> Repo.all(timeout: :infinity) |> process_fee_data_from_db() {:ok, gas_prices} catch @@ -63,6 +95,69 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do {:error, error} end + defp process_fee_data_from_db([]) do + %{ + "slow" => nil, + "average" => nil, + "fast" => nil + } + end + + defp process_fee_data_from_db(fees) do + fees_length = Enum.count(fees) + + %{ + slow_gas_price: slow_gas_price, + average_gas_price: average_gas_price, + fast_gas_price: fast_gas_price, + slow: slow, + average: average, + fast: fast + } = + fees + |> Enum.reduce( + &Map.merge(&1, &2, fn + _, v1, v2 when nil not in [v1, v2] -> Decimal.add(v1, v2) + _, v1, v2 -> v1 || v2 + end) + ) + |> Map.new(fn + {key, nil} -> {key, nil} + {key, value} -> {key, Decimal.div(value, fees_length)} + end) + + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + {slow_fee, average_fee, fast_fee} = + case {nil not in [slow, average, fast], EthereumJSONRPC.fetch_block_by_tag("pending", json_rpc_named_arguments)} do + {true, {:ok, %Blocks{blocks_params: [%{base_fee_per_gas: base_fee}]}}} when not is_nil(base_fee) -> + base_fee_wei = base_fee |> Decimal.new() |> Wei.from(:wei) + + { + priority_with_base_fee(slow, base_fee_wei), + priority_with_base_fee(average, base_fee_wei), + priority_with_base_fee(fast, base_fee_wei) + } + + _ -> + {gas_price(slow_gas_price), gas_price(average_gas_price), gas_price(fast_gas_price)} + end + + %{ + "slow" => slow_fee, + "average" => average_fee, + "fast" => fast_fee + } + end + + defp priority_with_base_fee(priority, base_fee) do + priority |> Wei.from(:wei) |> Wei.sum(base_fee) |> Wei.to(:gwei) |> Decimal.to_float() |> Float.ceil(2) + end + + defp gas_price(value) do + value |> Wei.from(:wei) |> Wei.to(:gwei) |> Decimal.to_float() |> Float.ceil(2) + end + defp num_of_blocks, do: Application.get_env(:explorer, __MODULE__)[:num_of_blocks] defp safelow, do: Application.get_env(:explorer, __MODULE__)[:safelow_percentile] @@ -102,40 +197,6 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do {:update, task} end - defp gas_price_percentile_to_gwei(gas_prices, percentile) do - gas_price_wei = percentile(gas_prices, percentile) - - if gas_price_wei do - gas_price_gwei = Wei.to(%Wei{value: Decimal.from_float(gas_price_wei)}, :gwei) - - gas_price_gwei_float = gas_price_gwei |> Decimal.to_float() - - if gas_price_gwei_float > 0.01 do - gas_price_gwei_float - |> Float.ceil(2) - else - gas_price_gwei_float - end - else - nil - end - end - - @spec percentile(list, number) :: number | nil - defp percentile([], _), do: nil - defp percentile([x], _), do: x - defp percentile(list, 0), do: Enum.min(list) - defp percentile(list, 100), do: Enum.max(list) - - defp percentile(list, n) when is_list(list) and is_number(n) do - s = Enum.sort(list) - r = n / 100.0 * (length(list) - 1) - f = :erlang.trunc(r) - lower = Enum.at(s, f) - upper = Enum.at(s, f + 1) - lower + (upper - lower) * (r - f) - end - # By setting this as a `callback` an async task will be started each time the # `gas_prices` expires (unless there is one already running) defp async_task_on_deletion({:delete, _, :gas_prices}), do: get_async_task() diff --git a/apps/explorer/lib/explorer/chain/events/publisher.ex b/apps/explorer/lib/explorer/chain/events/publisher.ex index 92f01bfc844d..8d47f337ec64 100644 --- a/apps/explorer/lib/explorer/chain/events/publisher.ex +++ b/apps/explorer/lib/explorer/chain/events/publisher.ex @@ -3,7 +3,7 @@ defmodule Explorer.Chain.Events.Publisher do Publishes events related to the Chain context. """ - @allowed_events ~w(addresses address_coin_balances address_token_balances blocks block_rewards internal_transactions last_block_number token_transfers transactions contract_verification_result token_total_supply changed_bytecode smart_contract_was_verified)a + @allowed_events ~w(addresses address_coin_balances address_token_balances address_current_token_balances blocks block_rewards internal_transactions last_block_number token_transfers transactions contract_verification_result token_total_supply changed_bytecode smart_contract_was_verified)a def broadcast(_data, false), do: :ok diff --git a/apps/explorer/lib/explorer/chain/events/subscriber.ex b/apps/explorer/lib/explorer/chain/events/subscriber.ex index 9862e2e6c043..fa0203cbaf90 100644 --- a/apps/explorer/lib/explorer/chain/events/subscriber.ex +++ b/apps/explorer/lib/explorer/chain/events/subscriber.ex @@ -3,7 +3,7 @@ defmodule Explorer.Chain.Events.Subscriber do Subscribes to events related to the Chain context. """ - @allowed_broadcast_events ~w(addresses address_coin_balances address_token_balances blocks block_rewards internal_transactions last_block_number token_transfers transactions contract_verification_result token_total_supply changed_bytecode smart_contract_was_verified)a + @allowed_broadcast_events ~w(addresses address_coin_balances address_token_balances address_current_token_balances blocks block_rewards internal_transactions last_block_number token_transfers transactions contract_verification_result token_total_supply changed_bytecode smart_contract_was_verified)a @allowed_broadcast_types ~w(catchup realtime on_demand contract_verification_result)a diff --git a/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex b/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex index 0903c70cb876..cc51fb87376c 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex @@ -273,11 +273,12 @@ defmodule Explorer.Chain.Import.Runner.Address.CurrentTokenBalances do ] ], where: - fragment("? < EXCLUDED.block_number", current_token_balance.block_number) or - (fragment("? = EXCLUDED.block_number", current_token_balance.block_number) and - fragment("EXCLUDED.value IS NOT NULL") and - (is_nil(current_token_balance.value_fetched_at) or - fragment("? < EXCLUDED.value_fetched_at", current_token_balance.value_fetched_at))) + fragment("EXCLUDED.value_fetched_at IS NOT NULL") and + (fragment("? < EXCLUDED.block_number", current_token_balance.block_number) or + (fragment("? = EXCLUDED.block_number", current_token_balance.block_number) and + fragment("EXCLUDED.value IS NOT NULL") and + (is_nil(current_token_balance.value_fetched_at) or + fragment("? < EXCLUDED.value_fetched_at", current_token_balance.value_fetched_at)))) ) end diff --git a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex index a3d99c65d6a5..3839dd58341c 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex @@ -374,6 +374,7 @@ defmodule Explorer.Chain.Import.Runner.Blocks do removed_consensus_block_hashes |> Enum.map(fn {number, _hash} -> number end) + |> Enum.reject(&Enum.member?(consensus_block_numbers, &1)) |> MissingRangesManipulator.add_ranges_by_block_numbers() {:ok, removed_consensus_block_hashes} diff --git a/apps/explorer/lib/explorer/chain/internal_transaction.ex b/apps/explorer/lib/explorer/chain/internal_transaction.ex index b91509a3dcf9..2d0c6ae2da02 100644 --- a/apps/explorer/lib/explorer/chain/internal_transaction.ex +++ b/apps/explorer/lib/explorer/chain/internal_transaction.ex @@ -444,8 +444,6 @@ defmodule Explorer.Chain.InternalTransaction do |> validate_call_error_or_result() |> check_constraint(:call_type, message: ~S|can't be blank when type is 'call'|, name: :call_has_call_type) |> check_constraint(:input, message: ~S|can't be blank when type is 'call'|, name: :call_has_call_type) - |> foreign_key_constraint(:from_address_hash) - |> foreign_key_constraint(:to_address_hash) |> foreign_key_constraint(:transaction_hash) |> unique_constraint(:index) end @@ -460,8 +458,6 @@ defmodule Explorer.Chain.InternalTransaction do |> validate_required(@create_required_fields) |> validate_create_error_or_result() |> check_constraint(:init, message: ~S|can't be blank when type is 'create'|, name: :create_has_init) - |> foreign_key_constraint(:created_contract_address_hash) - |> foreign_key_constraint(:from_address_hash) |> foreign_key_constraint(:transaction_hash) |> unique_constraint(:index) end @@ -474,8 +470,6 @@ defmodule Explorer.Chain.InternalTransaction do changeset |> cast(attrs, @selfdestruct_allowed_fields) |> validate_required(@selfdestruct_required_fields) - |> foreign_key_constraint(:from_address_hash) - |> foreign_key_constraint(:to_address_hash) |> unique_constraint(:index) end diff --git a/apps/explorer/lib/explorer/chain/search.ex b/apps/explorer/lib/explorer/chain/search.ex new file mode 100644 index 000000000000..b2439545a424 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/search.ex @@ -0,0 +1,523 @@ +defmodule Explorer.Chain.Search do + @moduledoc """ + Search related functions + """ + import Ecto.Query, + only: [ + from: 2, + limit: 2, + order_by: 3, + subquery: 1, + union: 2, + where: 3 + ] + + import Explorer.Chain, only: [select_repo: 1] + + alias Explorer.{Chain, PagingOptions} + alias Explorer.Tags.{AddressTag, AddressToTag} + + alias Explorer.Chain.{ + Address, + Block, + SmartContract, + Token, + Transaction + } + + @doc """ + Search function used in web interface. Returns paginated search results + """ + @spec joint_search(PagingOptions.t(), integer(), binary(), [Chain.api?()] | []) :: list + def joint_search(paging_options, offset, raw_string, options \\ []) do + string = String.trim(raw_string) + + case prepare_search_term(string) do + {:some, term} -> + tokens_query = search_token_query(term) + contracts_query = search_contract_query(term) + labels_query = search_label_query(term) + tx_query = search_tx_query(string) + address_query = search_address_query(string) + block_query = search_block_query(string) + + basic_query = + from( + tokens in subquery(tokens_query), + union: ^contracts_query, + union: ^labels_query + ) + + query = + cond do + address_query -> + basic_query + |> union(^address_query) + + tx_query -> + basic_query + |> union(^tx_query) + |> union(^block_query) + + block_query -> + basic_query + |> union(^block_query) + + true -> + basic_query + end + + ordered_query = + from(items in subquery(query), + order_by: [ + desc: items.priority, + desc_nulls_last: items.circulating_market_cap, + desc_nulls_last: items.exchange_rate, + desc_nulls_last: items.is_verified_via_admin_panel, + desc_nulls_last: items.holder_count, + asc: items.name, + desc: items.inserted_at + ], + limit: ^paging_options.page_size, + offset: ^offset + ) + + paginated_ordered_query = + ordered_query + |> page_search_results(paging_options) + + search_results = select_repo(options).all(paginated_ordered_query) + + search_results + |> Enum.map(fn result -> + result + |> compose_result_checksummed_address_hash() + |> format_timestamp() + end) + + _ -> + [] + end + end + + @doc """ + Search function. Differences from joint_search/4: + 1. Returns all the found categories (amount of results up to `paging_options.page_size`). + For example if was found 50 tokens, 50 smart-contracts, 50 labels, 1 address, 1 transaction and 2 blocks (impossible, just example) and page_size=50. Then function will return: + [1 address, 1 transaction, 2 blocks, 16 tokens, 15 smart-contracts, 15 labels] + 2. Results couldn't be paginated + """ + @spec balanced_unpaginated_search(PagingOptions.t(), binary(), [Chain.api?()] | []) :: list + def balanced_unpaginated_search(paging_options, raw_search_query, options \\ []) do + search_query = String.trim(raw_search_query) + + case prepare_search_term(search_query) do + {:some, term} -> + tokens_result = + term + |> search_token_query() + |> order_by([token], + desc_nulls_last: token.circulating_market_cap, + desc_nulls_last: token.fiat_value, + desc_nulls_last: token.is_verified_via_admin_panel, + desc_nulls_last: token.holder_count, + asc: token.name, + desc: token.inserted_at + ) + |> limit(^paging_options.page_size) + |> select_repo(options).all() + + contracts_result = + term + |> search_contract_query() + |> order_by([items], asc: items.name, desc: items.inserted_at) + |> limit(^paging_options.page_size) + |> select_repo(options).all() + + labels_result = + term + |> search_label_query() + |> order_by([att, at], asc: at.display_name, desc: att.inserted_at) + |> limit(^paging_options.page_size) + |> select_repo(options).all() + + tx_result = + if query = search_tx_query(search_query) do + query + |> select_repo(options).all() + else + [] + end + + address_result = + if query = search_address_query(search_query) do + query + |> select_repo(options).all() + else + [] + end + + blocks_result = + if query = search_block_query(search_query) do + query + |> limit(^paging_options.page_size) + |> select_repo(options).all() + else + [] + end + + non_empty_lists = + [tokens_result, contracts_result, labels_result, tx_result, address_result, blocks_result] + |> Enum.filter(fn list -> Enum.count(list) > 0 end) + |> Enum.sort_by(fn list -> Enum.count(list) end, :asc) + + to_take = + non_empty_lists + |> Enum.map(fn list -> Enum.count(list) end) + |> take_all_categories(List.duplicate(0, Enum.count(non_empty_lists)), paging_options.page_size) + + non_empty_lists + |> Enum.zip_reduce(to_take, [], fn x, y, acc -> acc ++ Enum.take(x, y) end) + |> Enum.map(fn result -> + result + |> compose_result_checksummed_address_hash() + |> format_timestamp() + end) + + _ -> + [] + end + end + + def prepare_search_term(string) do + case Regex.scan(~r/[a-zA-Z0-9]+/, string) do + [_ | _] = words -> + term_final = + words + |> Enum.map_join(" & ", fn [word] -> word <> ":*" end) + + {:some, term_final} + + _ -> + :none + end + end + + defp search_label_query(term) do + inner_query = + from(tag in AddressTag, + where: fragment("to_tsvector('english', ?) @@ to_tsquery(?)", tag.display_name, ^term), + select: tag + ) + + from(att in AddressToTag, + inner_join: at in subquery(inner_query), + on: att.tag_id == at.id, + left_join: smart_contract in SmartContract, + on: att.address_hash == smart_contract.address_hash, + select: %{ + address_hash: att.address_hash, + tx_hash: fragment("CAST(NULL AS bytea)"), + block_hash: fragment("CAST(NULL AS bytea)"), + type: "label", + name: at.display_name, + symbol: ^nil, + holder_count: ^nil, + inserted_at: att.inserted_at, + block_number: 0, + icon_url: nil, + token_type: nil, + timestamp: fragment("NULL::timestamp without time zone"), + verified: not is_nil(smart_contract), + exchange_rate: nil, + total_supply: nil, + circulating_market_cap: nil, + priority: 1, + is_verified_via_admin_panel: nil + } + ) + end + + defp search_token_query(term) do + from(token in Token, + left_join: smart_contract in SmartContract, + on: token.contract_address_hash == smart_contract.address_hash, + where: fragment("to_tsvector('english', ? || ' ' || ?) @@ to_tsquery(?)", token.symbol, token.name, ^term), + select: %{ + address_hash: token.contract_address_hash, + tx_hash: fragment("CAST(NULL AS bytea)"), + block_hash: fragment("CAST(NULL AS bytea)"), + type: "token", + name: token.name, + symbol: token.symbol, + holder_count: token.holder_count, + inserted_at: token.inserted_at, + block_number: 0, + icon_url: token.icon_url, + token_type: token.type, + timestamp: fragment("NULL::timestamp without time zone"), + verified: not is_nil(smart_contract), + exchange_rate: token.fiat_value, + total_supply: token.total_supply, + circulating_market_cap: token.circulating_market_cap, + priority: 0, + is_verified_via_admin_panel: token.is_verified_via_admin_panel + } + ) + end + + defp search_contract_query(term) do + from(smart_contract in SmartContract, + left_join: address in Address, + on: smart_contract.address_hash == address.hash, + where: fragment("to_tsvector('english', ?) @@ to_tsquery(?)", smart_contract.name, ^term), + select: %{ + address_hash: smart_contract.address_hash, + tx_hash: fragment("CAST(NULL AS bytea)"), + block_hash: fragment("CAST(NULL AS bytea)"), + type: "contract", + name: smart_contract.name, + symbol: ^nil, + holder_count: ^nil, + inserted_at: address.inserted_at, + block_number: 0, + icon_url: nil, + token_type: nil, + timestamp: fragment("NULL::timestamp without time zone"), + verified: true, + exchange_rate: nil, + total_supply: nil, + circulating_market_cap: nil, + priority: 0, + is_verified_via_admin_panel: nil + } + ) + end + + defp search_address_query(term) do + case Chain.string_to_address_hash(term) do + {:ok, address_hash} -> + from(address in Address, + left_join: + address_name in subquery( + from(name in Address.Name, + where: name.address_hash == ^address_hash, + order_by: [desc: name.primary], + limit: 1 + ) + ), + on: address.hash == address_name.address_hash, + where: address.hash == ^address_hash, + select: %{ + address_hash: address.hash, + tx_hash: fragment("CAST(NULL AS bytea)"), + block_hash: fragment("CAST(NULL AS bytea)"), + type: "address", + name: address_name.name, + symbol: ^nil, + holder_count: ^nil, + inserted_at: address.inserted_at, + block_number: 0, + icon_url: nil, + token_type: nil, + timestamp: fragment("NULL::timestamp without time zone"), + verified: address.verified, + exchange_rate: nil, + total_supply: nil, + circulating_market_cap: nil, + priority: 0, + is_verified_via_admin_panel: nil + } + ) + + _ -> + nil + end + end + + defp search_tx_query(term) do + case Chain.string_to_transaction_hash(term) do + {:ok, tx_hash} -> + from(transaction in Transaction, + left_join: block in Block, + on: transaction.block_hash == block.hash, + where: transaction.hash == ^tx_hash, + select: %{ + address_hash: fragment("CAST(NULL AS bytea)"), + tx_hash: transaction.hash, + block_hash: fragment("CAST(NULL AS bytea)"), + type: "transaction", + name: ^nil, + symbol: ^nil, + holder_count: ^nil, + inserted_at: transaction.inserted_at, + block_number: 0, + icon_url: nil, + token_type: nil, + timestamp: block.timestamp, + verified: nil, + exchange_rate: nil, + total_supply: nil, + circulating_market_cap: nil, + priority: 0, + is_verified_via_admin_panel: nil + } + ) + + _ -> + nil + end + end + + defp search_block_query(term) do + case Chain.string_to_block_hash(term) do + {:ok, block_hash} -> + from(block in Block, + where: block.hash == ^block_hash, + select: %{ + address_hash: fragment("CAST(NULL AS bytea)"), + tx_hash: fragment("CAST(NULL AS bytea)"), + block_hash: block.hash, + type: "block", + name: ^nil, + symbol: ^nil, + holder_count: ^nil, + inserted_at: block.inserted_at, + block_number: block.number, + icon_url: nil, + token_type: nil, + timestamp: block.timestamp, + verified: nil, + exchange_rate: nil, + total_supply: nil, + circulating_market_cap: nil, + priority: 0, + is_verified_via_admin_panel: nil + } + ) + + _ -> + case Integer.parse(term) do + {block_number, ""} -> + from(block in Block, + where: block.number == ^block_number, + select: %{ + address_hash: fragment("CAST(NULL AS bytea)"), + tx_hash: fragment("CAST(NULL AS bytea)"), + block_hash: block.hash, + type: "block", + name: ^nil, + symbol: ^nil, + holder_count: ^nil, + inserted_at: block.inserted_at, + block_number: block.number, + icon_url: nil, + token_type: nil, + timestamp: block.timestamp, + verified: nil, + exchange_rate: nil, + total_supply: nil, + circulating_market_cap: nil, + priority: 0, + is_verified_via_admin_panel: nil + } + ) + + _ -> + nil + end + end + end + + defp page_search_results(query, %PagingOptions{key: nil}), do: query + + defp page_search_results(query, %PagingOptions{ + key: {_address_hash, _tx_hash, _block_hash, holder_count, name, inserted_at, item_type} + }) + when holder_count in [nil, ""] do + where( + query, + [item], + (item.name > ^name and item.type == ^item_type) or + (item.name == ^name and item.inserted_at < ^inserted_at and + item.type == ^item_type) or + item.type != ^item_type + ) + end + + # credo:disable-for-next-line + defp page_search_results(query, %PagingOptions{ + key: {_address_hash, _tx_hash, _block_hash, holder_count, name, inserted_at, item_type} + }) do + where( + query, + [item], + (item.holder_count < ^holder_count and item.type == ^item_type) or + (item.holder_count == ^holder_count and item.name > ^name and item.type == ^item_type) or + (item.holder_count == ^holder_count and item.name == ^name and item.inserted_at < ^inserted_at and + item.type == ^item_type) or + item.type != ^item_type + ) + end + + defp take_all_categories([], taken_lengths, _remained), do: taken_lengths + + defp take_all_categories(lengths, taken_lengths, remained) do + non_zero_count = count_non_zero(lengths) + + target = if(remained < non_zero_count, do: 1, else: div(remained, non_zero_count)) + + {lengths_updated, %{result: taken_lengths_reversed}} = + Enum.map_reduce(lengths, %{result: [], sum: 0}, fn el, acc -> + taken = + cond do + acc[:sum] >= remained -> + 0 + + el < target -> + el + + true -> + target + end + + {el - taken, %{result: [taken | acc[:result]], sum: acc[:sum] + taken}} + end) + + taken_lengths = + taken_lengths + |> Enum.zip_reduce(Enum.reverse(taken_lengths_reversed), [], fn x, y, acc -> [x + y | acc] end) + |> Enum.reverse() + + remained = remained - Enum.sum(taken_lengths_reversed) + + if remained > 0 and count_non_zero(lengths_updated) > 0 do + take_all_categories(lengths_updated, taken_lengths, remained) + else + taken_lengths + end + end + + defp count_non_zero(list) do + Enum.reduce(list, 0, fn el, acc -> acc + if el > 0, do: 1, else: 0 end) + end + + defp compose_result_checksummed_address_hash(result) do + if result.address_hash do + result + |> Map.put(:address_hash, Address.checksum(result.address_hash)) + else + result + end + end + + # For some reasons timestamp for blocks and txs returns as ~N[2023-06-25 19:39:47.339493] + defp format_timestamp(result) do + if result.timestamp do + result + |> Map.put(:timestamp, DateTime.from_naive!(result.timestamp, "Etc/UTC")) + else + result + end + end +end diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index c1cb17f1022e..6b3696d28259 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -424,30 +424,53 @@ defmodule Explorer.Chain.SmartContract do defp upsert_contract_methods(changeset), do: changeset - defp error_message(:compilation), do: "There was an error compiling your contract." - defp error_message(:compiler_version), do: "Compiler version does not match, please try again." - defp error_message(:generated_bytecode), do: "Bytecode does not match, please try again." - defp error_message(:constructor_arguments), do: "Constructor arguments do not match, please try again." - defp error_message(:name), do: "Wrong contract name, please try again." - defp error_message(:json), do: "Invalid JSON file." + defp error_message(:compilation), do: error_message_with_log("There was an error compiling your contract.") + + defp error_message(:compiler_version), + do: error_message_with_log("Compiler version does not match, please try again.") + + defp error_message(:generated_bytecode), do: error_message_with_log("Bytecode does not match, please try again.") + + defp error_message(:constructor_arguments), + do: error_message_with_log("Constructor arguments do not match, please try again.") + + defp error_message(:name), do: error_message_with_log("Wrong contract name, please try again.") + defp error_message(:json), do: error_message_with_log("Invalid JSON file.") defp error_message(:autodetect_constructor_arguments_failed), - do: "Autodetection of constructor arguments failed. Please try to input constructor arguments manually." + do: + error_message_with_log( + "Autodetection of constructor arguments failed. Please try to input constructor arguments manually." + ) defp error_message(:no_creation_data), - do: "The contract creation transaction has not been indexed yet. Please wait a few minutes and try again." + do: + error_message_with_log( + "The contract creation transaction has not been indexed yet. Please wait a few minutes and try again." + ) - defp error_message(:unknown_error), do: "Unable to verify: unknown error." - defp error_message(:deployed_bytecode), do: "Deployed bytecode does not correspond to contract creation code." + defp error_message(:unknown_error), do: error_message_with_log("Unable to verify: unknown error.") - defp error_message(string) when is_binary(string), do: string + defp error_message(:deployed_bytecode), + do: error_message_with_log("Deployed bytecode does not correspond to contract creation code.") + + defp error_message(:contract_source_code), do: error_message_with_log("Empty contract source code.") + + defp error_message(string) when is_binary(string), do: error_message_with_log(string) + defp error_message(%{"message" => string} = error) when is_map(error), do: error_message_with_log(string) defp error_message(error) do Logger.warn(fn -> ["Unknown verifier error: ", inspect(error)] end) "There was an error validating your contract, please try again." end - defp error_message(:compilation, error_message), do: "There was an error compiling your contract: #{error_message}" + defp error_message(:compilation, error_message), + do: error_message_with_log("There was an error compiling your contract: #{error_message}") + + defp error_message_with_log(error_string) do + Logger.error("Smart-contract verification error: #{error_string}") + error_string + end defp select_error_field(:no_creation_data), do: :address_hash defp select_error_field(:compiler_version), do: :compiler_version diff --git a/apps/explorer/lib/explorer/chain/supply/rsk.ex b/apps/explorer/lib/explorer/chain/supply/rsk.ex index 1037dd4cf79e..2eb9caa93350 100644 --- a/apps/explorer/lib/explorer/chain/supply/rsk.ex +++ b/apps/explorer/lib/explorer/chain/supply/rsk.ex @@ -5,7 +5,7 @@ defmodule Explorer.Chain.Supply.RSK do use Explorer.Chain.Supply - import Ecto.Query, only: [from: 2] + import Ecto.Query, only: [from: 2, subquery: 1] import EthereumJSONRPC, only: [integer_to_quantity: 1] alias EthereumJSONRPC.FetchedBalances @@ -16,7 +16,9 @@ defmodule Explorer.Chain.Supply.RSK do @cache_name :rsk_balance @balance_key :balance + @rsk_bridge_contract_address "0x0000000000000000000000000000000001000006" + @spec market_cap(any()) :: Decimal.t() def market_cap(%{usd_value: usd_value}) when not is_nil(usd_value) do btc = circulating() @@ -25,31 +27,32 @@ defmodule Explorer.Chain.Supply.RSK do def market_cap(_), do: Decimal.new(0) - @doc "Equivalent to getting the circulating value " + @doc "Equivalent to getting the circulating value" def supply_for_days(days) do now = Timex.now() - balances_query = + base_query = from(balance in CoinBalance, join: block in Block, on: block.number == balance.block_number, where: block.consensus == true, - where: balance.address_hash == ^"0x0000000000000000000000000000000001000006", - where: block.timestamp > ^Timex.shift(now, days: -days), - distinct: fragment("date_trunc('day', ?)", block.timestamp), - select: {block.timestamp, balance.value} + where: balance.address_hash == ^@rsk_bridge_contract_address, + select: %{timestamp: block.timestamp, value: balance.value} + ) + + balances_query = + from(q in subquery(base_query), + where: q.timestamp > ^Timex.shift(now, days: -days), + distinct: fragment("date_trunc('day', ?)", q.timestamp), + select: {q.timestamp, q.value} ) balance_before_query = - from(balance in CoinBalance, - join: block in Block, - on: block.number == balance.block_number, - where: block.consensus == true, - where: balance.address_hash == ^"0x0000000000000000000000000000000001000006", - where: block.timestamp <= ^Timex.shift(Timex.now(), days: -days), - order_by: [desc: block.timestamp], + from(q in subquery(base_query), + where: q.timestamp <= ^Timex.shift(Timex.now(), days: -days), + order_by: [desc: q.timestamp], limit: 1, - select: balance.value + select: q.value ) by_day = @@ -105,7 +108,7 @@ defmodule Explorer.Chain.Supply.RSK do max_number = BlockNumber.get_max() params = [ - %{block_quantity: integer_to_quantity(max_number), hash_data: "0x0000000000000000000000000000000001000006"} + %{block_quantity: integer_to_quantity(max_number), hash_data: @rsk_bridge_contract_address} ] json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) @@ -116,7 +119,7 @@ defmodule Explorer.Chain.Supply.RSK do errors: [], params_list: [ %{ - address_hash: "0x0000000000000000000000000000000001000006", + address_hash: @rsk_bridge_contract_address, value: value } ] diff --git a/apps/explorer/lib/explorer/chain/token.ex b/apps/explorer/lib/explorer/chain/token.ex index b59f6ef2de5d..3b9eb2e95e18 100644 --- a/apps/explorer/lib/explorer/chain/token.ex +++ b/apps/explorer/lib/explorer/chain/token.ex @@ -41,6 +41,7 @@ defmodule Explorer.Chain.Token do * `fiat_value` - The price of a token in a configured currency (USD by default). * `circulating_market_cap` - The circulating market cap of a token in a configured currency (USD by default). * `icon_url` - URL of the token's icon. + * `is_verified_via_admin_panel` - is token verified via admin panel. """ @type t :: %Token{ name: String.t(), @@ -56,7 +57,8 @@ defmodule Explorer.Chain.Token do total_supply_updated_at_block: non_neg_integer() | nil, fiat_value: Decimal.t() | nil, circulating_market_cap: Decimal.t() | nil, - icon_url: String.t() + icon_url: String.t(), + is_verified_via_admin_panel: boolean() } @derive {Poison.Encoder, @@ -89,6 +91,7 @@ defmodule Explorer.Chain.Token do field(:fiat_value, :decimal) field(:circulating_market_cap, :decimal) field(:icon_url, :string) + field(:is_verified_via_admin_panel, :boolean) belongs_to( :contract_address, @@ -103,14 +106,13 @@ defmodule Explorer.Chain.Token do end @required_attrs ~w(contract_address_hash type)a - @optional_attrs ~w(cataloged decimals name symbol total_supply skip_metadata total_supply_updated_at_block updated_at fiat_value circulating_market_cap icon_url)a + @optional_attrs ~w(cataloged decimals name symbol total_supply skip_metadata total_supply_updated_at_block updated_at fiat_value circulating_market_cap icon_url is_verified_via_admin_panel)a @doc false def changeset(%Token{} = token, params \\ %{}) do token |> cast(params, @required_attrs ++ @optional_attrs) |> validate_required(@required_attrs) - |> foreign_key_constraint(:contract_address) |> trim_name() |> sanitize_token_input(:name) |> sanitize_token_input(:symbol) diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index d1abd9971e74..ff17c0f768cb 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -136,9 +136,6 @@ defmodule Explorer.Chain.TokenTransfer do struct |> cast(params, @required_attrs ++ @optional_attrs) |> validate_required(@required_attrs) - |> foreign_key_constraint(:from_address) - |> foreign_key_constraint(:to_address) - |> foreign_key_constraint(:token_contract_address) |> foreign_key_constraint(:transaction) end diff --git a/apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex b/apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex index 5ca1520cb2b5..573ab8a988a5 100644 --- a/apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex +++ b/apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex @@ -5,8 +5,9 @@ defmodule Explorer.Counters.AddressTransactionsGasUsageCounter do use GenServer alias Ecto.Changeset - alias Explorer.{Chain, Repo} + alias Explorer.Chain.Address.Counters alias Explorer.Counters.Helper + alias Explorer.Repo @cache_name :address_transactions_gas_usage_counter @last_update_key "last_update" @@ -67,7 +68,7 @@ defmodule Explorer.Counters.AddressTransactionsGasUsageCounter do defp update_cache(address) do address_hash_string = to_string(address.hash) put_into_cache("hash_#{address_hash_string}_#{@last_update_key}", Helper.current_time()) - new_data = Chain.address_to_gas_usage_count(address) + new_data = Counters.address_to_gas_usage_count(address) put_into_cache("hash_#{address_hash_string}", new_data) put_into_db(address, new_data) end diff --git a/apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex b/apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex index ffec588f3131..db3c82da3b48 100644 --- a/apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex +++ b/apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex @@ -5,8 +5,9 @@ defmodule Explorer.Counters.AddressTokenTransfersCounter do use GenServer alias Ecto.Changeset - alias Explorer.{Chain, Repo} + alias Explorer.Chain.Address.Counters alias Explorer.Counters.Helper + alias Explorer.Repo @cache_name :address_token_transfers_counter @last_update_key "last_update" @@ -67,7 +68,7 @@ defmodule Explorer.Counters.AddressTokenTransfersCounter do defp update_cache(address) do address_hash_string = to_string(address.hash) put_into_cache("hash_#{address_hash_string}_#{@last_update_key}", Helper.current_time()) - new_data = Chain.address_to_token_transfer_count(address) + new_data = Counters.address_to_token_transfer_count(address) put_into_cache("hash_#{address_hash_string}", new_data) put_into_db(address, new_data) end diff --git a/apps/explorer/lib/explorer/counters/address_transactions_counter.ex b/apps/explorer/lib/explorer/counters/address_transactions_counter.ex index e3c6dfffb690..7b2c912335fa 100644 --- a/apps/explorer/lib/explorer/counters/address_transactions_counter.ex +++ b/apps/explorer/lib/explorer/counters/address_transactions_counter.ex @@ -5,8 +5,9 @@ defmodule Explorer.Counters.AddressTransactionsCounter do use GenServer alias Ecto.Changeset - alias Explorer.{Chain, Repo} + alias Explorer.Chain.Address.Counters alias Explorer.Counters.Helper + alias Explorer.Repo @cache_name :address_transactions_counter @last_update_key "last_update" @@ -67,7 +68,7 @@ defmodule Explorer.Counters.AddressTransactionsCounter do defp update_cache(address) do address_hash_string = to_string(address.hash) put_into_cache("hash_#{address_hash_string}_#{@last_update_key}", Helper.current_time()) - new_data = Chain.address_to_transaction_count(address) + new_data = Counters.address_to_transaction_count(address) put_into_cache("hash_#{address_hash_string}", new_data) put_into_db(address, new_data) end diff --git a/apps/explorer/lib/explorer/counters/addresses_counter.ex b/apps/explorer/lib/explorer/counters/addresses_counter.ex index 820d96a9152f..51fb845bb945 100644 --- a/apps/explorer/lib/explorer/counters/addresses_counter.ex +++ b/apps/explorer/lib/explorer/counters/addresses_counter.ex @@ -7,7 +7,7 @@ defmodule Explorer.Counters.AddressesCounter do use GenServer - alias Explorer.Chain + alias Explorer.Chain.Address.Counters @table :addresses_counter @@ -104,7 +104,7 @@ defmodule Explorer.Counters.AddressesCounter do Consolidates the info by populating the `:ets` table with the current database information. """ def consolidate do - counter = Chain.count_addresses() + counter = Counters.count_addresses() insert_counter({cache_key(), counter}) end diff --git a/apps/explorer/lib/explorer/counters/addresses_with_balance_counter.ex b/apps/explorer/lib/explorer/counters/addresses_with_balance_counter.ex index 1358e52a21e8..53621029afd7 100644 --- a/apps/explorer/lib/explorer/counters/addresses_with_balance_counter.ex +++ b/apps/explorer/lib/explorer/counters/addresses_with_balance_counter.ex @@ -7,7 +7,7 @@ defmodule Explorer.Counters.AddressesWithBalanceCounter do use GenServer - alias Explorer.Chain + alias Explorer.Chain.Address.Counters @table :addresses_with_balance_counter @@ -104,7 +104,7 @@ defmodule Explorer.Counters.AddressesWithBalanceCounter do Consolidates the info by populating the `:ets` table with the current database information. """ def consolidate do - counter = Chain.count_addresses_with_balance() + counter = Counters.count_addresses_with_balance() insert_counter({cache_key(), counter}) end diff --git a/apps/explorer/lib/explorer/eth_rpc.ex b/apps/explorer/lib/explorer/eth_rpc.ex index ed7e6830802b..889fdabbba6e 100644 --- a/apps/explorer/lib/explorer/eth_rpc.ex +++ b/apps/explorer/lib/explorer/eth_rpc.ex @@ -296,17 +296,14 @@ defmodule Explorer.EthRPC do defp paging_options(%{ "paging_options" => %{ "logIndex" => log_index, - "transactionIndex" => transaction_index, "blockNumber" => block_number } - }) - when is_integer(transaction_index) do + }) do with {:ok, parsed_block_number} <- to_number(block_number, "invalid block number"), {:ok, parsed_log_index} <- to_number(log_index, "invalid log index") do {:ok, %{ log_index: parsed_log_index, - transaction_index: transaction_index, block_number: parsed_block_number }} end diff --git a/apps/explorer/lib/explorer/etherscan/logs.ex b/apps/explorer/lib/explorer/etherscan/logs.ex index 006c0a2e191b..c7ae91e34732 100644 --- a/apps/explorer/lib/explorer/etherscan/logs.ex +++ b/apps/explorer/lib/explorer/etherscan/logs.ex @@ -248,16 +248,14 @@ defmodule Explorer.Etherscan.Logs do defp where_multiple_topics_match(query, _, _, _), do: query - defp page_logs(query, %{block_number: nil, transaction_index: nil, log_index: nil}) do + defp page_logs(query, %{block_number: nil, log_index: nil}) do query end - defp page_logs(query, %{block_number: block_number, transaction_index: transaction_index, log_index: log_index}) do + defp page_logs(query, %{block_number: block_number, log_index: log_index}) do from( data in query, - where: - data.index > ^log_index and data.block_number >= ^block_number and - data.transaction_index >= ^transaction_index + where: data.index > ^log_index and data.block_number >= ^block_number ) end diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex b/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex index 96455e7c327c..045766198150 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex @@ -3,6 +3,8 @@ defmodule Explorer.SmartContract.Solidity.Publisher do Module responsible to control the contract verification. """ + require Logger + import Explorer.SmartContract.Helper, only: [cast_libraries: 1] alias Explorer.Chain @@ -10,6 +12,10 @@ defmodule Explorer.SmartContract.Solidity.Publisher do alias Explorer.SmartContract.{CompilerVersion, Helper} alias Explorer.SmartContract.Solidity.Verifier + @sc_verification_via_flattened_file_started "Smart-contract verification via flattened file started" + @sc_verification_via_standard_json_input_started "Smart-contract verification via standard json input started" + @sc_verification_via_multipart_files_started "Smart-contract verification via multipart files started" + @doc """ Evaluates smart contract authenticity and saves its details. @@ -27,6 +33,7 @@ defmodule Explorer.SmartContract.Solidity.Publisher do """ def publish(address_hash, params, external_libraries \\ %{}) do + Logger.info(@sc_verification_via_flattened_file_started) params_with_external_libraries = add_external_libraries(params, external_libraries) case Verifier.evaluate_authenticity(address_hash, params_with_external_libraries) do @@ -65,6 +72,8 @@ defmodule Explorer.SmartContract.Solidity.Publisher do end def publish_with_standard_json_input(%{"address_hash" => address_hash} = params, json_input) do + Logger.info(@sc_verification_via_standard_json_input_started) + case Verifier.evaluate_authenticity_via_standard_json_input(address_hash, params, json_input) do {:ok, %{ @@ -102,6 +111,7 @@ defmodule Explorer.SmartContract.Solidity.Publisher do end def publish_with_multi_part_files(%{"address_hash" => address_hash} = params, external_libraries \\ %{}, files) do + Logger.info(@sc_verification_via_multipart_files_started) params_with_external_libraries = add_external_libraries(params, external_libraries) case Verifier.evaluate_authenticity_via_multi_part_files(address_hash, params_with_external_libraries, files) do @@ -187,6 +197,8 @@ defmodule Explorer.SmartContract.Solidity.Publisher do end defp create_or_update_smart_contract(address_hash, attrs) do + Logger.info("Publish successfully verified Solidity smart-contract #{address_hash} into the DB") + if Chain.smart_contract_verified?(address_hash) do Chain.update_smart_contract(attrs, attrs.external_libraries, attrs.secondary_sources) else @@ -209,6 +221,8 @@ defmodule Explorer.SmartContract.Solidity.Publisher do verification_with_files? ) + Logger.error("Solidity smart-contract verification #{address_hash} failed because of the error #{error}") + %{changeset | action: :insert} end diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex b/apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex index b8dbc3f81dba..b9ae134d54cb 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex @@ -3,6 +3,8 @@ defmodule Explorer.SmartContract.Solidity.PublisherWorker do Background smart contract verification worker. """ + require Logger + use Que.Worker, concurrency: 5 alias Explorer.Chain.Events.Publisher, as: EventsPublisher @@ -68,6 +70,8 @@ defmodule Explorer.SmartContract.Solidity.PublisherWorker do {:error, changeset} end + Logger.info("Smart-contract #{address_hash} verification: broadcast verification results") + if conn do EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result, conn}}], :on_demand) else diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex b/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex index 57c11709abd5..ee5554a0361d 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex @@ -11,8 +11,10 @@ defmodule Explorer.SmartContract.Solidity.Verifier do import Explorer.SmartContract.Helper, only: [cast_libraries: 1, prepare_bytecode_for_microservice: 3, contract_creation_input: 1] + # import Explorer.Chain.SmartContract, only: [:function_description] alias ABI.{FunctionSelector, TypeDecoder} alias Explorer.Chain + alias Explorer.Chain.{Data, Hash, SmartContract} alias Explorer.SmartContract.RustVerifierInterface alias Explorer.SmartContract.Solidity.CodeCompiler @@ -533,4 +535,53 @@ defmodule Explorer.SmartContract.Solidity.Verifier do def parse_boolean(false), do: false def parse_boolean(_), do: false + + @doc """ + Function tries to parse constructor args from smart contract creation input. + 1. using `extract_meta_from_deployed_bytecode/1` we derive CBOR metadata string + 2. using metadata we split creation_tx_input and try to decode resulting constructor arguments + 2.1. if we successfully decoded args using constructor's abi, then return constructor args + 2.2 otherwise return nil + """ + @spec parse_constructor_arguments_for_sourcify_contract(Hash.Address.t(), SmartContract.abi()) :: nil | binary + def parse_constructor_arguments_for_sourcify_contract(address_hash, abi) do + parse_constructor_arguments_for_sourcify_contract(address_hash, abi, Chain.smart_contract_bytecode(address_hash)) + end + + @doc """ + Clause for cases when we already can pass deployed bytecode to this function (in order to avoid excessive read DB accesses) + """ + @spec parse_constructor_arguments_for_sourcify_contract( + Hash.Address.t(), + SmartContract.abi(), + binary | Explorer.Chain.Data.t() + ) :: nil | binary + def parse_constructor_arguments_for_sourcify_contract(address_hash, abi, deployed_bytecode) + when is_binary(deployed_bytecode) do + creation_tx_input = + case Chain.smart_contract_creation_tx_bytecode(address_hash) do + %{init: init, created_contract_code: _created_contract_code} -> + "0x" <> init_without_0x = init + init_without_0x + + _ -> + nil + end + + with true <- has_constructor_with_params?(abi), + check_function <- parse_constructor_and_return_check_function(abi), + false <- is_nil(creation_tx_input) || deployed_bytecode == "0x", + {meta, meta_length} <- extract_meta_from_deployed_bytecode(deployed_bytecode), + [_bytecode, constructor_args] <- String.split(creation_tx_input, meta <> meta_length), + ^constructor_args <- check_function.(constructor_args) do + constructor_args + else + _ -> + nil + end + end + + def parse_constructor_arguments_for_sourcify_contract(address_hash, abi, deployed_bytecode) do + parse_constructor_arguments_for_sourcify_contract(address_hash, abi, Data.to_string(deployed_bytecode)) + end end diff --git a/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex b/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex index 64f91345f787..e5c38116d06f 100644 --- a/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex +++ b/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex @@ -5,6 +5,8 @@ defmodule Explorer.SmartContract.Vyper.Publisher do import Explorer.SmartContract.Helper, only: [cast_libraries: 1] + require Logger + alias Explorer.Chain alias Explorer.Chain.SmartContract alias Explorer.SmartContract.CompilerVersion @@ -119,6 +121,7 @@ defmodule Explorer.SmartContract.Vyper.Publisher do end def publish_smart_contract(address_hash, params, abi) do + Logger.info("Publish successfully verified Vyper smart-contract #{address_hash} into the DB") attrs = address_hash |> attributes(params, abi) Chain.create_smart_contract(attrs, attrs.external_libraries, attrs.secondary_sources) @@ -136,6 +139,8 @@ defmodule Explorer.SmartContract.Vyper.Publisher do verification_with_files? ) + Logger.error("Vyper smart-contract verification #{address_hash} failed because of the error #{error}") + %{changeset | action: :insert} end diff --git a/apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex b/apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex index 45b8feccffa9..690efc346635 100644 --- a/apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex +++ b/apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex @@ -3,6 +3,8 @@ defmodule Explorer.SmartContract.Vyper.PublisherWorker do Background smart contract verification worker. """ + require Logger + use Que.Worker, concurrency: 5 alias Explorer.Chain.Events.Publisher, as: EventsPublisher @@ -34,6 +36,8 @@ defmodule Explorer.SmartContract.Vyper.PublisherWorker do {:error, changeset} end + Logger.info("Smart-contract #{address_hash} verification: broadcast verification results") + if conn do EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result, conn}}], :on_demand) else diff --git a/apps/explorer/lib/explorer/utility/missing_block_range.ex b/apps/explorer/lib/explorer/utility/missing_block_range.ex index b9355d0c6192..4892334d5fd5 100644 --- a/apps/explorer/lib/explorer/utility/missing_block_range.ex +++ b/apps/explorer/lib/explorer/utility/missing_block_range.ex @@ -61,6 +61,7 @@ defmodule Explorer.Utility.MissingBlockRange do update_range(range_1, %{from_number: range_2.from_number}) _ -> + delete_ranges_between(max_number, min_number) insert_range(%{from_number: max_number, to_number: min_number}) end end diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index fb2cb75bfa64..7bf6eb62cc2d 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -24,7 +24,7 @@ defmodule Explorer.Mixfile do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "5.2.1", + version: "5.2.2", xref: [exclude: [BlockScoutWeb.WebRouter.Helpers]] ] end diff --git a/apps/explorer/priv/repo/migrations/20230809134253_add_is_verified_via_admin_panel.exs b/apps/explorer/priv/repo/migrations/20230809134253_add_is_verified_via_admin_panel.exs new file mode 100644 index 000000000000..1cfa57cb6fca --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20230809134253_add_is_verified_via_admin_panel.exs @@ -0,0 +1,9 @@ +defmodule Explorer.Repo.Migrations.AddIsVerifiedViaAdminPanel do + use Ecto.Migration + + def change do + alter table(:tokens) do + add(:is_verified_via_admin_panel, :boolean, null: true, default: false) + end + end +end diff --git a/apps/explorer/priv/repo/migrations/20230815131151_drop_logs_address_hash_foreign_key.exs b/apps/explorer/priv/repo/migrations/20230815131151_drop_logs_address_hash_foreign_key.exs new file mode 100644 index 000000000000..e847cb14d74f --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20230815131151_drop_logs_address_hash_foreign_key.exs @@ -0,0 +1,8 @@ +# cspell:ignore fkey +defmodule Explorer.Repo.Migrations.DropLogsAddressHashForeignKey do + use Ecto.Migration + + def change do + drop_if_exists(constraint(:logs, :logs_address_hash_fkey)) + end +end diff --git a/apps/explorer/priv/repo/migrations/20230816061723_drop_token_transfers_and_transactions_address_foreign_key.exs b/apps/explorer/priv/repo/migrations/20230816061723_drop_token_transfers_and_transactions_address_foreign_key.exs new file mode 100644 index 000000000000..1cfe0a5fb3d7 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20230816061723_drop_token_transfers_and_transactions_address_foreign_key.exs @@ -0,0 +1,13 @@ +# cspell:ignore fkey +defmodule Explorer.Repo.Migrations.DropTokenTransfersAndTransactionsAddressForeignKey do + use Ecto.Migration + + def change do + drop_if_exists(constraint(:token_transfers, :token_transfers_from_address_hash_fkey)) + drop_if_exists(constraint(:token_transfers, :token_transfers_to_address_hash_fkey)) + drop_if_exists(constraint(:token_transfers, :token_transfers_token_contract_address_hash_fkey)) + drop_if_exists(constraint(:transactions, :transactions_created_contract_address_hash_fkey)) + drop_if_exists(constraint(:transactions, :transactions_from_address_hash_fkey)) + drop_if_exists(constraint(:transactions, :transactions_to_address_hash_fkey)) + end +end diff --git a/apps/explorer/priv/repo/migrations/20230817061317_drop_address_foreign_keys.exs b/apps/explorer/priv/repo/migrations/20230817061317_drop_address_foreign_keys.exs new file mode 100644 index 000000000000..f10c301b76e3 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20230817061317_drop_address_foreign_keys.exs @@ -0,0 +1,14 @@ +# cspell:ignore fkey +defmodule Explorer.Repo.Migrations.DropAddressForeignKeys do + use Ecto.Migration + + def change do + drop_if_exists(constraint(:address_coin_balances, :address_coin_balances_address_hash_fkey)) + drop_if_exists(constraint(:address_token_balances, :address_token_balances_address_hash_fkey)) + drop_if_exists(constraint(:address_current_token_balances, :address_current_token_balances_address_hash_fkey)) + drop_if_exists(constraint(:tokens, :tokens_contract_address_hash_fkey)) + drop_if_exists(constraint(:internal_transactions, :internal_transactions_created_contract_address_hash_fkey)) + drop_if_exists(constraint(:internal_transactions, :internal_transactions_from_address_hash_fkey)) + drop_if_exists(constraint(:internal_transactions, :internal_transactions_to_address_hash_fkey)) + end +end diff --git a/apps/explorer/priv/repo/migrations/20230821120625_drop_rest_address_foreign_keys.exs b/apps/explorer/priv/repo/migrations/20230821120625_drop_rest_address_foreign_keys.exs new file mode 100644 index 000000000000..36cdd2353fa5 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20230821120625_drop_rest_address_foreign_keys.exs @@ -0,0 +1,14 @@ +# cspell:ignore fkey +defmodule Explorer.Repo.Migrations.DropRestAddressForeignKeys do + use Ecto.Migration + + def change do + drop_if_exists(constraint(:address_coin_balances_daily, :address_coin_balances_daily_address_hash_fkey)) + drop_if_exists(constraint(:address_to_tags, :address_to_tags_address_hash_fkey)) + drop_if_exists(constraint(:block_rewards, :block_rewards_address_hash_fkey)) + drop_if_exists(constraint(:decompiled_smart_contracts, :decompiled_smart_contracts_address_hash_fkey)) + drop_if_exists(constraint(:smart_contracts, :smart_contracts_address_hash_fkey)) + drop_if_exists(constraint(:withdrawals, :withdrawals_address_hash_fkey)) + drop_if_exists(constraint(:blocks, :blocks_miner_hash_fkey)) + end +end diff --git a/apps/explorer/test/explorer/chain/cache/gas_price_oracle_test.exs b/apps/explorer/test/explorer/chain/cache/gas_price_oracle_test.exs index 630301488aad..d7004d11d0ed 100644 --- a/apps/explorer/test/explorer/chain/cache/gas_price_oracle_test.exs +++ b/apps/explorer/test/explorer/chain/cache/gas_price_oracle_test.exs @@ -1,10 +1,53 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do use Explorer.DataCase + import Mox + alias Explorer.Chain.Cache.GasPriceOracle + @block %{ + "difficulty" => "0x0", + "gasLimit" => "0x0", + "gasUsed" => "0x0", + "hash" => "0x29c850324e357f3c0c836d79860c5af55f7b651e5d7ee253c1af1b14908af49c", + "extraData" => "0x0", + "logsBloom" => "0x0", + "miner" => "0x0", + "number" => "0x1", + "parentHash" => "0x0", + "receiptsRoot" => "0x0", + "size" => "0x0", + "sha3Uncles" => "0x0", + "stateRoot" => "0x0", + "timestamp" => "0x0", + "baseFeePerGas" => "0x1DCD6500", + "totalDifficulty" => "0x0", + "transactions" => [ + %{ + "blockHash" => "0x29c850324e357f3c0c836d79860c5af55f7b651e5d7ee253c1af1b14908af49c", + "blockNumber" => "0x1", + "from" => "0x0", + "gas" => "0x0", + "gasPrice" => "0x0", + "hash" => "0xa2e81bb56b55ba3dab2daf76501b50dfaad240cccb905dbf89d65c7a84a4a48e", + "input" => "0x", + "nonce" => "0x0", + "r" => "0x0", + "s" => "0x0", + "to" => "0x0", + "transactionIndex" => "0x0", + "v" => "0x0", + "value" => "0x0" + } + ], + "transactionsRoot" => "0x0", + "uncles" => [] + } + describe "get_average_gas_price/4" do test "returns nil percentile values if no blocks in the DB" do + expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end) + assert {:ok, %{ "slow" => nil, @@ -14,6 +57,8 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do end test "returns nil percentile values if blocks are empty in the DB" do + expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end) + insert(:block) insert(:block) insert(:block) @@ -27,6 +72,8 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do end test "returns nil percentile values for blocks with failed txs in the DB" do + expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end) + block = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391") :transaction @@ -51,6 +98,8 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do end test "returns nil percentile values for transactions with 0 gas price aka 'whitelisted transactions' in the DB" do + expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end) + block1 = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391") block2 = insert(:block, number: 101, hash: "0x76c3da57334fffdc66c0d954dce1a910fcff13ec889a13b2d8b0b6e9440ce729") @@ -87,6 +136,8 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do end test "returns the same percentile values if gas price is the same over transactions" do + expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end) + block1 = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391") block2 = insert(:block, number: 101, hash: "0x76c3da57334fffdc66c0d954dce1a910fcff13ec889a13b2d8b0b6e9440ce729") @@ -123,6 +174,8 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do end test "returns correct min gas price from the block" do + expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end) + block1 = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391") block2 = insert(:block, number: 101, hash: "0x76c3da57334fffdc66c0d954dce1a910fcff13ec889a13b2d8b0b6e9440ce729") @@ -165,12 +218,14 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do assert {:ok, %{ "slow" => 1.0, - "average" => 1.0, - "fast" => 1.0 + "average" => 2.0, + "fast" => 2.0 }} = GasPriceOracle.get_average_gas_price(3, 35, 60, 90) end test "returns correct average percentile" do + expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end) + block1 = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391") block2 = insert(:block, number: 101, hash: "0x76c3da57334fffdc66c0d954dce1a910fcff13ec889a13b2d8b0b6e9440ce729") block3 = insert(:block, number: 102, hash: "0x659b2a1cc4dd1a5729900cf0c81c471d1c7891b2517bf9466f7fba56ead2fca0") @@ -213,7 +268,64 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do assert {:ok, %{ - "average" => 4.0 + "average" => 3.34 + }} = GasPriceOracle.get_average_gas_price(3, 35, 60, 90) + end + + test "returns correct gas price for EIP-1559 transactions" do + expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end) + + block1 = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391") + block2 = insert(:block, number: 101, hash: "0x76c3da57334fffdc66c0d954dce1a910fcff13ec889a13b2d8b0b6e9440ce729") + + :transaction + |> insert( + status: :ok, + block_hash: block1.hash, + block_number: block1.number, + cumulative_gas_used: 884_322, + gas_used: 106_025, + index: 0, + gas_price: 1_000_000_000, + max_priority_fee_per_gas: 1_000_000_000, + max_fee_per_gas: 1_000_000_000, + hash: "0xac2a7dab94d965893199e7ee01649e2d66f0787a4c558b3118c09e80d4df8269" + ) + + :transaction + |> insert( + status: :ok, + block_hash: block2.hash, + block_number: block2.number, + cumulative_gas_used: 884_322, + gas_used: 106_025, + index: 0, + gas_price: 1_000_000_000, + max_priority_fee_per_gas: 1_000_000_000, + max_fee_per_gas: 1_000_000_000, + hash: "0x5d5c2776f96704e7845f7d3c1fbba6685ab6efd6f82b6cd11d549f3b3a46bd03" + ) + + :transaction + |> insert( + status: :ok, + block_hash: block2.hash, + block_number: block2.number, + cumulative_gas_used: 884_322, + gas_used: 106_025, + index: 1, + gas_price: 3_000_000_000, + max_priority_fee_per_gas: 3_000_000_000, + max_fee_per_gas: 3_000_000_000, + hash: "0x906b80861b4a0921acfbb91a7b527227b0d32adabc88bc73e8c52ff714e55016" + ) + + assert {:ok, + %{ + # including base fee + "slow" => 1.5, + "average" => 2.5, + "fast" => 2.5 }} = GasPriceOracle.get_average_gas_price(3, 35, 60, 90) end end diff --git a/apps/explorer/test/explorer/chain/csv_export/address_log_csv_exporter_test.exs b/apps/explorer/test/explorer/chain/csv_export/address_log_csv_exporter_test.exs index b863792f8165..bff1ca818d33 100644 --- a/apps/explorer/test/explorer/chain/csv_export/address_log_csv_exporter_test.exs +++ b/apps/explorer/test/explorer/chain/csv_export/address_log_csv_exporter_test.exs @@ -1,6 +1,7 @@ defmodule Explorer.Chain.AddressLogCsvExporterTest do use Explorer.DataCase + alias Explorer.Chain.Address alias Explorer.Chain.CSVExport.AddressLogCsvExporter describe "export/3" do @@ -74,7 +75,7 @@ defmodule Explorer.Chain.AddressLogCsvExporterTest do assert result.index == to_string(log.index) assert result.block_number == to_string(log.block_number) assert result.block_hash == to_string(log.block_hash) - assert result.address == String.downcase(to_string(log.address)) + assert result.address == Address.checksum(log.address.hash) assert result.data == to_string(log.data) assert result.first_topic == to_string(log.first_topic) assert result.second_topic == to_string(log.second_topic) diff --git a/apps/explorer/test/explorer/chain/import/runner/address/current_token_balances_test.exs b/apps/explorer/test/explorer/chain/import/runner/address/current_token_balances_test.exs index 31eba0553de0..fa8d97e0a5c2 100644 --- a/apps/explorer/test/explorer/chain/import/runner/address/current_token_balances_test.exs +++ b/apps/explorer/test/explorer/chain/import/runner/address/current_token_balances_test.exs @@ -218,7 +218,8 @@ defmodule Explorer.Chain.Import.Runner.Address.CurrentTokenBalancesTest do address_hash: address.hash, block_number: 2, token_contract_address_hash: token.contract_address_hash, - value: Decimal.new(200) + value: Decimal.new(200), + value_fetched_at: DateTime.utc_now() }, options ) @@ -300,7 +301,8 @@ defmodule Explorer.Chain.Import.Runner.Address.CurrentTokenBalancesTest do address_hash: address_hash, token_contract_address_hash: token_contract_address_hash, block_number: block_number, - value: value + value: value, + value_fetched_at: DateTime.utc_now() }, options ) @@ -344,7 +346,8 @@ defmodule Explorer.Chain.Import.Runner.Address.CurrentTokenBalancesTest do address_hash: address_hash, token_contract_address_hash: token_contract_address_hash, block_number: block_number, - value: value + value: value, + value_fetched_at: DateTime.utc_now() }, options ) @@ -404,13 +407,15 @@ defmodule Explorer.Chain.Import.Runner.Address.CurrentTokenBalancesTest do address_hash: non_holder_becomes_holder_address_hash, token_contract_address_hash: token_contract_address_hash, block_number: block_number, - value: non_holder_becomes_holder_value + value: non_holder_becomes_holder_value, + value_fetched_at: DateTime.utc_now() }, %{ address_hash: holder_becomes_non_holder_address_hash, token_contract_address_hash: token_contract_address_hash, block_number: block_number, - value: holder_becomes_non_holder_value + value: holder_becomes_non_holder_value, + value_fetched_at: DateTime.utc_now() } ], options diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index fbfc00668853..728d7adc2971 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -28,6 +28,7 @@ defmodule Explorer.ChainTest do } alias Explorer.{Chain, Etherscan} + alias Explorer.Chain.Address.Counters alias Explorer.Chain.Cache.Block, as: BlockCache alias Explorer.Chain.Cache.Transaction, as: TransactionCache alias Explorer.Chain.Cache.PendingBlockOperation, as: PendingBlockOperationCache @@ -84,7 +85,7 @@ defmodule Explorer.ChainTest do start_supervised!(AddressesWithBalanceCounter) AddressesWithBalanceCounter.consolidate() - addresses_with_balance = Chain.count_addresses_with_balance_from_cache() + addresses_with_balance = Counters.count_addresses_with_balance_from_cache() assert is_integer(addresses_with_balance) assert addresses_with_balance == 2 @@ -100,7 +101,7 @@ defmodule Explorer.ChainTest do start_supervised!(AddressesCounter) AddressesCounter.consolidate() - addresses_with_balance = Chain.address_estimated_count() + addresses_with_balance = Counters.address_estimated_count() assert is_integer(addresses_with_balance) assert addresses_with_balance == 3 @@ -108,7 +109,7 @@ defmodule Explorer.ChainTest do test "returns 0 on empty table" do start_supervised!(AddressesCounter) - assert 0 == Chain.address_estimated_count() + assert 0 == Counters.address_estimated_count() end end @@ -875,7 +876,7 @@ defmodule Explorer.ChainTest do |> insert(nonce: 100, from_address: address) |> with_block(insert(:block, number: 1000)) - assert Chain.total_transactions_sent_by_address(address.hash) == 101 + assert Counters.total_transactions_sent_by_address(address.hash) == 101 end test "returns 0 when the address did not send transactions" do @@ -885,7 +886,7 @@ defmodule Explorer.ChainTest do |> insert(nonce: 100, to_address: address) |> with_block(insert(:block, number: 1000)) - assert Chain.total_transactions_sent_by_address(address.hash) == 0 + assert Counters.total_transactions_sent_by_address(address.hash) == 0 end end @@ -1099,13 +1100,13 @@ defmodule Explorer.ChainTest do test "without transactions" do %Address{hash: address_hash} = insert(:address) - assert Chain.address_to_incoming_transaction_count(address_hash) == 0 + assert Counters.address_to_incoming_transaction_count(address_hash) == 0 end test "with transactions" do %Transaction{to_address: to_address} = insert(:transaction) - assert Chain.address_to_incoming_transaction_count(to_address.hash) == 1 + assert Counters.address_to_incoming_transaction_count(to_address.hash) == 1 end end diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index 9c33824972eb..28093d950e19 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -142,14 +142,18 @@ defmodule Explorer.Factory do def address_to_tag_factory do %AddressToTag{ - tag: %AddressTag{ - label: sequence("label"), - display_name: sequence("display_name") - }, + tag: build(:address_tag), address: build(:address) } end + def address_tag_factory do + %AddressTag{ + label: sequence("label"), + display_name: sequence("display_name") + } + end + def account_watchlist_address_factory do hash = build(:address).hash @@ -656,7 +660,8 @@ defmodule Explorer.Factory do type: "ERC-20", cataloged: true, icon_url: sequence("https://example.com/icon"), - fiat_value: 10.1 + fiat_value: 10.1, + is_verified_via_admin_panel: Enum.random([true, false]) } end diff --git a/apps/indexer/lib/indexer/block/catchup/fetcher.ex b/apps/indexer/lib/indexer/block/catchup/fetcher.ex index 6a7d63e8710d..740d788a251a 100644 --- a/apps/indexer/lib/indexer/block/catchup/fetcher.ex +++ b/apps/indexer/lib/indexer/block/catchup/fetcher.ex @@ -55,7 +55,9 @@ defmodule Indexer.Block.Catchup.Fetcher do shrunk: false } - missing_ranges -> + latest_missing_ranges -> + missing_ranges = filter_consensus_blocks(latest_missing_ranges) + first.._ = List.first(missing_ranges) _..last = List.last(missing_ranges) @@ -85,6 +87,21 @@ defmodule Indexer.Block.Catchup.Fetcher do end end + defp filter_consensus_blocks(ranges) do + filtered_ranges = + ranges + |> Enum.map(&Chain.missing_block_number_ranges(&1)) + |> List.flatten() + + consensus_blocks = ranges_to_numbers(ranges) -- ranges_to_numbers(filtered_ranges) + + consensus_blocks + |> numbers_to_ranges() + |> MissingRangesManipulator.clear_batch() + + filtered_ranges + end + @doc """ The number of blocks to request in one call to the JSONRPC. Defaults to 10. Block requests also include the transactions for those blocks. *These transactions @@ -306,6 +323,12 @@ defmodule Indexer.Block.Catchup.Fetcher do ) end + defp ranges_to_numbers(ranges) do + ranges + |> Enum.map(&Enum.to_list/1) + |> List.flatten() + end + defp put_memory_monitor(sequence_options, %__MODULE__{memory_monitor: nil}) when is_list(sequence_options), do: sequence_options diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index d7b9be8d31e7..cffd739843bf 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -29,7 +29,7 @@ defmodule Indexer.Block.Fetcher do UncleBlock } - alias Indexer.{Prometheus, Tracer} + alias Indexer.{Prometheus, TokenBalances, Tracer} alias Indexer.Transform.{ AddressCoinBalances, @@ -182,6 +182,9 @@ defmodule Indexer.Block.Fetcher do address_coin_balances: %{params: coin_balances_params_set}, address_coin_balances_daily: %{params: coin_balances_params_daily_set}, address_token_balances: %{params: address_token_balances}, + address_current_token_balances: %{ + params: address_token_balances |> MapSet.to_list() |> TokenBalances.to_address_current_token_balances() + }, blocks: %{params: blocks}, block_second_degree_relations: %{params: block_second_degree_relations_params}, block_rewards: %{errors: beneficiaries_errors, params: beneficiaries_with_gas_payment}, diff --git a/apps/indexer/lib/indexer/block/realtime/fetcher.ex b/apps/indexer/lib/indexer/block/realtime/fetcher.ex index 720672987370..1b3a8fcb74fc 100644 --- a/apps/indexer/lib/indexer/block/realtime/fetcher.ex +++ b/apps/indexer/lib/indexer/block/realtime/fetcher.ex @@ -30,6 +30,7 @@ defmodule Indexer.Block.Realtime.Fetcher do alias Explorer.Chain.Cache.Accounts alias Explorer.Chain.Events.Publisher alias Explorer.Counters.AverageBlockTime + alias Explorer.Utility.MissingRangesManipulator alias Indexer.{Block, Tracer} alias Indexer.Block.Realtime.TaskSupervisor alias Indexer.Fetcher.CoinBalance @@ -310,6 +311,7 @@ defmodule Indexer.Block.Realtime.Fetcher do case result do {:ok, %{inserted: inserted, errors: []}} -> log_import_timings(inserted, fetch_duration, time_before) + MissingRangesManipulator.clear_batch([block_number_to_fetch..block_number_to_fetch]) Logger.debug("Fetched and imported.") {:ok, %{inserted: _, errors: [_ | _] = errors}} -> diff --git a/apps/indexer/lib/indexer/fetcher/token.ex b/apps/indexer/lib/indexer/fetcher/token.ex index 57b815a03e54..9d4b0dd253f3 100644 --- a/apps/indexer/lib/indexer/fetcher/token.ex +++ b/apps/indexer/lib/indexer/fetcher/token.ex @@ -14,12 +14,7 @@ defmodule Indexer.Fetcher.Token do @behaviour BufferedTask - @defaults [ - flush_interval: 300, - max_batch_size: 1, - max_concurrency: 10, - task_supervisor: Indexer.Fetcher.Token.TaskSupervisor - ] + @default_max_concurrency 10 @doc false def child_spec([init_options, gen_server_options]) do @@ -32,7 +27,7 @@ defmodule Indexer.Fetcher.Token do end merged_init_opts = - @defaults + defaults() |> Keyword.merge(mergeable_init_options) |> Keyword.put(:state, state) @@ -81,4 +76,13 @@ defmodule Indexer.Fetcher.Token do {:ok, _} = Chain.update_token(token, token_params) :ok end + + defp defaults do + [ + flush_interval: 300, + max_batch_size: 1, + max_concurrency: Application.get_env(:indexer, __MODULE__)[:concurrency] || @default_max_concurrency, + task_supervisor: Indexer.Fetcher.Token.TaskSupervisor + ] + end end diff --git a/apps/indexer/lib/indexer/fetcher/token_balance.ex b/apps/indexer/lib/indexer/fetcher/token_balance.ex index 9a40d88e6f0b..93add63827d2 100644 --- a/apps/indexer/lib/indexer/fetcher/token_balance.ex +++ b/apps/indexer/lib/indexer/fetcher/token_balance.ex @@ -26,6 +26,7 @@ defmodule Indexer.Fetcher.TokenBalance do @behaviour BufferedTask @default_max_batch_size 100 + @default_max_concurrency 10 @max_retries 3 @@ -75,8 +76,7 @@ defmodule Indexer.Fetcher.TokenBalance do token_balance |> entry() |> reducer.(acc) - end, - true + end ) final @@ -235,7 +235,7 @@ defmodule Indexer.Fetcher.TokenBalance do [ flush_interval: 300, max_batch_size: Application.get_env(:indexer, __MODULE__)[:batch_size] || @default_max_batch_size, - max_concurrency: 10, + max_concurrency: Application.get_env(:indexer, __MODULE__)[:concurrency] || @default_max_concurrency, task_supervisor: Indexer.Fetcher.TokenBalance.TaskSupervisor ] end diff --git a/apps/indexer/lib/indexer/fetcher/token_balance_on_demand.ex b/apps/indexer/lib/indexer/fetcher/token_balance_on_demand.ex index 3f3c05bb7932..d1dfcf6d3364 100644 --- a/apps/indexer/lib/indexer/fetcher/token_balance_on_demand.ex +++ b/apps/indexer/lib/indexer/fetcher/token_balance_on_demand.ex @@ -9,15 +9,18 @@ defmodule Indexer.Fetcher.TokenBalanceOnDemand do alias Explorer.Chain alias Explorer.Chain.Address.CurrentTokenBalance alias Explorer.Chain.Cache.BlockNumber + alias Explorer.Chain.Events.Publisher alias Explorer.Chain.Hash alias Explorer.Counters.AverageBlockTime alias Explorer.Token.BalanceReader alias Timex.Duration + require Logger + ## Interface - @spec trigger_fetch(Hash.t(), [CurrentTokenBalance.t()]) :: :ok - def trigger_fetch(address_hash, current_token_balances) do + @spec trigger_fetch(Hash.t()) :: :ok + def trigger_fetch(address_hash) do latest_block_number = latest_block_number() case stale_balance_window(latest_block_number) do @@ -25,7 +28,7 @@ defmodule Indexer.Fetcher.TokenBalanceOnDemand do :current stale_balance_window -> - do_trigger_fetch(address_hash, current_token_balances, latest_block_number, stale_balance_window) + do_trigger_fetch(address_hash, latest_block_number, stale_balance_window) end end @@ -36,7 +39,6 @@ defmodule Indexer.Fetcher.TokenBalanceOnDemand do Decimal.t() | nil, non_neg_integer() ) :: {:ok, pid} - def trigger_historic_fetch(address_hash, contract_address_hash, token_type, token_id, block_number) do Task.start(fn -> do_trigger_historic_fetch(address_hash, contract_address_hash, token_type, token_id, block_number) @@ -45,10 +47,11 @@ defmodule Indexer.Fetcher.TokenBalanceOnDemand do ## Implementation - defp do_trigger_fetch(address_hash, current_token_balances, latest_block_number, stale_balance_window) + defp do_trigger_fetch(address_hash, latest_block_number, stale_balance_window) when not is_nil(address_hash) do stale_current_token_balances = - current_token_balances + address_hash + |> Chain.fetch_last_token_balances_include_unfetched() |> Enum.filter(fn current_token_balance -> current_token_balance.block_number < stale_balance_window end) if Enum.count(stale_current_token_balances) > 0 do @@ -61,51 +64,101 @@ defmodule Indexer.Fetcher.TokenBalanceOnDemand do end defp fetch_and_update(block_number, address_hash, stale_current_token_balances) do - current_token_balances_update_params = + %{erc_1155: erc_1155_ctbs, other: other_ctbs, tokens: tokens} = stale_current_token_balances - |> Enum.map(fn %{token_id: token_id} = stale_current_token_balance -> - stale_current_token_balances_to_fetch = [ - %{ - token_contract_address_hash: - "0x" <> Base.encode16(stale_current_token_balance.token.contract_address_hash.bytes), - address_hash: "0x" <> Base.encode16(address_hash.bytes), - block_number: block_number, - token_id: token_id && Decimal.to_integer(token_id) - } - ] - - balance_response = - case stale_current_token_balance.token_type do - "ERC-1155" -> BalanceReader.get_balances_of_erc_1155(stale_current_token_balances_to_fetch) - _ -> BalanceReader.get_balances_of(stale_current_token_balances_to_fetch) + |> Enum.reduce(%{erc_1155: [], other: [], tokens: %{}}, fn %{token_id: token_id} = stale_current_token_balance, + acc -> + prepared_ctb = %{ + token_contract_address_hash: + "0x" <> Base.encode16(stale_current_token_balance.token.contract_address_hash.bytes), + address_hash: "0x" <> Base.encode16(address_hash.bytes), + block_number: block_number, + token_id: token_id && Decimal.to_integer(token_id), + token_type: stale_current_token_balance.token_type + } + + updated_tokens = + Map.put_new( + acc[:tokens], + stale_current_token_balance.token.contract_address_hash.bytes, + stale_current_token_balance.token + ) + + result = + if stale_current_token_balance.token_type == "ERC-1155" do + Map.put(acc, :erc_1155, [prepared_ctb | acc[:erc_1155]]) + else + Map.put(acc, :other, [prepared_ctb | acc[:other]]) end - updated_balance = balance_response[:ok] - - if updated_balance do - %{} - |> Map.put(:address_hash, stale_current_token_balance.address_hash) - |> Map.put(:token_contract_address_hash, stale_current_token_balance.token.contract_address_hash) - |> Map.put(:token_type, stale_current_token_balance.token.type) - |> Map.put(:token_id, token_id) - |> Map.put(:block_number, block_number) - |> Map.put(:value, Decimal.new(updated_balance)) - |> Map.put(:value_fetched_at, DateTime.utc_now()) - else - nil - end + Map.put(result, :tokens, updated_tokens) end) + erc_1155_ctbs_reversed = Enum.reverse(erc_1155_ctbs) + other_ctbs_reversed = Enum.reverse(other_ctbs) + + updated_erc_1155_ctbs = + if Enum.count(erc_1155_ctbs_reversed) > 0 do + erc_1155_ctbs_reversed + |> BalanceReader.get_balances_of_erc_1155() + |> Enum.zip(erc_1155_ctbs_reversed) + |> Enum.map(&prepare_updated_balance(&1, block_number)) + else + [] + end + + updated_other_ctbs = + if Enum.count(other_ctbs_reversed) > 0 do + other_ctbs_reversed + |> BalanceReader.get_balances_of() + |> Enum.zip(other_ctbs_reversed) + |> Enum.map(&prepare_updated_balance(&1, block_number)) + else + [] + end + filtered_current_token_balances_update_params = - current_token_balances_update_params + (updated_erc_1155_ctbs ++ updated_other_ctbs) |> Enum.filter(&(!is_nil(&1))) - Chain.import(%{ - address_current_token_balances: %{ - params: filtered_current_token_balances_update_params + {:ok, + %{ + address_current_token_balances: imported_ctbs + }} = + Chain.import(%{ + address_current_token_balances: %{ + params: filtered_current_token_balances_update_params + }, + broadcast: false + }) + + Publisher.broadcast( + %{ + address_current_token_balances: %{ + address_hash: to_string(address_hash), + address_current_token_balances: + imported_ctbs + |> Enum.map(fn ctb -> %CurrentTokenBalance{ctb | token: tokens[ctb.token_contract_address_hash.bytes]} end) + } }, - broadcast: :on_demand - }) + :on_demand + ) + end + + defp prepare_updated_balance({{:ok, updated_balance}, stale_current_token_balance}, block_number) do + %{} + |> Map.put(:address_hash, stale_current_token_balance.address_hash) + |> Map.put(:token_contract_address_hash, stale_current_token_balance.token_contract_address_hash) + |> Map.put(:token_type, stale_current_token_balance.token_type) + |> Map.put(:token_id, stale_current_token_balance.token_id) + |> Map.put(:block_number, block_number) + |> Map.put(:value, Decimal.new(updated_balance)) + |> Map.put(:value_fetched_at, DateTime.utc_now()) + end + + defp prepare_updated_balance({{:error, error}, _ctb}, _block_number) do + Logger.warn(fn -> ["Error on updating current token balance: ", inspect(error)] end) + nil end defp do_trigger_historic_fetch(address_hash, contract_address_hash, token_type, token_id, block_number) do diff --git a/apps/indexer/mix.exs b/apps/indexer/mix.exs index b24c16733a2e..444a7a27e772 100644 --- a/apps/indexer/mix.exs +++ b/apps/indexer/mix.exs @@ -14,7 +14,7 @@ defmodule Indexer.MixProject do elixirc_paths: elixirc_paths(Mix.env()), lockfile: "../../mix.lock", start_permanent: Mix.env() == :prod, - version: "5.2.1" + version: "5.2.2" ] end diff --git a/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs b/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs index 77f7a63512dc..774ebfe3a5ab 100644 --- a/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs +++ b/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs @@ -50,37 +50,42 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisorTest do EthereumJSONRPC.Mox |> stub(:json_rpc, fn # latest block number to seed starting block number for genesis and realtime tasks - %{method: "eth_getBlockByNumber", params: ["latest", false]}, _options -> + [%{id: id, method: "eth_getBlockByNumber", params: ["latest", false]}], _options -> {:ok, - %{ - "author" => "0xe2ac1c6843a33f81ae4935e5ef1277a392990381", - "difficulty" => "0xfffffffffffffffffffffffffffffffe", - "extraData" => "0xd583010a068650617269747986312e32362e32826c69", - "gasLimit" => "0x7a1200", - "gasUsed" => "0x0", - "hash" => "0x627baabf5a17c0cfc547b6903ac5e19eaa91f30d9141be1034e3768f6adbc94e", - "logsBloom" => - "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "miner" => "0xe2ac1c6843a33f81ae4935e5ef1277a392990381", - "number" => block_quantity, - "parentHash" => "0x006edcaa1e6fde822908783bc4ef1ad3675532d542fce53537557391cfe34c3c", - "receiptsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "sealFields" => [ - "0x841240b30d", - "0xb84158bc4fa5891934bc94c5dca0301867ce4f35925ef46ea187496162668210bba61b4cda09d7e0dca2f1dd041fad498ced6697aeef72656927f52c55b630f2591c01" - ], - "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "signature" => - "58bc4fa5891934bc94c5dca0301867ce4f35925ef46ea187496162668210bba61b4cda09d7e0dca2f1dd041fad498ced6697aeef72656927f52c55b630f2591c01", - "size" => "0x243", - "stateRoot" => "0x9a8111062667f7b162851a1cbbe8aece5ff12e761b3dcee93b787fcc12548cf7", - "step" => "306230029", - "timestamp" => "0x5b437f41", - "totalDifficulty" => "0x342337ffffffffffffffffffffffffed8d29bb", - "transactions" => [], - "transactionsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "uncles" => [] - }} + [ + %{ + id: id, + result: %{ + "author" => "0xe2ac1c6843a33f81ae4935e5ef1277a392990381", + "difficulty" => "0xfffffffffffffffffffffffffffffffe", + "extraData" => "0xd583010a068650617269747986312e32362e32826c69", + "gasLimit" => "0x7a1200", + "gasUsed" => "0x0", + "hash" => "0x627baabf5a17c0cfc547b6903ac5e19eaa91f30d9141be1034e3768f6adbc94e", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0xe2ac1c6843a33f81ae4935e5ef1277a392990381", + "number" => block_quantity, + "parentHash" => "0x006edcaa1e6fde822908783bc4ef1ad3675532d542fce53537557391cfe34c3c", + "receiptsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sealFields" => [ + "0x841240b30d", + "0xb84158bc4fa5891934bc94c5dca0301867ce4f35925ef46ea187496162668210bba61b4cda09d7e0dca2f1dd041fad498ced6697aeef72656927f52c55b630f2591c01" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "58bc4fa5891934bc94c5dca0301867ce4f35925ef46ea187496162668210bba61b4cda09d7e0dca2f1dd041fad498ced6697aeef72656927f52c55b630f2591c01", + "size" => "0x243", + "stateRoot" => "0x9a8111062667f7b162851a1cbbe8aece5ff12e761b3dcee93b787fcc12548cf7", + "step" => "306230029", + "timestamp" => "0x5b437f41", + "totalDifficulty" => "0x342337ffffffffffffffffffffffffed8d29bb", + "transactions" => [], + "transactionsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles" => [] + } + } + ]} [%{method: "trace_block"} | _] = requests, _options -> {:ok, Enum.map(requests, fn %{id: id} -> %{id: id, result: []} end)} diff --git a/config/runtime.exs b/config/runtime.exs index abf72d6e81ab..94b5b6abd17c 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -168,6 +168,9 @@ config :ethereum_jsonrpc, EthereumJSONRPC.Geth, config :ethereum_jsonrpc, EthereumJSONRPC.PendingTransaction, type: System.get_env("ETHEREUM_JSONRPC_PENDING_TRANSACTIONS_TYPE", "default") +config :ethereum_jsonrpc, EthereumJSONRPC.RequestCoordinator, + wait_per_timeout: ConfigHelper.parse_time_env_var("ETHEREUM_JSONRPC_WAIT_PER_TIMEOUT", "20s") + ################ ### Explorer ### ################ @@ -449,8 +452,11 @@ config :indexer, Indexer.Fetcher.PendingTransaction.Supervisor, System.get_env("ETHEREUM_JSONRPC_VARIANT") == "besu" || ConfigHelper.parse_bool_env_var("INDEXER_DISABLE_PENDING_TRANSACTIONS_FETCHER") +config :indexer, Indexer.Fetcher.Token, concurrency: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_CONCURRENCY", 10) + config :indexer, Indexer.Fetcher.TokenBalance, - batch_size: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_BALANCES_BATCH_SIZE", 100) + batch_size: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_BALANCES_BATCH_SIZE", 100), + concurrency: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_BALANCES_CONCURRENCY", 10) config :indexer, Indexer.Fetcher.TokenBalanceOnDemand, threshold: ConfigHelper.parse_time_env_var("TOKEN_BALANCE_ON_DEMAND_FETCHER_THRESHOLD", "1h"), diff --git a/cspell.json b/cspell.json index 2c733cb6d8ad..c35c242239f4 100644 --- a/cspell.json +++ b/cspell.json @@ -519,7 +519,8 @@ "erts", "Asfpp", "Nerg", - "secp" + "secp", + "qwertyuioiuytrewertyuioiuytrertyuio" ], "enableFiletypes": [ "dotenv", diff --git a/docker-compose/docker-compose-no-build-external-backend.yml b/docker-compose/docker-compose-no-build-external-backend.yml new file mode 100644 index 000000000000..3e1726bf846e --- /dev/null +++ b/docker-compose/docker-compose-no-build-external-backend.yml @@ -0,0 +1,52 @@ +version: '3.8' + +services: + redis_db: + extends: + file: ./services/docker-compose-redis.yml + service: redis_db + + db: + extends: + file: ./services/docker-compose-db.yml + service: db + + smart-contract-verifier: + extends: + file: ./services/docker-compose-smart-contract-verifier.yml + service: smart-contract-verifier + + visualizer: + extends: + file: ./services/docker-compose-visualizer.yml + service: visualizer + + sig-provider: + extends: + file: ./services/docker-compose-sig-provider.yml + service: sig-provider + + frontend: + extends: + file: ./services/docker-compose-frontend.yml + service: frontend + + stats-db: + extends: + file: ./services/docker-compose-stats.yml + service: stats-db + + stats: + depends_on: + - stats-db + extends: + file: ./services/docker-compose-stats.yml + service: stats + + proxy: + depends_on: + - frontend + - stats + extends: + file: ./services/docker-compose-nginx.yml + service: proxy diff --git a/docker-compose/docker-compose-no-build-external-frontend.yml b/docker-compose/docker-compose-no-build-external-frontend.yml new file mode 100644 index 000000000000..bc96e61f854d --- /dev/null +++ b/docker-compose/docker-compose-no-build-external-frontend.yml @@ -0,0 +1,85 @@ +version: '3.8' + +services: + redis_db: + extends: + file: ./services/docker-compose-redis.yml + service: redis_db + + db: + extends: + file: ./services/docker-compose-db.yml + service: db + + backend: + depends_on: + - db + - smart-contract-verifier + - redis_db + image: blockscout/blockscout:${DOCKER_TAG:-master} + pull_policy: always + restart: always + stop_grace_period: 5m + container_name: 'blockscout' + links: + - db:database + command: sh -c "bin/blockscout eval \"Elixir.Explorer.ReleaseTasks.create_and_migrate()\" && bin/blockscout start" + extra_hosts: + - 'host.docker.internal:host-gateway' + env_file: + - ./envs/common-blockscout.env + environment: + ETHEREUM_JSONRPC_VARIANT: 'ganache' + ETHEREUM_JSONRPC_HTTP_URL: http://host.docker.internal:8545/ + ETHEREUM_JSONRPC_TRACE_URL: http://host.docker.internal:8545/ + ETHEREUM_JSONRPC_WS_URL: ws://host.docker.internal:8545/ + INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER: 'true' + INDEXER_DISABLE_PENDING_TRANSACTIONS_FETCHER: 'true' + DATABASE_URL: postgresql://postgres:@host.docker.internal:7432/blockscout?ssl=false + ECTO_USE_SSL: 'false' + SECRET_KEY_BASE: '56NtB48ear7+wMSf0IQuWDAAazhpb31qyc7GiyspBP2vh7t5zlCsF5QDv76chXeN' + CHAIN_ID: '1337' + API_V2_ENABLED: 'true' + MIX_ENV: 'prod' + ACCOUNT_ENABLED: 'false' + ports: + - 4000:4000 + volumes: + - ./logs/:/app/logs/ + + smart-contract-verifier: + extends: + file: ./services/docker-compose-smart-contract-verifier.yml + service: smart-contract-verifier + + visualizer: + extends: + file: ./services/docker-compose-visualizer.yml + service: visualizer + + sig-provider: + extends: + file: ./services/docker-compose-sig-provider.yml + service: sig-provider + + stats-db: + depends_on: + - backend + extends: + file: ./services/docker-compose-stats.yml + service: stats-db + + stats: + depends_on: + - stats-db + extends: + file: ./services/docker-compose-stats.yml + service: stats + + proxy: + depends_on: + - backend + - stats + extends: + file: ./services/docker-compose-nginx.yml + service: proxy \ No newline at end of file diff --git a/docker-compose/docker-compose-no-build-frontend.yml b/docker-compose/docker-compose-no-build-frontend.yml index e52017c7f78b..663d580398dc 100644 --- a/docker-compose/docker-compose-no-build-frontend.yml +++ b/docker-compose/docker-compose-no-build-frontend.yml @@ -31,6 +31,7 @@ services: environment: ETHEREUM_JSONRPC_VARIANT: 'ganache' ETHEREUM_JSONRPC_HTTP_URL: http://host.docker.internal:8545/ + ETHEREUM_JSONRPC_TRACE_URL: http://host.docker.internal:8545/ ETHEREUM_JSONRPC_WS_URL: ws://host.docker.internal:8545/ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER: 'true' INDEXER_DISABLE_PENDING_TRANSACTIONS_FETCHER: 'true' @@ -76,14 +77,16 @@ services: stats: depends_on: - - backend + - stats-db extends: file: ./services/docker-compose-stats.yml service: stats proxy: depends_on: + - backend - frontend + - stats extends: file: ./services/docker-compose-nginx.yml service: proxy diff --git a/docker-compose/docker-compose-no-build-geth-clique-consensus.yml b/docker-compose/docker-compose-no-build-geth-clique-consensus.yml index bfcb31cb9d27..fb0924808f3e 100644 --- a/docker-compose/docker-compose-no-build-geth-clique-consensus.yml +++ b/docker-compose/docker-compose-no-build-geth-clique-consensus.yml @@ -32,6 +32,7 @@ services: ETHEREUM_JSONRPC_VARIANT: 'geth' BLOCK_TRANSFORMER: 'clique' ETHEREUM_JSONRPC_HTTP_URL: http://host.docker.internal:8545/ + ETHEREUM_JSONRPC_TRACE_URL: http://host.docker.internal:8545/ DATABASE_URL: postgresql://postgres:@host.docker.internal:7432/blockscout?ssl=false ECTO_USE_SSL: 'false' SECRET_KEY_BASE: '56NtB48ear7+wMSf0IQuWDAAazhpb31qyc7GiyspBP2vh7t5zlCsF5QDv76chXeN' diff --git a/docker-compose/docker-compose-no-build-geth.yml b/docker-compose/docker-compose-no-build-geth.yml index d8aa27370019..acd74b684871 100644 --- a/docker-compose/docker-compose-no-build-geth.yml +++ b/docker-compose/docker-compose-no-build-geth.yml @@ -31,6 +31,7 @@ services: environment: ETHEREUM_JSONRPC_VARIANT: 'geth' ETHEREUM_JSONRPC_HTTP_URL: http://host.docker.internal:8545/ + ETHEREUM_JSONRPC_TRACE_URL: http://host.docker.internal:8545/ DATABASE_URL: postgresql://postgres:@host.docker.internal:7432/blockscout?ssl=false ECTO_USE_SSL: 'false' SECRET_KEY_BASE: '56NtB48ear7+wMSf0IQuWDAAazhpb31qyc7GiyspBP2vh7t5zlCsF5QDv76chXeN' diff --git a/docker-compose/docker-compose-no-rust-services.yml b/docker-compose/docker-compose-no-rust-services.yml index a35253cd3279..af87b47c1c28 100644 --- a/docker-compose/docker-compose-no-rust-services.yml +++ b/docker-compose/docker-compose-no-rust-services.yml @@ -27,7 +27,7 @@ services: CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED: "" CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL: "" ADMIN_PANEL_ENABLED: "" - RELEASE_VERSION: 5.2.1 + RELEASE_VERSION: 5.2.2 restart: always stop_grace_period: 5m container_name: 'blockscout' diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index 9fdb484b9699..93552c9d9cac 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -28,7 +28,7 @@ services: CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED: "" CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL: "" ADMIN_PANEL_ENABLED: "" - RELEASE_VERSION: 5.2.1 + RELEASE_VERSION: 5.2.2 restart: always stop_grace_period: 5m container_name: 'blockscout' diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index a1a94b46cdf9..2b93ad54fb44 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -13,6 +13,7 @@ LOGO=/images/blockscout_logo.svg ETHEREUM_JSONRPC_TRANSPORT=http ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES=false # ETHEREUM_JSONRPC_HTTP_HEADERS= +# ETHEREUM_JSONRPC_WAIT_PER_TIMEOUT= IPC_PATH= NETWORK_PATH=/ BLOCKSCOUT_HOST= @@ -114,7 +115,9 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_COIN_BALANCES_CONCURRENCY= # INDEXER_RECEIPTS_BATCH_SIZE= # INDEXER_RECEIPTS_CONCURRENCY= +# INDEXER_TOKEN_CONCURRENCY= # INDEXER_TOKEN_BALANCES_BATCH_SIZE= +# INDEXER_TOKEN_BALANCES_CONCURRENCY= # INDEXER_TX_ACTIONS_ENABLE= # INDEXER_TX_ACTIONS_MAX_TOKEN_CACHE_SIZE= # INDEXER_TX_ACTIONS_REINDEX_FIRST_BLOCK= diff --git a/docker-compose/envs/common-frontend.env b/docker-compose/envs/common-frontend.env index 84da58952bc6..f1c77cdfa7ca 100644 --- a/docker-compose/envs/common-frontend.env +++ b/docker-compose/envs/common-frontend.env @@ -1,26 +1,20 @@ NEXT_PUBLIC_API_HOST=localhost -NEXT_PUBLIC_API_PORT=81 NEXT_PUBLIC_API_PROTOCOL=http -NEXT_PUBLIC_STATS_API_HOST=http://localhost:82 +NEXT_PUBLIC_STATS_API_HOST=http://localhost:8080 NEXT_PUBLIC_NETWORK_NAME=Göerli NEXT_PUBLIC_NETWORK_SHORT_NAME=Göerli -NEXT_PUBLIC_NETWORK_ASSETS_PATHNAME=ethereum NEXT_PUBLIC_NETWORK_ID=5 NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 NEXT_PUBLIC_API_BASE_PATH=/ NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth-goerli.json -NEXT_PUBLIC_FOOTER_GITHUB_LINK=https://github.com/blockscout/blockscout -NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom -NEXT_PUBLIC_APP_ENV=staging -NEXT_PUBLIC_APP_INSTANCE=eth_goerli NEXT_PUBLIC_APP_HOST=localhost +NEXT_PUBLIC_APP_PROTOCOL=http NEXT_PUBLIC_HOMEPAGE_CHARTS="['daily_txs']" -NEXT_PUBLIC_VISUALIZE_API_HOST=http://visualizer:80 +NEXT_PUBLIC_VISUALIZE_API_HOST=http://localhost:8081 NEXT_PUBLIC_IS_TESTNET='true' NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/goerli.svg NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/goerli.svg -NEXT_PUBLIC_HOMEPAGE_PLATE_GRADIENT="radial-gradient(103.03% 103.03% at 0% 0%, rgba(183, 148, 244, 0.8) 0%, rgba(0, 163, 196, 0.8) 100%)" NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR="rgb(255, 255, 255)" NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL='ws' diff --git a/docker-compose/envs/common-stats.env b/docker-compose/envs/common-stats.env index aa1f48225e6b..f5eed636bab6 100644 --- a/docker-compose/envs/common-stats.env +++ b/docker-compose/envs/common-stats.env @@ -15,7 +15,6 @@ STATS__CREATE_DATABASE=false STATS__RUN_MIGRATIONS=false STATS__DEFAULT_SCHEDULE=0 0 1 * * * * STATS__FORCE_UPDATE_ON_START=false -STATS__CHARTS_CONFIG=config/charts.toml STATS__METRICS__ENABLED=false STATS__METRICS__ADDR=0.0.0.0:6060 diff --git a/docker-compose/proxy/default.conf.template b/docker-compose/proxy/default.conf.template new file mode 100644 index 000000000000..1b20024c4d3a --- /dev/null +++ b/docker-compose/proxy/default.conf.template @@ -0,0 +1,104 @@ +map $http_upgrade $connection_upgrade { + + default upgrade; + '' close; +} + +server { + listen 80; + server_name localhost; + proxy_http_version 1.1; + + location / { + proxy_pass ${BACK_PROXY_PASS}; + proxy_http_version 1.1; + proxy_set_header Host "$host"; + proxy_set_header X-Real-IP "$remote_addr"; + proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for"; + proxy_set_header X-Forwarded-Proto "$scheme"; + proxy_set_header Upgrade "$http_upgrade"; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + } + location = / { + proxy_pass ${FRONT_PROXY_PASS}; + proxy_http_version 1.1; + proxy_set_header Host "$host"; + proxy_set_header X-Real-IP "$remote_addr"; + proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for"; + proxy_set_header X-Forwarded-Proto "$scheme"; + proxy_set_header Upgrade "$http_upgrade"; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + } + location ~ ^/(_next|node-api|apps|account|accounts|static|auth/profile|auth/unverified-email|txs|tx|blocks|block|login|address|stats|search-results|token|tokens|visualize|api-docs|csv-export|verified-contracts|graphiql|withdrawals) { + proxy_pass ${FRONT_PROXY_PASS}; + proxy_http_version 1.1; + proxy_set_header Host "$host"; + proxy_set_header X-Real-IP "$remote_addr"; + proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for"; + proxy_set_header X-Forwarded-Proto "$scheme"; + proxy_set_header Upgrade "$http_upgrade"; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + } +} +server { + listen 8080; + server_name localhost; + proxy_http_version 1.1; + proxy_hide_header Access-Control-Allow-Origin; + proxy_hide_header Access-Control-Allow-Methods; + add_header 'Access-Control-Allow-Origin' 'http://localhost' always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + add_header 'Access-Control-Allow-Methods' 'PUT, GET, POST, OPTIONS, DELETE, PATCH' always; + + location / { + proxy_pass http://stats:8050/; + proxy_http_version 1.1; + proxy_set_header Host "$host"; + proxy_set_header X-Real-IP "$remote_addr"; + proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for"; + proxy_set_header X-Forwarded-Proto "$scheme"; + proxy_set_header Upgrade "$http_upgrade"; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + } +} +server { + listen 8081; + server_name localhost; + proxy_http_version 1.1; + proxy_hide_header Access-Control-Allow-Origin; + proxy_hide_header Access-Control-Allow-Methods; + add_header 'Access-Control-Allow-Origin' 'http://localhost' always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + add_header 'Access-Control-Allow-Methods' 'PUT, GET, POST, OPTIONS, DELETE, PATCH' always; + add_header 'Access-Control-Allow-Headers' 'DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,x-csrf-token' always; + + location / { + proxy_pass http://visualizer:8050/; + proxy_http_version 1.1; + proxy_buffering off; + proxy_set_header Host "$host"; + proxy_set_header X-Real-IP "$remote_addr"; + proxy_connect_timeout 30m; + proxy_read_timeout 30m; + proxy_send_timeout 30m; + proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for"; + proxy_set_header X-Forwarded-Proto "$scheme"; + proxy_set_header Upgrade "$http_upgrade"; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' 'http://localhost' always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + add_header 'Access-Control-Allow-Methods' 'PUT, GET, POST, OPTIONS, DELETE, PATCH' always; + add_header 'Access-Control-Allow-Headers' 'DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,x-csrf-token' always; + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain charset=UTF-8'; + add_header 'Content-Length' 0; + return 204; + } + } +} \ No newline at end of file diff --git a/docker-compose/proxy/nginx.conf b/docker-compose/proxy/nginx.conf deleted file mode 100644 index d20a44c819eb..000000000000 --- a/docker-compose/proxy/nginx.conf +++ /dev/null @@ -1,62 +0,0 @@ -server { - listen 80; - server_name localhost; - proxy_http_version 1.1; - location / { - proxy_pass http://frontend:3000; - } - location /socket/v2 { - proxy_pass http://backend:4000; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - } - location /api/ { - proxy_pass http://backend:4000/api; - } -} - -server { - listen 81; - server_name localhost; - proxy_http_version 1.1; - proxy_hide_header Access-Control-Allow-Origin; - proxy_hide_header Access-Control-Allow-Methods; - add_header 'Access-Control-Allow-Origin' 'http://localhost' always; - add_header 'Access-Control-Allow-Methods' 'PUT, GET, POST, OPTIONS, DELETE, PATCH' always; - - location / { - proxy_pass http://backend:4000; - } - - location /socket { - proxy_pass http://backend:4000; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - } -} - -server { - listen 82; - server_name localhost; - proxy_http_version 1.1; - proxy_hide_header Access-Control-Allow-Origin; - proxy_hide_header Access-Control-Allow-Methods; - add_header 'Access-Control-Allow-Origin' 'http://localhost' always; - add_header 'Access-Control-Allow-Credentials' 'true' always; - add_header 'Access-Control-Allow-Methods' 'PUT, GET, POST, OPTIONS, DELETE, PATCH' always; - - location / { - proxy_pass http://stats:8050; - } - location /socket/v2 { - proxy_pass http://backend:4000; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - } -} \ No newline at end of file diff --git a/docker-compose/services/docker-compose-nginx.yml b/docker-compose/services/docker-compose-nginx.yml index 2d2bd87dc127..e54401d3f5ae 100644 --- a/docker-compose/services/docker-compose-nginx.yml +++ b/docker-compose/services/docker-compose-nginx.yml @@ -1,14 +1,17 @@ version: '3.8' -services: +services: proxy: image: nginx + container_name: proxy + extra_hosts: + - 'host.docker.internal:host-gateway' volumes: - - type: bind - source: ../proxy/nginx.conf - target: /etc/nginx/conf.d/default.conf - read_only: true + - "../proxy:/etc/nginx/templates" + environment: + BACK_PROXY_PASS: ${BACK_PROXY_PASS:-http://backend:4000} + FRONT_PROXY_PASS: ${FRONT_PROXY_PASS:-http://frontend:3000} ports: - 80:80 - - 81:81 - - 82:82 \ No newline at end of file + - 8080:8080 + - 8081:8081 diff --git a/docker-compose/services/docker-compose-stats.yml b/docker-compose/services/docker-compose-stats.yml index 4b14419fa631..b6ea6552b1da 100644 --- a/docker-compose/services/docker-compose-stats.yml +++ b/docker-compose/services/docker-compose-stats.yml @@ -12,10 +12,10 @@ services: POSTGRES_HOST_AUTH_METHOD: 'trust' ports: - 7433:5432 + volumes: + - ./stats-db-data:/var/lib/postgresql/data/ stats: - depends_on: - - stats-db image: ghcr.io/blockscout/stats:${STATS_DOCKER_TAG:-latest} pull_policy: always platform: linux/amd64 @@ -32,5 +32,3 @@ services: - STATS__RUN_MIGRATIONS=true ports: - 8153:8050 - volumes: - - ./stats-db-data:/var/lib/postgresql/data/ diff --git a/docker/Dockerfile b/docker/Dockerfile index 9b42fcbe71b2..9193b154aec7 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -37,7 +37,7 @@ RUN mix do deps.get, local.rebar --force, deps.compile ADD apps ./apps ADD config ./config ADD rel ./rel -ADD *.exs . +ADD *.exs ./ RUN apk add --update nodejs npm diff --git a/docker/Makefile b/docker/Makefile index b9f15c84c0b4..3f948c710617 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -7,7 +7,7 @@ BS_CONTAINER_NAME := blockscout PG_CONTAINER_IMAGE := postgres:14 PG_CONTAINER_NAME := db THIS_FILE = $(lastword $(MAKEFILE_LIST)) -RELEASE_VERSION ?= '5.2.1' +RELEASE_VERSION ?= '5.2.2' PORT ?= '4000' TAG := $(RELEASE_VERSION)-commit-$(shell git log -1 --pretty=format:"%h") STABLE_TAG := $(RELEASE_VERSION) @@ -63,6 +63,9 @@ endif ifdef ETHEREUM_JSONRPC_HTTP_HEADERS BLOCKSCOUT_CONTAINER_PARAMS += -e 'ETHEREUM_JSONRPC_HTTP_HEADERS=$(ETHEREUM_JSONRPC_HTTP_HEADERS)' endif +ifdef ETHEREUM_JSONRPC_WAIT_PER_TIMEOUT + BLOCKSCOUT_CONTAINER_PARAMS += -e 'ETHEREUM_JSONRPC_WAIT_PER_TIMEOUT=$(ETHEREUM_JSONRPC_WAIT_PER_TIMEOUT)' +endif ifdef IPC_PATH BLOCKSCOUT_CONTAINER_PARAMS += -e 'IPC_PATH=$(IPC_PATH)' endif @@ -561,9 +564,15 @@ endif ifdef INDEXER_EMPTY_BLOCKS_SANITIZER_BATCH_SIZE BLOCKSCOUT_CONTAINER_PARAMS += -e 'INDEXER_EMPTY_BLOCKS_SANITIZER_BATCH_SIZE=$(INDEXER_EMPTY_BLOCKS_SANITIZER_BATCH_SIZE)' endif +ifdef INDEXER_TOKEN_CONCURRENCY + BLOCKSCOUT_CONTAINER_PARAMS += -e 'INDEXER_TOKEN_CONCURRENCY=$(INDEXER_TOKEN_CONCURRENCY)' +endif ifdef INDEXER_TOKEN_BALANCES_BATCH_SIZE BLOCKSCOUT_CONTAINER_PARAMS += -e 'INDEXER_TOKEN_BALANCES_BATCH_SIZE=$(INDEXER_TOKEN_BALANCES_BATCH_SIZE)' endif +ifdef INDEXER_TOKEN_BALANCES_CONCURRENCY + BLOCKSCOUT_CONTAINER_PARAMS += -e 'INDEXER_TOKEN_BALANCES_CONCURRENCY=$(INDEXER_TOKEN_BALANCES_CONCURRENCY)' +endif ifdef INDEXER_REALTIME_FETCHER_MAX_GAP BLOCKSCOUT_CONTAINER_PARAMS += -e 'INDEXER_REALTIME_FETCHER_MAX_GAP=$(INDEXER_REALTIME_FETCHER_MAX_GAP)' endif diff --git a/mix.exs b/mix.exs index 744ac916a089..9983272fe866 100644 --- a/mix.exs +++ b/mix.exs @@ -7,7 +7,7 @@ defmodule BlockScout.Mixfile do [ # app: :block_scout, # aliases: aliases(config_env()), - version: "5.2.1", + version: "5.2.2", apps_path: "apps", deps: deps(), dialyzer: dialyzer(), diff --git a/mix.lock b/mix.lock index 419fe5f791e7..48442769907c 100644 --- a/mix.lock +++ b/mix.lock @@ -38,7 +38,7 @@ "digital_token": {:hex, :digital_token, "0.6.0", "13e6de581f0b1f6c686f7c7d12ab11a84a7b22fa79adeb4b50eec1a2d278d258", [:mix], [{:cldr_utils, "~> 2.17", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "2455d626e7c61a128b02a4a8caddb092548c3eb613ac6f6a85e4cbb6caddc4d1"}, "earmark_parser": {:hex, :earmark_parser, "1.4.33", "3c3fd9673bb5dcc9edc28dd90f50c87ce506d1f71b70e3de69aa8154bc695d44", [:mix], [], "hexpm", "2d526833729b59b9fdb85785078697c72ac5e5066350663e5be6a1182da61b8f"}, "ecto": {:hex, :ecto, "3.10.3", "eb2ae2eecd210b4eb8bece1217b297ad4ff824b4384c0e3fdd28aaf96edd6135", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "44bec74e2364d491d70f7e42cd0d690922659d329f6465e89feb8a34e8cd3433"}, - "ecto_sql": {:hex, :ecto_sql, "3.10.1", "6ea6b3036a0b0ca94c2a02613fd9f742614b5cfe494c41af2e6571bb034dd94c", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f6a25bdbbd695f12c8171eaff0851fa4c8e72eec1e98c7364402dda9ce11c56b"}, + "ecto_sql": {:hex, :ecto_sql, "3.10.2", "6b98b46534b5c2f8b8b5f03f126e75e2a73c64f3c071149d32987a5378b0fdbd", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "68c018debca57cb9235e3889affdaec7a10616a4e3a80c99fa1d01fdafaa9007"}, "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_abi": {:hex, :ex_abi, "0.6.0", "8cf1fef9490dea0834bc201d399635e72178df05dea87b1c933478762dede142", [:mix], [{:ex_keccak, "~> 0.7.1", [hex: :ex_keccak, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "b03e5fe07371db3ceceb2d536cc32658dcba47b79952469e3e71d7690495e8d8"}, @@ -47,8 +47,8 @@ "ex_cldr_lists": {:hex, :ex_cldr_lists, "2.10.0", "4d4c9877da2d0417fd832907d69974e8328969f75fafc79b05ccf85f549f6281", [:mix], [{:ex_cldr_numbers, "~> 2.25", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:ex_doc, "~> 0.18", [hex: :ex_doc, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "adc040cde7b97f7fd7c0b35dd69ddb6fcf607303ae6355bb1851deae1f8b0652"}, "ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.31.3", "6ec8b18c395c0e8788d46da806f8f2abcbe4b0d809226d2a91363e9ccd85f2f5", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:digital_token, "~> 0.3 or ~> 1.0", [hex: :digital_token, repo: "hexpm", optional: false]}, {:ex_cldr, "~> 2.37", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, ">= 2.14.2", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "b519de08ecc4a6402038f3aa75e8654f78ebd6fa714b7e585531504e648588fd"}, "ex_cldr_units": {:hex, :ex_cldr_units, "3.16.2", "dbad303fba819981c578234e2aaf19d72efca16ea8b1c6ee46b26232cb45e232", [:mix], [{:cldr_utils, "~> 2.24", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ex_cldr_lists, "~> 2.10", [hex: :ex_cldr_lists, repo: "hexpm", optional: false]}, {:ex_cldr_numbers, "~> 2.31", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:ex_doc, "~> 0.18", [hex: :ex_doc, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "851095319fb3205c1549619da742cd53a2804c1d9c204cf84014021e2a6ea7e5"}, - "ex_doc": {:hex, :ex_doc, "0.30.3", "bfca4d340e3b95f2eb26e72e4890da83e2b3a5c5b0e52607333bf5017284b063", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "fbc8702046c1d25edf79de376297e608ac78cdc3a29f075484773ad1718918b6"}, - "ex_json_schema": {:hex, :ex_json_schema, "0.9.3", "fc17c50d410fd99fa6e814e1aed60122d8ff2578b869d17a9db1ce1c621382b6", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "b79962d09cefd33001706255187bdb483a0c2b4442d5edc6822eb7574a8df0a8"}, + "ex_doc": {:hex, :ex_doc, "0.30.5", "aa6da96a5c23389d7dc7c381eba862710e108cee9cfdc629b7ec021313900e9e", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "88a1e115dcb91cefeef7e22df4a6ebbe4634fbf98b38adcbc25c9607d6d9d8e6"}, + "ex_json_schema": {:hex, :ex_json_schema, "0.10.1", "e03b746b6675a750c0bb1a5cc919f61353f7ab8450977e11ceede20e6180c560", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "66a64e60dadad89914d92f89c7e7906c57de75a8b79ac2480d0d53e1b8096fb0"}, "ex_keccak": {:hex, :ex_keccak, "0.7.1", "0169f4b0c5073c5df61581d6282b12f1a1b764dcfcda4eeb1c819b5194c9ced0", [:mix], [{:rustler, ">= 0.0.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.6.1", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "c18c19f66b6545b4b46b0c71c0cc0079de84e30b26365a92961e91697e8724ed"}, "ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"}, "ex_rlp": {:hex, :ex_rlp, "0.6.0", "985391d2356a7cb8712a4a9a2deb93f19f2fbca0323f5c1203fcaf64d077e31e", [:mix], [], "hexpm", "7135db93b861d9e76821039b60b00a6a22d2c4e751bf8c444bffe7a042f1abaf"}, @@ -57,13 +57,13 @@ "exactor": {:hex, :exactor, "2.2.4", "5efb4ddeb2c48d9a1d7c9b465a6fffdd82300eb9618ece5d34c3334d5d7245b1", [:mix], [], "hexpm", "1222419f706e01bfa1095aec9acf6421367dcfab798a6f67c54cf784733cd6b5"}, "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm", "32e95820a97cffea67830e91514a2ad53b888850442d6d395f53a1ac60c82e07"}, "expo": {:hex, :expo, "0.4.1", "1c61d18a5df197dfda38861673d392e642649a9cef7694d2f97a587b2cfb319b", [:mix], [], "hexpm", "2ff7ba7a798c8c543c12550fa0e2cbc81b95d4974c65855d8d15ba7b37a1ce47"}, - "exvcr": {:hex, :exvcr, "0.14.1", "d9aacec631ed379e366bce5b3c68f6ec5b92b89142f9379425854e80a28c1107", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:finch, "~> 0.16", [hex: :finch, repo: "hexpm", optional: true]}, {:httpoison, "~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "5f1e0854fbad0c4bb64ae8cdd289eeace90b8a0a209788c2f840d70f86a2d63c"}, + "exvcr": {:hex, :exvcr, "0.14.3", "e7d93b3b25919fbb653b06bc015cab99f658400eae96ed8e59758f1bb12ae167", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:finch, "~> 0.16", [hex: :finch, repo: "hexpm", optional: true]}, {:httpoison, "~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "38fa7d918aeda0ecc8e70346225c1299d21d4c432061c832f23a421ac2ed791d"}, "file_info": {:hex, :file_info, "0.0.4", "2e0e77f211e833f38ead22cb29ce53761d457d80b3ffe0ffe0eb93880b0963b2", [:mix], [{:mimetype_parser, "~> 0.1.2", [hex: :mimetype_parser, repo: "hexpm", optional: false]}], "hexpm", "50e7ad01c2c8b9339010675fe4dc4a113b8d6ca7eddce24d1d74fd0e762781a5"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "floki": {:hex, :floki, "0.34.3", "5e2dcaec5d7c228ce5b1d3501502e308b2d79eb655e4191751a1fe491c37feac", [:mix], [], "hexpm", "9577440eea5b97924b4bf3c7ea55f7b8b6dce589f9b28b096cc294a8dc342341"}, "flow": {:hex, :flow, "1.2.4", "1dd58918287eb286656008777cb32714b5123d3855956f29aa141ebae456922d", [:mix], [{:gen_stage, "~> 1.0", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm", "874adde96368e71870f3510b91e35bc31652291858c86c0e75359cbdd35eb211"}, "gen_stage": {:hex, :gen_stage, "1.2.1", "19d8b5e9a5996d813b8245338a28246307fd8b9c99d1237de199d21efc4c76a1", [:mix], [], "hexpm", "83e8be657fa05b992ffa6ac1e3af6d57aa50aace8f691fcf696ff02f8335b001"}, - "gettext": {:hex, :gettext, "0.22.3", "c8273e78db4a0bb6fba7e9f0fd881112f349a3117f7f7c598fa18c66c888e524", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "935f23447713954a6866f1bb28c3a878c4c011e802bcd68a726f5e558e4b64bd"}, + "gettext": {:hex, :gettext, "0.23.1", "821e619a240e6000db2fc16a574ef68b3bd7fe0167ccc264a81563cc93e67a31", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "19d744a36b809d810d610b57c27b934425859d158ebd56561bc41f7eeb8795db"}, "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"}, "hammer": {:hex, :hammer, "6.1.0", "f263e3c3e9946bd410ea0336b2abe0cb6260af4afb3a221e1027540706e76c55", [:make, :mix], [{:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm", "b47e415a562a6d072392deabcd58090d8a41182cf9044cdd6b0d0faaaf68ba57"}, "hammer_backend_redis": {:hex, :hammer_backend_redis, "6.1.2", "eb296bb4924928e24135308b2afc189201fd09411c870c6bbadea444a49b2f2c", [:mix], [{:hammer, "~> 6.0", [hex: :hammer, repo: "hexpm", optional: false]}, {:redix, "~> 1.1", [hex: :redix, repo: "hexpm", optional: false]}], "hexpm", "217ea066278910543a5e9b577d5bf2425419446b94fe76bdd9f255f39feec9fa"}, @@ -123,7 +123,7 @@ "redix": {:hex, :redix, "1.2.3", "3036e7c6080c42e1bbaa9168d1e28e367b01e8960a640a899b8ef8067273cb5e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:nimble_options, "~> 0.5.0 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "14e2bca8a03fad297a78a3d201032df260ee5f0e0ef9c173c0f9ca5b3e0331b7"}, "remote_ip": {:hex, :remote_ip, "1.1.0", "cb308841595d15df3f9073b7c39243a1dd6ca56e5020295cb012c76fbec50f2d", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "616ffdf66aaad6a72fc546dabf42eed87e2a99e97b09cbd92b10cc180d02ed74"}, "rustler_precompiled": {:hex, :rustler_precompiled, "0.6.1", "160b545bce8bf9a3f1b436b2c10f53574036a0db628e40f393328cbbe593602f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "0dd269fa261c4e3df290b12031c575fff07a542749f7b0e8b744d72d66c43600"}, - "sobelow": {:hex, :sobelow, "0.12.2", "45f4d500e09f95fdb5a7b94c2838d6b26625828751d9f1127174055a78542cf5", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "2f0b617dce551db651145662b84c8da4f158e7abe049a76daaaae2282df01c5d"}, + "sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"}, "spandex": {:hex, :spandex, "3.2.0", "f8cd40146ea988c87f3c14054150c9a47ba17e53cd4515c00e1f93c29c45404d", [:mix], [{:decorator, "~> 1.2", [hex: :decorator, repo: "hexpm", optional: true]}, {:optimal, "~> 0.3.3", [hex: :optimal, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d0a7d5aef4c5af9cf5467f2003e8a5d8d2bdae3823a6cc95d776b9a2251d4d03"}, "spandex_datadog": {:hex, :spandex_datadog, "1.3.0", "cabe82980f55612a8befa6c12904b1a429bf17faf7271a94b9aae278af6362cf", [:mix], [{:msgpax, "~> 2.2.1 or ~> 2.3", [hex: :msgpax, repo: "hexpm", optional: false]}, {:spandex, "~> 3.0", [hex: :spandex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c826e4e29d1612e866b2c7bae2df3beeee84fc57351968c2772672afe59b789c"}, "spandex_ecto": {:hex, :spandex_ecto, "0.7.0", "259ad2feb7c834e774ec623f99c0fbacca8d60a73be212f92b75e37f853c81be", [:mix], [{:spandex, "~> 2.2 or ~> 3.0", [hex: :spandex, repo: "hexpm", optional: false]}], "hexpm", "c64784be79d95538013b7c60828830411c5c7aff1f4e8d66dfe564b3c83b500e"}, @@ -138,7 +138,7 @@ "ueberauth": {:hex, :ueberauth, "0.10.5", "806adb703df87e55b5615cf365e809f84c20c68aa8c08ff8a416a5a6644c4b02", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3efd1f31d490a125c7ed453b926f7c31d78b97b8a854c755f5c40064bf3ac9e1"}, "ueberauth_auth0": {:hex, :ueberauth_auth0, "2.1.0", "0632d5844049fa2f26823f15e1120aa32f27df6f27ce515a4b04641736594bf4", [:mix], [{:oauth2, "~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "8d3b30fa27c95c9e82c30c4afb016251405706d2e9627e603c3c9787fd1314fc"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "wallaby": {:hex, :wallaby, "0.30.5", "c6a8dbb6f3195dbfe080b50ba707973983e32446f6f9fac514a43918682696bb", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}, {:httpoison, "~> 0.12 or ~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_ecto, ">= 3.0.0", [hex: :phoenix_ecto, repo: "hexpm", optional: true]}, {:web_driver_client, "~> 0.2.0", [hex: :web_driver_client, repo: "hexpm", optional: false]}], "hexpm", "d759711983c90aaa5338b8b9dcff0c9eb0609ac0a45071f4ef9cbb298bb54077"}, + "wallaby": {:hex, :wallaby, "0.30.6", "7dc4c1213f3b52c4152581d126632bc7e06892336d3a0f582853efeeabd45a71", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}, {:httpoison, "~> 0.12 or ~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_ecto, ">= 3.0.0", [hex: :phoenix_ecto, repo: "hexpm", optional: true]}, {:web_driver_client, "~> 0.2.0", [hex: :web_driver_client, repo: "hexpm", optional: false]}], "hexpm", "50950c1d968549b54c20e16175c68c7fc0824138e2bb93feb11ef6add8eb23d4"}, "web_driver_client": {:hex, :web_driver_client, "0.2.0", "63b76cd9eb3b0716ec5467a0f8bead73d3d9612e63f7560d21357f03ad86e31a", [:mix], [{:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:tesla, "~> 1.3", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "83cc6092bc3e74926d1c8455f0ce927d5d1d36707b74d9a65e38c084aab0350f"}, "websocket_client": {:git, "https://github.com/blockscout/websocket_client.git", "0b4ecc5b1fb8a0bd1c8352728da787c20add53aa", [branch: "master"]}, } diff --git a/rel/config.exs b/rel/config.exs index 7d4f453589ca..3782a1b80d82 100644 --- a/rel/config.exs +++ b/rel/config.exs @@ -71,7 +71,7 @@ end # will be used by default release :blockscout do - set version: "5.2.1-beta" + set version: "5.2.2-beta" set applications: [ :runtime_tools, block_scout_web: :permanent,