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..645ad98490ba 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/config.yml @@ -12,6 +12,8 @@ on: - production-sokol-stg - production-rsk-stg - production-lukso-stg + - production-immutable-stg + - production-zksync-stg - staging-l2 pull_request: branches: @@ -47,7 +49,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 +107,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 +131,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 +154,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 +163,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 +194,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 +220,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 +249,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 +297,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 +343,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 +400,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 +454,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 +519,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 +583,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 +626,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 cf8c9344c091..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 @@ -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-eth-goerli.yml b/.github/workflows/publish-docker-image-for-eth-goerli.yml index 99da783794be..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 @@ -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-eth.yml b/.github/workflows/publish-docker-image-for-eth.yml index a4598fa845b4..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 @@ -44,9 +44,9 @@ jobs: 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 index abf6071504bc..92eff232935f 100644 --- a/.github/workflows/publish-docker-image-for-immutable.yml +++ b/.github/workflows/publish-docker-image-for-immutable.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: immutable steps: - name: Check out the repo @@ -53,9 +53,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-l2-staging.yml b/.github/workflows/publish-docker-image-for-l2-staging.yml index 4a85493e9efb..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 @@ -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-lukso.yml b/.github/workflows/publish-docker-image-for-lukso.yml index 96109f811cf1..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 @@ -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-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 fca3a4d13f04..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 @@ -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-xdai.yml b/.github/workflows/publish-docker-image-for-xdai.yml index 1f047a818b84..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 @@ -53,9 +53,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-zksync.yml b/.github/workflows/publish-docker-image-for-zksync.yml new file mode 100644 index 000000000000..e37e195a118a --- /dev/null +++ b/.github/workflows/publish-docker-image-for-zksync.yml @@ -0,0 +1,54 @@ +# 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-zksync-stg +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: 5.2.2 + DOCKER_CHAIN_NAME: zksync + steps: + - name: Check out the repo + uses: actions/checkout@v3 + + - 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-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 811490b27479..8660565682db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,21 +4,120 @@ ### Features +- [#8382](https://github.com/blockscout/blockscout/pull/8382) - Add sitemap.xml +- [#8313](https://github.com/blockscout/blockscout/pull/8313) - Add batches to TokenInstance fetchers +- [#8285](https://github.com/blockscout/blockscout/pull/8285), [#8399](https://github.com/blockscout/blockscout/pull/8399) - Add CG/CMC coin price sources +- [#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 +- [#8269](https://github.com/blockscout/blockscout/pull/8269) - Don't push back to sequence on catchup exception +- [#8362](https://github.com/blockscout/blockscout/pull/8362), [#8398](https://github.com/blockscout/blockscout/pull/8398) - Drop token balances tokens foreign key + +### Fixes + +- [#8446](https://github.com/blockscout/blockscout/pull/8446) - Fix market cap calculation in case of CMC +- [#8431](https://github.com/blockscout/blockscout/pull/8431) - Fix contracts' output decoding +- [#8354](https://github.com/blockscout/blockscout/pull/8354) - Hotfix for proper addresses' tokens displaying +- [#8350](https://github.com/blockscout/blockscout/pull/8350) - Add Base Mainnet support for tx actions +- [#8282](https://github.com/blockscout/blockscout/pull/8282) - NFT fetcher improvements +- [#8287](https://github.com/blockscout/blockscout/pull/8287) - Add separate hackney pool for TokenInstance fetchers +- [#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 +- [#8355](https://github.com/blockscout/blockscout/pull/8355) - Fix current token balances redefining + +### Chore + +- [#8442](https://github.com/blockscout/blockscout/pull/8442) - Unify burn address definition +- [#8321](https://github.com/blockscout/blockscout/pull/8321) - Add curl into resulting Docker image +- [#8319](https://github.com/blockscout/blockscout/pull/8319) - Add MIX_ENV: 'prod' to docker-compose +- [#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 -- [#8100](https://github.com/blockscout/blockscout/pull/8100) - Extend docker-compose configs with new config when front is running externally +- [#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 @@ -42,6 +141,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 @@ -94,7 +194,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 @@ -221,7 +321,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/apps/block_scout_web/assets/css/components/_tile.scss b/apps/block_scout_web/assets/css/components/_tile.scss index c1a741f460bb..209008e240a5 100644 --- a/apps/block_scout_web/assets/css/components/_tile.scss +++ b/apps/block_scout_web/assets/css/components/_tile.scss @@ -184,6 +184,12 @@ $tile-body-a-color: #5959d8 !default; color: $tile-body-a-color; } + &[class*="status--error"] { + .tile-transaction-type-block { + word-break: break-word; + } + } + .tile-body { a { color: $tile-body-a-color; @@ -353,8 +359,8 @@ $tile-body-a-color: #5959d8 !default; padding-right: 6px; } } - .tile-type-block { - overflow: hidden; + .tile-type-block { + overflow: hidden; } .row { @include media-breakpoint-down(lg) { diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 99113b248e40..5e1b1f86300a 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -7,24 +7,24 @@ "name": "blockscout", "license": "GPL-3.0", "dependencies": { - "@amplitude/analytics-browser": "^2.1.3", - "@fortawesome/fontawesome-free": "^6.4.0", + "@amplitude/analytics-browser": "^2.2.3", + "@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", + "assert": "^2.1.0", + "bignumber.js": "^9.1.2", "bootstrap": "^4.6.0", - "chart.js": "^4.3.2", + "chart.js": "^4.4.0", "chartjs-adapter-luxon": "^1.3.1", "clipboard": "^2.0.11", - "core-js": "^3.32.0", + "core-js": "^3.32.2", "crypto-browserify": "^3.12.0", "dropzone": "^5.9.3", "eth-net-props": "^1.0.41", "highlight.js": "^11.8.0", "https-browserify": "^1.0.0", "humps": "^2.0.1", - "jquery": "^3.7.0", + "jquery": "^3.7.1", "js-cookie": "^3.0.5", "lodash.debounce": "^4.0.8", "lodash.differenceby": "^4.8.0", @@ -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.3", "malihu-custom-scrollbar-plugin": "3.1.5", "mixpanel-browser": "^2.47.0", "moment": "^2.29.4", @@ -54,42 +54,42 @@ "path-parser": "^6.1.0", "phoenix": "file:../../../deps/phoenix", "phoenix_html": "file:../../../deps/phoenix_html", - "photoswipe": "^5.3.8", + "photoswipe": "^5.4.0", "pikaday": "^1.8.2", "popper.js": "^1.14.7", "reduce-reducers": "^1.0.4", "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", + "url": "^0.11.2", "util": "^0.12.5", - "viewerjs": "^1.11.4", + "viewerjs": "^1.11.5", "web3": "^1.10.0", "web3modal": "^1.9.12", "xss": "^1.0.14" }, "devDependencies": { - "@babel/core": "^7.22.9", - "@babel/preset-env": "^7.22.9", - "autoprefixer": "^10.4.14", + "@babel/core": "^7.22.17", + "@babel/preset-env": "^7.22.15", + "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.46.0", + "eslint": "^8.49.0", "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.28.0", + "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.2", - "jest-environment-jsdom": "^29.6.2", + "jest": "^29.6.4", + "jest-environment-jsdom": "^29.6.4", "mini-css-extract-plugin": "^2.7.6", - "postcss": "^8.4.27", + "postcss": "^8.4.29", "postcss-loader": "^7.3.3", - "sass": "^1.64.2", + "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.3", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.1.3.tgz", - "integrity": "sha512-DQqjyRdkmUEHxGBuMEDOPNTssE0of+xZa2WtjmsxEZcIhtUSjViEJfRMg6Eup8O72m+7S+wQO3psfQecuZ1OnQ==", - "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.5", - "@amplitude/plugin-web-attribution-browser": "^2.0.5", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.2.3.tgz", + "integrity": "sha512-vuKG8/jqtsAFe0xK0ZGtXDxH7oPx989VIoCpUi97bkfxholySCNHjVMd5Q8D4Mqm/3eDH7YZhkFwEb0JFyCAfA==", + "dependencies": { + "@amplitude/analytics-client-common": "^2.0.5", + "@amplitude/analytics-core": "^2.0.4", + "@amplitude/analytics-types": "^2.1.2", + "@amplitude/plugin-page-view-tracking-browser": "^2.0.9", + "@amplitude/plugin-web-attribution-browser": "^2.0.9", "tslib": "^2.4.1" } }, @@ -134,75 +134,75 @@ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" }, "node_modules/@amplitude/analytics-client-common": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.0.4.tgz", - "integrity": "sha512-X0+zE8sODcQ2ioj9ZB98Gr/9FCRDiJuSixefaLrfng/4x5VwHK0if8biCqqBHXu6HlMpeMFrCyiABUTDT87QVA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.0.5.tgz", + "integrity": "sha512-5BrGl188h4Ayx4Z2e1x4I3Z8ykC+ap65cy8ShBByiaBBrR40gnXSuLZR7xeex3lvTp2b5lMBcVCqArdRbeZrgQ==", "dependencies": { "@amplitude/analytics-connector": "^1.4.8", - "@amplitude/analytics-core": "^2.0.3", - "@amplitude/analytics-types": "^2.1.1", + "@amplitude/analytics-core": "^2.0.4", + "@amplitude/analytics-types": "^2.1.2", "tslib": "^2.4.1" } }, "node_modules/@amplitude/analytics-client-common/node_modules/tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/@amplitude/analytics-connector": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-connector/-/analytics-connector-1.4.8.tgz", - "integrity": "sha512-dFW7c7Wb6Ng7vbmzwbaXZSpqfBx37ukamJV9ErFYYS8vGZK/Hkbt3M7fZHBI4WFU6CCwakr2ZXPme11uGPYWkQ==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-connector/-/analytics-connector-1.5.0.tgz", + "integrity": "sha512-T8mOYzB9RRxckzhL0NTHwdge9xuFxXEOplC8B1Y3UX3NHa3BLh7DlBUZlCOwQgMc2nxDfnSweDL5S3bhC+W90g==" }, "node_modules/@amplitude/analytics-core": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.0.3.tgz", - "integrity": "sha512-tpD1gCmPpzPNPumQT1ecOJtuan5OsQdKp9AX8YKc7t1/K3xHzGo3FH3JvdaAJVYYWeZV40bp/JL6wJiYIzyZjA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.0.4.tgz", + "integrity": "sha512-AM4g1ucaAJuFqaMBg7FiqwKHveyV2QpZ3yPxw3OxNCgZz2QmqeYE1bp47x4FlfzNsoGyuYqRKs1mCbmGobAYWA==", "dependencies": { - "@amplitude/analytics-types": "^2.1.1", + "@amplitude/analytics-types": "^2.1.2", "tslib": "^2.4.1" } }, "node_modules/@amplitude/analytics-core/node_modules/tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/@amplitude/analytics-types": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.1.1.tgz", - "integrity": "sha512-H3vebPR9onRdp0WzAZmI/4qmAE903uLOd2ZfMeHsVc1zaFTTCk46SoCuV4IrlF+VILrDw9Fy6gC9yl5N2PZcJQ==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.1.2.tgz", + "integrity": "sha512-ASKwH9g+5gglTHr7h7miK8J/ofIzuEtGRDCjnZAtRbE6+laoOfCLYPPJXMYz0k1x+rIhLO/6I6WWjT7zchmpyA==" }, "node_modules/@amplitude/plugin-page-view-tracking-browser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.0.5.tgz", - "integrity": "sha512-dmW2dckEaI/Z9DQ+3RNJU32cF4c6iYsFamvu73LDu/sttE0gmqWDRvZhh/B7j/VDxYmobySMUEV1kinjbn8vXg==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.0.9.tgz", + "integrity": "sha512-OjhAxvQ52lDcRap2sjUbYEcSM6bzeDa6SdBx6vCaeXswvjafcH9LeDPawLUHgaqvQJiAhA7lxrQ7ThfH0P6c0g==", "dependencies": { - "@amplitude/analytics-client-common": "^2.0.4", - "@amplitude/analytics-types": "^2.1.1", + "@amplitude/analytics-client-common": "^2.0.5", + "@amplitude/analytics-types": "^2.1.2", "tslib": "^2.4.1" } }, "node_modules/@amplitude/plugin-page-view-tracking-browser/node_modules/tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/@amplitude/plugin-web-attribution-browser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-attribution-browser/-/plugin-web-attribution-browser-2.0.5.tgz", - "integrity": "sha512-apLZ4XV0Xbq4E3sHY25zubiyqHX9IWupWk+RTMNqHZJyhC7JuA3ttVFU3p0fWM/XeumITMRPL2NcG9R9WO4y9Q==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-attribution-browser/-/plugin-web-attribution-browser-2.0.9.tgz", + "integrity": "sha512-QrNgieAEXEBbtnsxYzfeJl2U/5XwCCvO3Dg0hntAtnTdu1A3HlN5ItRtoHg0jGBbu4jpSbvag72UlkCotqJ+Yg==", "dependencies": { - "@amplitude/analytics-client-common": "^2.0.4", - "@amplitude/analytics-core": "^2.0.3", - "@amplitude/analytics-types": "^2.1.1", + "@amplitude/analytics-client-common": "^2.0.5", + "@amplitude/analytics-core": "^2.0.4", + "@amplitude/analytics-types": "^2.1.2", "tslib": "^2.4.1" } }, "node_modules/@amplitude/plugin-web-attribution-browser/node_modules/tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "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.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dependencies": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" @@ -248,24 +249,24 @@ } }, "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.17", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.17.tgz", + "integrity": "sha512-2EENLmhpwplDux5PSsZnSbnSkB3tZ6QTksgO25xwEL7pIDcNOMhF5v/s6RzwjMZzZzw9Ofc30gHv5ChCC8pifQ==", "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/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.22.17", + "@babel/helpers": "^7.22.15", + "@babel/parser": "^7.22.16", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.17", + "@babel/types": "^7.22.17", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", + "json5": "^2.2.3", "semver": "^6.3.1" }, "engines": { @@ -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.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", + "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", "dependencies": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.22.15", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -314,21 +315,18 @@ } }, "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.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dependencies": { "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", "browserslist": "^4.21.9", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { @@ -345,9 +343,9 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz", - "integrity": "sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.11.tgz", + "integrity": "sha512-y1grdYL4WzmUDBRGK0pDbIoFd7UZKoDurDzWEoNMYoj1EL+foGRQNyPWDcC+YyegN5y1DUsFFmzjGijB3nSVAQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", @@ -355,10 +353,10 @@ "@babel/helper-function-name": "^7.22.5", "@babel/helper-member-expression-to-functions": "^7.22.5", "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "semver": "^6.3.0" + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -444,26 +442,26 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.17.tgz", + "integrity": "sha512-XouDDhQESrLHTpnBtCKExJdyY4gJCdrvH2Pyv8r8kovX2U8G0dRUOT45T9XlbLtuu9CLXP15eusnkprhoPV5iQ==", "dependencies": { "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-validator-identifier": "^7.22.15" }, "engines": { "node": ">=6.9.0" @@ -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" @@ -511,20 +508,20 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz", - "integrity": "sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", + "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-member-expression-to-functions": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-optimise-call-expression": "^7.22.5" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-simple-access": { @@ -570,56 +567,55 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz", + "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", "engines": { "node": ">=6.9.0" } }, "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.15", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz", + "integrity": "sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.6", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.15", + "@babel/types": "^7.22.15" }, "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.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", + "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", "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.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", + "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==", "bin": { "parser": "bin/babel-parser.js" }, @@ -638,9 +634,9 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", - "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz", + "integrity": "sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -653,14 +649,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", - "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz", + "integrity": "sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.5" + "@babel/plugin-transform-optional-chaining": "^7.22.15" }, "engines": { "node": ">=6.9.0" @@ -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.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.15.tgz", + "integrity": "sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w==", "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.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.15.tgz", + "integrity": "sha512-G1czpdJBZCtngoK1sJgloLiOHUnkb/bLZwqVZD8kXmq0ZnVfTTWUcs9OWtp0mBtYJ+4LQY1fllqBkOIPhXmFmw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1071,12 +1051,12 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", - "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz", + "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.11", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, @@ -1088,18 +1068,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", - "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz", + "integrity": "sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-function-name": "^7.22.5", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", "@babel/helper-split-export-declaration": "^7.22.6", "globals": "^11.1.0" }, @@ -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.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.15.tgz", + "integrity": "sha512-HzG8sFl1ZVGTme74Nw+X01XsUTqERVQ6/RLHo3XjGRzm7XD6QTtfS3NJotVgCGy8BzkDqRjRBD8dAyJn5TuvSQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1173,9 +1153,9 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", - "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz", + "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1205,9 +1185,9 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", - "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz", + "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1221,9 +1201,9 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", - "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz", + "integrity": "sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1253,9 +1233,9 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", - "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz", + "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1284,9 +1264,9 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", - "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz", + "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1331,12 +1311,12 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", - "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.15.tgz", + "integrity": "sha512-jWL4eh90w0HQOTKP2MoXXUpVxilxsB2Vl4ji69rSjS3EcZ/v4sBmn+A3NpepuJzBhOaEBbR7udonlHHn5DWidg==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-simple-access": "^7.22.5" }, @@ -1348,13 +1328,13 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", - "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.11.tgz", + "integrity": "sha512-rIqHmHoMEOhI3VkVf5jQ15l539KrwhzqcBO6wdCNWPWc/JWt9ILNYNUssbRpeq0qWns8svuw8LnMNCvWBIJ8wA==", "dev": true, "dependencies": { "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.9", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.5" }, @@ -1413,9 +1393,9 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", - "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz", + "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1429,9 +1409,9 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", - "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz", + "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1445,16 +1425,16 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", - "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz", + "integrity": "sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", + "@babel/compat-data": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.22.5" + "@babel/plugin-transform-parameters": "^7.22.15" }, "engines": { "node": ">=6.9.0" @@ -1480,9 +1460,9 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", - "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz", + "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==", "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.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.15.tgz", + "integrity": "sha512-ngQ2tBhq5vvSJw2Q2Z9i7ealNkpDMU0rGWnHPKqRZO0tzZ5tlaoz4hDvhXioOoaE0X2vfNss1djwg0DXlfu30A==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1513,9 +1493,9 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", - "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz", + "integrity": "sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1544,13 +1524,13 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", - "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", + "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.11", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-private-property-in-object": "^7.14.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,17 +1758,17 @@ } }, "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.15", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.15.tgz", + "integrity": "sha512-tZFHr54GBkHk6hQuVA8w4Fmq+MSPsfvMG0vPnOYyTnJpyfMqybL8/MbNCPRT9zc2KBO2pe4tq15g6Uno4Jpoag==", "dev": true, "dependencies": { "@babel/compat-data": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.15", "@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", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.15", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.15", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -1809,58 +1789,58 @@ "@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.15", "@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.15", "@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-class-static-block": "^7.22.11", + "@babel/plugin-transform-classes": "^7.22.15", "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.15", "@babel/plugin-transform-dotall-regex": "^7.22.5", "@babel/plugin-transform-duplicate-keys": "^7.22.5", - "@babel/plugin-transform-dynamic-import": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.11", "@babel/plugin-transform-exponentiation-operator": "^7.22.5", - "@babel/plugin-transform-export-namespace-from": "^7.22.5", - "@babel/plugin-transform-for-of": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.11", + "@babel/plugin-transform-for-of": "^7.22.15", "@babel/plugin-transform-function-name": "^7.22.5", - "@babel/plugin-transform-json-strings": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.11", "@babel/plugin-transform-literals": "^7.22.5", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", "@babel/plugin-transform-member-expression-literals": "^7.22.5", "@babel/plugin-transform-modules-amd": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.5", - "@babel/plugin-transform-modules-systemjs": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.15", + "@babel/plugin-transform-modules-systemjs": "^7.22.11", "@babel/plugin-transform-modules-umd": "^7.22.5", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", "@babel/plugin-transform-new-target": "^7.22.5", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", - "@babel/plugin-transform-numeric-separator": "^7.22.5", - "@babel/plugin-transform-object-rest-spread": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", + "@babel/plugin-transform-numeric-separator": "^7.22.11", + "@babel/plugin-transform-object-rest-spread": "^7.22.15", "@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-parameters": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.11", + "@babel/plugin-transform-optional-chaining": "^7.22.15", + "@babel/plugin-transform-parameters": "^7.22.15", "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.11", "@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.15", + "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": { @@ -1947,31 +1925,31 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "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.17", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.17.tgz", + "integrity": "sha512-xK4Uwm0JnAMvxYZxOVecss85WxTEIbTa7bnGyf/+EgCL5Zt3U7htUpEOWv9detPlamGKuRzCqw74xVglDWpPdg==", "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.7", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", "@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.16", + "@babel/types": "^7.22.17", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1980,12 +1958,12 @@ } }, "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.17", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz", + "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==", "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.15", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2055,9 +2033,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz", - "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==", + "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.46.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz", - "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", + "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2524,18 +2502,18 @@ } }, "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" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -2600,16 +2578,16 @@ } }, "node_modules/@jest/console": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.2.tgz", - "integrity": "sha512-0N0yZof5hi44HAR2pPS+ikJ3nzKNoZdVu8FffRf3wy47I7Dm7etk/3KetMdRUqzVd16V4O2m2ISpNTbnIuqy1w==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", + "integrity": "sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", + "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.2", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.2.tgz", - "integrity": "sha512-Oj+5B+sDMiMWLhPFF+4/DvHOf+U10rgvCLGPHP8Xlsy/7QxS51aU/eBngudHlJXnaWD5EohAgJ4js+T6pa+zOg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz", + "integrity": "sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==", "dev": true, "dependencies": { - "@jest/console": "^29.6.2", - "@jest/reporters": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@jest/reporters": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@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.2", - "jest-haste-map": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-resolve-dependencies": "^29.6.2", - "jest-runner": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", - "jest-watcher": "^29.6.2", + "jest-changed-files": "^29.6.3", + "jest-config": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-resolve-dependencies": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "jest-watcher": "^29.6.4", "micromatch": "^4.0.4", - "pretty-format": "^29.6.2", + "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.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.2.tgz", - "integrity": "sha512-AEcW43C7huGd/vogTddNNTDRpO6vQ2zaQNrttvWV18ArBx9Z56h7BIsXkNFJVOO4/kblWEQz30ckw0+L3izc+Q==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz", + "integrity": "sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==", "dev": true, "dependencies": { - "@jest/fake-timers": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.2" + "jest-mock": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.2.tgz", - "integrity": "sha512-m6DrEJxVKjkELTVAztTLyS/7C92Y2b0VYqmDROYKLLALHn8T/04yPs70NADUYPrV3ruI+H3J0iUIuhkjp7vkfg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==", "dev": true, "dependencies": { - "expect": "^29.6.2", - "jest-snapshot": "^29.6.2" + "expect": "^29.6.4", + "jest-snapshot": "^29.6.4" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.2.tgz", - "integrity": "sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz", + "integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==", "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.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.2.tgz", - "integrity": "sha512-euZDmIlWjm1Z0lJ1D0f7a0/y5Kh/koLFMUBE5SUYWrmy8oNhJpbTBDAP6CxKnadcMLDoDf4waRYCe35cH6G6PA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz", + "integrity": "sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==", "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.2", - "jest-mock": "^29.6.2", - "jest-util": "^29.6.2" + "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.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.2.tgz", - "integrity": "sha512-cjuJmNDjs6aMijCmSa1g2TNG4Lby/AeU7/02VtpW+SLcZXzOLK2GpN2nLqcFjmhy3B3AoPeQVx7BnyOf681bAw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz", + "integrity": "sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/expect": "^29.6.2", - "@jest/types": "^29.6.1", - "jest-mock": "^29.6.2" + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@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.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.2.tgz", - "integrity": "sha512-sWtijrvIav8LgfJZlrGCdN0nP2EWbakglJY49J1Y5QihcQLfy7ovyxxjJBRXMNltgt4uPtEcFmIMbVshEDfFWw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz", + "integrity": "sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@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.2", - "jest-util": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", "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.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.2.tgz", - "integrity": "sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", + "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.6.2", + "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.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.2.tgz", - "integrity": "sha512-3VKFXzcV42EYhMCsJQURptSqnyjqCGbtLuX5Xxb6Pm6gUf1wIRIl+mandIRGJyWKgNKYF9cnstti6Ls5ekduqw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz", + "integrity": "sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==", "dev": true, "dependencies": { - "@jest/console": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@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.2", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.2.tgz", - "integrity": "sha512-GVYi6PfPwVejO7slw6IDO0qKVum5jtrJ3KoLGbgBWyr2qr4GaxFV6su+ZAjdTX75Sr1DkMFRk09r2ZVa+wtCGw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz", + "integrity": "sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.2", + "@jest/test-result": "^29.6.4", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", + "jest-haste-map": "^29.6.4", "slash": "^3.0.0" }, "engines": { @@ -3075,22 +3053,22 @@ } }, "node_modules/@jest/transform": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.2.tgz", - "integrity": "sha512-ZqCqEISr58Ce3U+buNFJYUktLJZOggfyvR+bZMaiV1e8B1SIvJbwZMrYz3gx/KAPn9EXmOmN+uB08yLCjWkQQg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz", + "integrity": "sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==", "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.2", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.2", + "jest-haste-map": "^29.6.4", + "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", @@ -4468,14 +4437,15 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "node_modules/assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "dependencies": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" } }, "node_modules/assert-plus": { @@ -4534,9 +4504,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": [ { @@ -4546,11 +4516,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", @@ -4591,15 +4565,15 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "node_modules/babel-jest": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.2.tgz", - "integrity": "sha512-BYCzImLos6J3BH/+HvUCHG1dTf2MzmAB4jaVxHV+29RZLjR29XuYTmsf2sdDwkrb+FczkGo3kOhE7ga6sI0P4A==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", + "integrity": "sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==", "dev": true, "dependencies": { - "@jest/transform": "^29.6.2", + "@jest/transform": "^29.6.4", "@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" @@ -4714,10 +4688,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", @@ -4743,22 +4733,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", @@ -4768,7 +4758,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": { @@ -4825,12 +4815,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": { @@ -4902,9 +4892,9 @@ } }, "node_modules/bignumber.js": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", - "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", "engines": { "node": "*" } @@ -5114,9 +5104,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", @@ -5132,9 +5122,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": { @@ -5384,9 +5374,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", @@ -5446,9 +5436,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.4.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.0.tgz", + "integrity": "sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==", "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -5824,9 +5814,9 @@ } }, "node_modules/core-js": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.0.tgz", - "integrity": "sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww==", + "version": "3.32.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.2.tgz", + "integrity": "sha512-pxXSw1mYZPDGvTQqEc5vgIb83jGQKFGYWY76z4a7weZXUolw3G+OvpZqSRcfYOoOVUQJYEPsWeQK8pKEnUtWxQ==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -6454,9 +6444,9 @@ } }, "node_modules/dedent": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.3.0.tgz", - "integrity": "sha512-7glNLfvdsMzZm3FpRY1CHuI2lbYDR+71YmrhmTZjYFD5pfT0ACgnGRdrrC9Mk2uICnzkcdelCx5at787UDGOvg==", + "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" @@ -6578,9 +6568,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" @@ -6722,9 +6712,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", @@ -6991,11 +6981,6 @@ "es6-symbol": "^3.1.1" } }, - "node_modules/es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" - }, "node_modules/es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -7114,16 +7099,16 @@ } }, "node_modules/eslint": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", - "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", + "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.1", - "@eslint/js": "^8.46.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.49.0", + "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.12.4", @@ -7133,7 +7118,7 @@ "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.2", + "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", @@ -7263,9 +7248,9 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.0.tgz", - "integrity": "sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q==", + "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", @@ -7277,13 +7262,12 @@ "eslint-import-resolver-node": "^0.3.7", "eslint-module-utils": "^2.8.0", "has": "^1.0.3", - "is-core-module": "^2.12.1", + "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.3", "semver": "^6.3.1", "tsconfig-paths": "^3.14.2" }, @@ -7475,9 +7459,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz", - "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", + "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" @@ -8338,17 +8322,16 @@ } }, "node_modules/expect": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.2.tgz", - "integrity": "sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.6.2", - "@types/node": "*", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2" + "@jest/expect-utils": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -9642,9 +9625,9 @@ } }, "node_modules/is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "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" }, @@ -9960,19 +9943,34 @@ } }, "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": { @@ -10038,15 +10036,15 @@ } }, "node_modules/jest": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.2.tgz", - "integrity": "sha512-8eQg2mqFbaP7CwfsTpCxQ+sHzw1WuNWL5UUvjnWP4hx2riGz9fPSzYOaU5q8/GqWn1TfgZIVTqYJygbGbWAANg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", + "integrity": "sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==", "dev": true, "dependencies": { - "@jest/core": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/core": "^29.6.4", + "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.6.2" + "jest-cli": "^29.6.4" }, "bin": { "jest": "bin/jest.js" @@ -10064,12 +10062,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": { @@ -10077,28 +10076,28 @@ } }, "node_modules/jest-circus": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.2.tgz", - "integrity": "sha512-G9mN+KOYIUe2sB9kpJkO9Bk18J4dTDArNFPwoZ7WKHKel55eKIS/u2bLthxgojwlf9NLCVQfgzM/WsOVvoC6Fw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz", + "integrity": "sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/expect": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.2", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", + "jest-each": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", "p-limit": "^3.1.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -10178,21 +10177,21 @@ } }, "node_modules/jest-cli": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.2.tgz", - "integrity": "sha512-TT6O247v6dCEX2UGHGyflMpxhnrL0DNqP2fRTKYm3nJJpCTfXX3GCMQPGFjXDoj0i5/Blp3jriKXFgdfmbYB6Q==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz", + "integrity": "sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==", "dev": true, "dependencies": { - "@jest/core": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/core": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@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.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", + "jest-config": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "prompts": "^2.0.1", "yargs": "^17.3.1" }, @@ -10282,31 +10281,31 @@ } }, "node_modules/jest-config": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.2.tgz", - "integrity": "sha512-VxwFOC8gkiJbuodG9CPtMRjBUNZEHxwfQXmIudSTzFWxaci3Qub1ddTRbFNQlD/zUeaifLndh/eDccFX4wCMQw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz", + "integrity": "sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.2", - "@jest/types": "^29.6.1", - "babel-jest": "^29.6.2", + "@jest/test-sequencer": "^29.6.4", + "@jest/types": "^29.6.3", + "babel-jest": "^29.6.4", "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.2", - "jest-environment-node": "^29.6.2", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-runner": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", + "jest-circus": "^29.6.4", + "jest-environment-node": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -10397,15 +10396,15 @@ } }, "node_modules/jest-diff": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.2.tgz", - "integrity": "sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz", + "integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "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" @@ -10482,9 +10481,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" @@ -10494,16 +10493,16 @@ } }, "node_modules/jest-each": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.2.tgz", - "integrity": "sha512-MsrsqA0Ia99cIpABBc3izS1ZYoYfhIy0NNWqPSE0YXbQjwchyt6B1HD2khzyPe1WiJA7hbxXy77ZoUQxn8UlSw==", + "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.2", - "pretty-format": "^29.6.2" + "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" @@ -10580,18 +10579,18 @@ } }, "node_modules/jest-environment-jsdom": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.6.2.tgz", - "integrity": "sha512-7oa/+266AAEgkzae8i1awNEfTfjwawWKLpiw2XesZmaoVVj9u9t8JOYx18cG29rbPNtkUlZ8V4b5Jb36y/VxoQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.6.4.tgz", + "integrity": "sha512-K6wfgUJ16DoMs02JYFid9lOsqfpoVtyJxpRlnTxUHzvZWBnnh2VNGRB9EC1Cro96TQdq5TtSjb3qUjNaJP9IyA==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/fake-timers": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", "@types/jsdom": "^20.0.0", "@types/node": "*", - "jest-mock": "^29.6.2", - "jest-util": "^29.6.2", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3", "jsdom": "^20.0.0" }, "engines": { @@ -10607,46 +10606,46 @@ } }, "node_modules/jest-environment-node": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.2.tgz", - "integrity": "sha512-YGdFeZ3T9a+/612c5mTQIllvWkddPbYcN2v95ZH24oWMbGA4GGS2XdIF92QMhUhvrjjuQWYgUGW2zawOyH63MQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz", + "integrity": "sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/fake-timers": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.2", - "jest-util": "^29.6.2" + "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.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.2.tgz", - "integrity": "sha512-+51XleTDAAysvU8rT6AnS1ZJ+WHVNqhj1k6nTvN2PYP+HjU3kqlaKQ1Lnw3NYW3bm2r8vq82X0Z1nDDHZMzHVA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz", + "integrity": "sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==", "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.2", - "jest-worker": "^29.6.2", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -10667,13 +10666,13 @@ } }, "node_modules/jest-haste-map/node_modules/jest-worker": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.2.tgz", - "integrity": "sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", + "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.6.2", + "jest-util": "^29.6.3", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -10697,28 +10696,28 @@ } }, "node_modules/jest-leak-detector": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.2.tgz", - "integrity": "sha512-aNqYhfp5uYEO3tdWMb2bfWv6f0b4I0LOxVRpnRLAeque2uqOVVMLh6khnTcE2qJ5wAKop0HcreM1btoysD6bPQ==", + "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.2" + "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.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.2.tgz", - "integrity": "sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz", + "integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.6.2", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -10795,18 +10794,18 @@ } }, "node_modules/jest-message-util": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.2.tgz", - "integrity": "sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ==", + "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.2", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -10885,14 +10884,14 @@ } }, "node_modules/jest-mock": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.2.tgz", - "integrity": "sha512-hoSv3lb3byzdKfwqCuT6uTscan471GUECqgNYykg6ob0yiAw3zYc7OrPnI9Qv8Wwoa4lC7AZ9hyS4AiIx5U2zg==", + "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.2" + "jest-util": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -10916,26 +10915,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.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.2.tgz", - "integrity": "sha512-G/iQUvZWI5e3SMFssc4ug4dH0aZiZpsDq9o1PtXTV1210Ztyb2+w+ZgQkB3iOiC5SmAEzJBOHWz6Hvrd+QnNPw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz", + "integrity": "sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==", "dev": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", + "jest-haste-map": "^29.6.4", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -10945,13 +10944,13 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.2.tgz", - "integrity": "sha512-LGqjDWxg2fuQQm7ypDxduLu/m4+4Lb4gczc13v51VMZbVP5tSBILqVx8qfWcsdP8f0G7aIqByIALDB0R93yL+w==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz", + "integrity": "sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==", "dev": true, "dependencies": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.6.2" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.6.4" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -11028,30 +11027,30 @@ } }, "node_modules/jest-runner": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.2.tgz", - "integrity": "sha512-wXOT/a0EspYgfMiYHxwGLPCZfC0c38MivAlb2lMEAlwHINKemrttu1uSbcGbfDV31sFaPWnWJPmb2qXM8pqZ4w==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz", + "integrity": "sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==", "dev": true, "dependencies": { - "@jest/console": "^29.6.2", - "@jest/environment": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@jest/environment": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@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.2", - "jest-haste-map": "^29.6.2", - "jest-leak-detector": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-resolve": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-util": "^29.6.2", - "jest-watcher": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-docblock": "^29.6.3", + "jest-environment-node": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-leak-detector": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-util": "^29.6.3", + "jest-watcher": "^29.6.4", + "jest-worker": "^29.6.4", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -11118,13 +11117,13 @@ } }, "node_modules/jest-runner/node_modules/jest-worker": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.2.tgz", - "integrity": "sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", + "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.6.2", + "jest-util": "^29.6.3", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -11170,31 +11169,31 @@ } }, "node_modules/jest-runtime": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.2.tgz", - "integrity": "sha512-2X9dqK768KufGJyIeLmIzToDmsN0m7Iek8QNxRSI/2+iPFYHF0jTwlO3ftn7gdKd98G/VQw9XJCk77rbTGZnJg==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/fake-timers": "^29.6.2", - "@jest/globals": "^29.6.2", - "@jest/source-map": "^29.6.0", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz", + "integrity": "sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/globals": "^29.6.4", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@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.2", - "jest-message-util": "^29.6.2", - "jest-mock": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -11273,9 +11272,9 @@ } }, "node_modules/jest-snapshot": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.2.tgz", - "integrity": "sha512-1OdjqvqmRdGNvWXr/YZHuyhh5DeaLp1p/F8Tht/MrMw4Kr1Uu/j4lRG+iKl1DAqUJDWxtQBMk41Lnf/JETYBRA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz", + "integrity": "sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -11283,20 +11282,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.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/expect-utils": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.6.2", + "expect": "^29.6.4", "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.2", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", "natural-compare": "^1.4.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "semver": "^7.5.3" }, "engines": { @@ -11389,12 +11388,12 @@ } }, "node_modules/jest-util": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.2.tgz", - "integrity": "sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w==", + "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", @@ -11476,17 +11475,17 @@ } }, "node_modules/jest-validate": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.2.tgz", - "integrity": "sha512-vGz0yMN5fUFRRbpJDPwxMpgSXW1LDKROHfBopAvDcmD6s+B/s8WJrwi+4bfH4SdInBA5C3P3BI19dBtKzx1Arg==", + "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.2" + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -11575,18 +11574,18 @@ } }, "node_modules/jest-watcher": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.2.tgz", - "integrity": "sha512-GZitlqkMkhkefjfN/p3SJjrDaxPflqxEAv3/ik10OirZqJGYH5rPiIsgVcfof0Tdqg3shQGdEIxDBx+B4tuLzA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz", + "integrity": "sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.6.2", + "jest-util": "^29.6.3", "string-length": "^4.0.1" }, "engines": { @@ -11711,9 +11710,9 @@ } }, "node_modules/jquery": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.0.tgz", - "integrity": "sha512-umpJ0/k8X0MvD1ds0P9SfowREz2LenHsQaxSohMZ5OMNEU2r0tf8pdeEFTHMFxWVxKNyU9rTtK3CWzUCTKJUeQ==" + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" }, "node_modules/jquery-mousewheel": { "version": "3.1.13", @@ -12333,9 +12332,9 @@ "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.3", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.3.tgz", + "integrity": "sha512-tFWBiv3h7z+T/tDaoxA8rqTxy1CHV6gHS//QdaH4pulbq/JuBSGgQspQQqcgnwdAx6pNI7cmvz5Sv/addzHmUg==", "engines": { "node": ">=12" } @@ -12883,9 +12882,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", @@ -13023,7 +13022,6 @@ "version": "4.1.4", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -13363,9 +13361,9 @@ "link": true }, "node_modules/photoswipe": { - "version": "5.3.8", - "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.3.8.tgz", - "integrity": "sha512-4vTzOQt8GP4Chsm0s+8j2xDtVHAEN252PxrU12A1zXauNn0zD5HRHgjALKO2GKTyBnTnOrJUOxbV8LTrFIMrYw==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.4.0.tgz", + "integrity": "sha512-PZvdK1D94TApU0MNWc9H6eXOolKJOMkgt7CJ9ZfIdkHR4CrEj47MOe4Vrlcv6ZpHslK+uKS6Ai3y3VIe7gsi+Q==", "engines": { "node": ">= 0.12.0" } @@ -13525,9 +13523,9 @@ } }, "node_modules/postcss": { - "version": "8.4.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", - "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", "dev": true, "funding": [ { @@ -14100,12 +14098,12 @@ } }, "node_modules/pretty-format": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.2.tgz", - "integrity": "sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==", + "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" }, @@ -14622,9 +14620,9 @@ "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" @@ -14978,9 +14976,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sass": { - "version": "1.64.2", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.64.2.tgz", - "integrity": "sha512-TnDlfc+CRnUAgLO9D8cQLFu/GIjJIzJCGkE7o4ekIGQOH7T3GetiRR/PsTWJUHhkzcSPrARkPI+gNWn5alCzDg==", + "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", @@ -15849,9 +15847,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" @@ -16403,12 +16401,12 @@ "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" }, "node_modules/url": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.1.tgz", - "integrity": "sha512-rWS3H04/+mzzJkv0eZ7vEDGiQbgquI1fGfOad6zKvgYQi1SzMmhl7c/DdRGxhaWrVH6z0qWITo8rpnxK/RfEhA==", + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.2.tgz", + "integrity": "sha512-7yIgNnrST44S7PJ5+jXbdIupfU1nWUdQJBFBeJRclPXiWgCvrSq5Frw8lr/i//n5sqDfzoKmBymMS81l4U/7cg==", "dependencies": { "punycode": "^1.4.1", - "qs": "^6.11.0" + "qs": "^6.11.2" } }, "node_modules/url-parse": { @@ -16537,9 +16535,9 @@ } }, "node_modules/viewerjs": { - "version": "1.11.4", - "resolved": "https://registry.npmjs.org/viewerjs/-/viewerjs-1.11.4.tgz", - "integrity": "sha512-/mnqMIwt5Bi9j59+48OqQtqgOx8oh186Xshdr/dqqBrakMSMlLt/jmeNHBod0PvOkesZf66ivQbWmtWYBlKetg==" + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/viewerjs/-/viewerjs-1.11.5.tgz", + "integrity": "sha512-nsvQkC5jnqZ/2ggFYWUH5gpUGPtFAYidsFh8Q7B7sioAdqJzSJrELvbu9ozUm0W+A2uHN5XuuiheHHB+dWiPEA==" }, "node_modules/w3c-hr-time": { "version": "1.0.2", @@ -17601,15 +17599,15 @@ "dev": true }, "@amplitude/analytics-browser": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.1.3.tgz", - "integrity": "sha512-DQqjyRdkmUEHxGBuMEDOPNTssE0of+xZa2WtjmsxEZcIhtUSjViEJfRMg6Eup8O72m+7S+wQO3psfQecuZ1OnQ==", - "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.5", - "@amplitude/plugin-web-attribution-browser": "^2.0.5", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.2.3.tgz", + "integrity": "sha512-vuKG8/jqtsAFe0xK0ZGtXDxH7oPx989VIoCpUi97bkfxholySCNHjVMd5Q8D4Mqm/3eDH7YZhkFwEb0JFyCAfA==", + "requires": { + "@amplitude/analytics-client-common": "^2.0.5", + "@amplitude/analytics-core": "^2.0.4", + "@amplitude/analytics-types": "^2.1.2", + "@amplitude/plugin-page-view-tracking-browser": "^2.0.9", + "@amplitude/plugin-web-attribution-browser": "^2.0.9", "tslib": "^2.4.1" }, "dependencies": { @@ -17621,81 +17619,81 @@ } }, "@amplitude/analytics-client-common": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.0.4.tgz", - "integrity": "sha512-X0+zE8sODcQ2ioj9ZB98Gr/9FCRDiJuSixefaLrfng/4x5VwHK0if8biCqqBHXu6HlMpeMFrCyiABUTDT87QVA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.0.5.tgz", + "integrity": "sha512-5BrGl188h4Ayx4Z2e1x4I3Z8ykC+ap65cy8ShBByiaBBrR40gnXSuLZR7xeex3lvTp2b5lMBcVCqArdRbeZrgQ==", "requires": { "@amplitude/analytics-connector": "^1.4.8", - "@amplitude/analytics-core": "^2.0.3", - "@amplitude/analytics-types": "^2.1.1", + "@amplitude/analytics-core": "^2.0.4", + "@amplitude/analytics-types": "^2.1.2", "tslib": "^2.4.1" }, "dependencies": { "tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, "@amplitude/analytics-connector": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-connector/-/analytics-connector-1.4.8.tgz", - "integrity": "sha512-dFW7c7Wb6Ng7vbmzwbaXZSpqfBx37ukamJV9ErFYYS8vGZK/Hkbt3M7fZHBI4WFU6CCwakr2ZXPme11uGPYWkQ==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-connector/-/analytics-connector-1.5.0.tgz", + "integrity": "sha512-T8mOYzB9RRxckzhL0NTHwdge9xuFxXEOplC8B1Y3UX3NHa3BLh7DlBUZlCOwQgMc2nxDfnSweDL5S3bhC+W90g==" }, "@amplitude/analytics-core": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.0.3.tgz", - "integrity": "sha512-tpD1gCmPpzPNPumQT1ecOJtuan5OsQdKp9AX8YKc7t1/K3xHzGo3FH3JvdaAJVYYWeZV40bp/JL6wJiYIzyZjA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.0.4.tgz", + "integrity": "sha512-AM4g1ucaAJuFqaMBg7FiqwKHveyV2QpZ3yPxw3OxNCgZz2QmqeYE1bp47x4FlfzNsoGyuYqRKs1mCbmGobAYWA==", "requires": { - "@amplitude/analytics-types": "^2.1.1", + "@amplitude/analytics-types": "^2.1.2", "tslib": "^2.4.1" }, "dependencies": { "tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, "@amplitude/analytics-types": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.1.1.tgz", - "integrity": "sha512-H3vebPR9onRdp0WzAZmI/4qmAE903uLOd2ZfMeHsVc1zaFTTCk46SoCuV4IrlF+VILrDw9Fy6gC9yl5N2PZcJQ==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.1.2.tgz", + "integrity": "sha512-ASKwH9g+5gglTHr7h7miK8J/ofIzuEtGRDCjnZAtRbE6+laoOfCLYPPJXMYz0k1x+rIhLO/6I6WWjT7zchmpyA==" }, "@amplitude/plugin-page-view-tracking-browser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.0.5.tgz", - "integrity": "sha512-dmW2dckEaI/Z9DQ+3RNJU32cF4c6iYsFamvu73LDu/sttE0gmqWDRvZhh/B7j/VDxYmobySMUEV1kinjbn8vXg==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.0.9.tgz", + "integrity": "sha512-OjhAxvQ52lDcRap2sjUbYEcSM6bzeDa6SdBx6vCaeXswvjafcH9LeDPawLUHgaqvQJiAhA7lxrQ7ThfH0P6c0g==", "requires": { - "@amplitude/analytics-client-common": "^2.0.4", - "@amplitude/analytics-types": "^2.1.1", + "@amplitude/analytics-client-common": "^2.0.5", + "@amplitude/analytics-types": "^2.1.2", "tslib": "^2.4.1" }, "dependencies": { "tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, "@amplitude/plugin-web-attribution-browser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-attribution-browser/-/plugin-web-attribution-browser-2.0.5.tgz", - "integrity": "sha512-apLZ4XV0Xbq4E3sHY25zubiyqHX9IWupWk+RTMNqHZJyhC7JuA3ttVFU3p0fWM/XeumITMRPL2NcG9R9WO4y9Q==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-attribution-browser/-/plugin-web-attribution-browser-2.0.9.tgz", + "integrity": "sha512-QrNgieAEXEBbtnsxYzfeJl2U/5XwCCvO3Dg0hntAtnTdu1A3HlN5ItRtoHg0jGBbu4jpSbvag72UlkCotqJ+Yg==", "requires": { - "@amplitude/analytics-client-common": "^2.0.4", - "@amplitude/analytics-core": "^2.0.3", - "@amplitude/analytics-types": "^2.1.1", + "@amplitude/analytics-client-common": "^2.0.5", + "@amplitude/analytics-core": "^2.0.4", + "@amplitude/analytics-types": "^2.1.2", "tslib": "^2.4.1" }, "dependencies": { "tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, @@ -17720,11 +17718,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.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "requires": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" } }, "@babel/compat-data": { @@ -17733,33 +17732,33 @@ "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.17", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.17.tgz", + "integrity": "sha512-2EENLmhpwplDux5PSsZnSbnSkB3tZ6QTksgO25xwEL7pIDcNOMhF5v/s6RzwjMZzZzw9Ofc30gHv5ChCC8pifQ==", "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/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.22.17", + "@babel/helpers": "^7.22.15", + "@babel/parser": "^7.22.16", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.17", + "@babel/types": "^7.22.17", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", + "json5": "^2.2.3", "semver": "^6.3.1" } }, "@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.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", + "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", "requires": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.22.15", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -17783,12 +17782,12 @@ } }, "@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.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "requires": { "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", "browserslist": "^4.21.9", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -17810,9 +17809,9 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz", - "integrity": "sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.11.tgz", + "integrity": "sha512-y1grdYL4WzmUDBRGK0pDbIoFd7UZKoDurDzWEoNMYoj1EL+foGRQNyPWDcC+YyegN5y1DUsFFmzjGijB3nSVAQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", @@ -17820,10 +17819,10 @@ "@babel/helper-function-name": "^7.22.5", "@babel/helper-member-expression-to-functions": "^7.22.5", "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "semver": "^6.3.0" + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" } }, "@babel/helper-create-regexp-features-plugin": { @@ -17882,23 +17881,23 @@ } }, "@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "requires": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.22.15" } }, "@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.17.tgz", + "integrity": "sha512-XouDDhQESrLHTpnBtCKExJdyY4gJCdrvH2Pyv8r8kovX2U8G0dRUOT45T9XlbLtuu9CLXP15eusnkprhoPV5iQ==", "requires": { "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-validator-identifier": "^7.22.15" } }, "@babel/helper-optimise-call-expression": { @@ -17916,29 +17915,25 @@ "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": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz", - "integrity": "sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", + "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-member-expression-to-functions": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-optimise-call-expression": "^7.22.5" } }, "@babel/helper-simple-access": { @@ -17972,70 +17967,69 @@ "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" }, "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==" + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz", + "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==" }, "@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==" + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==" }, "@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.15", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz", + "integrity": "sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==", "requires": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.6", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@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.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", + "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", "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.16", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", + "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", - "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz", + "integrity": "sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" } }, "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", - "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz", + "integrity": "sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.5" + "@babel/plugin-transform-optional-chaining": "^7.22.15" } }, "@babel/plugin-proposal-private-property-in-object": { @@ -18045,16 +18039,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", @@ -18255,14 +18239,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.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.15.tgz", + "integrity": "sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w==", "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" } }, @@ -18287,9 +18271,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.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.15.tgz", + "integrity": "sha512-G1czpdJBZCtngoK1sJgloLiOHUnkb/bLZwqVZD8kXmq0ZnVfTTWUcs9OWtp0mBtYJ+4LQY1fllqBkOIPhXmFmw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" @@ -18306,29 +18290,29 @@ } }, "@babel/plugin-transform-class-static-block": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", - "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz", + "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.11", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-class-static-block": "^7.14.5" } }, "@babel/plugin-transform-classes": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", - "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz", + "integrity": "sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-function-name": "^7.22.5", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", "@babel/helper-split-export-declaration": "^7.22.6", "globals": "^11.1.0" } @@ -18344,9 +18328,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.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.15.tgz", + "integrity": "sha512-HzG8sFl1ZVGTme74Nw+X01XsUTqERVQ6/RLHo3XjGRzm7XD6QTtfS3NJotVgCGy8BzkDqRjRBD8dAyJn5TuvSQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" @@ -18372,9 +18356,9 @@ } }, "@babel/plugin-transform-dynamic-import": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", - "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz", + "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", @@ -18392,9 +18376,9 @@ } }, "@babel/plugin-transform-export-namespace-from": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", - "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz", + "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", @@ -18402,9 +18386,9 @@ } }, "@babel/plugin-transform-for-of": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", - "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz", + "integrity": "sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" @@ -18422,9 +18406,9 @@ } }, "@babel/plugin-transform-json-strings": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", - "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz", + "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", @@ -18441,9 +18425,9 @@ } }, "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", - "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz", + "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", @@ -18470,24 +18454,24 @@ } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", - "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.15.tgz", + "integrity": "sha512-jWL4eh90w0HQOTKP2MoXXUpVxilxsB2Vl4ji69rSjS3EcZ/v4sBmn+A3NpepuJzBhOaEBbR7udonlHHn5DWidg==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-simple-access": "^7.22.5" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", - "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.11.tgz", + "integrity": "sha512-rIqHmHoMEOhI3VkVf5jQ15l539KrwhzqcBO6wdCNWPWc/JWt9ILNYNUssbRpeq0qWns8svuw8LnMNCvWBIJ8wA==", "dev": true, "requires": { "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.9", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.5" } @@ -18522,9 +18506,9 @@ } }, "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", - "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz", + "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", @@ -18532,9 +18516,9 @@ } }, "@babel/plugin-transform-numeric-separator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", - "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz", + "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", @@ -18542,16 +18526,16 @@ } }, "@babel/plugin-transform-object-rest-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", - "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz", + "integrity": "sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==", "dev": true, "requires": { - "@babel/compat-data": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", + "@babel/compat-data": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.22.5" + "@babel/plugin-transform-parameters": "^7.22.15" } }, "@babel/plugin-transform-object-super": { @@ -18565,9 +18549,9 @@ } }, "@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", - "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz", + "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", @@ -18575,9 +18559,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.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.15.tgz", + "integrity": "sha512-ngQ2tBhq5vvSJw2Q2Z9i7ealNkpDMU0rGWnHPKqRZO0tzZ5tlaoz4hDvhXioOoaE0X2vfNss1djwg0DXlfu30A==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5", @@ -18586,9 +18570,9 @@ } }, "@babel/plugin-transform-parameters": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", - "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz", + "integrity": "sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.22.5" @@ -18605,13 +18589,13 @@ } }, "@babel/plugin-transform-private-property-in-object": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", - "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", + "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.11", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" } @@ -18626,13 +18610,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": { @@ -18715,9 +18699,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" @@ -18754,17 +18738,17 @@ } }, "@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.15", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.15.tgz", + "integrity": "sha512-tZFHr54GBkHk6hQuVA8w4Fmq+MSPsfvMG0vPnOYyTnJpyfMqybL8/MbNCPRT9zc2KBO2pe4tq15g6Uno4Jpoag==", "dev": true, "requires": { "@babel/compat-data": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.15", "@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", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.15", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.15", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -18785,66 +18769,66 @@ "@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.15", "@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.15", "@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-class-static-block": "^7.22.11", + "@babel/plugin-transform-classes": "^7.22.15", "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.15", "@babel/plugin-transform-dotall-regex": "^7.22.5", "@babel/plugin-transform-duplicate-keys": "^7.22.5", - "@babel/plugin-transform-dynamic-import": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.11", "@babel/plugin-transform-exponentiation-operator": "^7.22.5", - "@babel/plugin-transform-export-namespace-from": "^7.22.5", - "@babel/plugin-transform-for-of": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.11", + "@babel/plugin-transform-for-of": "^7.22.15", "@babel/plugin-transform-function-name": "^7.22.5", - "@babel/plugin-transform-json-strings": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.11", "@babel/plugin-transform-literals": "^7.22.5", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", "@babel/plugin-transform-member-expression-literals": "^7.22.5", "@babel/plugin-transform-modules-amd": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.5", - "@babel/plugin-transform-modules-systemjs": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.15", + "@babel/plugin-transform-modules-systemjs": "^7.22.11", "@babel/plugin-transform-modules-umd": "^7.22.5", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", "@babel/plugin-transform-new-target": "^7.22.5", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", - "@babel/plugin-transform-numeric-separator": "^7.22.5", - "@babel/plugin-transform-object-rest-spread": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", + "@babel/plugin-transform-numeric-separator": "^7.22.11", + "@babel/plugin-transform-object-rest-spread": "^7.22.15", "@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-parameters": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.11", + "@babel/plugin-transform-optional-chaining": "^7.22.15", + "@babel/plugin-transform-parameters": "^7.22.15", "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.11", "@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.15", + "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", @@ -18855,36 +18839,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" } @@ -18904,39 +18886,39 @@ } }, "@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@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.17", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.17.tgz", + "integrity": "sha512-xK4Uwm0JnAMvxYZxOVecss85WxTEIbTa7bnGyf/+EgCL5Zt3U7htUpEOWv9detPlamGKuRzCqw74xVglDWpPdg==", "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.7", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", "@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.16", + "@babel/types": "^7.22.17", "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.17", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.17.tgz", + "integrity": "sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg==", "requires": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.15", "to-fast-properties": "^2.0.0" } }, @@ -18991,9 +18973,9 @@ "dev": true }, "@eslint/eslintrc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz", - "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==", + "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", @@ -19014,9 +18996,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" @@ -19040,9 +19022,9 @@ } }, "@eslint/js": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz", - "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", + "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", "dev": true }, "@ethereumjs/common": { @@ -19258,14 +19240,14 @@ } }, "@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", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -19313,16 +19295,16 @@ "dev": true }, "@jest/console": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.2.tgz", - "integrity": "sha512-0N0yZof5hi44HAR2pPS+ikJ3nzKNoZdVu8FffRf3wy47I7Dm7etk/3KetMdRUqzVd16V4O2m2ISpNTbnIuqy1w==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", + "integrity": "sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==", "dev": true, "requires": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", "slash": "^3.0.0" }, "dependencies": { @@ -19378,37 +19360,37 @@ } }, "@jest/core": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.2.tgz", - "integrity": "sha512-Oj+5B+sDMiMWLhPFF+4/DvHOf+U10rgvCLGPHP8Xlsy/7QxS51aU/eBngudHlJXnaWD5EohAgJ4js+T6pa+zOg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz", + "integrity": "sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==", "dev": true, "requires": { - "@jest/console": "^29.6.2", - "@jest/reporters": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@jest/reporters": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@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.2", - "jest-haste-map": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-resolve-dependencies": "^29.6.2", - "jest-runner": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", - "jest-watcher": "^29.6.2", + "jest-changed-files": "^29.6.3", + "jest-config": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-resolve-dependencies": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "jest-watcher": "^29.6.4", "micromatch": "^4.0.4", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -19465,73 +19447,73 @@ } }, "@jest/environment": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.2.tgz", - "integrity": "sha512-AEcW43C7huGd/vogTddNNTDRpO6vQ2zaQNrttvWV18ArBx9Z56h7BIsXkNFJVOO4/kblWEQz30ckw0+L3izc+Q==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz", + "integrity": "sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==", "dev": true, "requires": { - "@jest/fake-timers": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.2" + "jest-mock": "^29.6.3" } }, "@jest/expect": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.2.tgz", - "integrity": "sha512-m6DrEJxVKjkELTVAztTLyS/7C92Y2b0VYqmDROYKLLALHn8T/04yPs70NADUYPrV3ruI+H3J0iUIuhkjp7vkfg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==", "dev": true, "requires": { - "expect": "^29.6.2", - "jest-snapshot": "^29.6.2" + "expect": "^29.6.4", + "jest-snapshot": "^29.6.4" } }, "@jest/expect-utils": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.2.tgz", - "integrity": "sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz", + "integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==", "dev": true, "requires": { - "jest-get-type": "^29.4.3" + "jest-get-type": "^29.6.3" } }, "@jest/fake-timers": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.2.tgz", - "integrity": "sha512-euZDmIlWjm1Z0lJ1D0f7a0/y5Kh/koLFMUBE5SUYWrmy8oNhJpbTBDAP6CxKnadcMLDoDf4waRYCe35cH6G6PA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz", + "integrity": "sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==", "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.2", - "jest-mock": "^29.6.2", - "jest-util": "^29.6.2" + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" } }, "@jest/globals": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.2.tgz", - "integrity": "sha512-cjuJmNDjs6aMijCmSa1g2TNG4Lby/AeU7/02VtpW+SLcZXzOLK2GpN2nLqcFjmhy3B3AoPeQVx7BnyOf681bAw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz", + "integrity": "sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==", "dev": true, "requires": { - "@jest/environment": "^29.6.2", - "@jest/expect": "^29.6.2", - "@jest/types": "^29.6.1", - "jest-mock": "^29.6.2" + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@jest/types": "^29.6.3", + "jest-mock": "^29.6.3" } }, "@jest/reporters": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.2.tgz", - "integrity": "sha512-sWtijrvIav8LgfJZlrGCdN0nP2EWbakglJY49J1Y5QihcQLfy7ovyxxjJBRXMNltgt4uPtEcFmIMbVshEDfFWw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz", + "integrity": "sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", @@ -19540,13 +19522,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.2", - "jest-util": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -19594,13 +19576,13 @@ "dev": true }, "jest-worker": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.2.tgz", - "integrity": "sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", + "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", "dev": true, "requires": { "@types/node": "*", - "jest-util": "^29.6.2", + "jest-util": "^29.6.3", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -19628,18 +19610,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", @@ -19648,46 +19630,46 @@ } }, "@jest/test-result": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.2.tgz", - "integrity": "sha512-3VKFXzcV42EYhMCsJQURptSqnyjqCGbtLuX5Xxb6Pm6gUf1wIRIl+mandIRGJyWKgNKYF9cnstti6Ls5ekduqw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz", + "integrity": "sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==", "dev": true, "requires": { - "@jest/console": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/test-sequencer": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.2.tgz", - "integrity": "sha512-GVYi6PfPwVejO7slw6IDO0qKVum5jtrJ3KoLGbgBWyr2qr4GaxFV6su+ZAjdTX75Sr1DkMFRk09r2ZVa+wtCGw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz", + "integrity": "sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==", "dev": true, "requires": { - "@jest/test-result": "^29.6.2", + "@jest/test-result": "^29.6.4", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", + "jest-haste-map": "^29.6.4", "slash": "^3.0.0" } }, "@jest/transform": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.2.tgz", - "integrity": "sha512-ZqCqEISr58Ce3U+buNFJYUktLJZOggfyvR+bZMaiV1e8B1SIvJbwZMrYz3gx/KAPn9EXmOmN+uB08yLCjWkQQg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz", + "integrity": "sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==", "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.2", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.2", + "jest-haste-map": "^29.6.4", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -19752,12 +19734,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": "*", @@ -19870,12 +19852,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", @@ -20850,14 +20826,15 @@ } }, "assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "requires": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" } }, "assert-plus": { @@ -20912,13 +20889,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", @@ -20941,15 +20918,15 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "babel-jest": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.2.tgz", - "integrity": "sha512-BYCzImLos6J3BH/+HvUCHG1dTf2MzmAB4jaVxHV+29RZLjR29XuYTmsf2sdDwkrb+FczkGo3kOhE7ga6sI0P4A==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", + "integrity": "sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==", "dev": true, "requires": { - "@jest/transform": "^29.6.2", + "@jest/transform": "^29.6.4", "@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" @@ -21027,12 +21004,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", @@ -21052,19 +21044,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", @@ -21121,12 +21113,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" } }, @@ -21172,9 +21164,9 @@ "dev": true }, "bignumber.js": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", - "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==" + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==" }, "binary-extensions": { "version": "2.2.0", @@ -21348,13 +21340,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" } }, @@ -21545,9 +21537,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", @@ -21584,9 +21576,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.4.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.0.tgz", + "integrity": "sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==", "requires": { "@kurkle/color": "^0.3.0" } @@ -21881,9 +21873,9 @@ } }, "core-js": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.0.tgz", - "integrity": "sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww==" + "version": "3.32.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.2.tgz", + "integrity": "sha512-pxXSw1mYZPDGvTQqEc5vgIb83jGQKFGYWY76z4a7weZXUolw3G+OvpZqSRcfYOoOVUQJYEPsWeQK8pKEnUtWxQ==" }, "core-js-compat": { "version": "3.31.0", @@ -22337,9 +22329,9 @@ } }, "dedent": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.3.0.tgz", - "integrity": "sha512-7glNLfvdsMzZm3FpRY1CHuI2lbYDR+71YmrhmTZjYFD5pfT0ACgnGRdrrC9Mk2uICnzkcdelCx5at787UDGOvg==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", "dev": true, "requires": {} }, @@ -22426,9 +22418,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": { @@ -22542,9 +22534,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", @@ -22763,11 +22755,6 @@ "es6-symbol": "^3.1.1" } }, - "es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" - }, "es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -22858,16 +22845,16 @@ } }, "eslint": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", - "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", + "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.1", - "@eslint/js": "^8.46.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.49.0", + "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.12.4", @@ -22877,7 +22864,7 @@ "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.2", + "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", @@ -23100,9 +23087,9 @@ } }, "eslint-plugin-import": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.0.tgz", - "integrity": "sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q==", + "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", @@ -23114,13 +23101,12 @@ "eslint-import-resolver-node": "^0.3.7", "eslint-module-utils": "^2.8.0", "has": "^1.0.3", - "is-core-module": "^2.12.1", + "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.3", "semver": "^6.3.1", "tsconfig-paths": "^3.14.2" }, @@ -23252,9 +23238,9 @@ } }, "eslint-visitor-keys": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz", - "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", + "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": { @@ -23863,17 +23849,16 @@ "dev": true }, "expect": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.2.tgz", - "integrity": "sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==", "dev": true, "requires": { - "@jest/expect-utils": "^29.6.2", - "@types/node": "*", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2" + "@jest/expect-utils": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3" } }, "express": { @@ -24842,9 +24827,9 @@ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" }, "is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "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" } @@ -25057,16 +25042,27 @@ "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": { @@ -25119,50 +25115,51 @@ } }, "jest": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.2.tgz", - "integrity": "sha512-8eQg2mqFbaP7CwfsTpCxQ+sHzw1WuNWL5UUvjnWP4hx2riGz9fPSzYOaU5q8/GqWn1TfgZIVTqYJygbGbWAANg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", + "integrity": "sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==", "dev": true, "requires": { - "@jest/core": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/core": "^29.6.4", + "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.6.2" + "jest-cli": "^29.6.4" } }, "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.2", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.2.tgz", - "integrity": "sha512-G9mN+KOYIUe2sB9kpJkO9Bk18J4dTDArNFPwoZ7WKHKel55eKIS/u2bLthxgojwlf9NLCVQfgzM/WsOVvoC6Fw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz", + "integrity": "sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==", "dev": true, "requires": { - "@jest/environment": "^29.6.2", - "@jest/expect": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.2", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", + "jest-each": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", "p-limit": "^3.1.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -25220,21 +25217,21 @@ } }, "jest-cli": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.2.tgz", - "integrity": "sha512-TT6O247v6dCEX2UGHGyflMpxhnrL0DNqP2fRTKYm3nJJpCTfXX3GCMQPGFjXDoj0i5/Blp3jriKXFgdfmbYB6Q==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz", + "integrity": "sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==", "dev": true, "requires": { - "@jest/core": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/core": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@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.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", + "jest-config": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "prompts": "^2.0.1", "yargs": "^17.3.1" }, @@ -25291,31 +25288,31 @@ } }, "jest-config": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.2.tgz", - "integrity": "sha512-VxwFOC8gkiJbuodG9CPtMRjBUNZEHxwfQXmIudSTzFWxaci3Qub1ddTRbFNQlD/zUeaifLndh/eDccFX4wCMQw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz", + "integrity": "sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==", "dev": true, "requires": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.2", - "@jest/types": "^29.6.1", - "babel-jest": "^29.6.2", + "@jest/test-sequencer": "^29.6.4", + "@jest/types": "^29.6.3", + "babel-jest": "^29.6.4", "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.2", - "jest-environment-node": "^29.6.2", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-runner": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", + "jest-circus": "^29.6.4", + "jest-environment-node": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -25372,15 +25369,15 @@ } }, "jest-diff": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.2.tgz", - "integrity": "sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz", + "integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==", "dev": true, "requires": { "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" }, "dependencies": { "ansi-styles": { @@ -25435,25 +25432,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.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.2.tgz", - "integrity": "sha512-MsrsqA0Ia99cIpABBc3izS1ZYoYfhIy0NNWqPSE0YXbQjwchyt6B1HD2khzyPe1WiJA7hbxXy77ZoUQxn8UlSw==", + "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.2", - "pretty-format": "^29.6.2" + "jest-get-type": "^29.6.3", + "jest-util": "^29.6.3", + "pretty-format": "^29.6.3" }, "dependencies": { "ansi-styles": { @@ -25508,57 +25505,57 @@ } }, "jest-environment-jsdom": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.6.2.tgz", - "integrity": "sha512-7oa/+266AAEgkzae8i1awNEfTfjwawWKLpiw2XesZmaoVVj9u9t8JOYx18cG29rbPNtkUlZ8V4b5Jb36y/VxoQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.6.4.tgz", + "integrity": "sha512-K6wfgUJ16DoMs02JYFid9lOsqfpoVtyJxpRlnTxUHzvZWBnnh2VNGRB9EC1Cro96TQdq5TtSjb3qUjNaJP9IyA==", "dev": true, "requires": { - "@jest/environment": "^29.6.2", - "@jest/fake-timers": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", "@types/jsdom": "^20.0.0", "@types/node": "*", - "jest-mock": "^29.6.2", - "jest-util": "^29.6.2", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3", "jsdom": "^20.0.0" } }, "jest-environment-node": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.2.tgz", - "integrity": "sha512-YGdFeZ3T9a+/612c5mTQIllvWkddPbYcN2v95ZH24oWMbGA4GGS2XdIF92QMhUhvrjjuQWYgUGW2zawOyH63MQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz", + "integrity": "sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ==", "dev": true, "requires": { - "@jest/environment": "^29.6.2", - "@jest/fake-timers": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.2", - "jest-util": "^29.6.2" + "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.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.2.tgz", - "integrity": "sha512-+51XleTDAAysvU8rT6AnS1ZJ+WHVNqhj1k6nTvN2PYP+HjU3kqlaKQ1Lnw3NYW3bm2r8vq82X0Z1nDDHZMzHVA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz", + "integrity": "sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==", "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.2", - "jest-worker": "^29.6.2", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -25570,13 +25567,13 @@ "dev": true }, "jest-worker": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.2.tgz", - "integrity": "sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", + "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", "dev": true, "requires": { "@types/node": "*", - "jest-util": "^29.6.2", + "jest-util": "^29.6.3", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" } @@ -25593,25 +25590,25 @@ } }, "jest-leak-detector": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.2.tgz", - "integrity": "sha512-aNqYhfp5uYEO3tdWMb2bfWv6f0b4I0LOxVRpnRLAeque2uqOVVMLh6khnTcE2qJ5wAKop0HcreM1btoysD6bPQ==", + "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.2" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" } }, "jest-matcher-utils": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.2.tgz", - "integrity": "sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz", + "integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==", "dev": true, "requires": { "chalk": "^4.0.0", - "jest-diff": "^29.6.2", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" }, "dependencies": { "ansi-styles": { @@ -25666,18 +25663,18 @@ } }, "jest-message-util": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.2.tgz", - "integrity": "sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ==", + "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.2", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -25734,14 +25731,14 @@ } }, "jest-mock": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.2.tgz", - "integrity": "sha512-hoSv3lb3byzdKfwqCuT6uTscan471GUECqgNYykg6ob0yiAw3zYc7OrPnI9Qv8Wwoa4lC7AZ9hyS4AiIx5U2zg==", + "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.2" + "jest-util": "^29.6.3" } }, "jest-pnp-resolver": { @@ -25752,23 +25749,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.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.2.tgz", - "integrity": "sha512-G/iQUvZWI5e3SMFssc4ug4dH0aZiZpsDq9o1PtXTV1210Ztyb2+w+ZgQkB3iOiC5SmAEzJBOHWz6Hvrd+QnNPw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz", + "integrity": "sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==", "dev": true, "requires": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", + "jest-haste-map": "^29.6.4", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -25826,40 +25823,40 @@ } }, "jest-resolve-dependencies": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.2.tgz", - "integrity": "sha512-LGqjDWxg2fuQQm7ypDxduLu/m4+4Lb4gczc13v51VMZbVP5tSBILqVx8qfWcsdP8f0G7aIqByIALDB0R93yL+w==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz", + "integrity": "sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==", "dev": true, "requires": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.6.2" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.6.4" } }, "jest-runner": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.2.tgz", - "integrity": "sha512-wXOT/a0EspYgfMiYHxwGLPCZfC0c38MivAlb2lMEAlwHINKemrttu1uSbcGbfDV31sFaPWnWJPmb2qXM8pqZ4w==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz", + "integrity": "sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==", "dev": true, "requires": { - "@jest/console": "^29.6.2", - "@jest/environment": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@jest/environment": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@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.2", - "jest-haste-map": "^29.6.2", - "jest-leak-detector": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-resolve": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-util": "^29.6.2", - "jest-watcher": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-docblock": "^29.6.3", + "jest-environment-node": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-leak-detector": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-util": "^29.6.3", + "jest-watcher": "^29.6.4", + "jest-worker": "^29.6.4", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -25905,13 +25902,13 @@ "dev": true }, "jest-worker": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.2.tgz", - "integrity": "sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", + "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", "dev": true, "requires": { "@types/node": "*", - "jest-util": "^29.6.2", + "jest-util": "^29.6.3", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -25949,31 +25946,31 @@ } }, "jest-runtime": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.2.tgz", - "integrity": "sha512-2X9dqK768KufGJyIeLmIzToDmsN0m7Iek8QNxRSI/2+iPFYHF0jTwlO3ftn7gdKd98G/VQw9XJCk77rbTGZnJg==", - "dev": true, - "requires": { - "@jest/environment": "^29.6.2", - "@jest/fake-timers": "^29.6.2", - "@jest/globals": "^29.6.2", - "@jest/source-map": "^29.6.0", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz", + "integrity": "sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==", + "dev": true, + "requires": { + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/globals": "^29.6.4", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@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.2", - "jest-message-util": "^29.6.2", - "jest-mock": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -26030,9 +26027,9 @@ } }, "jest-snapshot": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.2.tgz", - "integrity": "sha512-1OdjqvqmRdGNvWXr/YZHuyhh5DeaLp1p/F8Tht/MrMw4Kr1Uu/j4lRG+iKl1DAqUJDWxtQBMk41Lnf/JETYBRA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz", + "integrity": "sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==", "dev": true, "requires": { "@babel/core": "^7.11.6", @@ -26040,20 +26037,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.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/expect-utils": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.6.2", + "expect": "^29.6.4", "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.2", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", "natural-compare": "^1.4.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "semver": "^7.5.3" }, "dependencies": { @@ -26118,12 +26115,12 @@ } }, "jest-util": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.2.tgz", - "integrity": "sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w==", + "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", @@ -26183,17 +26180,17 @@ } }, "jest-validate": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.2.tgz", - "integrity": "sha512-vGz0yMN5fUFRRbpJDPwxMpgSXW1LDKROHfBopAvDcmD6s+B/s8WJrwi+4bfH4SdInBA5C3P3BI19dBtKzx1Arg==", + "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.2" + "pretty-format": "^29.6.3" }, "dependencies": { "ansi-styles": { @@ -26254,18 +26251,18 @@ } }, "jest-watcher": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.2.tgz", - "integrity": "sha512-GZitlqkMkhkefjfN/p3SJjrDaxPflqxEAv3/ik10OirZqJGYH5rPiIsgVcfof0Tdqg3shQGdEIxDBx+B4tuLzA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz", + "integrity": "sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==", "dev": true, "requires": { - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.6.2", + "jest-util": "^29.6.3", "string-length": "^4.0.1" }, "dependencies": { @@ -26355,9 +26352,9 @@ "dev": true }, "jquery": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.0.tgz", - "integrity": "sha512-umpJ0/k8X0MvD1ds0P9SfowREz2LenHsQaxSohMZ5OMNEU2r0tf8pdeEFTHMFxWVxKNyU9rTtK3CWzUCTKJUeQ==" + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" }, "jquery-mousewheel": { "version": "3.1.13", @@ -26894,9 +26891,9 @@ "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.3", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.3.tgz", + "integrity": "sha512-tFWBiv3h7z+T/tDaoxA8rqTxy1CHV6gHS//QdaH4pulbq/JuBSGgQspQQqcgnwdAx6pNI7cmvz5Sv/addzHmUg==" }, "make-dir": { "version": "4.0.0", @@ -27347,9 +27344,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", @@ -27446,7 +27443,6 @@ "version": "4.1.4", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -27702,9 +27698,9 @@ "version": "file:../../../deps/phoenix_html" }, "photoswipe": { - "version": "5.3.8", - "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.3.8.tgz", - "integrity": "sha512-4vTzOQt8GP4Chsm0s+8j2xDtVHAEN252PxrU12A1zXauNn0zD5HRHgjALKO2GKTyBnTnOrJUOxbV8LTrFIMrYw==" + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.4.0.tgz", + "integrity": "sha512-PZvdK1D94TApU0MNWc9H6eXOolKJOMkgt7CJ9ZfIdkHR4CrEj47MOe4Vrlcv6ZpHslK+uKS6Ai3y3VIe7gsi+Q==" }, "picocolors": { "version": "1.0.0", @@ -27804,9 +27800,9 @@ "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" }, "postcss": { - "version": "8.4.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", - "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", "dev": true, "requires": { "nanoid": "^3.3.6", @@ -28154,12 +28150,12 @@ "dev": true }, "pretty-format": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.2.tgz", - "integrity": "sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==", + "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" }, @@ -28562,9 +28558,9 @@ "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" @@ -28833,9 +28829,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { - "version": "1.64.2", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.64.2.tgz", - "integrity": "sha512-TnDlfc+CRnUAgLO9D8cQLFu/GIjJIzJCGkE7o4ekIGQOH7T3GetiRR/PsTWJUHhkzcSPrARkPI+gNWn5alCzDg==", + "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", @@ -29463,9 +29459,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", @@ -29859,12 +29855,12 @@ "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" }, "url": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.1.tgz", - "integrity": "sha512-rWS3H04/+mzzJkv0eZ7vEDGiQbgquI1fGfOad6zKvgYQi1SzMmhl7c/DdRGxhaWrVH6z0qWITo8rpnxK/RfEhA==", + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.2.tgz", + "integrity": "sha512-7yIgNnrST44S7PJ5+jXbdIupfU1nWUdQJBFBeJRclPXiWgCvrSq5Frw8lr/i//n5sqDfzoKmBymMS81l4U/7cg==", "requires": { "punycode": "^1.4.1", - "qs": "^6.11.0" + "qs": "^6.11.2" }, "dependencies": { "punycode": { @@ -29969,9 +29965,9 @@ } }, "viewerjs": { - "version": "1.11.4", - "resolved": "https://registry.npmjs.org/viewerjs/-/viewerjs-1.11.4.tgz", - "integrity": "sha512-/mnqMIwt5Bi9j59+48OqQtqgOx8oh186Xshdr/dqqBrakMSMlLt/jmeNHBod0PvOkesZf66ivQbWmtWYBlKetg==" + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/viewerjs/-/viewerjs-1.11.5.tgz", + "integrity": "sha512-nsvQkC5jnqZ/2ggFYWUH5gpUGPtFAYidsFh8Q7B7sioAdqJzSJrELvbu9ozUm0W+A2uHN5XuuiheHHB+dWiPEA==" }, "w3c-hr-time": { "version": "1.0.2", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 28a60def3631..964bd513fb28 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -19,24 +19,24 @@ "eslint": "eslint js/**" }, "dependencies": { - "@fortawesome/fontawesome-free": "^6.4.0", - "@amplitude/analytics-browser": "^2.1.3", + "@fortawesome/fontawesome-free": "^6.4.2", + "@amplitude/analytics-browser": "^2.2.3", "@tarekraafat/autocomplete.js": "^10.2.7", "@walletconnect/web3-provider": "^1.8.0", - "assert": "^2.0.0", - "bignumber.js": "^9.1.1", + "assert": "^2.1.0", + "bignumber.js": "^9.1.2", "bootstrap": "^4.6.0", - "chart.js": "^4.3.2", + "chart.js": "^4.4.0", "chartjs-adapter-luxon": "^1.3.1", "clipboard": "^2.0.11", - "core-js": "^3.32.0", + "core-js": "^3.32.2", "crypto-browserify": "^3.12.0", "dropzone": "^5.9.3", "eth-net-props": "^1.0.41", "highlight.js": "^11.8.0", "https-browserify": "^1.0.0", "humps": "^2.0.1", - "jquery": "^3.7.0", + "jquery": "^3.7.1", "js-cookie": "^3.0.5", "lodash.debounce": "^4.0.8", "lodash.differenceby": "^4.8.0", @@ -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.3", "malihu-custom-scrollbar-plugin": "3.1.5", "mixpanel-browser": "^2.47.0", "moment": "^2.29.4", @@ -66,42 +66,42 @@ "path-parser": "^6.1.0", "phoenix": "file:../../../deps/phoenix", "phoenix_html": "file:../../../deps/phoenix_html", - "photoswipe": "^5.3.8", + "photoswipe": "^5.4.0", "pikaday": "^1.8.2", "popper.js": "^1.14.7", "reduce-reducers": "^1.0.4", "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", + "url": "^0.11.2", "util": "^0.12.5", - "viewerjs": "^1.11.4", + "viewerjs": "^1.11.5", "web3": "^1.10.0", "web3modal": "^1.9.12", "xss": "^1.0.14" }, "devDependencies": { - "@babel/core": "^7.22.9", - "@babel/preset-env": "^7.22.9", - "autoprefixer": "^10.4.14", + "@babel/core": "^7.22.17", + "@babel/preset-env": "^7.22.15", + "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.46.0", + "eslint": "^8.49.0", "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.28.0", + "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.2", - "jest-environment-jsdom": "^29.6.2", + "jest": "^29.6.4", + "jest-environment-jsdom": "^29.6.4", "mini-css-extract-plugin": "^2.7.6", - "postcss": "^8.4.27", + "postcss": "^8.4.29", "postcss-loader": "^7.3.3", - "sass": "^1.64.2", + "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..f0ead554c72d 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 @@ -4,6 +4,8 @@ defmodule BlockScoutWeb.AddressChannel do """ use BlockScoutWeb, :channel + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + alias BlockScoutWeb.API.V2.AddressView, as: AddressViewAPI alias BlockScoutWeb.API.V2.SmartContractView, as: SmartContractViewAPI alias BlockScoutWeb.API.V2.TransactionView, as: TransactionViewAPI @@ -28,11 +30,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") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @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 +229,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/channels/token_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/token_channel.ex index ac4295e6b010..0de43f328859 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/token_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/token_channel.ex @@ -4,6 +4,8 @@ defmodule BlockScoutWeb.TokenChannel do """ use BlockScoutWeb, :channel + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + alias BlockScoutWeb.{CurrencyHelper, TokensView} alias BlockScoutWeb.Tokens.TransferView alias Explorer.Chain @@ -12,7 +14,7 @@ defmodule BlockScoutWeb.TokenChannel do intercept(["token_transfer", "token_total_supply"]) - {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash def join("tokens:" <> _transaction_hash, _params, socket) do diff --git a/apps/block_scout_web/lib/block_scout_web/channels/transaction_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/transaction_channel.ex index df026d0b0b58..5c1247786a05 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/transaction_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/transaction_channel.ex @@ -4,6 +4,8 @@ defmodule BlockScoutWeb.TransactionChannel do """ use BlockScoutWeb, :channel + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + alias BlockScoutWeb.API.V2.TransactionView, as: TransactionViewV2 alias BlockScoutWeb.{TransactionRawTraceView, TransactionView} alias Explorer.Chain @@ -12,7 +14,7 @@ defmodule BlockScoutWeb.TransactionChannel do intercept(["pending_transaction", "transaction", "raw_trace"]) - {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash def join("transactions:new_transaction", _params, socket) do 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/address_token_transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex index e8d21d0d2c97..25bec31b7dd4 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex @@ -13,6 +13,8 @@ defmodule BlockScoutWeb.AddressTokenTransferController do import BlockScoutWeb.Chain, only: [current_filter: 1, next_page_params: 3, paging_options: 1, split_list_by_page: 1] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + @transaction_necessity_by_association [ necessity_by_association: %{ [created_contract_address: :names] => :optional, @@ -29,7 +31,7 @@ defmodule BlockScoutWeb.AddressTokenTransferController do } ] - {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash def index( diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex index 815c8227a6f3..639e5cc39466 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex @@ -6,10 +6,9 @@ defmodule BlockScoutWeb.AddressTransactionController do use BlockScoutWeb, :controller import BlockScoutWeb.Account.AuthController, only: [current_user: 1] - import BlockScoutWeb.Chain, only: [current_filter: 1, paging_options: 1, next_page_params: 3, split_list_by_page: 1] - import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] alias BlockScoutWeb.{AccessHelper, Controller, TransactionView} alias Explorer.{Chain, Market} @@ -40,7 +39,7 @@ defmodule BlockScoutWeb.AddressTransactionController do } ] - {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do 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..7002858350ba 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 @@ -6,6 +6,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do import BlockScoutWeb.PagingHelper, only: [current_filter: 1, delete_parameters_from_next_page_params: 1, search_query: 1] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] import Explorer.SmartContract.Solidity.Verifier, only: [parse_boolean: 1] alias BlockScoutWeb.{AccessHelper, AddressView} @@ -26,8 +27,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do @api_true [api?: true] - @burn_address "0x0000000000000000000000000000000000000000" - action_fallback(BlockScoutWeb.API.V2.FallbackController) def smart_contract(conn, %{"address_hash" => address_hash_string} = params) do @@ -82,7 +81,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 +94,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 @@ -109,7 +108,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do address.smart_contract |> SmartContract.get_implementation_address_hash(@api_true) |> Tuple.to_list() - |> List.first() || @burn_address + |> List.first() || burn_address_hash_string() conn |> put_status(200) @@ -131,11 +130,15 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do address.smart_contract |> SmartContract.get_implementation_address_hash(@api_true) |> Tuple.to_list() - |> List.first() || @burn_address + |> List.first() || burn_address_hash_string() 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 +203,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 f85e5f54be31..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, 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/block_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex index 74ef3f6b2f1e..4a92e7c5ca52 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex @@ -11,12 +11,13 @@ defmodule BlockScoutWeb.BlockTransactionController do ] import Explorer.Chain, only: [hash_to_block: 2, number_to_block: 2] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] alias BlockScoutWeb.{Controller, TransactionView} alias Explorer.Chain alias Phoenix.View - {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash def index(conn, %{"block_hash_or_number" => formatted_block_hash_or_number, "type" => "JSON"} = params) do 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/pending_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/pending_transaction_controller.ex index 906fbc3194b4..f709855b000a 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/pending_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/pending_transaction_controller.ex @@ -2,12 +2,13 @@ defmodule BlockScoutWeb.PendingTransactionController do use BlockScoutWeb, :controller import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] alias BlockScoutWeb.{Controller, TransactionView} alias Explorer.Chain alias Phoenix.View - {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash def index(conn, %{"type" => "JSON"} = params) do diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex index d692d6de1471..f3406fc6cab6 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex @@ -1,11 +1,13 @@ defmodule BlockScoutWeb.RecentTransactionsController do use BlockScoutWeb, :controller + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + alias Explorer.{Chain, PagingOptions} alias Explorer.Chain.Hash alias Phoenix.View - {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash def index(conn, _params) do diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/robots_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/robots_controller.ex new file mode 100644 index 000000000000..a482fb1a73c7 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/robots_controller.ex @@ -0,0 +1,13 @@ +defmodule BlockScoutWeb.RobotsController do + use BlockScoutWeb, :controller + + def robots(conn, _params) do + conn + |> render("robots.txt") + end + + def sitemap(conn, _params) do + conn + |> render("sitemap.xml") + end +end 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/controllers/smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex index b403e0e7d592..30832eee4aae 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex @@ -7,8 +7,7 @@ defmodule BlockScoutWeb.SmartContractController do alias Explorer.SmartContract.{Reader, Writer} import Explorer.SmartContract.Solidity.Verifier, only: [parse_boolean: 1] - - @burn_address "0x0000000000000000000000000000000000000000" + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] def index(conn, %{"hash" => address_hash_string, "type" => contract_type, "action" => action} = params) do address_options = [ @@ -30,9 +29,9 @@ defmodule BlockScoutWeb.SmartContractController do address.smart_contract |> SmartContract.get_implementation_address_hash() |> Tuple.to_list() - |> List.first() || @burn_address + |> List.first() || burn_address_hash_string() else - @burn_address + burn_address_hash_string() end functions = @@ -137,7 +136,7 @@ defmodule BlockScoutWeb.SmartContractController do address: %{hash: address_hash}, custom_abi: true, contract_abi: contract_abi, - implementation_address: @burn_address, + implementation_address: burn_address_hash_string(), implementation_abi: [], contract_type: contract_type, action: action diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex index 7909faf07674..0eaa0115adc8 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex @@ -8,8 +8,9 @@ defmodule BlockScoutWeb.Tokens.Instance.TransferController do alias Phoenix.View import BlockScoutWeb.Chain, only: [split_list_by_page: 1, paging_options: 1, next_page_params: 3] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] - {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id_str, "type" => "JSON"} = params) do diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex index a349af147408..b36159b82b66 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex @@ -12,8 +12,9 @@ defmodule BlockScoutWeb.Tokens.TransferController do alias Phoenix.View import BlockScoutWeb.Chain, only: [split_list_by_page: 1, paging_options: 1, next_page_params: 3] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] - {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash def index(conn, %{"token_id" => address_hash_string, "type" => "JSON"} = params) do diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex index 5983ab8920cd..51a75bf85fcb 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex @@ -14,6 +14,7 @@ defmodule BlockScoutWeb.TransactionController do import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] import BlockScoutWeb.Models.GetTransactionTags, only: [get_transaction_with_addresses_tags: 2] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] alias BlockScoutWeb.{ AccessHelper, @@ -36,7 +37,7 @@ defmodule BlockScoutWeb.TransactionController do :token_transfers => :optional } - {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash @default_options [ diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex index e2020a47bcca..7ea2d6e74e6c 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex @@ -16,8 +16,9 @@ defmodule BlockScoutWeb.TransactionStateController do import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] import BlockScoutWeb.Models.GetTransactionTags, only: [get_transaction_with_addresses_tags: 2] import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] - {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex index d0f7fed1584f..061fde450ad4 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex @@ -5,12 +5,13 @@ defmodule BlockScoutWeb.TransactionTokenTransferController do import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] import BlockScoutWeb.Models.GetTransactionTags, only: [get_transaction_with_addresses_tags: 2] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] alias BlockScoutWeb.{AccessHelper, Controller, TransactionController, TransactionTokenTransferView} alias Explorer.{Chain, Market} alias Phoenix.View - {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash def index(conn, %{"transaction_id" => transaction_hash_string, "type" => "JSON"} = params) do diff --git a/apps/block_scout_web/lib/block_scout_web/endpoint.ex b/apps/block_scout_web/lib/block_scout_web/endpoint.ex index 9036939eb195..10d8d99f36b6 100644 --- a/apps/block_scout_web/lib/block_scout_web/endpoint.ex +++ b/apps/block_scout_web/lib/block_scout_web/endpoint.ex @@ -29,7 +29,6 @@ defmodule BlockScoutWeb.Endpoint do browserconfig.xml mstile-150x150.png safari-pinned-tab.svg - robots.txt ), only_matching: ~w(manifest) ) diff --git a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex index e679627e8a74..3971a5463a64 100644 --- a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex @@ -4,13 +4,15 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do """ import BlockScoutWeb.Chain, only: [default_paging_options: 0] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + alias Explorer.Chain.Transaction.StateChange alias Explorer.{Chain, PagingOptions} alias Explorer.Chain.{Block, Transaction, Wei} alias Explorer.Chain.Cache.StateChanges alias Indexer.Fetcher.{CoinBalanceOnDemand, TokenBalanceOnDemand} - {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash def state_changes(transaction, options \\ []) 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..b56899ce80f3 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, @@ -53,6 +47,8 @@ defmodule BlockScoutWeb.Router do scope "/", BlockScoutWeb do pipe_through(:browser) + get("/robots.txt", RobotsController, :robots) + get("/sitemap.xml", RobotsController, :sitemap) get("/api-docs", APIDocsController, :index) get("/eth-rpc-api-docs", APIDocsController, :eth_rpc) end 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/assets/static/robots.txt b/apps/block_scout_web/lib/block_scout_web/templates/robots/robots.txt.eex similarity index 76% rename from apps/block_scout_web/assets/static/robots.txt rename to apps/block_scout_web/lib/block_scout_web/templates/robots/robots.txt.eex index 3c9c7c01f30b..e2f1434e2429 100644 --- a/apps/block_scout_web/assets/static/robots.txt +++ b/apps/block_scout_web/lib/block_scout_web/templates/robots/robots.txt.eex @@ -3,3 +3,4 @@ # To ban all spiders from the entire site uncomment the next two lines: # User-agent: * # Disallow: / +Sitemap: <%= APIDocsView.blockscout_url(true) %>/sitemap.xml diff --git a/apps/block_scout_web/lib/block_scout_web/templates/robots/sitemap.xml.eex b/apps/block_scout_web/lib/block_scout_web/templates/robots/sitemap.xml.eex new file mode 100644 index 000000000000..0c5334f21aa4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/robots/sitemap.xml.eex @@ -0,0 +1,53 @@ + +<% host = APIDocsView.blockscout_url(true) %> +<% date = to_string(Date.utc_today()) %> +<% non_parameterized_urls = ["/", "/txs", "/blocks", "/accounts", "/verified-contracts", "/tokens", "/apps", "/stats", "/api-docs", "/graphiql", "/search-results", "/withdrawals", "/l2-deposits", "/l2-output-roots", "/l2-txn-batches", "/l2-withdrawals"] %> +<% params = [paging_options: %PagingOptions{page_size: limit()}] %> + + <%= for url <- non_parameterized_urls do %> + + <%= host %><%= url %> + <%= date %> + + <% end %> + + <% addresses = Chain.list_top_addresses(params) %> + <%= for {address, _} <- addresses do %> + + <%= host %>/address/<%= to_string(address) %> + <%= date %> + + <% end %> + + <% txs = Chain.recent_transactions(params, [:validated]) %> + <%= for tx <- txs do %> + + <%= host %>/tx/<%= to_string(tx.hash) %> + <%= date %> + + <% end %> + + <% blocks = Chain.list_blocks(params) %> + <%= for block <- blocks do %> + + <%= host %>/block/<%= to_string(block.number) %> + <%= date %> + + <% end %> + + <% tokens = Chain.list_top_tokens(nil, params) %> + <%= for token <- tokens do %> + + <%= host %>/token/<%= to_string(token.contract_address_hash) %> + <%= date %> + + <% end %> + + <% smart_contracts_hashes = Chain.verified_contracts_top(limit()) %> + <%= for hash <- smart_contracts_hashes do %> + + <%= host %>/address/<%= Address.checksum(hash) %>?tab=contract + <%= date %> + + <% end %> + \ 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/helper.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex index b0e25f978d02..9b82e01ede77 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex @@ -104,19 +104,15 @@ defmodule BlockScoutWeb.API.V2.Helper do def is_verified(%Address{smart_contract: %NotLoaded{}}), do: nil def is_verified(%Address{smart_contract: _}), do: true - def market_cap(:standard, %{available_supply: available_supply, usd_value: usd_value}) + def market_cap(:standard, %{available_supply: available_supply, usd_value: usd_value, market_cap_usd: market_cap_usd}) when is_nil(available_supply) or is_nil(usd_value) do - Decimal.new(0) + max(Decimal.new(0), market_cap_usd) end def market_cap(:standard, %{available_supply: available_supply, usd_value: usd_value}) do Decimal.mult(available_supply, usd_value) end - def market_cap(:standard, exchange_rate) do - exchange_rate.market_cap_usd - end - def market_cap(module, exchange_rate) do module.market_cap(exchange_rate) end 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/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex index 6a35a0c6fbd3..1c102bf0cc11 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex @@ -1,6 +1,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do use BlockScoutWeb, :view + import Explorer.SmartContract.Reader, only: [zip_tuple_values_with_types: 2] + alias ABI.FunctionSelector alias BlockScoutWeb.API.V2.{Helper, TransactionView} alias BlockScoutWeb.SmartContractView @@ -284,28 +286,31 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do %{"type" => type, "value" => render_json(value, type)} end - def render_json(value, type) when type in [:address, "address", "address payable"] do - SmartContractView.cast_address(value) - end - - def render_json(value, type) when type in [:string, "string"] do - to_string(value) - end - def render_json(value, type) when is_tuple(value) do value - |> SmartContractView.zip_tuple_values_with_types(type) + |> zip_tuple_values_with_types(type) |> Enum.map(fn {type, value} -> render_json(value, type) end) end def render_json(value, type) when is_list(value) do + type = + if String.ends_with?(type, "[]") do + String.slice(type, 0..-3) + else + type + end + value |> Enum.map(&render_json(&1, type)) end - def render_json(value, _type) when is_binary(value) do - SmartContractView.binary_to_utf_string(value) + def render_json(value, type) when type in [:address, "address", "address payable"] do + SmartContractView.cast_address(value) + end + + def render_json(value, type) when type in [:string, "string"] do + to_string(value) end def render_json(value, _type) 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/views/robots_view.ex b/apps/block_scout_web/lib/block_scout_web/views/robots_view.ex new file mode 100644 index 000000000000..20e4cca0596f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/robots_view.ex @@ -0,0 +1,10 @@ +defmodule BlockScoutWeb.RobotsView do + use BlockScoutWeb, :view + + alias BlockScoutWeb.APIDocsView + alias Explorer.{Chain, PagingOptions} + alias Explorer.Chain.Address + + @limit 200 + defp limit, do: @limit +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex index 4a96a93adb6c..f0245adc2dfc 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex @@ -1,6 +1,8 @@ defmodule BlockScoutWeb.SmartContractView do use BlockScoutWeb, :view + import Explorer.SmartContract.Reader, only: [zip_tuple_values_with_types: 2] + alias Explorer.Chain alias Explorer.Chain.{Address, Transaction} alias Explorer.Chain.Hash.Address, as: HashAddress @@ -72,7 +74,7 @@ defmodule BlockScoutWeb.SmartContractView do String.starts_with?(type, "bytes") -> values = value - |> Enum.map_join(", ", &binary_to_utf_string(&1)) + |> Enum.join(", ") render_array_type_value(type, values, fetch_name(names, index)) @@ -107,6 +109,9 @@ defmodule BlockScoutWeb.SmartContractView do def values_with_type(value, string, names, index, _components) when string in ["string", :string], do: render_type_value("string", Helper.sanitize_input(value), fetch_name(names, index)) + def values_with_type(value, "bytes" <> _ = bytes_type, names, index, _components), + do: render_type_value(bytes_type, Helper.sanitize_input(value), fetch_name(names, index)) + def values_with_type(value, bytes, names, index, _components) when bytes in [:bytes], do: render_type_value("bytes", Helper.sanitize_input(value), fetch_name(names, index)) @@ -114,7 +119,7 @@ defmodule BlockScoutWeb.SmartContractView do do: render_type_value("bool", Helper.sanitize_input(to_string(value)), fetch_name(names, index)) def values_with_type(value, type, names, index, _components), - do: render_type_value(type, Helper.sanitize_input(binary_to_utf_string(value)), fetch_name(names, index)) + do: render_type_value(type, Helper.sanitize_input(value), fetch_name(names, index)) def values_with_type(value, :error, _components), do: render_type_value("error", Helper.sanitize_input(value), "error") @@ -158,78 +163,6 @@ defmodule BlockScoutWeb.SmartContractView do end) end - def zip_tuple_values_with_types(value, type) do - types_string = - type - |> String.slice(6..-2) - - types = - if String.trim(types_string) == "" do - [] - else - types_string - |> String.split(",") - end - - {tuple_types, _} = - types - |> Enum.reduce({[], nil}, fn val, acc -> - {arr, to_merge} = acc - - if to_merge do - compose_array_if_to_merge(arr, val, to_merge) - else - compose_array_else(arr, val, to_merge) - end - end) - - values_list = - value - |> Tuple.to_list() - - Enum.zip(tuple_types, values_list) - end - - def compose_array_if_to_merge(arr, val, to_merge) do - if count_string_symbols(val)["]"] > count_string_symbols(val)["["] do - updated_arr = update_last_list_item(arr, val) - {updated_arr, !to_merge} - else - updated_arr = update_last_list_item(arr, val) - {updated_arr, to_merge} - end - end - - def compose_array_else(arr, val, to_merge) do - if count_string_symbols(val)["["] > count_string_symbols(val)["]"] do - # credo:disable-for-next-line - {arr ++ [val], !to_merge} - else - # credo:disable-for-next-line - {arr ++ [val], to_merge} - end - end - - defp update_last_list_item(arr, new_val) do - arr - |> Enum.with_index() - |> Enum.map(fn {item, index} -> - if index == Enum.count(arr) - 1 do - item <> "," <> new_val - else - item - end - end) - end - - defp count_string_symbols(str) do - str - |> String.graphemes() - |> Enum.reduce(%{"[" => 0, "]" => 0}, fn char, acc -> - Map.update(acc, char, 1, &(&1 + 1)) - end) - end - def binary_to_utf_string(item) do case Integer.parse(to_string(item)) do {item_integer, ""} -> 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 a101102aaa40..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}, 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..ee6f156fa525 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") @@ -1534,21 +1535,29 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do ctbs_erc_20 = for _ <- 0..50 do - insert(:address_current_token_balance_with_token_id, address: address, token_type: "ERC-20", token_id: nil) + insert(:address_current_token_balance_with_token_id_and_fixed_token_type, + address: address, + token_type: "ERC-20", + token_id: nil + ) |> Repo.preload([:token]) end - |> Enum.sort_by(fn x -> x.value end, :asc) + |> Enum.sort_by(fn x -> Decimal.to_float(Decimal.mult(x.value, x.token.fiat_value)) end, :asc) ctbs_erc_721 = for _ <- 0..50 do - insert(:address_current_token_balance_with_token_id, address: address, token_type: "ERC-721", token_id: nil) + insert(:address_current_token_balance_with_token_id_and_fixed_token_type, + address: address, + token_type: "ERC-721", + token_id: nil + ) |> Repo.preload([:token]) end |> Enum.sort_by(fn x -> x.value end, :asc) ctbs_erc_1155 = for _ <- 0..50 do - insert(:address_current_token_balance_with_token_id, + insert(:address_current_token_balance_with_token_id_and_fixed_token_type, address: address, token_type: "ERC-1155", token_id: Enum.random(1..100_000) 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..6799c10396db 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 @@ -702,6 +702,148 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "method_id" => Base.encode16(id, case: :lower) } in response end + + test "get correct bytes value 1", %{conn: conn} do + abi = [ + %{ + "inputs" => [], + "name" => "all_messages_hash", + "outputs" => [ + %{ + "internalType" => "bytes32", + "name" => "", + "type" => "bytes32" + } + ], + "stateMutability" => "view", + "type" => "function" + } + ] + + id_1 = + abi + |> ABI.parse_specification() + |> Enum.at(0) + |> Map.fetch!(:method_id) + + target_contract = insert(:smart_contract, abi: abi) + address_hash_string = to_string(target_contract.address_hash) + + EthereumJSONRPC.Mox + |> expect( + :json_rpc, + fn [ + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0x1dd69d06", to: ^address_hash_string}, + "latest" + ] + } + ], + _opts -> + {:ok, + [ + %{ + id: id, + jsonrpc: "2.0", + result: "0x0000000000000000000000000000000000000000000000000000000000000000" + } + ]} + end + ) + + request = get(conn, "/api/v2/smart-contracts/#{target_contract.address_hash}/methods-read") + assert response = json_response(request, 200) + + assert %{ + "inputs" => [], + "name" => "all_messages_hash", + "outputs" => [ + %{ + "value" => "0x0000000000000000000000000000000000000000000000000000000000000000", + "type" => "bytes32" + } + ], + "stateMutability" => "view", + "type" => "function", + "method_id" => Base.encode16(id_1, case: :lower), + "names" => ["bytes32"] + } in response + end + + test "get correct bytes value 2", %{conn: conn} do + abi = [ + %{ + "inputs" => [], + "name" => "FRAUD_STRING", + "outputs" => [ + %{ + "internalType" => "bytes", + "name" => "", + "type" => "bytes" + } + ], + "stateMutability" => "view", + "type" => "function" + } + ] + + id_2 = + abi + |> ABI.parse_specification() + |> Enum.at(0) + |> Map.fetch!(:method_id) + + target_contract = insert(:smart_contract, abi: abi) + address_hash_string = to_string(target_contract.address_hash) + + EthereumJSONRPC.Mox + |> expect( + :json_rpc, + fn [ + %{ + id: id, + method: "eth_call", + params: [ + %{data: "0x46b2eb9b", to: ^address_hash_string}, + "latest" + ] + } + ], + _opts -> + {:ok, + [ + %{ + id: id, + jsonrpc: "2.0", + result: + "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000322d2d5468697320697320612062616420737472696e672e204e6f626f64792073617973207468697320737472696e672e2d2d0000000000000000000000000000" + } + ]} + end + ) + + request = get(conn, "/api/v2/smart-contracts/#{target_contract.address_hash}/methods-read") + assert response = json_response(request, 200) + + assert %{ + "inputs" => [], + "name" => "FRAUD_STRING", + "outputs" => [ + %{ + "value" => + "0x2d2d5468697320697320612062616420737472696e672e204e6f626f64792073617973207468697320737472696e672e2d2d", + "type" => "bytes" + } + ], + "stateMutability" => "view", + "type" => "function", + "method_id" => Base.encode16(id_2, case: :lower), + "names" => ["bytes"] + } in response + end end describe "/smart-contracts/{address_hash}/query-read-method" do @@ -1205,6 +1347,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert [ %{ + "method_id" => "49ba1b49", "type" => "function", "stateMutability" => "nonpayable", "outputs" => [], @@ -1271,7 +1414,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 +2056,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/block_scout_web/test/block_scout_web/views/smart_contract_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/smart_contract_view_test.exs index 7ba53f18ced2..cb63435730b2 100644 --- a/apps/block_scout_web/test/block_scout_web/views/smart_contract_view_test.exs +++ b/apps/block_scout_web/test/block_scout_web/views/smart_contract_view_test.exs @@ -8,47 +8,32 @@ defmodule BlockScoutWeb.SmartContractViewTest do describe "values_with_type/1" do test "complex data type case" do value = - {<<156, 209, 70, 119, 249, 170, 85, 105, 179, 187, 179, 81, 252, 214, 125, 17, 21, 170, 86, 58, 225, 98, 66, - 118, 211, 212, 230, 127, 179, 214, 249, 38>>, 23_183_417, true, + {"0x9cd14677f9aa5569b3bbb351fcd67d1115aa563ae1624276d3d4e67fb3d6f926", 23_183_417, true, [ {<<164, 118, 64, 69, 133, 31, 23, 170, 96, 182, 200, 232, 182, 32, 114, 190, 169, 83, 133, 33>>, [ - <<15, 103, 152, 165, 96, 121, 58, 84, 195, 188, 254, 134, 169, 60, 222, 30, 115, 8, 125, 148, 76, 14, 162, - 5, 68, 19, 125, 65, 33, 57, 104, 133>>, - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 61, 111, 131, 12, 226, 99, 202, 233, 135, 25, 57, 130, 25, 44, - 217, 144, 68, 43, 83>> - ], - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 178, 96, 212, 241, 78, 0, 0>>}, + "0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885", + "0x000000000000000000000000bf3d6f830ce263cae987193982192cd990442b53" + ], "0x000000000000000000000000000000000000000000000000aab260d4f14e0000"}, {<<164, 118, 64, 69, 133, 31, 23, 170, 96, 182, 200, 232, 182, 32, 114, 190, 169, 83, 133, 33>>, [ - <<221, 242, 82, 173, 27, 226, 200, 155, 105, 194, 176, 104, 252, 55, 141, 170, 149, 43, 167, 241, 99, 196, - 161, 22, 40, 245, 90, 77, 245, 35, 179, 239>>, - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>, - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 61, 111, 131, 12, 226, 99, 202, 233, 135, 25, 57, 130, 25, 44, - 217, 144, 68, 43, 83>> - ], - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 178, 96, 212, 241, 78, 0, 0>>}, + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000bf3d6f830ce263cae987193982192cd990442b53" + ], "0x000000000000000000000000000000000000000000000000aab260d4f14e0000"}, {<<166, 139, 214, 89, 169, 22, 127, 61, 60, 1, 186, 151, 118, 161, 32, 141, 174, 143, 0, 59>>, [ - <<47, 154, 96, 152, 212, 80, 58, 18, 119, 121, 186, 151, 95, 95, 107, 4, 248, 66, 54, 43, 24, 9, 243, 70, - 152, 158, 154, 188, 11, 77, 237, 182>>, - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 61, 111, 131, 12, 226, 99, 202, 233, 135, 25, 57, 130, 25, 44, - 217, 144, 68, 43, 83>>, - <<0, 5, 0, 0, 36, 155, 252, 47, 60, 200, 214, 143, 107, 107, 247, 35, 14, 160, 168, 237, 133, 61, 231, 49, - 0, 0, 0, 0, 0, 0, 2, 79>> - ], - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 178, 96, 212, 241, 78, 0, 0>>}, + "0x2f9a6098d4503a127779ba975f5f6b04f842362b1809f346989e9abc0b4dedb6", + "0x000000000000000000000000bf3d6f830ce263cae987193982192cd990442b53", + "0x00050000249bfc2f3cc8d68f6b6bf7230ea0a8ed853de731000000000000024f" + ], "0x000000000000000000000000000000000000000000000000aab260d4f14e0000"}, {<<254, 68, 107, 239, 29, 191, 122, 254, 36, 232, 30, 5, 188, 139, 39, 28, 27, 169, 165, 96>>, [ - <<39, 51, 62, 219, 139, 220, 212, 10, 10, 233, 68, 251, 18, 27, 94, 45, 98, 234, 120, 38, 131, 148, 102, - 84, 160, 245, 230, 7, 169, 8, 213, 120>>, - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 95, 197, 45, 138, 86, 59, 47, 24, 28, 106, 82, 125, 66, 46, 21, - 146, 201, 236, 250>>, - <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 139, 214, 89, 169, 22, 127, 61, 60, 1, 186, 151, 118, 161, 32, - 141, 174, 143, 0, 59>>, - <<0, 5, 0, 0, 36, 155, 252, 47, 60, 200, 214, 143, 107, 107, 247, 35, 14, 160, 168, 237, 133, 61, 231, 49, - 0, 0, 0, 0, 0, 0, 2, 79>> - ], <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1>>} + "0x27333edb8bdcd40a0ae944fb121b5e2d62ea782683946654a0f5e607a908d578", + "0x0000000000000000000000002a5fc52d8a563b2f181c6a527d422e1592c9ecfa", + "0x000000000000000000000000a68bd659a9167f3d3c01ba9776a1208dae8f003b", + "0x00050000249bfc2f3cc8d68f6b6bf7230ea0a8ed853de731000000000000024f" + ], "0x0000000000000000000000000000000000000000000000000000000000000001"} ]} type = "tuple[bytes32,uint256,bool,tuple[address,bytes32[],bytes][]]" 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/encoder.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex index 221c082b0218..37573679aa51 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex @@ -69,7 +69,7 @@ defmodule EthereumJSONRPC.Encoder do end end - def decode_result(result, selectors, _leave_error_as_map) when is_list(selectors) do + def decode_result(%{id: id, result: _result} = result, selectors, _leave_error_as_map) when is_list(selectors) do selectors |> Enum.map(fn selector -> try do @@ -78,7 +78,7 @@ defmodule EthereumJSONRPC.Encoder do _ -> :error end end) - |> Enum.find(fn decode -> + |> Enum.find({id, {:error, :unable_to_decode}}, fn decode -> case decode do {_id, {:ok, _}} -> true _ -> false diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs index b6caba1eda1a..83c294152620 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/explorer/lib/explorer/account/notifier/forbidden_address.ex b/apps/explorer/lib/explorer/account/notifier/forbidden_address.ex index 26d5e3836cbe..6022c87fcc7d 100644 --- a/apps/explorer/lib/explorer/account/notifier/forbidden_address.ex +++ b/apps/explorer/lib/explorer/account/notifier/forbidden_address.ex @@ -3,8 +3,10 @@ defmodule Explorer.Account.Notifier.ForbiddenAddress do Check if address is forbidden to notify """ + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + @blacklist [ - "0x0000000000000000000000000000000000000000", + burn_address_hash_string(), "0x000000000000000000000000000000000000dEaD" ] diff --git a/apps/explorer/lib/explorer/account/notifier/summary.ex b/apps/explorer/lib/explorer/account/notifier/summary.ex index 6e7833c904ad..db39423bb48c 100644 --- a/apps/explorer/lib/explorer/account/notifier/summary.ex +++ b/apps/explorer/lib/explorer/account/notifier/summary.ex @@ -3,6 +3,8 @@ defmodule Explorer.Account.Notifier.Summary do Compose a summary from transactions """ + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + alias Explorer alias Explorer.Account.Notifier.Summary alias Explorer.{Chain, Repo} @@ -154,10 +156,8 @@ defmodule Explorer.Account.Notifier.Summary do def fetch_summary(_, _), do: :nothing - @burn_address "0x0000000000000000000000000000000000000000" - def method(%{from_address_hash: from, to_address_hash: to}) do - {:ok, burn_address} = format_address(@burn_address) + {:ok, burn_address} = format_address(burn_address_hash_string()) cond do burn_address == from -> "mint" diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index dd4200d0f0c0..5b4d96d9b940 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -26,6 +26,7 @@ defmodule Explorer.Chain do ] import EthereumJSONRPC, only: [integer_to_quantity: 1, fetch_block_internal_transactions: 2] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] require Logger @@ -55,6 +56,7 @@ defmodule Explorer.Chain do InternalTransaction, Log, PendingBlockOperation, + Search, SmartContract, SmartContractAdditionalSource, Token, @@ -81,24 +83,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 +114,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 " @@ -132,8 +123,6 @@ defmodule Explorer.Chain do # keccak256("Error(string)") @revert_error_method_id "08c379a0" - @burn_address_hash_str "0x0000000000000000000000000000000000000000" - @limit_showing_transactions 10_000 @default_page_size 50 @@ -178,56 +167,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 +266,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 +948,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 +1346,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 +1465,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 +1488,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 +2092,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 +2156,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 +2172,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 +2188,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 +3795,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 +4257,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 @@ -4991,6 +4445,11 @@ defmodule Explorer.Chain do ) :: {:ok, accumulator} when accumulator: term() def stream_token_instances_with_error(initial, reducer, limited? \\ false) when is_function(reducer, 2) do + # likely to get valid metadata + high_priority = ["request error: 429", ":checkout_timeout", ":econnrefused", ":timeout"] + # almost impossible to get valid metadata + negative_priority = ["VM execution error", "no uri", "invalid json"] + Instance |> where([instance], not is_nil(instance.error)) |> select([instance], %{ @@ -4998,6 +4457,7 @@ defmodule Explorer.Chain do token_id: instance.token_id, updated_at: instance.updated_at }) + |> order_by([instance], desc: instance.error in ^high_priority, asc: instance.error in ^negative_priority) |> add_fetcher_limit(limited?) |> Repo.stream_reduce(initial, reducer) end @@ -5235,6 +4695,9 @@ defmodule Explorer.Chain do end end + @doc """ + Expects map of change params. Inserts using on_conflict: :replace_all + """ @spec upsert_token_instance(map()) :: {:ok, Instance.t()} | {:error, Ecto.Changeset.t()} def upsert_token_instance(params) do changeset = Instance.changeset(%Instance{}, params) @@ -5245,6 +4708,14 @@ defmodule Explorer.Chain do ) end + @doc """ + Inserts list of token instances via upsert_token_instance/1. + """ + @spec upsert_token_instances_list([map()]) :: list() + def upsert_token_instances_list(instances) do + Enum.map(instances, &upsert_token_instance/1) + end + @doc """ Update a new `t:Token.t/0` record. @@ -5295,6 +4766,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 +5056,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) @@ -6326,7 +5804,7 @@ defmodule Explorer.Chain do @spec get_token_transfer_type(TokenTransfer.t()) :: :token_burning | :token_minting | :token_spawning | :token_transfer def get_token_transfer_type(transfer) do - {:ok, burn_address_hash} = Chain.string_to_address_hash(@burn_address_hash_str) + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) cond do transfer.to_address_hash == burn_address_hash && transfer.from_address_hash !== burn_address_hash -> @@ -6650,55 +6128,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 -> @@ -6891,4 +6320,18 @@ defmodule Explorer.Chain do } ) end + + @spec verified_contracts_top(non_neg_integer()) :: [Hash.Address.t()] + def verified_contracts_top(limit) do + query = + from(contract in SmartContract, + inner_join: address in Address, + on: contract.address_hash == address.hash, + order_by: [desc: address.transactions_count], + limit: ^limit, + select: contract.address_hash + ) + + Repo.all(query) + end end 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..50af3af4dd5d 100644 --- a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex @@ -10,6 +10,7 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do import Ecto.Changeset import Ecto.Query, only: [from: 2, limit: 2, offset: 2, order_by: 3, preload: 2, dynamic: 2] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] alias Explorer.{Chain, PagingOptions, Repo} alias Explorer.Chain.{Address, Block, Hash, Token} @@ -74,11 +75,9 @@ 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 - {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash @doc """ @@ -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. """ @@ -170,10 +188,10 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do ctb in __MODULE__, where: ctb.address_hash == ^address_hash, where: ctb.value > 0, - where: ctb.token_type == ^type, left_join: t in assoc(ctb, :token), on: ctb.token_contract_address_hash == t.contract_address_hash, preload: [token: t], + where: t.type == ^type, select: ctb, select_merge: ^%{fiat_value: fiat_balance}, order_by: ^[desc_nulls_last: fiat_balance], diff --git a/apps/explorer/lib/explorer/chain/address/token_balance.ex b/apps/explorer/lib/explorer/chain/address/token_balance.ex index bdf7d42293b9..ab7a75b62486 100644 --- a/apps/explorer/lib/explorer/chain/address/token_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/token_balance.ex @@ -9,6 +9,8 @@ defmodule Explorer.Chain.Address.TokenBalance do use Explorer.Schema + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + alias Explorer.Chain alias Explorer.Chain.Address.TokenBalance alias Explorer.Chain.{Address, Block, Hash, Token} @@ -65,12 +67,10 @@ 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 - {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash @doc """ diff --git a/apps/explorer/lib/explorer/chain/block/reward.ex b/apps/explorer/lib/explorer/chain/block/reward.ex index b57af7a8cfa1..fd6296a550ea 100644 --- a/apps/explorer/lib/explorer/chain/block/reward.ex +++ b/apps/explorer/lib/explorer/chain/block/reward.ex @@ -5,6 +5,8 @@ defmodule Explorer.Chain.Block.Reward do use Explorer.Schema + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + alias Explorer.Application.Constants alias Explorer.{Chain, PagingOptions} alias Explorer.Chain.Block.Reward.AddressType @@ -34,8 +36,6 @@ defmodule Explorer.Chain.Block.Reward do "constant" => true } - @empty_address "0x0000000000000000000000000000000000000000" - @typedoc """ The validation reward given related to a block. @@ -218,7 +218,7 @@ defmodule Explorer.Chain.Block.Reward do payout_key_hash = call_contract(keys_manager_contract_address, @get_payout_by_mining_abi, get_payout_by_mining_params) - if payout_key_hash == @empty_address do + if payout_key_hash == burn_address_hash_string() do mining_key else choose_key(payout_key_hash, mining_key) @@ -248,7 +248,7 @@ defmodule Explorer.Chain.Block.Reward do case Reader.query_contract(address, abi, params, false) do %{^method_id => {:ok, [result]}} -> result - _ -> @empty_address + _ -> burn_address_hash_string() end end 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..adc1106be2c3 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} @@ -540,8 +541,10 @@ defmodule Explorer.Chain.Import.Runner.Blocks do address_hash: new_current_token_balance.address_hash, token_contract_address_hash: new_current_token_balance.token_contract_address_hash, token_id: new_current_token_balance.token_id, + token_type: tb.token_type, block_number: new_current_token_balance.block_number, value: tb.value, + value_fetched_at: tb.value_fetched_at, inserted_at: over(min(tb.inserted_at), :w), updated_at: over(max(tb.updated_at), :w) }, 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..adb743d34773 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -21,8 +21,26 @@ defmodule Explorer.Chain.SmartContract do alias Explorer.SmartContract.Reader alias Timex.Duration - @burn_address_hash_str "0x0000000000000000000000000000000000000000" - @burn_address_hash_str_32 "0x0000000000000000000000000000000000000000000000000000000000000000" + # supported signatures: + # 5c60da1b = keccak256(implementation()) + @implementation_signature "5c60da1b" + # aaf10f42 = keccak256(getImplementation()) + @get_implementation_signature "aaf10f42" + + @burn_address_hash_string "0x0000000000000000000000000000000000000000" + @burn_address_hash_string_32 "0x0000000000000000000000000000000000000000000000000000000000000000" + + defguard is_burn_signature(term) when term in ["0x", "0x0", @burn_address_hash_string_32] + defguard is_burn_signature_or_nil(term) when is_burn_signature(term) or term == nil + defguard is_burn_signature_extended(term) when is_burn_signature(term) or term == @burn_address_hash_string + + @doc """ + Returns burn address hash + """ + @spec burn_address_hash_string() :: String.t() + def burn_address_hash_string do + @burn_address_hash_string + end @typep api? :: {:api?, true | false} @@ -424,30 +442,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: error_message_with_log("Unable to verify: unknown error.") - 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(:deployed_bytecode), + do: error_message_with_log("Deployed bytecode does not correspond to contract creation code.") - defp error_message(string) when is_binary(string), do: string + 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 @@ -676,10 +717,10 @@ defmodule Explorer.Chain.SmartContract do implementation_address = cond do implementation_method_abi -> - get_implementation_address_hash_basic("5c60da1b", proxy_address_hash, abi) + get_implementation_address_hash_basic(@implementation_signature, proxy_address_hash, abi) get_implementation_method_abi -> - get_implementation_address_hash_basic("aaf10f42", proxy_address_hash, abi) + get_implementation_address_hash_basic(@get_implementation_signature, proxy_address_hash, abi) master_copy_method_abi -> get_implementation_address_hash_from_master_copy_pattern(proxy_address_hash) @@ -709,7 +750,7 @@ defmodule Explorer.Chain.SmartContract do json_rpc_named_arguments ) do {:ok, empty_address} - when empty_address in ["0x", "0x0", @burn_address_hash_str_32, nil] -> + when is_burn_signature_or_nil(empty_address) -> fetch_beacon_proxy_implementation(proxy_address_hash, json_rpc_named_arguments) {:ok, implementation_logic_address} -> @@ -745,13 +786,13 @@ defmodule Explorer.Chain.SmartContract do json_rpc_named_arguments ) do {:ok, empty_address} - when empty_address in ["0x", "0x0", @burn_address_hash_str_32, nil] -> + when is_burn_signature_or_nil(empty_address) -> fetch_openzeppelin_proxy_implementation(proxy_address_hash, json_rpc_named_arguments) {:ok, beacon_contract_address} -> case beacon_contract_address |> abi_decode_address_output() - |> get_implementation_address_hash_basic("5c60da1b", implementation_method_abi) do + |> get_implementation_address_hash_basic(@implementation_signature, implementation_method_abi) do <> -> {:ok, implementation_address} @@ -776,7 +817,7 @@ defmodule Explorer.Chain.SmartContract do json_rpc_named_arguments ) do {:ok, empty_address} - when empty_address in ["0x", "0x0", @burn_address_hash_str_32] -> + when is_burn_signature(empty_address) -> {:ok, "0x"} {:ok, logic_contract_address} -> @@ -788,9 +829,6 @@ defmodule Explorer.Chain.SmartContract do end defp get_implementation_address_hash_basic(signature, proxy_address_hash, abi) do - # supported signatures: - # 5c60da1b = keccak256(implementation()) - # aaf10f42 = keccak256(getImplementation()) implementation_address = case Reader.query_contract( proxy_address_hash, @@ -820,7 +858,7 @@ defmodule Explorer.Chain.SmartContract do json_rpc_named_arguments ) do {:ok, empty_address} - when empty_address in ["0x", "0x0", @burn_address_hash_str_32] -> + when is_burn_signature(empty_address) -> {:ok, "0x"} {:ok, logic_contract_address} -> @@ -836,12 +874,7 @@ defmodule Explorer.Chain.SmartContract do defp save_implementation_data(nil, _, _, _), do: {nil, nil} defp save_implementation_data(empty_address_hash_string, proxy_address_hash, metadata_from_verified_twin, options) - when empty_address_hash_string in [ - "0x", - "0x0", - @burn_address_hash_str_32, - @burn_address_hash_str - ] do + when is_burn_signature_extended(empty_address_hash_string) do if is_nil(metadata_from_verified_twin) or !metadata_from_verified_twin do proxy_address_hash |> Chain.address_hash_to_smart_contract_without_twin(options) @@ -909,7 +942,7 @@ defmodule Explorer.Chain.SmartContract do defp abi_decode_address_output(nil), do: nil - defp abi_decode_address_output("0x"), do: @burn_address_hash_str + defp abi_decode_address_output("0x"), do: burn_address_hash_string() defp abi_decode_address_output(address) when is_binary(address) do if String.length(address) > 42 do 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/etherscan.ex b/apps/explorer/lib/explorer/etherscan.ex index 604bfae66fc2..4663c3c9b9db 100644 --- a/apps/explorer/lib/explorer/etherscan.ex +++ b/apps/explorer/lib/explorer/etherscan.ex @@ -4,6 +4,7 @@ defmodule Explorer.Etherscan do """ import Ecto.Query, only: [from: 2, where: 3, or_where: 3, union: 2, subquery: 1, order_by: 3] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] alias Explorer.Etherscan.Logs alias Explorer.{Chain, Repo} @@ -21,8 +22,6 @@ defmodule Explorer.Etherscan do end_timestamp: nil } - @burn_address_hash_str "0x0000000000000000000000000000000000000000" - @doc """ Returns the maximum allowed page size number. @@ -585,7 +584,7 @@ defmodule Explorer.Etherscan do @spec fetch_sum_coin_total_supply_minus_burnt() :: non_neg_integer def fetch_sum_coin_total_supply_minus_burnt do - {:ok, burn_address_hash} = Chain.string_to_address_hash(@burn_address_hash_str) + {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) query = from( diff --git a/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex b/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex index 4c726ccc5106..d9ce085dee93 100644 --- a/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex +++ b/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex @@ -152,6 +152,17 @@ defmodule Explorer.ExchangeRates.Source.CoinGecko do end end + @doc """ + Converts date time string into DateTime object formatted as date + """ + @spec date(String.t()) :: Date.t() + def date(date_time_string) do + with {:ok, datetime, _} <- DateTime.from_iso8601(date_time_string) do + datetime + |> DateTime.to_date() + end + end + defp api_key do config(:api_key) || nil end diff --git a/apps/explorer/lib/explorer/exchange_rates/source/coin_market_cap.ex b/apps/explorer/lib/explorer/exchange_rates/source/coin_market_cap.ex index fb1b5472a3fb..0ef28b6c395b 100644 --- a/apps/explorer/lib/explorer/exchange_rates/source/coin_market_cap.ex +++ b/apps/explorer/lib/explorer/exchange_rates/source/coin_market_cap.ex @@ -18,7 +18,7 @@ defmodule Explorer.ExchangeRates.Source.CoinMarketCap do last_updated = get_last_updated(token_properties) current_price = get_current_price(token_properties) - id = token_properties && token_properties["id"] + id = token_properties["id"] btc_value = if Application.get_env(:explorer, Explorer.ExchangeRates)[:fetch_btc_value], @@ -40,8 +40,8 @@ defmodule Explorer.ExchangeRates.Source.CoinMarketCap do id: id, last_updated: last_updated, market_cap_usd: to_decimal(market_cap_data_usd), - name: token_properties && token_properties["name"], - symbol: token_properties && String.upcase(token_properties["symbol"]), + name: token_properties["name"], + symbol: String.upcase(token_properties["symbol"]), usd_value: current_price, volume_24h_usd: to_decimal(total_volume_data_usd) } @@ -98,45 +98,54 @@ defmodule Explorer.ExchangeRates.Source.CoinMarketCap do config(:coin_id) end - defp get_token_properties(market_data) do - token_values_list = - market_data - |> Map.values() - - if Enum.count(token_values_list) > 0 do - token_values = token_values_list |> Enum.at(0) - - if Enum.count(token_values) > 0 do + @doc """ + Extracts token properties from CoinMarketCap coin endpoint response + """ + @spec get_token_properties(map()) :: map() + def get_token_properties(market_data) do + with token_values_list <- market_data |> Map.values(), + true <- Enum.count(token_values_list) > 0, + token_values <- token_values_list |> Enum.at(0), + true <- Enum.count(token_values) > 0 do + if is_list(token_values) do token_values |> Enum.at(0) else - %{} + token_values end else - %{} + _ -> %{} end end defp get_circulating_supply(token_properties) do - token_properties && token_properties["circulating_supply"] + token_properties["circulating_supply"] end defp get_total_supply(token_properties) do - token_properties && token_properties["total_supply"] + token_properties["total_supply"] end - defp get_market_cap_data_usd(token_properties) do - token_properties && token_properties["quote"] && + @doc """ + Extracts market cap in usd from token properties, which are returned in get_token_properties/1 + """ + @spec get_market_cap_data_usd(map()) :: String.t() + def get_market_cap_data_usd(token_properties) do + token_properties["quote"] && token_properties["quote"]["USD"] && token_properties["quote"]["USD"]["market_cap"] end defp get_total_volume_data_usd(token_properties) do - token_properties && token_properties["quote"] && + token_properties["quote"] && token_properties["quote"]["USD"] && token_properties["quote"]["USD"]["volume_24h"] end - defp get_last_updated(token_properties) do + @doc """ + Extracts last updated from token properties, which are returned in get_token_properties/1 + """ + @spec get_last_updated(map()) :: DateTime.t() + def get_last_updated(token_properties) do last_updated_data = token_properties && token_properties["last_updated"] if last_updated_data do @@ -147,8 +156,12 @@ defmodule Explorer.ExchangeRates.Source.CoinMarketCap do end end - defp get_current_price(token_properties) do - if token_properties && token_properties["quote"] && token_properties["quote"]["USD"] && + @doc """ + Extracts current price from token properties, which are returned in get_token_properties/1 + """ + @spec get_current_price(map()) :: String.t() | non_neg_integer() + def get_current_price(token_properties) do + if token_properties["quote"] && token_properties["quote"]["USD"] && token_properties["quote"]["USD"]["price"] do to_decimal(token_properties["quote"]["USD"]["price"]) else diff --git a/apps/explorer/lib/explorer/market/history/cataloger.ex b/apps/explorer/lib/explorer/market/history/cataloger.ex index 0672412d3311..a6bb33604bdd 100644 --- a/apps/explorer/lib/explorer/market/history/cataloger.ex +++ b/apps/explorer/lib/explorer/market/history/cataloger.ex @@ -8,26 +8,15 @@ defmodule Explorer.Market.History.Cataloger do source will follow exponential backoff `100ms * 2^(n+1)` where `n` is the number of failed requests. - ## Configuration - - The following example shows the configurable values in a sample config. - - config :explorer, Explorer.Market.History.Cataloger, - # fetch interval in milliseconds - history_fetch_interval: :timer.minutes(60), - # Base backoff in milliseconds for failed requests to history API - base_backoff: 100 - """ use GenServer require Logger + alias Explorer.History.Process, as: HistoryProcess alias Explorer.Market - @typep milliseconds :: non_neg_integer() - @price_failed_attempts 10 @market_cap_failed_attempts 3 @@ -108,6 +97,16 @@ defmodule Explorer.Market.History.Cataloger do GenServer.start_link(__MODULE__, :ok, name: __MODULE__) end + @spec config_or_default(atom(), term(), term()) :: term() + defp config_or_default(key, module, default) do + Application.get_env(:explorer, module)[key] || default + end + + @spec config_or_default(atom(), term()) :: term() + defp config_or_default(key, default) do + Application.get_env(:explorer, __MODULE__)[key] || default + end + defp market_cap_history(records, state) do Market.bulk_insert_history(records) @@ -118,30 +117,24 @@ defmodule Explorer.Market.History.Cataloger do {:noreply, state} end - @spec base_backoff :: milliseconds() - defp base_backoff do - config_or_default(:base_backoff, 100) - end - - @spec config_or_default(atom(), term()) :: term() - defp config_or_default(key, default) do - Application.get_env(:explorer, __MODULE__, [])[key] || default - end - @spec source_price() :: module() defp source_price do - config_or_default(:source, Explorer.Market.History.Source.Price.CryptoCompare) + config_or_default(:price_source, Explorer.ExchangeRates.Source, Explorer.Market.History.Source.Price.CryptoCompare) end @spec source_market_cap() :: module() defp source_market_cap do - config_or_default(:source_market_cap, Explorer.Market.History.Source.MarketCap.CoinGecko) + config_or_default( + :market_cap_source, + Explorer.ExchangeRates.Source, + Explorer.Market.History.Source.MarketCap.CoinGecko + ) end @spec fetch_price_history(non_neg_integer(), non_neg_integer()) :: Task.t() defp fetch_price_history(day_count, failed_attempts \\ 0) do Task.Supervisor.async_nolink(Explorer.MarketTaskSupervisor, fn -> - Process.sleep(delay(failed_attempts)) + Process.sleep(HistoryProcess.delay(failed_attempts)) if failed_attempts < @price_failed_attempts do {:price_history, {day_count, failed_attempts, source_price().fetch_price_history(day_count)}} @@ -154,7 +147,7 @@ defmodule Explorer.Market.History.Cataloger do @spec fetch_market_cap_history(non_neg_integer()) :: Task.t() defp fetch_market_cap_history(failed_attempts \\ 0) do Task.Supervisor.async_nolink(Explorer.MarketTaskSupervisor, fn -> - Process.sleep(delay(failed_attempts)) + Process.sleep(HistoryProcess.delay(failed_attempts)) if failed_attempts < @market_cap_failed_attempts do {:market_cap_history, {failed_attempts, source_market_cap().fetch_market_cap()}} @@ -186,14 +179,4 @@ defmodule Explorer.Market.History.Cataloger do price_records end end - - @spec delay(non_neg_integer()) :: milliseconds() - defp delay(0), do: 0 - defp delay(1), do: base_backoff() - - defp delay(failed_attempts) do - # Simulates 2^n - multiplier = Enum.reduce(2..failed_attempts, 1, fn _, acc -> 2 * acc end) - multiplier * base_backoff() - end end diff --git a/apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex b/apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex index 9875f0d0a663..fee23a9b9cd3 100644 --- a/apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex +++ b/apps/explorer/lib/explorer/market/history/source/market_cap/coin_gecko.ex @@ -37,14 +37,6 @@ defmodule Explorer.Market.History.Source.MarketCap.CoinGecko do end end - @spec date(String.t()) :: Date.t() - defp date(date_time_string) do - with {:ok, datetime, _} <- DateTime.from_iso8601(date_time_string) do - datetime - |> DateTime.to_date() - end - end - @spec format_data(term()) :: SourceMarketCap.record() | nil defp format_data(nil), do: nil @@ -54,7 +46,7 @@ defmodule Explorer.Market.History.Source.MarketCap.CoinGecko do %{ market_cap: Decimal.new(to_string(market_cap["usd"])), - date: date(data["last_updated"]) + date: ExchangeRatesSourceCoinGecko.date(data["last_updated"]) } end end diff --git a/apps/explorer/lib/explorer/market/history/source/market_cap/coin_market_cap.ex b/apps/explorer/lib/explorer/market/history/source/market_cap/coin_market_cap.ex new file mode 100644 index 000000000000..9a95ed1e73dd --- /dev/null +++ b/apps/explorer/lib/explorer/market/history/source/market_cap/coin_market_cap.ex @@ -0,0 +1,54 @@ +defmodule Explorer.Market.History.Source.MarketCap.CoinMarketCap do + @moduledoc """ + Adapter for fetching current market from CoinMarketCap. + """ + + alias Explorer.ExchangeRates.Source + alias Explorer.ExchangeRates.Source.CoinMarketCap, as: ExchangeRatesSourceCoinMarketCap + alias Explorer.Market.History.Source.MarketCap, as: SourceMarketCap + + import Source, only: [to_decimal: 1] + + @behaviour SourceMarketCap + + @impl SourceMarketCap + def fetch_market_cap do + url = ExchangeRatesSourceCoinMarketCap.source_url() + + if url do + case Source.http_request(url, ExchangeRatesSourceCoinMarketCap.headers()) do + {:ok, data} -> + result = + data + |> format_data() + + {:ok, result} + + _ -> + :error + end + else + :error + end + end + + @spec format_data(term()) :: SourceMarketCap.record() | nil + defp format_data(nil), do: nil + + defp format_data(%{"data" => _} = json_data) do + market_data = json_data["data"] + token_properties = ExchangeRatesSourceCoinMarketCap.get_token_properties(market_data) + + last_updated = + token_properties + |> ExchangeRatesSourceCoinMarketCap.get_last_updated() + |> DateTime.to_date() + + market_cap_data_usd = ExchangeRatesSourceCoinMarketCap.get_market_cap_data_usd(token_properties) + + %{ + market_cap: to_decimal(market_cap_data_usd), + date: last_updated + } + end +end diff --git a/apps/explorer/lib/explorer/market/history/source/price/coin_gecko.ex b/apps/explorer/lib/explorer/market/history/source/price/coin_gecko.ex new file mode 100644 index 000000000000..e4cf9dfa15a3 --- /dev/null +++ b/apps/explorer/lib/explorer/market/history/source/price/coin_gecko.ex @@ -0,0 +1,59 @@ +defmodule Explorer.Market.History.Source.Price.CoinGecko do + @moduledoc """ + Adapter for fetching current market from CoinGecko. + """ + + alias Explorer.ExchangeRates.Source + alias Explorer.ExchangeRates.Source.CoinGecko, as: ExchangeRatesSourceCoinGecko + alias Explorer.Market.History.Source.Price, as: SourcePrice + + @behaviour SourcePrice + + @impl SourcePrice + def fetch_price_history(_previous_days \\ nil) do + url = ExchangeRatesSourceCoinGecko.source_url() + + if url do + case Source.http_request(url, ExchangeRatesSourceCoinGecko.headers()) do + {:ok, data} -> + result = + data + |> format_data() + + {:ok, result} + + _ -> + :error + end + else + :error + end + end + + @spec format_data(term()) :: SourcePrice.record() | nil + defp format_data(nil), do: nil + + defp format_data(data) do + market_data = data["market_data"] + current_price = market_data["current_price"] + current_price_usd = Decimal.new(to_string(current_price["usd"])) + price_change_percentage_24h_in_currency = market_data["price_change_percentage_24h_in_currency"] + + delta_perc = Decimal.new(to_string(price_change_percentage_24h_in_currency["usd"])) + + delta = + current_price_usd + |> Decimal.mult(delta_perc) + |> Decimal.div(100) + + opening_price = Decimal.add(current_price_usd, delta) + + [ + %{ + closing_price: current_price_usd, + date: ExchangeRatesSourceCoinGecko.date(data["last_updated"]), + opening_price: opening_price + } + ] + end +end diff --git a/apps/explorer/lib/explorer/market/history/source/price/coin_market_cap.ex b/apps/explorer/lib/explorer/market/history/source/price/coin_market_cap.ex new file mode 100644 index 000000000000..0a8c4bf28dae --- /dev/null +++ b/apps/explorer/lib/explorer/market/history/source/price/coin_market_cap.ex @@ -0,0 +1,55 @@ +defmodule Explorer.Market.History.Source.Price.CoinMarketCap do + @moduledoc """ + Adapter for fetching current market from CoinMarketCap. + """ + + alias Explorer.ExchangeRates.Source + alias Explorer.ExchangeRates.Source.CoinMarketCap, as: ExchangeRatesSourceCoinMarketCap + alias Explorer.Market.History.Source.Price, as: SourcePrice + + @behaviour SourcePrice + + @impl SourcePrice + def fetch_price_history(_previous_days \\ nil) do + url = ExchangeRatesSourceCoinMarketCap.source_url() + + if url do + case Source.http_request(url, ExchangeRatesSourceCoinMarketCap.headers()) do + {:ok, data} -> + result = + data + |> format_data() + + {:ok, result} + + _ -> + :error + end + else + :error + end + end + + @spec format_data(term()) :: SourcePrice.record() | nil + defp format_data(nil), do: nil + + defp format_data(%{"data" => _} = json_data) do + market_data = json_data["data"] + token_properties = ExchangeRatesSourceCoinMarketCap.get_token_properties(market_data) + + last_updated = + token_properties + |> ExchangeRatesSourceCoinMarketCap.get_last_updated() + |> DateTime.to_date() + + current_price_usd = ExchangeRatesSourceCoinMarketCap.get_current_price(token_properties) + + [ + %{ + closing_price: current_price_usd, + date: last_updated, + opening_price: current_price_usd + } + ] + end +end diff --git a/apps/explorer/lib/explorer/smart_contract/reader.ex b/apps/explorer/lib/explorer/smart_contract/reader.ex index 4fa3e8dadf86..19b8a86af152 100644 --- a/apps/explorer/lib/explorer/smart_contract/reader.ex +++ b/apps/explorer/lib/explorer/smart_contract/reader.ex @@ -754,6 +754,27 @@ defmodule Explorer.SmartContract.Reader do Map.put_new(output, "value", Encoder.unescape(value)) end + defp new_value(%{"type" => "tuple" <> _types = type} = output, values, index) do + value = Enum.at(values, index) + + result = + if String.ends_with?(type, "[]") do + value + |> Enum.map(fn tuple -> new_value(%{"type" => String.slice(type, 0..-3)}, [tuple], 0) end) + |> flat_arrays_map() + else + value + |> zip_tuple_values_with_types(type) + |> Enum.map(fn {type, part_value} -> + new_value(%{"type" => type}, [part_value], 0) + end) + |> flat_arrays_map() + |> List.to_tuple() + end + + Map.put_new(output, "value", result) + end + defp new_value(output, [value], _index) do Map.put_new(output, "value", value) end @@ -762,6 +783,68 @@ defmodule Explorer.SmartContract.Reader do Map.put_new(output, "value", Enum.at(values, index)) end + defp flat_arrays_map(%{"value" => value}) do + flat_arrays_map(value) + end + + defp flat_arrays_map(value) when is_list(value) do + Enum.map(value, &flat_arrays_map/1) + end + + defp flat_arrays_map(value) when is_tuple(value) do + value + |> Tuple.to_list() + |> flat_arrays_map() + |> List.to_tuple() + end + + defp flat_arrays_map(value) do + value + end + + @spec zip_tuple_values_with_types(tuple, binary) :: [{binary, any}] + def zip_tuple_values_with_types(value, type) do + types_string = + type + |> String.slice(6..-2) + + types = + if String.trim(types_string) == "" do + [] + else + types_string + |> String.graphemes() + end + + tuple_types = + types + |> Enum.reduce( + {[""], 0}, + fn + ",", {types_acc, 0} -> + {["" | types_acc], 0} + + char, {[acc | types_acc], bracket_stack} -> + new_bracket_stack = + case char do + "[" -> bracket_stack + 1 + "]" -> bracket_stack - 1 + _ -> bracket_stack + end + + {[acc <> char | types_acc], new_bracket_stack} + end + ) + |> elem(0) + |> Enum.reverse() + + values_list = + value + |> Tuple.to_list() + + Enum.zip(tuple_types, values_list) + end + @spec bytes_to_string(<<_::_*8>>) :: String.t() defp bytes_to_string(value) do if value do 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/priv/repo/migrations/20230831122819_drop_current_token_balances_tokens_foreign_key.exs b/apps/explorer/priv/repo/migrations/20230831122819_drop_current_token_balances_tokens_foreign_key.exs new file mode 100644 index 000000000000..3befa9600c66 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20230831122819_drop_current_token_balances_tokens_foreign_key.exs @@ -0,0 +1,10 @@ +# cspell:ignore fkey +defmodule Explorer.Repo.Migrations.DropCurrentTokenBalancesTokensForeignKey do + use Ecto.Migration + + def change do + drop_if_exists( + constraint(:address_current_token_balances, :address_current_token_balances_token_contract_address_hash_fkey) + ) + end +end diff --git a/apps/explorer/priv/repo/migrations/20230905085809_drop_token_balances_tokens_foreign_key.exs b/apps/explorer/priv/repo/migrations/20230905085809_drop_token_balances_tokens_foreign_key.exs new file mode 100644 index 000000000000..265933ff4c23 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20230905085809_drop_token_balances_tokens_foreign_key.exs @@ -0,0 +1,8 @@ +# cspell:ignore fkey +defmodule Explorer.Repo.Migrations.DropTokenBalancesTokensForeignKey do + use Ecto.Migration + + def change do + drop_if_exists(constraint(:address_token_balances, :address_token_balances_token_contract_address_hash_fkey)) + 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/explorer/exchange_rates/source/coin_market_cap_test.exs b/apps/explorer/test/explorer/exchange_rates/source/coin_market_cap_test.exs index c9bfea7ad1f5..1d957f4e4aa0 100644 --- a/apps/explorer/test/explorer/exchange_rates/source/coin_market_cap_test.exs +++ b/apps/explorer/test/explorer/exchange_rates/source/coin_market_cap_test.exs @@ -34,4 +34,103 @@ defmodule Explorer.ExchangeRates.Source.CoinMarketCapTest do CoinMarketCap.source_url("ETH") end end + + @token_properties %{ + "circulating_supply" => 0, + "cmc_rank" => 2977, + "date_added" => "2021-12-06T11:25:31.000Z", + "id" => 15658, + "infinite_supply" => false, + "is_active" => 1, + "is_fiat" => 0, + "last_updated" => "2023-09-12T09:03:00.000Z", + "max_supply" => 210_240_000, + "name" => "Qitmeer Network", + "num_market_pairs" => 10, + "platform" => nil, + "quote" => %{ + "USD" => %{ + "fully_diluted_market_cap" => 27_390_222.61, + "last_updated" => "2023-09-12T09:03:00.000Z", + "market_cap" => 0, + "market_cap_dominance" => 0, + "percent_change_1h" => -0.14807635, + "percent_change_24h" => -4.05784287, + "percent_change_30d" => -20.18918329, + "percent_change_60d" => 85.21384726, + "percent_change_7d" => -5.49776979, + "percent_change_90d" => 22.27442093, + "price" => 0.13028073920022334, + "tvl" => nil, + "volume_24h" => 93766.09652096, + "volume_change_24h" => -0.9423 + } + }, + "self_reported_circulating_supply" => 71_348_557, + "self_reported_market_cap" => 9_295_342.74682927, + "slug" => "qitmeer-network", + "symbol" => "MEER", + "tags" => [], + "total_supply" => 71_348_557, + "tvl_ratio" => nil + } + + @market_data_multiple_tokens %{ + "MEER" => [ + @token_properties, + %{ + "circulating_supply" => nil, + "cmc_rank" => nil, + "date_added" => "2023-05-12T15:52:05.000Z", + "id" => 25240, + "infinite_supply" => false, + "is_active" => 0, + "is_fiat" => 0, + "last_updated" => "2023-09-12T09:05:15.725Z", + "max_supply" => 210_240_000, + "name" => "Meer Coin", + "num_market_pairs" => nil, + "platform" => nil, + "quote" => %{ + "USD" => %{ + "fully_diluted_market_cap" => nil, + "last_updated" => "2023-09-12T09:05:15.725Z", + "market_cap" => nil, + "market_cap_dominance" => nil, + "percent_change_1h" => nil, + "percent_change_24h" => nil, + "percent_change_30d" => nil, + "percent_change_60d" => nil, + "percent_change_7d" => nil, + "percent_change_90d" => nil, + "price" => 0, + "tvl" => nil, + "volume_24h" => nil, + "volume_change_24h" => nil + } + }, + "self_reported_circulating_supply" => nil, + "self_reported_market_cap" => nil, + "slug" => "meer-coin", + "symbol" => "MEER", + "tags" => [], + "total_supply" => nil, + "tvl_ratio" => nil + } + ] + } + + @market_data_single_token %{ + "15658" => @token_properties + } + + describe "get_token_properties/1" do + test "returns a single token property, when market_data contains multiple tokens" do + assert CoinMarketCap.get_token_properties(@market_data_multiple_tokens) == @token_properties + end + + test "returns a single token property, when market_data contains a single token" do + assert CoinMarketCap.get_token_properties(@market_data_single_token) == @token_properties + end + end end diff --git a/apps/explorer/test/explorer/market/history/cataloger_test.exs b/apps/explorer/test/explorer/market/history/cataloger_test.exs index 0acc04a7f24b..68f2aa9779cc 100644 --- a/apps/explorer/test/explorer/market/history/cataloger_test.exs +++ b/apps/explorer/test/explorer/market/history/cataloger_test.exs @@ -6,7 +6,9 @@ defmodule Explorer.Market.History.CatalogerTest do alias Explorer.Market.MarketHistory alias Explorer.Market.History.Cataloger alias Explorer.Market.History.Source.Price.TestSource + alias Explorer.Market.History.Source.Price.CryptoCompare alias Explorer.Repo + alias Plug.Conn setup do Application.put_env(:explorer, Cataloger, source: TestSource) @@ -19,6 +21,38 @@ defmodule Explorer.Market.History.CatalogerTest do end test "handle_info with `{:fetch_price_history, days}`" do + bypass = Bypass.open() + Application.put_env(:explorer, CryptoCompare, base_url: "http://localhost:#{bypass.port}") + + resp = """ + { + "Response": "Success", + "Type": 100, + "Aggregated": false, + "TimeTo": 1522569618, + "TimeFrom": 1522566018, + "FirstValueInArray": true, + "ConversionType": { + "type": "multiply", + "conversionSymbol": "ETH" + }, + "Data": [{ + "time": 1522566018, + "high": 10, + "low": 5, + "open": 5, + "volumefrom": 0, + "volumeto": 0, + "close": 10, + "conversionType": "multiply", + "conversionSymbol": "ETH" + }], + "RateLimit": {}, + "HasWarning": false + } + """ + + Bypass.expect(bypass, fn conn -> Conn.resp(conn, 200, resp) end) records = [%{date: ~D[2018-04-01], closing_price: Decimal.new(10), opening_price: Decimal.new(5)}] expect(TestSource, :fetch_price_history, fn 1 -> {:ok, records} end) set_mox_global() diff --git a/apps/explorer/test/explorer/token/instance_metadata_retriever_test.exs b/apps/explorer/test/explorer/token/instance_metadata_retriever_test.exs index e9ef52dc583e..a9255d04c796 100644 --- a/apps/explorer/test/explorer/token/instance_metadata_retriever_test.exs +++ b/apps/explorer/test/explorer/token/instance_metadata_retriever_test.exs @@ -1,8 +1,7 @@ -defmodule Explorer.Token.InstanceMetadataRetrieverTest do +defmodule Explorer.Token.MetadataRetrieverTest do use EthereumJSONRPC.Case - alias EthereumJSONRPC.Encoder - alias Explorer.Token.InstanceMetadataRetriever + alias Indexer.Fetcher.TokenInstance.MetadataRetriever alias Plug.Conn import Mox @@ -90,7 +89,7 @@ defmodule Explorer.Token.InstanceMetadataRetrieverTest do assert %{ "c87b56dd" => {:ok, ["https://vault.warriders.com/18290729947667102496.json"]} } == - InstanceMetadataRetriever.query_contract( + MetadataRetriever.query_contract( "0x5caebd3b32e210e85ce3e9d51638b9c445481567", %{ "c87b56dd" => [18_290_729_947_667_102_496] @@ -133,7 +132,7 @@ defmodule Explorer.Token.InstanceMetadataRetrieverTest do assert %{ "0e89341c" => {:ok, ["https://vault.warriders.com/18290729947667102496.json"]} } == - InstanceMetadataRetriever.query_contract( + MetadataRetriever.query_contract( "0x5caebd3b32e210e85ce3e9d51638b9c445481567", %{ "0e89341c" => [18_290_729_947_667_102_496] @@ -164,28 +163,7 @@ defmodule Explorer.Token.InstanceMetadataRetrieverTest do end) assert {:ok, %{metadata: %{"name" => "Sérgio Mendonça"}}} == - InstanceMetadataRetriever.fetch_json(%{ - "c87b56dd" => {:ok, ["http://localhost:#{bypass.port}#{path}"]} - }) - end - - test "fetches json metadata for kitties" do - Application.put_env(:explorer, :http_adapter, Explorer.Mox.HTTPoison) - - result = - "{\"id\":100500,\"name\":\"KittyBlue_2_Lemonade\",\"generation\":20,\"genes\":\"623509754227292470437941473598751240781530569131665917719736997423495595\",\"created_at\":\"2017-12-06T01:56:27.000Z\",\"birthday\":\"2017-12-06T00:00:00.000Z\",\"image_url\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/100500.svg\",\"image_url_cdn\":\"https://img.cn.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/100500.svg\",\"color\":\"strawberry\",\"background_color\":\"#ffe0e5\",\"bio\":\"Shalom! I'm KittyBlue_2_Lemonade. I'm a professional Foreign Film Director and I love cantaloupe. I'm convinced that the world is flat. One day I'll prove it. It's pawesome to meet you!\",\"kitty_type\":null,\"is_fancy\":false,\"is_exclusive\":false,\"is_special_edition\":false,\"fancy_type\":null,\"language\":\"en\",\"is_prestige\":false,\"prestige_type\":null,\"prestige_ranking\":null,\"prestige_time_limit\":null,\"status\":{\"is_ready\":true,\"is_gestating\":false,\"cooldown\":1410310201506,\"dynamic_cooldown\":1475064986478,\"cooldown_index\":10,\"cooldown_end_block\":0,\"pending_tx_type\":null,\"pending_tx_since\":null},\"purrs\":{\"count\":1,\"is_purred\":false},\"watchlist\":{\"count\":0,\"is_watchlisted\":false},\"hatcher\":{\"address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\",\"image\":\"14\",\"nickname\":\"KittyBlu\",\"hasDapper\":false,\"twitter_id\":null,\"twitter_image_url\":null,\"twitter_handle\":null},\"auction\":{},\"offer\":{},\"owner\":{\"address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\",\"hasDapper\":false,\"twitter_id\":null,\"twitter_image_url\":null,\"twitter_handle\":null,\"image\":\"14\",\"nickname\":\"KittyBlu\"},\"matron\":{\"id\":46234,\"name\":\"KittyBlue_1_Limegreen\",\"generation\":10,\"enhanced_cattributes\":[{\"type\":\"body\",\"kittyId\":19631,\"position\":105,\"description\":\"cymric\"},{\"type\":\"coloreyes\",\"kittyId\":40356,\"position\":263,\"description\":\"limegreen\"},{\"type\":\"eyes\",\"kittyId\":3185,\"position\":16,\"description\":\"raisedbrow\"},{\"type\":\"pattern\",\"kittyId\":46234,\"position\":-1,\"description\":\"totesbasic\"},{\"type\":\"mouth\",\"kittyId\":46234,\"position\":-1,\"description\":\"happygokitty\"},{\"type\":\"colorprimary\",\"kittyId\":46234,\"position\":-1,\"description\":\"greymatter\"},{\"type\":\"colorsecondary\",\"kittyId\":46234,\"position\":-1,\"description\":\"lemonade\"},{\"type\":\"colortertiary\",\"kittyId\":46234,\"position\":-1,\"description\":\"granitegrey\"}],\"owner_wallet_address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\",\"owner\":{\"address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\"},\"created_at\":\"2017-12-03T21:29:17.000Z\",\"image_url\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/46234.svg\",\"image_url_cdn\":\"https://img.cn.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/46234.svg\",\"color\":\"limegreen\",\"is_fancy\":false,\"kitty_type\":null,\"is_exclusive\":false,\"is_special_edition\":false,\"fancy_type\":null,\"status\":{\"is_ready\":true,\"is_gestating\":false,\"cooldown\":1486487069384},\"hatched\":true,\"wrapped\":false,\"image_url_png\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/46234.png\"},\"sire\":{\"id\":82090,\"name\":null,\"generation\":19,\"enhanced_cattributes\":[{\"type\":\"body\",\"kittyId\":82090,\"position\":-1,\"description\":\"himalayan\"},{\"type\":\"coloreyes\",\"kittyId\":82090,\"position\":-1,\"description\":\"strawberry\"},{\"type\":\"eyes\",\"kittyId\":82090,\"position\":-1,\"description\":\"thicccbrowz\"},{\"type\":\"pattern\",\"kittyId\":82090,\"position\":-1,\"description\":\"totesbasic\"},{\"type\":\"mouth\",\"kittyId\":82090,\"position\":-1,\"description\":\"pouty\"},{\"type\":\"colorprimary\",\"kittyId\":82090,\"position\":-1,\"description\":\"aquamarine\"},{\"type\":\"colorsecondary\",\"kittyId\":82090,\"position\":-1,\"description\":\"chocolate\"},{\"type\":\"colortertiary\",\"kittyId\":82090,\"position\":-1,\"description\":\"granitegrey\"}],\"owner_wallet_address\":\"0x798fdad0cedc4b298fc7d53a982fa0c5f447eaa5\",\"owner\":{\"address\":\"0x798fdad0cedc4b298fc7d53a982fa0c5f447eaa5\"},\"created_at\":\"2017-12-05T06:30:05.000Z\",\"image_url\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/82090.svg\",\"image_url_cdn\":\"https://img.cn.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/82090.svg\",\"color\":\"strawberry\",\"is_fancy\":false,\"is_exclusive\":false,\"is_special_edition\":false,\"fancy_type\":null,\"status\":{\"is_ready\":true,\"is_gestating\":false,\"cooldown\":1486619010030},\"kitty_type\":null,\"hatched\":true,\"wrapped\":false,\"image_url_png\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/82090.png\"},\"children\":[],\"hatched\":true,\"wrapped\":false,\"enhanced_cattributes\":[{\"type\":\"colorprimary\",\"description\":\"greymatter\",\"position\":null,\"kittyId\":100500},{\"type\":\"coloreyes\",\"description\":\"strawberry\",\"position\":null,\"kittyId\":100500},{\"type\":\"body\",\"description\":\"himalayan\",\"position\":null,\"kittyId\":100500},{\"type\":\"colorsecondary\",\"description\":\"lemonade\",\"position\":null,\"kittyId\":100500},{\"type\":\"mouth\",\"description\":\"pouty\",\"position\":null,\"kittyId\":100500},{\"type\":\"pattern\",\"description\":\"totesbasic\",\"position\":null,\"kittyId\":100500},{\"type\":\"eyes\",\"description\":\"thicccbrowz\",\"position\":null,\"kittyId\":100500},{\"type\":\"colortertiary\",\"description\":\"kittencream\",\"position\":null,\"kittyId\":100500},{\"type\":\"secret\",\"description\":\"se5\",\"position\":-1,\"kittyId\":100500},{\"type\":\"purrstige\",\"description\":\"pu20\",\"position\":-1,\"kittyId\":100500}],\"variation\":null,\"variation_ranking\":null,\"image_url_png\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/100500.png\",\"items\":[]}" - - Explorer.Mox.HTTPoison - |> expect(:get, fn "https://api.cryptokitties.co/kitties/100500", _headers, _options -> - {:ok, %HTTPoison.Response{status_code: 200, body: result}} - end) - - {:ok, %{metadata: metadata}} = - InstanceMetadataRetriever.fetch_metadata("0x06012c8cf97bead5deae237070f9587f8e7a266d", 100_500) - - assert Map.get(metadata, "name") == "KittyBlue_2_Lemonade" - - Application.put_env(:explorer, :http_adapter, HTTPoison) + MetadataRetriever.fetch_json({:ok, ["http://localhost:#{bypass.port}#{path}"]}) end test "fetches json metadata when HTTP status 301", %{bypass: bypass} do @@ -212,120 +190,19 @@ defmodule Explorer.Token.InstanceMetadataRetrieverTest do Conn.resp(conn, 200, json) end) - {:ok, %{metadata: metadata}} = - InstanceMetadataRetriever.fetch_metadata_from_uri("http://localhost:#{bypass.port}#{path}") + {:ok, %{metadata: metadata}} = MetadataRetriever.fetch_metadata_from_uri("http://localhost:#{bypass.port}#{path}") assert Map.get(metadata, "attributes") == Jason.decode!(attributes) end - test "replace {id} with actual token_id", %{bypass: bypass} do - json = """ - { - "name": "Sérgio Mendonça {id}" - } - """ - - abi = - [ - %{ - "type" => "function", - "stateMutability" => "nonpayable", - "payable" => false, - "outputs" => [], - "name" => "tokenURI", - "inputs" => [ - %{"type" => "string", "name" => "name", "internalType" => "string"} - ] - } - ] - |> ABI.parse_specification() - |> Enum.at(0) - - encoded_url = - abi - |> Encoder.encode_function_call(["http://localhost:#{bypass.port}/api/card/{id}"]) - |> String.replace("4cf12d26", "") - - EthereumJSONRPC.Mox - |> expect(:json_rpc, fn [ - %{ - id: 0, - jsonrpc: "2.0", - method: "eth_call", - params: [ - %{ - data: - "0xc87b56dd0000000000000000000000000000000000000000000000000000000000000309", - to: "0x5caebd3b32e210e85ce3e9d51638b9c445481567" - }, - "latest" - ] - } - ], - _options -> - {:ok, - [ - %{ - id: 0, - jsonrpc: "2.0", - error: %{code: -32000, message: "execution reverted"} - } - ]} - end) - |> expect(:json_rpc, fn [ - %{ - id: 0, - jsonrpc: "2.0", - method: "eth_call", - params: [ - %{ - data: - "0x0e89341c0000000000000000000000000000000000000000000000000000000000000309", - to: "0x5caebd3b32e210e85ce3e9d51638b9c445481567" - }, - "latest" - ] - } - ], - _options -> + test "decodes json file in tokenURI" do + data = {:ok, [ - %{ - id: 0, - jsonrpc: "2.0", - result: encoded_url - } + "data:application/json,{\"name\":\"Home%20Address%20-%200x0000000000C1A6066c6c8B9d63e9B6E8865dC117\",\"description\":\"This%20NFT%20can%20be%20redeemed%20on%20HomeWork%20to%20grant%20a%20controller%20the%20exclusive%20right%20to%20deploy%20contracts%20with%20arbitrary%20bytecode%20to%20the%20designated%20home%20address.\",\"image\":\"data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNDQgNzIiPjxzdHlsZT48IVtDREFUQVsuQntzdHJva2UtbGluZWpvaW46cm91bmR9LkN7c3Ryb2tlLW1pdGVybGltaXQ6MTB9LkR7c3Ryb2tlLXdpZHRoOjJ9LkV7ZmlsbDojOWI5YjlhfS5Ge3N0cm9rZS1saW5lY2FwOnJvdW5kfV1dPjwvc3R5bGU+PGcgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMiAwIDAgMS4wMiA4LjEgMCkiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xOSAzMmgzNHYyNEgxOXoiLz48ZyBzdHJva2U9IiMwMDAiIGNsYXNzPSJCIEMgRCI+PHBhdGggZmlsbD0iI2E1NzkzOSIgZD0iTTI1IDQwaDl2MTZoLTl6Ii8+PHBhdGggZmlsbD0iIzkyZDNmNSIgZD0iTTQwIDQwaDh2N2gtOHoiLz48cGF0aCBmaWxsPSIjZWE1YTQ3IiBkPSJNNTMgMzJIMTl2LTFsMTYtMTYgMTggMTZ6Ii8+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTE5IDMyaDM0djI0SDE5eiIvPjxwYXRoIGZpbGw9IiNlYTVhNDciIGQ9Ik0yOSAyMWwtNSA1di05aDV6Ii8+PC9nPjwvZz48ZyB0cmFuc2Zvcm09Im1hdHJpeCguODQgMCAwIC44NCA2NSA1KSI+PHBhdGggZD0iTTkuNSAyMi45bDQuOCA2LjRhMy4xMiAzLjEyIDAgMCAxLTMgMi4ybC00LjgtNi40Yy4zLTEuNCAxLjYtMi40IDMtMi4yeiIgZmlsbD0iI2QwY2ZjZSIvPjxwYXRoIGZpbGw9IiMwMTAxMDEiIGQ9Ik00MS43IDM4LjVsNS4xLTYuNSIvPjxwYXRoIGQ9Ik00Mi45IDI3LjhMMTguNCA1OC4xIDI0IDYybDIxLjgtMjcuMyAyLjMtMi44eiIgY2xhc3M9IkUiLz48cGF0aCBmaWxsPSIjMDEwMTAxIiBkPSJNNDMuNCAyOS4zbC00LjcgNS44Ii8+PHBhdGggZD0iTTQ2LjggMzJjMy4yIDIuNiA4LjcgMS4yIDEyLjEtMy4yczMuNi05LjkuMy0xMi41bC01LjEgNi41LTIuOC0uMS0uNy0yLjcgNS4xLTYuNWMtMy4yLTIuNi04LjctMS4yLTEyLjEgMy4ycy0zLjYgOS45LS4zIDEyLjUiIGNsYXNzPSJFIi8+PHBhdGggZmlsbD0iI2E1NzkzOSIgZD0iTTI3LjMgMjZsMTEuOCAxNS43IDMuNCAyLjQgOS4xIDE0LjQtMy4yIDIuMy0xIC43LTEwLjItMTMuNi0xLjMtMy45LTExLjgtMTUuN3oiLz48cGF0aCBkPSJNMTIgMTkuOWw1LjkgNy45IDEwLjItNy42LTMuNC00LjVzNi44LTUuMSAxMC43LTQuNWMwIDAtNi42LTMtMTMuMyAxLjFTMTIgMTkuOSAxMiAxOS45eiIgY2xhc3M9IkUiLz48ZyBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAiIGNsYXNzPSJCIEMgRCI+PHBhdGggZD0iTTUyIDU4LjlMNDAuOSA0My4ybC0zLjEtMi4zLTEwLjYtMTQuNy0yLjkgMi4yIDEwLjYgMTQuNyAxLjEgMy42IDExLjUgMTUuNXpNMTIuNSAxOS44bDUuOCA4IDEwLjMtNy40LTMuMy00LjZzNi45LTUgMTAuOC00LjNjMCAwLTYuNi0zLjEtMTMuMy45cy0xMC4zIDcuNC0xMC4zIDcuNHptLTIuNiAyLjlsNC43IDYuNWMtLjUgMS4zLTEuNyAyLjEtMyAyLjJsLTQuNy02LjVjLjMtMS40IDEuNi0yLjQgMy0yLjJ6Ii8+PHBhdGggZD0iTTQxLjMgMzguNWw1LjEtNi41bS0zLjUtMi43bC00LjYgNS44bTguMS0zLjFjMy4yIDIuNiA4LjcgMS4yIDEyLjEtMy4yczMuNi05LjkuMy0xMi41bC01LjEgNi41LTIuOC0uMS0uOC0yLjcgNS4xLTYuNWMtMy4yLTIuNi04LjctMS4yLTEyLjEgMy4yLTMuNCA0LjMtMy42IDkuOS0uMyAxMi41IiBjbGFzcz0iRiIvPjxwYXRoIGQ9Ik0zMC44IDQ0LjRMMTkgNTguOWw0IDMgMTAtMTIuNyIgY2xhc3M9IkYiLz48L2c+PC9nPjwvc3ZnPg==\"}" ]} - end) - - Bypass.expect( - bypass, - "GET", - "/api/card/0000000000000000000000000000000000000000000000000000000000000309", - fn conn -> - Conn.resp(conn, 200, json) - end - ) - assert {:ok, - %{ - metadata: %{ - "name" => "Sérgio Mendonça 0000000000000000000000000000000000000000000000000000000000000309" - } - }} == - InstanceMetadataRetriever.fetch_metadata("0x5caebd3b32e210e85ce3e9d51638b9c445481567", 777) - end - - test "decodes json file in tokenURI" do - data = %{ - "c87b56dd" => - {:ok, - [ - "data:application/json,{\"name\":\"Home%20Address%20-%200x0000000000C1A6066c6c8B9d63e9B6E8865dC117\",\"description\":\"This%20NFT%20can%20be%20redeemed%20on%20HomeWork%20to%20grant%20a%20controller%20the%20exclusive%20right%20to%20deploy%20contracts%20with%20arbitrary%20bytecode%20to%20the%20designated%20home%20address.\",\"image\":\"data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNDQgNzIiPjxzdHlsZT48IVtDREFUQVsuQntzdHJva2UtbGluZWpvaW46cm91bmR9LkN7c3Ryb2tlLW1pdGVybGltaXQ6MTB9LkR7c3Ryb2tlLXdpZHRoOjJ9LkV7ZmlsbDojOWI5YjlhfS5Ge3N0cm9rZS1saW5lY2FwOnJvdW5kfV1dPjwvc3R5bGU+PGcgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMiAwIDAgMS4wMiA4LjEgMCkiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xOSAzMmgzNHYyNEgxOXoiLz48ZyBzdHJva2U9IiMwMDAiIGNsYXNzPSJCIEMgRCI+PHBhdGggZmlsbD0iI2E1NzkzOSIgZD0iTTI1IDQwaDl2MTZoLTl6Ii8+PHBhdGggZmlsbD0iIzkyZDNmNSIgZD0iTTQwIDQwaDh2N2gtOHoiLz48cGF0aCBmaWxsPSIjZWE1YTQ3IiBkPSJNNTMgMzJIMTl2LTFsMTYtMTYgMTggMTZ6Ii8+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTE5IDMyaDM0djI0SDE5eiIvPjxwYXRoIGZpbGw9IiNlYTVhNDciIGQ9Ik0yOSAyMWwtNSA1di05aDV6Ii8+PC9nPjwvZz48ZyB0cmFuc2Zvcm09Im1hdHJpeCguODQgMCAwIC44NCA2NSA1KSI+PHBhdGggZD0iTTkuNSAyMi45bDQuOCA2LjRhMy4xMiAzLjEyIDAgMCAxLTMgMi4ybC00LjgtNi40Yy4zLTEuNCAxLjYtMi40IDMtMi4yeiIgZmlsbD0iI2QwY2ZjZSIvPjxwYXRoIGZpbGw9IiMwMTAxMDEiIGQ9Ik00MS43IDM4LjVsNS4xLTYuNSIvPjxwYXRoIGQ9Ik00Mi45IDI3LjhMMTguNCA1OC4xIDI0IDYybDIxLjgtMjcuMyAyLjMtMi44eiIgY2xhc3M9IkUiLz48cGF0aCBmaWxsPSIjMDEwMTAxIiBkPSJNNDMuNCAyOS4zbC00LjcgNS44Ii8+PHBhdGggZD0iTTQ2LjggMzJjMy4yIDIuNiA4LjcgMS4yIDEyLjEtMy4yczMuNi05LjkuMy0xMi41bC01LjEgNi41LTIuOC0uMS0uNy0yLjcgNS4xLTYuNWMtMy4yLTIuNi04LjctMS4yLTEyLjEgMy4ycy0zLjYgOS45LS4zIDEyLjUiIGNsYXNzPSJFIi8+PHBhdGggZmlsbD0iI2E1NzkzOSIgZD0iTTI3LjMgMjZsMTEuOCAxNS43IDMuNCAyLjQgOS4xIDE0LjQtMy4yIDIuMy0xIC43LTEwLjItMTMuNi0xLjMtMy45LTExLjgtMTUuN3oiLz48cGF0aCBkPSJNMTIgMTkuOWw1LjkgNy45IDEwLjItNy42LTMuNC00LjVzNi44LTUuMSAxMC43LTQuNWMwIDAtNi42LTMtMTMuMyAxLjFTMTIgMTkuOSAxMiAxOS45eiIgY2xhc3M9IkUiLz48ZyBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAiIGNsYXNzPSJCIEMgRCI+PHBhdGggZD0iTTUyIDU4LjlMNDAuOSA0My4ybC0zLjEtMi4zLTEwLjYtMTQuNy0yLjkgMi4yIDEwLjYgMTQuNyAxLjEgMy42IDExLjUgMTUuNXpNMTIuNSAxOS44bDUuOCA4IDEwLjMtNy40LTMuMy00LjZzNi45LTUgMTAuOC00LjNjMCAwLTYuNi0zLjEtMTMuMy45cy0xMC4zIDcuNC0xMC4zIDcuNHptLTIuNiAyLjlsNC43IDYuNWMtLjUgMS4zLTEuNyAyLjEtMyAyLjJsLTQuNy02LjVjLjMtMS40IDEuNi0yLjQgMy0yLjJ6Ii8+PHBhdGggZD0iTTQxLjMgMzguNWw1LjEtNi41bS0zLjUtMi43bC00LjYgNS44bTguMS0zLjFjMy4yIDIuNiA4LjcgMS4yIDEyLjEtMy4yczMuNi05LjkuMy0xMi41bC01LjEgNi41LTIuOC0uMS0uOC0yLjcgNS4xLTYuNWMtMy4yLTIuNi04LjctMS4yLTEyLjEgMy4yLTMuNCA0LjMtMy42IDkuOS0uMyAxMi41IiBjbGFzcz0iRiIvPjxwYXRoIGQ9Ik0zMC44IDQ0LjRMMTkgNTguOWw0IDMgMTAtMTIuNyIgY2xhc3M9IkYiLz48L2c+PC9nPjwvc3ZnPg==\"}" - ]} - } - - assert InstanceMetadataRetriever.fetch_json(data) == + assert MetadataRetriever.fetch_json(data) == {:ok, %{ metadata: %{ @@ -339,15 +216,13 @@ defmodule Explorer.Token.InstanceMetadataRetrieverTest do end test "decodes base64 encoded json file in tokenURI" do - data = %{ - "c87b56dd" => - {:ok, - [ - "data:application/json;base64,eyJuYW1lIjogIi54ZGFpIiwgImRlc2NyaXB0aW9uIjogIlB1bmsgRG9tYWlucyBkaWdpdGFsIGlkZW50aXR5LiBWaXNpdCBodHRwczovL3B1bmsuZG9tYWlucy8iLCAiaW1hZ2UiOiAiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhacFpYZENiM2c5SWpBZ01DQTFNREFnTlRBd0lpQjNhV1IwYUQwaU5UQXdJaUJvWldsbmFIUTlJalV3TUNJK1BHUmxabk0rUEd4cGJtVmhja2R5WVdScFpXNTBJR2xrUFNKbmNtRmtJaUI0TVQwaU1DVWlJSGt4UFNJd0pTSWdlREk5SWpFd01DVWlJSGt5UFNJd0pTSStQSE4wYjNBZ2IyWm1jMlYwUFNJd0pTSWdjM1I1YkdVOUluTjBiM0F0WTI5c2IzSTZjbWRpS0RVNExERTNMREV4TmlrN2MzUnZjQzF2Y0dGamFYUjVPakVpSUM4K1BITjBiM0FnYjJabWMyVjBQU0l4TURBbElpQnpkSGxzWlQwaWMzUnZjQzFqYjJ4dmNqcHlaMklvTVRFMkxESTFMREUzS1R0emRHOXdMVzl3WVdOcGRIazZNU0lnTHo0OEwyeHBibVZoY2tkeVlXUnBaVzUwUGp3dlpHVm1jejQ4Y21WamRDQjRQU0l3SWlCNVBTSXdJaUIzYVdSMGFEMGlOVEF3SWlCb1pXbG5hSFE5SWpVd01DSWdabWxzYkQwaWRYSnNLQ05uY21Ga0tTSXZQangwWlhoMElIZzlJalV3SlNJZ2VUMGlOVEFsSWlCa2IyMXBibUZ1ZEMxaVlYTmxiR2x1WlQwaWJXbGtaR3hsSWlCbWFXeHNQU0ozYUdsMFpTSWdkR1Y0ZEMxaGJtTm9iM0k5SW0xcFpHUnNaU0lnWm05dWRDMXphWHBsUFNKNExXeGhjbWRsSWo0dWVHUmhhVHd2ZEdWNGRENDhkR1Y0ZENCNFBTSTFNQ1VpSUhrOUlqY3dKU0lnWkc5dGFXNWhiblF0WW1GelpXeHBibVU5SW0xcFpHUnNaU0lnWm1sc2JEMGlkMmhwZEdVaUlIUmxlSFF0WVc1amFHOXlQU0p0YVdSa2JHVWlQbkIxYm1zdVpHOXRZV2x1Y3p3dmRHVjRkRDQ4TDNOMlp6ND0ifQ==" - ]} - } + data = + {:ok, + [ + "data:application/json;base64,eyJuYW1lIjogIi54ZGFpIiwgImRlc2NyaXB0aW9uIjogIlB1bmsgRG9tYWlucyBkaWdpdGFsIGlkZW50aXR5LiBWaXNpdCBodHRwczovL3B1bmsuZG9tYWlucy8iLCAiaW1hZ2UiOiAiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhacFpYZENiM2c5SWpBZ01DQTFNREFnTlRBd0lpQjNhV1IwYUQwaU5UQXdJaUJvWldsbmFIUTlJalV3TUNJK1BHUmxabk0rUEd4cGJtVmhja2R5WVdScFpXNTBJR2xrUFNKbmNtRmtJaUI0TVQwaU1DVWlJSGt4UFNJd0pTSWdlREk5SWpFd01DVWlJSGt5UFNJd0pTSStQSE4wYjNBZ2IyWm1jMlYwUFNJd0pTSWdjM1I1YkdVOUluTjBiM0F0WTI5c2IzSTZjbWRpS0RVNExERTNMREV4TmlrN2MzUnZjQzF2Y0dGamFYUjVPakVpSUM4K1BITjBiM0FnYjJabWMyVjBQU0l4TURBbElpQnpkSGxzWlQwaWMzUnZjQzFqYjJ4dmNqcHlaMklvTVRFMkxESTFMREUzS1R0emRHOXdMVzl3WVdOcGRIazZNU0lnTHo0OEwyeHBibVZoY2tkeVlXUnBaVzUwUGp3dlpHVm1jejQ4Y21WamRDQjRQU0l3SWlCNVBTSXdJaUIzYVdSMGFEMGlOVEF3SWlCb1pXbG5hSFE5SWpVd01DSWdabWxzYkQwaWRYSnNLQ05uY21Ga0tTSXZQangwWlhoMElIZzlJalV3SlNJZ2VUMGlOVEFsSWlCa2IyMXBibUZ1ZEMxaVlYTmxiR2x1WlQwaWJXbGtaR3hsSWlCbWFXeHNQU0ozYUdsMFpTSWdkR1Y0ZEMxaGJtTm9iM0k5SW0xcFpHUnNaU0lnWm05dWRDMXphWHBsUFNKNExXeGhjbWRsSWo0dWVHUmhhVHd2ZEdWNGRENDhkR1Y0ZENCNFBTSTFNQ1VpSUhrOUlqY3dKU0lnWkc5dGFXNWhiblF0WW1GelpXeHBibVU5SW0xcFpHUnNaU0lnWm1sc2JEMGlkMmhwZEdVaUlIUmxlSFF0WVc1amFHOXlQU0p0YVdSa2JHVWlQbkIxYm1zdVpHOXRZV2x1Y3p3dmRHVjRkRDQ4TDNOMlp6ND0ifQ==" + ]} - assert InstanceMetadataRetriever.fetch_json(data) == + assert MetadataRetriever.fetch_json(data) == {:ok, %{ metadata: %{ @@ -360,15 +235,13 @@ defmodule Explorer.Token.InstanceMetadataRetrieverTest do end test "decodes base64 encoded json file (with unicode string) in tokenURI" do - data = %{ - "c87b56dd" => - {:ok, - [ - "data:application/json;base64,eyJkZXNjcmlwdGlvbiI6ICJQdW5rIERvbWFpbnMgZGlnaXRhbCBpZGVudGl0eSDDry4gVmlzaXQgaHR0cHM6Ly9wdW5rLmRvbWFpbnMvIn0=" - ]} - } + data = + {:ok, + [ + "data:application/json;base64,eyJkZXNjcmlwdGlvbiI6ICJQdW5rIERvbWFpbnMgZGlnaXRhbCBpZGVudGl0eSDDry4gVmlzaXQgaHR0cHM6Ly9wdW5rLmRvbWFpbnMvIn0=" + ]} - assert InstanceMetadataRetriever.fetch_json(data) == + assert MetadataRetriever.fetch_json(data) == {:ok, %{ metadata: %{ @@ -390,20 +263,18 @@ defmodule Explorer.Token.InstanceMetadataRetrieverTest do Conn.resp(conn, 200, json) end) - data = %{ - "c87b56dd" => - {:ok, - [ - "http://localhost:#{bypass.port}#{path}" - ]} - } + data = + {:ok, + [ + "http://localhost:#{bypass.port}#{path}" + ]} assert {:ok, %{ metadata: %{ "image" => "https://ipfs.io/ipfs/bafybeig6nlmyzui7llhauc52j2xo5hoy4lzp6442lkve5wysdvjkizxonu" } - }} == InstanceMetadataRetriever.fetch_json(data) + }} == MetadataRetriever.fetch_json(data) end test "Fetches metadata from ipfs", %{bypass: bypass} do @@ -419,18 +290,16 @@ defmodule Explorer.Token.InstanceMetadataRetrieverTest do Conn.resp(conn, 200, json) end) - data = %{ - "c87b56dd" => - {:ok, - [ - "http://localhost:#{bypass.port}#{path}" - ]} - } + data = + {:ok, + [ + "http://localhost:#{bypass.port}#{path}" + ]} {:ok, %{ metadata: metadata - }} = InstanceMetadataRetriever.fetch_json(data) + }} = MetadataRetriever.fetch_json(data) assert "ipfs://bafybeihxuj3gxk7x5p36amzootyukbugmx3pw7dyntsrohg3se64efkuga/51.png" == Map.get(metadata, "image") end @@ -438,13 +307,11 @@ defmodule Explorer.Token.InstanceMetadataRetrieverTest do test "Fetches metadata from '${url}'", %{bypass: bypass} do path = "/data/8/8578.json" - data = %{ - "c87b56dd" => - {:ok, - [ - "'http://localhost:#{bypass.port}#{path}'" - ]} - } + data = + {:ok, + [ + "'http://localhost:#{bypass.port}#{path}'" + ]} json = """ { @@ -467,17 +334,15 @@ defmodule Explorer.Token.InstanceMetadataRetrieverTest do assert {:ok, %{ metadata: Jason.decode!(json) - }} == InstanceMetadataRetriever.fetch_json(data) + }} == MetadataRetriever.fetch_json(data) end test "Process custom execution reverted" do - data = %{ - "c87b56dd" => - {:error, - "(3) execution reverted: Nonexistent token (0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000114e6f6e6578697374656e7420746f6b656e000000000000000000000000000000)"} - } + data = + {:error, + "(3) execution reverted: Nonexistent token (0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000114e6f6e6578697374656e7420746f6b656e000000000000000000000000000000)"} - assert {:ok, %{error: "VM execution error"}} == InstanceMetadataRetriever.fetch_json(data) + assert {:ok, %{error: "VM execution error"}} == MetadataRetriever.fetch_json(data) end test "Process CIDv0 IPFS links" do @@ -507,7 +372,7 @@ defmodule Explorer.Token.InstanceMetadataRetrieverTest do "name" => "asda", "salePrice" => 34 } - }} == InstanceMetadataRetriever.fetch_json(%{"0e89341c" => {:ok, [data]}}) + }} == MetadataRetriever.fetch_json({:ok, [data]}) Application.put_env(:explorer, :http_adapter, HTTPoison) end @@ -553,54 +418,7 @@ defmodule Explorer.Token.InstanceMetadataRetrieverTest do %{ metadata: Jason.decode!(json) }} == - InstanceMetadataRetriever.fetch_json(%{"0e89341c" => {:ok, ["http://localhost:#{bypass.port}#{path}"]}}) - end - - test "fetch ipfs of ipfs/{id} format" do - EthereumJSONRPC.Mox - |> expect(:json_rpc, fn [ - %{ - id: 0, - jsonrpc: "2.0", - method: "eth_call", - params: [ - %{ - data: - "0xc87b56dd0000000000000000000000000000000000000000000000000000000000000000", - to: "0x7e01CC81fCfdf6a71323900288A69e234C464f63" - }, - "latest" - ] - } - ], - _options -> - {:ok, - [ - %{ - id: 0, - jsonrpc: "2.0", - result: - "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000033697066732f516d6439707654684577676a544262456b4e6d6d47466263704a4b773137666e524241543454643472636f67323200000000000000000000000000" - } - ]} - end) - - Application.put_env(:explorer, :http_adapter, Explorer.Mox.HTTPoison) - - Explorer.Mox.HTTPoison - |> expect(:get, fn "https://ipfs.io/ipfs/Qmd9pvThEwgjTBbEkNmmGFbcpJKw17fnRBAT4Td4rcog22", _headers, _options -> - {:ok, %HTTPoison.Response{status_code: 200, body: "123", headers: [{"Content-Type", "image/jpg"}]}} - end) - - assert {:ok, - %{ - metadata: %{ - "image" => "https://ipfs.io/ipfs/Qmd9pvThEwgjTBbEkNmmGFbcpJKw17fnRBAT4Td4rcog22" - } - }} == - InstanceMetadataRetriever.fetch_metadata("0x7e01CC81fCfdf6a71323900288A69e234C464f63", 0) - - Application.put_env(:explorer, :http_adapter, HTTPoison) + MetadataRetriever.fetch_json({:ok, ["http://localhost:#{bypass.port}#{path}"]}) end end end diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index 9c33824972eb..da2e13f6669d 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 @@ -893,7 +898,23 @@ defmodule Explorer.Factory do %CurrentTokenBalance{ address: build(:address), - token_contract_address_hash: insert(:token).contract_address_hash, + token_contract_address_hash: insert(:token, type: token_type).contract_address_hash, + block_number: block_number(), + value: Enum.random(1_000_000_000_000_000_000..10_000_000_000_000_000_000), + value_fetched_at: DateTime.utc_now(), + token_id: token_id, + token_type: token_type + } + end + + def address_current_token_balance_with_token_id_and_fixed_token_type_factory(%{ + token_type: token_type, + address: address, + token_id: token_id + }) do + %CurrentTokenBalance{ + address: address, + token_contract_address_hash: insert(:token, type: token_type).contract_address_hash, block_number: block_number(), value: Enum.random(1_000_000_000_000_000_000..10_000_000_000_000_000_000), value_fetched_at: DateTime.utc_now(), diff --git a/apps/explorer/test/support/fakes/no_op_price_source.ex b/apps/explorer/test/support/fakes/no_op_price_source.ex new file mode 100644 index 000000000000..b9a460ac8876 --- /dev/null +++ b/apps/explorer/test/support/fakes/no_op_price_source.ex @@ -0,0 +1,12 @@ +defmodule Explorer.ExchangeRates.Source.NoOpPriceSource do + @moduledoc false + + alias Explorer.Market.History.Source.Price, as: SourcePrice + + @behaviour SourcePrice + + @impl SourcePrice + def fetch_price_history(_previous_days) do + {:ok, []} + end +end diff --git a/apps/indexer/lib/indexer/application.ex b/apps/indexer/lib/indexer/application.ex index e47f5deeed23..9984918e06f9 100644 --- a/apps/indexer/lib/indexer/application.ex +++ b/apps/indexer/lib/indexer/application.ex @@ -24,7 +24,13 @@ defmodule Indexer.Application do json_rpc_named_arguments = Application.fetch_env!(:indexer, :json_rpc_named_arguments) + pool_size = + Application.get_env(:indexer, Indexer.Fetcher.TokenInstance.Retry)[:concurrency] + + Application.get_env(:indexer, Indexer.Fetcher.TokenInstance.Realtime)[:concurrency] + + Application.get_env(:indexer, Indexer.Fetcher.TokenInstance.Sanitize)[:concurrency] + base_children = [ + :hackney_pool.child_spec(:token_instance_fetcher, max_connections: pool_size), {Memory.Monitor, [memory_monitor_options, [name: memory_monitor_name]]}, {CoinBalanceOnDemand.Supervisor, [json_rpc_named_arguments]}, {TokenTotalSupplyOnDemand.Supervisor, []}, diff --git a/apps/indexer/lib/indexer/block/catchup/fetcher.ex b/apps/indexer/lib/indexer/block/catchup/fetcher.ex index 6a7d63e8710d..3b5fcbbc31fa 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 @@ -230,9 +247,6 @@ defmodule Indexer.Block.Catchup.Fetcher do rescue exception -> Logger.error(fn -> [Exception.format(:error, exception, __STACKTRACE__), ?\n, ?\n, "Retrying."] end) - - push_back(sequence, range) - {:error, exception} end @@ -306,6 +320,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/lib/indexer/fetcher/token_instance/helper.ex b/apps/indexer/lib/indexer/fetcher/token_instance/helper.ex index 57bc8ca37f58..e5e2256ff9eb 100644 --- a/apps/indexer/lib/indexer/fetcher/token_instance/helper.ex +++ b/apps/indexer/lib/indexer/fetcher/token_instance/helper.ex @@ -3,45 +3,171 @@ defmodule Indexer.Fetcher.TokenInstance.Helper do Common functions for Indexer.Fetcher.TokenInstance fetchers """ alias Explorer.Chain - alias Explorer.Chain.{Hash, Token.Instance} - alias Explorer.Token.InstanceMetadataRetriever - - @spec fetch_instance(Hash.Address.t(), Decimal.t() | non_neg_integer()) :: {:ok, Instance.t()} - def fetch_instance(token_contract_address_hash, token_id) do - token_id = prepare_token_id(token_id) - - case InstanceMetadataRetriever.fetch_metadata(to_string(token_contract_address_hash), token_id) do - {:ok, %{metadata: metadata}} -> - params = %{ - token_id: token_id, - token_contract_address_hash: token_contract_address_hash, - metadata: metadata, - error: nil + alias Explorer.SmartContract.Reader + alias Indexer.Fetcher.TokenInstance.MetadataRetriever + + @cryptokitties_address_hash "0x06012c8cf97bead5deae237070f9587f8e7a266d" + + @token_uri "c87b56dd" + @uri "0e89341c" + + @erc_721_1155_abi [ + %{ + "type" => "function", + "stateMutability" => "view", + "payable" => false, + "outputs" => [ + %{"type" => "string", "name" => ""} + ], + "name" => "tokenURI", + "inputs" => [ + %{ + "type" => "uint256", + "name" => "_tokenId" + } + ], + "constant" => true + }, + %{ + "type" => "function", + "stateMutability" => "view", + "payable" => false, + "outputs" => [ + %{ + "type" => "string", + "name" => "", + "internalType" => "string" } + ], + "name" => "uri", + "inputs" => [ + %{ + "type" => "uint256", + "name" => "_id", + "internalType" => "uint256" + } + ], + "constant" => true + } + ] + + @spec batch_fetch_instances([%{}]) :: list() + def batch_fetch_instances(token_instances) do + token_instances = + Enum.map(token_instances, fn + %{contract_address_hash: hash, token_id: token_id} -> {hash, token_id} + {_, _} = tuple -> tuple + end) + + splitted = + Enum.group_by(token_instances, fn {contract_address_hash, _token_id} -> + to_string(contract_address_hash) == @cryptokitties_address_hash + end) - {:ok, _result} = Chain.upsert_token_instance(params) + cryptokitties = + (splitted[true] || []) + |> Enum.map(fn {contract_address_hash, token_id} -> + {{:ok, ["https://api.cryptokitties.co/kitties/{id}"]}, to_string(token_id), contract_address_hash, token_id} + end) - {:ok, %{error: error}} -> - upsert_token_instance_with_error(token_id, token_contract_address_hash, error) + other = splitted[false] || [] - {:error_code, code} -> - upsert_token_instance_with_error(token_id, token_contract_address_hash, "request error: #{code}") + token_types_map = + Enum.reduce(other, %{}, fn {contract_address_hash, _token_id}, acc -> + address_hash_string = to_string(contract_address_hash) - {:error, reason} -> - upsert_token_instance_with_error(token_id, token_contract_address_hash, reason) - end + Map.put_new(acc, address_hash_string, Chain.get_token_type(contract_address_hash)) + end) + + contract_results = + (other + |> Enum.map(fn {contract_address_hash, token_id} -> + token_id = prepare_token_id(token_id) + contract_address_hash_string = to_string(contract_address_hash) + + prepare_request(token_types_map[contract_address_hash_string], contract_address_hash_string, token_id) + end) + |> Reader.query_contracts(@erc_721_1155_abi, [], false) + |> Enum.zip_reduce(other, [], fn result, {contract_address_hash, token_id}, acc -> + token_id = prepare_token_id(token_id) + + [ + {result, normalize_token_id(token_types_map[to_string(contract_address_hash)], token_id), + contract_address_hash, token_id} + | acc + ] + end) + |> Enum.reverse()) ++ + cryptokitties + + contract_results + |> Enum.map(fn {result, normalized_token_id, _contract_address_hash, _token_id} -> + Task.async(fn -> MetadataRetriever.fetch_json(result, normalized_token_id) end) + end) + |> Task.yield_many(:infinity) + |> Enum.zip(contract_results) + |> Enum.map(fn {{_task, res}, {_result, _normalized_token_id, contract_address_hash, token_id}} -> + case res do + {:ok, result} -> + result_to_insert_params(result, contract_address_hash, token_id) + + {:exit, reason} -> + result_to_insert_params( + {:error, MetadataRetriever.truncate_error("Terminated:" <> inspect(reason))}, + contract_address_hash, + token_id + ) + end + end) + |> Chain.upsert_token_instances_list() end defp prepare_token_id(%Decimal{} = token_id), do: Decimal.to_integer(token_id) defp prepare_token_id(token_id), do: token_id - defp upsert_token_instance_with_error(token_id, token_contract_address_hash, error) do - params = %{ + defp prepare_request("ERC-721", contract_address_hash_string, token_id) do + %{ + contract_address: contract_address_hash_string, + method_id: @token_uri, + args: [token_id], + block_number: nil + } + end + + defp prepare_request(_token_type, contract_address_hash_string, token_id) do + %{ + contract_address: contract_address_hash_string, + method_id: @uri, + args: [token_id], + block_number: nil + } + end + + defp normalize_token_id("ERC-721", _token_id), do: nil + + defp normalize_token_id(_token_type, token_id), + do: token_id |> Integer.to_string(16) |> String.downcase() |> String.pad_leading(64, "0") + + defp result_to_insert_params({:ok, %{metadata: metadata}}, token_contract_address_hash, token_id) do + %{ token_id: token_id, token_contract_address_hash: token_contract_address_hash, - error: error + metadata: metadata, + error: nil } + end + + defp result_to_insert_params({:error_code, code}, token_contract_address_hash, token_id), + do: token_instance_map_with_error(token_id, token_contract_address_hash, "request error: #{code}") - {:ok, _result} = Chain.upsert_token_instance(params) + defp result_to_insert_params({:error, reason}, token_contract_address_hash, token_id), + do: token_instance_map_with_error(token_id, token_contract_address_hash, reason) + + defp token_instance_map_with_error(token_id, token_contract_address_hash, error) do + %{ + token_id: token_id, + token_contract_address_hash: token_contract_address_hash, + error: error + } end end diff --git a/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex b/apps/indexer/lib/indexer/fetcher/token_instance/metadata_retriever.ex similarity index 76% rename from apps/explorer/lib/explorer/token/instance_metadata_retriever.ex rename to apps/indexer/lib/indexer/fetcher/token_instance/metadata_retriever.ex index 6967a756aed3..89c383f5a5d0 100644 --- a/apps/explorer/lib/explorer/token/instance_metadata_retriever.ex +++ b/apps/indexer/lib/indexer/fetcher/token_instance/metadata_retriever.ex @@ -1,6 +1,6 @@ -defmodule Explorer.Token.InstanceMetadataRetriever do +defmodule Indexer.Fetcher.TokenInstance.MetadataRetriever do @moduledoc """ - Fetches ERC721 token instance metadata. + Fetches ERC-721 & ERC-1155 token instance metadata. """ require Logger @@ -8,55 +8,6 @@ defmodule Explorer.Token.InstanceMetadataRetriever do alias Explorer.SmartContract.Reader alias HTTPoison.{Error, Response} - @token_uri "c87b56dd" - - @abi [ - %{ - "type" => "function", - "stateMutability" => "view", - "payable" => false, - "outputs" => [ - %{"type" => "string", "name" => ""} - ], - "name" => "tokenURI", - "inputs" => [ - %{ - "type" => "uint256", - "name" => "_tokenId" - } - ], - "constant" => true - } - ] - - @uri "0e89341c" - - @abi_uri [ - %{ - "type" => "function", - "stateMutability" => "view", - "payable" => false, - "outputs" => [ - %{ - "type" => "string", - "name" => "", - "internalType" => "string" - } - ], - "name" => "uri", - "inputs" => [ - %{ - "type" => "uint256", - "name" => "_id", - "internalType" => "uint256" - } - ], - "constant" => true - } - ] - - @cryptokitties_address_hash "0x06012c8cf97bead5deae237070f9587f8e7a266d" - @no_uri_error "no uri" @vm_execution_error "VM execution error" @ipfs_protocol "ipfs://" @@ -69,54 +20,29 @@ defmodule Explorer.Token.InstanceMetadataRetriever do @ignored_hosts ["localhost", "127.0.0.1", "0.0.0.0", "", nil] - def fetch_metadata(unquote(@cryptokitties_address_hash), token_id) do - %{@token_uri => {:ok, ["https://api.cryptokitties.co/kitties/{id}"]}} - |> fetch_json(to_string(token_id)) - end - - def fetch_metadata(contract_address_hash, token_id) do - # c87b56dd = keccak256(tokenURI(uint256)) - contract_functions = %{@token_uri => [token_id]} - - res = - contract_address_hash - |> query_contract(contract_functions, @abi) - |> fetch_json() - - if res == {:ok, %{error: @vm_execution_error}} do - hex_normalized_token_id = token_id |> Integer.to_string(16) |> String.downcase() |> String.pad_leading(64, "0") - - contract_functions_uri = %{@uri => [token_id]} - - contract_address_hash - |> query_contract(contract_functions_uri, @abi_uri) - |> fetch_json(hex_normalized_token_id) - else - res - end - end - def query_contract(contract_address_hash, contract_functions, abi) do Reader.query_contract(contract_address_hash, abi, contract_functions, false) end + @doc """ + Fetch/parse metadata using smart-contract's response + """ + @spec fetch_json(any, binary() | nil) :: {:error, binary} | {:error_code, any} | {:ok, %{metadata: any}} def fetch_json(uri, hex_token_id \\ nil) - def fetch_json(uri, _hex_token_id) when uri in [%{@token_uri => {:ok, [""]}}, %{@uri => {:ok, [""]}}] do - {:ok, %{error: @no_uri_error}} - end - - def fetch_json(%{@token_uri => uri}, hex_token_id) do - fetch_json_from_uri(uri, hex_token_id) + def fetch_json(uri, _hex_token_id) when uri in [{:ok, [""]}, {:ok, [""]}] do + {:error, @no_uri_error} end - def fetch_json(%{@uri => uri}, hex_token_id) do + def fetch_json(uri, hex_token_id) do fetch_json_from_uri(uri, hex_token_id) end defp fetch_json_from_uri({:error, error}, _hex_token_id) do + error = to_string(error) + if error =~ "execution reverted" or error =~ @vm_execution_error do - {:ok, %{error: @vm_execution_error}} + {:error, @vm_execution_error} else Logger.debug(["Unknown metadata format error #{inspect(error)}."], fetcher: :token_instances) @@ -245,7 +171,8 @@ defmodule Explorer.Token.InstanceMetadataRetriever do def fetch_metadata_from_uri_inner(uri, hex_token_id) do case Application.get_env(:explorer, :http_adapter).get(uri, [], recv_timeout: 30_000, - follow_redirect: true + follow_redirect: true, + hackney: [pool: :token_instance_fetcher] ) do {:ok, %Response{body: body, status_code: 200, headers: headers}} -> content_type = get_content_type_from_headers(headers) @@ -350,5 +277,9 @@ defmodule Explorer.Token.InstanceMetadataRetriever do String.replace(token_uri, @erc1155_token_id_placeholder, hex_token_id) end - defp truncate_error(error), do: String.slice(error, 0, @max_error_length) + @doc """ + Truncate error string to @max_error_length symbols + """ + @spec truncate_error(binary()) :: binary() + def truncate_error(error), do: String.slice(error, 0, @max_error_length) end diff --git a/apps/indexer/lib/indexer/fetcher/token_instance/realtime.ex b/apps/indexer/lib/indexer/fetcher/token_instance/realtime.ex index 3f82494b55b6..804c9258c851 100644 --- a/apps/indexer/lib/indexer/fetcher/token_instance/realtime.ex +++ b/apps/indexer/lib/indexer/fetcher/token_instance/realtime.ex @@ -32,10 +32,12 @@ defmodule Indexer.Fetcher.TokenInstance.Realtime do end @impl BufferedTask - def run([%{contract_address_hash: hash, token_id: token_id}], _json_rpc_named_arguments) do - if not Chain.token_instance_exists?(token_id, hash) do - fetch_instance(hash, token_id) - end + def run(token_instances, _) when is_list(token_instances) do + token_instances + |> Enum.filter(fn %{contract_address_hash: hash, token_id: token_id} -> + not Chain.token_instance_exists?(token_id, hash) + end) + |> batch_fetch_instances() :ok end @@ -75,7 +77,7 @@ defmodule Indexer.Fetcher.TokenInstance.Realtime do [ flush_interval: 100, max_concurrency: Application.get_env(:indexer, __MODULE__)[:concurrency] || @default_max_concurrency, - max_batch_size: @default_max_batch_size, + max_batch_size: Application.get_env(:indexer, __MODULE__)[:batch_size] || @default_max_batch_size, poll: false, task_supervisor: __MODULE__.TaskSupervisor ] diff --git a/apps/indexer/lib/indexer/fetcher/token_instance/retry.ex b/apps/indexer/lib/indexer/fetcher/token_instance/retry.ex index 24b2df433f81..a09bce459698 100644 --- a/apps/indexer/lib/indexer/fetcher/token_instance/retry.ex +++ b/apps/indexer/lib/indexer/fetcher/token_instance/retry.ex @@ -13,7 +13,7 @@ defmodule Indexer.Fetcher.TokenInstance.Retry do @behaviour BufferedTask - @default_max_batch_size 1 + @default_max_batch_size 10 @default_max_concurrency 10 @doc false @@ -40,14 +40,16 @@ defmodule Indexer.Fetcher.TokenInstance.Retry do end @impl BufferedTask - def run([%{contract_address_hash: hash, token_id: token_id, updated_at: updated_at}], _json_rpc_named_arguments) do + def run(token_instances, _json_rpc_named_arguments) when is_list(token_instances) do refetch_interval = Application.get_env(:indexer, __MODULE__)[:refetch_interval] - if updated_at - |> DateTime.add(refetch_interval, :millisecond) - |> DateTime.compare(DateTime.utc_now()) != :gt do - fetch_instance(hash, token_id) - end + token_instances + |> Enum.filter(fn %{contract_address_hash: _hash, token_id: _token_id, updated_at: updated_at} -> + updated_at + |> DateTime.add(refetch_interval, :millisecond) + |> DateTime.compare(DateTime.utc_now()) != :gt + end) + |> batch_fetch_instances() :ok end @@ -56,7 +58,7 @@ defmodule Indexer.Fetcher.TokenInstance.Retry do [ flush_interval: :timer.minutes(10), max_concurrency: Application.get_env(:indexer, __MODULE__)[:concurrency] || @default_max_concurrency, - max_batch_size: @default_max_batch_size, + max_batch_size: Application.get_env(:indexer, __MODULE__)[:batch_size] || @default_max_batch_size, task_supervisor: __MODULE__.TaskSupervisor ] end diff --git a/apps/indexer/lib/indexer/fetcher/token_instance/sanitize.ex b/apps/indexer/lib/indexer/fetcher/token_instance/sanitize.ex index 31c594ec0055..1bc3a70bd1f1 100644 --- a/apps/indexer/lib/indexer/fetcher/token_instance/sanitize.ex +++ b/apps/indexer/lib/indexer/fetcher/token_instance/sanitize.ex @@ -13,7 +13,7 @@ defmodule Indexer.Fetcher.TokenInstance.Sanitize do @behaviour BufferedTask - @default_max_batch_size 1 + @default_max_batch_size 10 @default_max_concurrency 10 @doc false def child_spec([init_options, gen_server_options]) do @@ -36,10 +36,12 @@ defmodule Indexer.Fetcher.TokenInstance.Sanitize do end @impl BufferedTask - def run([%{contract_address_hash: hash, token_id: token_id}], _json_rpc_named_arguments) do - if not Chain.token_instance_exists?(token_id, hash) do - fetch_instance(hash, token_id) - end + def run(token_instances, _) when is_list(token_instances) do + token_instances + |> Enum.filter(fn %{contract_address_hash: hash, token_id: token_id} -> + not Chain.token_instance_exists?(token_id, hash) + end) + |> batch_fetch_instances() :ok end @@ -48,7 +50,7 @@ defmodule Indexer.Fetcher.TokenInstance.Sanitize do [ flush_interval: :infinity, max_concurrency: Application.get_env(:indexer, __MODULE__)[:concurrency] || @default_max_concurrency, - max_batch_size: @default_max_batch_size, + max_batch_size: Application.get_env(:indexer, __MODULE__)[:batch_size] || @default_max_batch_size, poll: false, task_supervisor: __MODULE__.TaskSupervisor ] diff --git a/apps/indexer/lib/indexer/transform/address_token_balances.ex b/apps/indexer/lib/indexer/transform/address_token_balances.ex index 3dc7b5be525d..cae8b2cbac7b 100644 --- a/apps/indexer/lib/indexer/transform/address_token_balances.ex +++ b/apps/indexer/lib/indexer/transform/address_token_balances.ex @@ -3,7 +3,7 @@ defmodule Indexer.Transform.AddressTokenBalances do Extracts `Explorer.Address.TokenBalance` params from other schema's params. """ - @burn_address "0x0000000000000000000000000000000000000000" + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] def params_set(%{} = import_options) do Enum.reduce(import_options, MapSet.new(), &reducer/2) @@ -35,7 +35,7 @@ defmodule Indexer.Transform.AddressTokenBalances do Enum.filter(token_transfers_params, &do_filter_burn_address/1) end - defp add_token_balance_address(map_set, unquote(@burn_address), _, _, _, _), do: map_set + defp add_token_balance_address(map_set, unquote(burn_address_hash_string()), _, _, _, _), do: map_set defp add_token_balance_address(map_set, address, token_contract_address, token_id, token_type, block_number) do MapSet.put(map_set, %{ @@ -47,7 +47,7 @@ defmodule Indexer.Transform.AddressTokenBalances do }) end - def do_filter_burn_address(%{to_address_hash: unquote(@burn_address), token_type: "ERC-721"}) do + def do_filter_burn_address(%{to_address_hash: unquote(burn_address_hash_string()), token_type: "ERC-721"}) do false end diff --git a/apps/indexer/lib/indexer/transform/token_transfers.ex b/apps/indexer/lib/indexer/transform/token_transfers.ex index 80d153d0f2cc..f5c3e3cabf06 100644 --- a/apps/indexer/lib/indexer/transform/token_transfers.ex +++ b/apps/indexer/lib/indexer/transform/token_transfers.ex @@ -5,13 +5,13 @@ defmodule Indexer.Transform.TokenTransfers do require Logger + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + alias ABI.TypeDecoder alias Explorer.Repo alias Explorer.Chain.{Token, TokenTransfer} alias Indexer.Fetcher.TokenTotalSupplyUpdater - @burn_address "0x0000000000000000000000000000000000000000" - @doc """ Returns a list of token transfers given a list of logs. """ @@ -51,7 +51,8 @@ defmodule Indexer.Transform.TokenTransfers do token_transfers |> Enum.filter(fn token_transfer -> - token_transfer.to_address_hash == @burn_address || token_transfer.from_address_hash == @burn_address + token_transfer.to_address_hash == burn_address_hash_string() || + token_transfer.from_address_hash == burn_address_hash_string() end) |> Enum.map(fn token_transfer -> token_transfer.token_contract_address_hash @@ -177,9 +178,9 @@ defmodule Indexer.Transform.TokenTransfers do {from_address_hash, to_address_hash} = if log.first_topic == TokenTransfer.weth_deposit_signature() do - {@burn_address, truncate_address_hash(log.second_topic)} + {burn_address_hash_string(), truncate_address_hash(log.second_topic)} else - {truncate_address_hash(log.second_topic), @burn_address} + {truncate_address_hash(log.second_topic), burn_address_hash_string()} end token_transfer = %{ @@ -315,7 +316,7 @@ defmodule Indexer.Transform.TokenTransfers do {token, token_transfer} end - defp truncate_address_hash(nil), do: "0x0000000000000000000000000000000000000000" + defp truncate_address_hash(nil), do: burn_address_hash_string() defp truncate_address_hash("0x000000000000000000000000" <> truncated_hash) do "0x#{truncated_hash}" diff --git a/apps/indexer/lib/indexer/transform/transaction_actions.ex b/apps/indexer/lib/indexer/transform/transaction_actions.ex index 549be9fa80cb..d22274decd98 100644 --- a/apps/indexer/lib/indexer/transform/transaction_actions.ex +++ b/apps/indexer/lib/indexer/transform/transaction_actions.ex @@ -6,6 +6,7 @@ defmodule Indexer.Transform.TransactionActions do require Logger import Ecto.Query, only: [from: 2] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] alias ABI.TypeDecoder alias Explorer.Chain.Cache.NetVersion @@ -18,10 +19,10 @@ defmodule Indexer.Transform.TransactionActions do @goerli 5 @optimism 10 @polygon 137 + @base_mainnet 8453 @base_goerli 84531 # @gnosis 100 - @burn_address "0x0000000000000000000000000000000000000000" @uniswap_v3_factory_abi [ %{ "inputs" => [ @@ -161,7 +162,7 @@ defmodule Indexer.Transform.TransactionActions do end defp parse_uniswap_v3(logs, actions, protocols_to_rewrite, chain_id) do - if Enum.member?([@mainnet, @goerli, @optimism, @polygon, @base_goerli], chain_id) and + if Enum.member?([@mainnet, @goerli, @optimism, @polygon, @base_mainnet, @base_goerli], chain_id) and (is_nil(protocols_to_rewrite) or Enum.empty?(protocols_to_rewrite) or Enum.member?(protocols_to_rewrite, "uniswap_v3")) do uniswap_v3_positions_nft = @@ -449,7 +450,7 @@ defmodule Indexer.Transform.TransactionActions do from = truncate_address_hash(log.second_topic) # credo:disable-for-next-line - if from == @burn_address do + if from == burn_address_hash_string() do to = truncate_address_hash(log.third_topic) [token_id] = decode_data(log.fourth_topic, [{:uint, 256}]) mint_nft_ids = Map.put_new(acc, to, %{ids: [], log_index: log.index}) @@ -650,8 +651,8 @@ defmodule Indexer.Transform.TransactionActions do end end) |> Enum.map(fn {pool_address, pool} -> - token0 = if is_address_correct?(pool.token0), do: String.downcase(pool.token0), else: @burn_address - token1 = if is_address_correct?(pool.token1), do: String.downcase(pool.token1), else: @burn_address + token0 = if is_address_correct?(pool.token0), do: String.downcase(pool.token0), else: burn_address_hash_string() + token1 = if is_address_correct?(pool.token1), do: String.downcase(pool.token1), else: burn_address_hash_string() fee = if pool.fee == "", do: 0, else: pool.fee # we will call getPool(token0, token1, fee) public getter @@ -998,7 +999,7 @@ defmodule Indexer.Transform.TransactionActions do if is_nil(first_topic), do: "", else: String.downcase(first_topic) end - defp truncate_address_hash(nil), do: @burn_address + defp truncate_address_hash(nil), do: burn_address_hash_string() defp truncate_address_hash("0x000000000000000000000000" <> truncated_hash) do "0x#{truncated_hash}" 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/fetcher/token_instance/helper_test.exs b/apps/indexer/test/indexer/fetcher/token_instance/helper_test.exs new file mode 100644 index 000000000000..8d9987f9de24 --- /dev/null +++ b/apps/indexer/test/indexer/fetcher/token_instance/helper_test.exs @@ -0,0 +1,178 @@ +defmodule Indexer.Fetcher.TokenInstance.HelperTest do + use EthereumJSONRPC.Case + use Explorer.DataCase + + alias Explorer.Chain.Token.Instance + alias EthereumJSONRPC.Encoder + alias Indexer.Fetcher.TokenInstance.Helper + alias Plug.Conn + + import Mox + + setup :verify_on_exit! + setup :set_mox_global + + setup do + bypass = Bypass.open() + + {:ok, bypass: bypass} + end + + describe "fetch instance tests" do + test "fetches json metadata for kitties" do + Application.put_env(:explorer, :http_adapter, Explorer.Mox.HTTPoison) + + result = + "{\"id\":100500,\"name\":\"KittyBlue_2_Lemonade\",\"generation\":20,\"genes\":\"623509754227292470437941473598751240781530569131665917719736997423495595\",\"created_at\":\"2017-12-06T01:56:27.000Z\",\"birthday\":\"2017-12-06T00:00:00.000Z\",\"image_url\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/100500.svg\",\"image_url_cdn\":\"https://img.cn.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/100500.svg\",\"color\":\"strawberry\",\"background_color\":\"#ffe0e5\",\"bio\":\"Shalom! I'm KittyBlue_2_Lemonade. I'm a professional Foreign Film Director and I love cantaloupe. I'm convinced that the world is flat. One day I'll prove it. It's pawesome to meet you!\",\"kitty_type\":null,\"is_fancy\":false,\"is_exclusive\":false,\"is_special_edition\":false,\"fancy_type\":null,\"language\":\"en\",\"is_prestige\":false,\"prestige_type\":null,\"prestige_ranking\":null,\"prestige_time_limit\":null,\"status\":{\"is_ready\":true,\"is_gestating\":false,\"cooldown\":1410310201506,\"dynamic_cooldown\":1475064986478,\"cooldown_index\":10,\"cooldown_end_block\":0,\"pending_tx_type\":null,\"pending_tx_since\":null},\"purrs\":{\"count\":1,\"is_purred\":false},\"watchlist\":{\"count\":0,\"is_watchlisted\":false},\"hatcher\":{\"address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\",\"image\":\"14\",\"nickname\":\"KittyBlu\",\"hasDapper\":false,\"twitter_id\":null,\"twitter_image_url\":null,\"twitter_handle\":null},\"auction\":{},\"offer\":{},\"owner\":{\"address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\",\"hasDapper\":false,\"twitter_id\":null,\"twitter_image_url\":null,\"twitter_handle\":null,\"image\":\"14\",\"nickname\":\"KittyBlu\"},\"matron\":{\"id\":46234,\"name\":\"KittyBlue_1_Limegreen\",\"generation\":10,\"enhanced_cattributes\":[{\"type\":\"body\",\"kittyId\":19631,\"position\":105,\"description\":\"cymric\"},{\"type\":\"coloreyes\",\"kittyId\":40356,\"position\":263,\"description\":\"limegreen\"},{\"type\":\"eyes\",\"kittyId\":3185,\"position\":16,\"description\":\"raisedbrow\"},{\"type\":\"pattern\",\"kittyId\":46234,\"position\":-1,\"description\":\"totesbasic\"},{\"type\":\"mouth\",\"kittyId\":46234,\"position\":-1,\"description\":\"happygokitty\"},{\"type\":\"colorprimary\",\"kittyId\":46234,\"position\":-1,\"description\":\"greymatter\"},{\"type\":\"colorsecondary\",\"kittyId\":46234,\"position\":-1,\"description\":\"lemonade\"},{\"type\":\"colortertiary\",\"kittyId\":46234,\"position\":-1,\"description\":\"granitegrey\"}],\"owner_wallet_address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\",\"owner\":{\"address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\"},\"created_at\":\"2017-12-03T21:29:17.000Z\",\"image_url\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/46234.svg\",\"image_url_cdn\":\"https://img.cn.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/46234.svg\",\"color\":\"limegreen\",\"is_fancy\":false,\"kitty_type\":null,\"is_exclusive\":false,\"is_special_edition\":false,\"fancy_type\":null,\"status\":{\"is_ready\":true,\"is_gestating\":false,\"cooldown\":1486487069384},\"hatched\":true,\"wrapped\":false,\"image_url_png\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/46234.png\"},\"sire\":{\"id\":82090,\"name\":null,\"generation\":19,\"enhanced_cattributes\":[{\"type\":\"body\",\"kittyId\":82090,\"position\":-1,\"description\":\"himalayan\"},{\"type\":\"coloreyes\",\"kittyId\":82090,\"position\":-1,\"description\":\"strawberry\"},{\"type\":\"eyes\",\"kittyId\":82090,\"position\":-1,\"description\":\"thicccbrowz\"},{\"type\":\"pattern\",\"kittyId\":82090,\"position\":-1,\"description\":\"totesbasic\"},{\"type\":\"mouth\",\"kittyId\":82090,\"position\":-1,\"description\":\"pouty\"},{\"type\":\"colorprimary\",\"kittyId\":82090,\"position\":-1,\"description\":\"aquamarine\"},{\"type\":\"colorsecondary\",\"kittyId\":82090,\"position\":-1,\"description\":\"chocolate\"},{\"type\":\"colortertiary\",\"kittyId\":82090,\"position\":-1,\"description\":\"granitegrey\"}],\"owner_wallet_address\":\"0x798fdad0cedc4b298fc7d53a982fa0c5f447eaa5\",\"owner\":{\"address\":\"0x798fdad0cedc4b298fc7d53a982fa0c5f447eaa5\"},\"created_at\":\"2017-12-05T06:30:05.000Z\",\"image_url\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/82090.svg\",\"image_url_cdn\":\"https://img.cn.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/82090.svg\",\"color\":\"strawberry\",\"is_fancy\":false,\"is_exclusive\":false,\"is_special_edition\":false,\"fancy_type\":null,\"status\":{\"is_ready\":true,\"is_gestating\":false,\"cooldown\":1486619010030},\"kitty_type\":null,\"hatched\":true,\"wrapped\":false,\"image_url_png\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/82090.png\"},\"children\":[],\"hatched\":true,\"wrapped\":false,\"enhanced_cattributes\":[{\"type\":\"colorprimary\",\"description\":\"greymatter\",\"position\":null,\"kittyId\":100500},{\"type\":\"coloreyes\",\"description\":\"strawberry\",\"position\":null,\"kittyId\":100500},{\"type\":\"body\",\"description\":\"himalayan\",\"position\":null,\"kittyId\":100500},{\"type\":\"colorsecondary\",\"description\":\"lemonade\",\"position\":null,\"kittyId\":100500},{\"type\":\"mouth\",\"description\":\"pouty\",\"position\":null,\"kittyId\":100500},{\"type\":\"pattern\",\"description\":\"totesbasic\",\"position\":null,\"kittyId\":100500},{\"type\":\"eyes\",\"description\":\"thicccbrowz\",\"position\":null,\"kittyId\":100500},{\"type\":\"colortertiary\",\"description\":\"kittencream\",\"position\":null,\"kittyId\":100500},{\"type\":\"secret\",\"description\":\"se5\",\"position\":-1,\"kittyId\":100500},{\"type\":\"purrstige\",\"description\":\"pu20\",\"position\":-1,\"kittyId\":100500}],\"variation\":null,\"variation_ranking\":null,\"image_url_png\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/100500.png\",\"items\":[]}" + + Explorer.Mox.HTTPoison + |> expect(:get, fn "https://api.cryptokitties.co/kitties/100500", _headers, _options -> + {:ok, %HTTPoison.Response{status_code: 200, body: result}} + end) + + insert(:token, + contract_address: build(:address, hash: "0x06012c8cf97bead5deae237070f9587f8e7a266d"), + type: "ERC-721" + ) + + [{:ok, %Instance{metadata: metadata}}] = + Helper.batch_fetch_instances([{"0x06012c8cf97bead5deae237070f9587f8e7a266d", 100_500}]) + + assert Map.get(metadata, "name") == "KittyBlue_2_Lemonade" + + Application.put_env(:explorer, :http_adapter, HTTPoison) + end + + test "replace {id} with actual token_id", %{bypass: bypass} do + json = """ + { + "name": "Sérgio Mendonça {id}" + } + """ + + abi = + [ + %{ + "type" => "function", + "stateMutability" => "nonpayable", + "payable" => false, + "outputs" => [], + "name" => "tokenURI", + "inputs" => [ + %{"type" => "string", "name" => "name", "internalType" => "string"} + ] + } + ] + |> ABI.parse_specification() + |> Enum.at(0) + + encoded_url = + abi + |> Encoder.encode_function_call(["http://localhost:#{bypass.port}/api/card/{id}"]) + |> String.replace("4cf12d26", "") + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_call", + params: [ + %{ + data: + "0x0e89341c0000000000000000000000000000000000000000000000000000000000000309", + to: "0x5caebd3b32e210e85ce3e9d51638b9c445481567" + }, + "latest" + ] + } + ], + _options -> + {:ok, + [ + %{ + id: 0, + jsonrpc: "2.0", + result: encoded_url + } + ]} + end) + + Bypass.expect( + bypass, + "GET", + "/api/card/0000000000000000000000000000000000000000000000000000000000000309", + fn conn -> + Conn.resp(conn, 200, json) + end + ) + + insert(:token, + contract_address: build(:address, hash: "0x5caebd3b32e210e85ce3e9d51638b9c445481567"), + type: "ERC-1155" + ) + + assert [ + {:ok, + %Instance{ + metadata: %{ + "name" => "Sérgio Mendonça 0000000000000000000000000000000000000000000000000000000000000309" + } + }} + ] = Helper.batch_fetch_instances([{"0x5caebd3b32e210e85ce3e9d51638b9c445481567", 777}]) + end + + test "fetch ipfs of ipfs/{id} format" do + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_call", + params: [ + %{ + data: + "0xc87b56dd0000000000000000000000000000000000000000000000000000000000000000", + to: "0x7e01CC81fCfdf6a71323900288A69e234C464f63" + }, + "latest" + ] + } + ], + _options -> + {:ok, + [ + %{ + id: 0, + jsonrpc: "2.0", + result: + "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000033697066732f516d6439707654684577676a544262456b4e6d6d47466263704a4b773137666e524241543454643472636f67323200000000000000000000000000" + } + ]} + end) + + Application.put_env(:explorer, :http_adapter, Explorer.Mox.HTTPoison) + + Explorer.Mox.HTTPoison + |> expect(:get, fn "https://ipfs.io/ipfs/Qmd9pvThEwgjTBbEkNmmGFbcpJKw17fnRBAT4Td4rcog22", _headers, _options -> + {:ok, %HTTPoison.Response{status_code: 200, body: "123", headers: [{"Content-Type", "image/jpg"}]}} + end) + + insert(:token, + contract_address: build(:address, hash: "0x7e01CC81fCfdf6a71323900288A69e234C464f63"), + type: "ERC-721" + ) + + assert [ + {:ok, + %Instance{ + metadata: %{ + "image" => "https://ipfs.io/ipfs/Qmd9pvThEwgjTBbEkNmmGFbcpJKw17fnRBAT4Td4rcog22" + } + }} + ] = Helper.batch_fetch_instances([{"0x7e01CC81fCfdf6a71323900288A69e234C464f63", 0}]) + + Application.put_env(:explorer, :http_adapter, HTTPoison) + end + end +end diff --git a/apps/indexer/test/test_helper.exs b/apps/indexer/test/test_helper.exs index 0df43337026a..381b6272bc29 100644 --- a/apps/indexer/test/test_helper.exs +++ b/apps/indexer/test/test_helper.exs @@ -17,6 +17,7 @@ end Mox.defmock(EthereumJSONRPC.Mox, for: EthereumJSONRPC.Transport) Mox.defmock(Indexer.BufferedTaskTest.RetryableTask, for: Indexer.BufferedTask) Mox.defmock(Indexer.BufferedTaskTest.ShrinkableTask, for: Indexer.BufferedTask) +Mox.defmock(Explorer.Mox.HTTPoison, for: HTTPoison.Base) ExUnit.configure(formatters: [JUnitFormatter, ExUnit.CLIFormatter]) ExUnit.start() diff --git a/config/config_helper.exs b/config/config_helper.exs index 23224e698b92..a7e0706d3088 100644 --- a/config/config_helper.exs +++ b/config/config_helper.exs @@ -1,6 +1,7 @@ defmodule ConfigHelper do import Bitwise alias Explorer.ExchangeRates.Source + alias Explorer.Market.History.Source.{MarketCap, Price} alias Indexer.Transform.Blocks @spec hackney_options() :: any() @@ -88,10 +89,29 @@ defmodule ConfigHelper do @spec exchange_rates_source() :: Source.CoinGecko | Source.CoinMarketCap def exchange_rates_source do - cond do - System.get_env("EXCHANGE_RATES_SOURCE") == "coin_gecko" -> Source.CoinGecko - System.get_env("EXCHANGE_RATES_SOURCE") == "coin_market_cap" -> Source.CoinMarketCap - true -> Source.CoinGecko + case System.get_env("EXCHANGE_RATES_MARKET_CAP_SOURCE") do + "coin_gecko" -> Source.CoinGecko + "coin_market_cap" -> Source.CoinMarketCap + _ -> Source.CoinGecko + end + end + + @spec exchange_rates_market_cap_source() :: MarketCap.CoinGecko | MarketCap.CoinMarketCap + def exchange_rates_market_cap_source do + case System.get_env("EXCHANGE_RATES_MARKET_CAP_SOURCE") do + "coin_gecko" -> MarketCap.CoinGecko + "coin_market_cap" -> MarketCap.CoinMarketCap + _ -> MarketCap.CoinGecko + end + end + + @spec exchange_rates_price_source() :: Price.CoinGecko | Price.CoinMarketCap | Price.CryptoCompare + def exchange_rates_price_source do + case System.get_env("EXCHANGE_RATES_PRICE_SOURCE") do + "coin_gecko" -> Price.CoinGecko + "coin_market_cap" -> Price.CoinMarketCap + "crypto_compare" -> Price.CryptoCompare + _ -> Price.CryptoCompare end end diff --git a/config/runtime.exs b/config/runtime.exs index 81f5e8aaec6a..e167253514c5 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -164,6 +164,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 ### ################ @@ -267,7 +270,10 @@ config :explorer, Explorer.ExchangeRates, enabled: !ConfigHelper.parse_bool_env_var("DISABLE_EXCHANGE_RATES"), fetch_btc_value: ConfigHelper.parse_bool_env_var("EXCHANGE_RATES_FETCH_BTC_VALUE") -config :explorer, Explorer.ExchangeRates.Source, source: ConfigHelper.exchange_rates_source() +config :explorer, Explorer.ExchangeRates.Source, + source: ConfigHelper.exchange_rates_source(), + price_source: ConfigHelper.exchange_rates_price_source(), + market_cap_source: ConfigHelper.exchange_rates_market_cap_source() config :explorer, Explorer.ExchangeRates.Source.CoinMarketCap, api_key: System.get_env("EXCHANGE_RATES_COINMARKETCAP_API_KEY"), @@ -445,8 +451,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"), @@ -502,13 +511,16 @@ config :indexer, Indexer.Fetcher.BlockReward, config :indexer, Indexer.Fetcher.TokenInstance.Retry, concurrency: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_INSTANCE_RETRY_CONCURRENCY", 10), + batch_size: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_INSTANCE_RETRY_BATCH_SIZE", 10), refetch_interval: ConfigHelper.parse_time_env_var("INDEXER_TOKEN_INSTANCE_RETRY_REFETCH_INTERVAL", "24h") config :indexer, Indexer.Fetcher.TokenInstance.Realtime, - concurrency: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_INSTANCE_REALTIME_CONCURRENCY", 10) + concurrency: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_INSTANCE_REALTIME_CONCURRENCY", 10), + batch_size: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_INSTANCE_REALTIME_BATCH_SIZE", 1) config :indexer, Indexer.Fetcher.TokenInstance.Sanitize, - concurrency: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_INSTANCE_SANITIZE_CONCURRENCY", 10) + concurrency: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_INSTANCE_SANITIZE_CONCURRENCY", 10), + batch_size: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_INSTANCE_SANITIZE_BATCH_SIZE", 10) config :indexer, Indexer.Fetcher.InternalTransaction, batch_size: ConfigHelper.parse_integer_env_var("INDEXER_INTERNAL_TRANSACTIONS_BATCH_SIZE", 10), diff --git a/config/test.exs b/config/test.exs index c87adaeda1de..92ddca6a93a0 100644 --- a/config/test.exs +++ b/config/test.exs @@ -10,6 +10,8 @@ config :logger, :ecto, config :logger, :error, path: Path.absname("logs/test/error.log") -config :explorer, Explorer.ExchangeRates, +config :explorer, Explorer.ExchangeRates, store: :none + +config :explorer, Explorer.ExchangeRates.Source, source: Explorer.ExchangeRates.Source.NoOpSource, - store: :none + price_source: Explorer.ExchangeRates.Source.NoOpPriceSource diff --git a/cspell.json b/cspell.json index 2c733cb6d8ad..ef83d12ef464 100644 --- a/cspell.json +++ b/cspell.json @@ -519,7 +519,12 @@ "erts", "Asfpp", "Nerg", - "secp" + "secp", + "qwertyuioiuytrewertyuioiuytrertyuio", + "urlset", + "lastmod", + "qitmeer", + "meer", ], "enableFiletypes": [ "dotenv", diff --git a/docker-compose/docker-compose-no-build-erigon.yml b/docker-compose/docker-compose-no-build-erigon.yml index 29231c45d1ef..5c93dd2547f3 100644 --- a/docker-compose/docker-compose-no-build-erigon.yml +++ b/docker-compose/docker-compose-no-build-erigon.yml @@ -35,6 +35,7 @@ services: DATABASE_URL: postgresql://postgres:@host.docker.internal:7432/blockscout?ssl=false ECTO_USE_SSL: 'false' SECRET_KEY_BASE: '56NtB48ear7+wMSf0IQuWDAAazhpb31qyc7GiyspBP2vh7t5zlCsF5QDv76chXeN' + MIX_ENV: 'prod' ports: - 4000:4000 volumes: 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 index 35dbc93a81b0..bc96e61f854d 100644 --- a/docker-compose/docker-compose-no-build-external-frontend.yml +++ b/docker-compose/docker-compose-no-build-external-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' @@ -40,6 +41,7 @@ services: CHAIN_ID: '1337' API_V2_ENABLED: 'true' MIX_ENV: 'prod' + ACCOUNT_ENABLED: 'false' ports: - 4000:4000 volumes: diff --git a/docker-compose/docker-compose-no-build-frontend.yml b/docker-compose/docker-compose-no-build-frontend.yml index 76ae066b383a..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' 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..7ce75af41903 100644 --- a/docker-compose/docker-compose-no-build-geth-clique-consensus.yml +++ b/docker-compose/docker-compose-no-build-geth-clique-consensus.yml @@ -32,9 +32,11 @@ 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' + MIX_ENV: 'prod' ports: - 4000:4000 volumes: diff --git a/docker-compose/docker-compose-no-build-geth.yml b/docker-compose/docker-compose-no-build-geth.yml index d8aa27370019..f163cdb1956a 100644 --- a/docker-compose/docker-compose-no-build-geth.yml +++ b/docker-compose/docker-compose-no-build-geth.yml @@ -31,9 +31,11 @@ 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' + MIX_ENV: 'prod' ports: - 4000:4000 volumes: diff --git a/docker-compose/docker-compose-no-build-nethermind.yml b/docker-compose/docker-compose-no-build-nethermind.yml index 4aba06749c91..9488dd597798 100644 --- a/docker-compose/docker-compose-no-build-nethermind.yml +++ b/docker-compose/docker-compose-no-build-nethermind.yml @@ -35,6 +35,7 @@ services: DATABASE_URL: postgresql://postgres:@host.docker.internal:7432/blockscout?ssl=false ECTO_USE_SSL: 'false' SECRET_KEY_BASE: '56NtB48ear7+wMSf0IQuWDAAazhpb31qyc7GiyspBP2vh7t5zlCsF5QDv76chXeN' + MIX_ENV: 'prod' ports: - 4000:4000 volumes: diff --git a/docker-compose/docker-compose-no-build-no-db-container.yml b/docker-compose/docker-compose-no-build-no-db-container.yml index 39232ea66c1d..ad4c844db6f9 100644 --- a/docker-compose/docker-compose-no-build-no-db-container.yml +++ b/docker-compose/docker-compose-no-build-no-db-container.yml @@ -26,6 +26,7 @@ services: DATABASE_URL: postgresql://postgres:@host.docker.internal:5432/blockscout?ssl=false ECTO_USE_SSL: 'false' SECRET_KEY_BASE: '56NtB48ear7+wMSf0IQuWDAAazhpb31qyc7GiyspBP2vh7t5zlCsF5QDv76chXeN' + MIX_ENV: 'prod' ports: - 4000:4000 volumes: diff --git a/docker-compose/docker-compose-no-rust-services.yml b/docker-compose/docker-compose-no-rust-services.yml index a35253cd3279..e6a6336433ff 100644 --- a/docker-compose/docker-compose-no-rust-services.yml +++ b/docker-compose/docker-compose-no-rust-services.yml @@ -27,7 +27,8 @@ 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 + MIX_ENV: 'prod' 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 c7dfa9eae682..a8ae85965cc8 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= @@ -31,7 +32,8 @@ EMISSION_FORMAT=DEFAULT # SUPPLY_MODULE= COIN= EXCHANGE_RATES_COIN= -# EXCHANGE_RATES_SOURCE= +# EXCHANGE_RATES_MARKET_CAP_SOURCE= +# EXCHANGE_RATES_PRICE_SOURCE= # EXCHANGE_RATES_COINGECKO_COIN_ID= # EXCHANGE_RATES_COINGECKO_API_KEY= # EXCHANGE_RATES_COINMARKETCAP_API_KEY= @@ -114,7 +116,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= @@ -208,3 +212,6 @@ ACCOUNT_REDIS_URL=redis://redis_db:6379 EIP_1559_ELASTICITY_MULTIPLIER=2 # API_SENSITIVE_ENDPOINTS_KEY= # ACCOUNT_VERIFICATION_EMAIL_RESEND_INTERVAL= +# INDEXER_TOKEN_INSTANCE_RETRY_BATCH_SIZE=10 +# INDEXER_TOKEN_INSTANCE_REALTIME_BATCH_SIZE=1 +# INDEXER_TOKEN_INSTANCE_SANITIZE_BATCH_SIZE=10 diff --git a/docker-compose/envs/common-frontend.env b/docker-compose/envs/common-frontend.env index 84da58952bc6..57e8a6a5f7a2 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_HOMEPAGE_CHARTS="['daily_txs']" -NEXT_PUBLIC_VISUALIZE_API_HOST=http://visualizer:80 -NEXT_PUBLIC_IS_TESTNET='true' +NEXT_PUBLIC_APP_PROTOCOL=http +NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] +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' +NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR=rgb(255, 255, 255) +NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws diff --git a/docker-compose/proxy/default.conf.template b/docker-compose/proxy/default.conf.template index 3967119647c0..0ea0e3bf9fb2 100644 --- a/docker-compose/proxy/default.conf.template +++ b/docker-compose/proxy/default.conf.template @@ -1,46 +1,72 @@ +map $http_upgrade $connection_upgrade { + + default upgrade; + '' close; +} + server { listen 80; server_name localhost; proxy_http_version 1.1; + location / { - proxy_pass ${FRONT_PROXY_PASS}; + 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 /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 = / { + 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 /api/ { - proxy_pass http://backend:4000/api; + location ~ ^/(_next|node-api|apps|account|accounts|favicon|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 81; + 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://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; + 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 82; + listen 8081; server_name localhost; proxy_http_version 1.1; proxy_hide_header Access-Control-Allow-Origin; @@ -48,15 +74,31 @@ server { 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://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; + 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/services/docker-compose-nginx.yml b/docker-compose/services/docker-compose-nginx.yml index 295913b4607b..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: - "../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/Dockerfile b/docker/Dockerfile index 9b42fcbe71b2..5a8569a1d0bc 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 @@ -70,7 +70,7 @@ ENV RELEASE_VERSION=${RELEASE_VERSION} ARG BLOCKSCOUT_VERSION ENV BLOCKSCOUT_VERSION=${BLOCKSCOUT_VERSION} -RUN apk --no-cache --update add jq +RUN apk --no-cache --update add jq curl WORKDIR /app diff --git a/docker/Makefile b/docker/Makefile index b9f15c84c0b4..3c5289bc6ac2 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 @@ -225,8 +228,11 @@ endif ifdef EXCHANGE_RATES_COIN BLOCKSCOUT_CONTAINER_PARAMS += -e 'EXCHANGE_RATES_COIN=$(EXCHANGE_RATES_COIN)' endif -ifdef EXCHANGE_RATES_SOURCE - BLOCKSCOUT_CONTAINER_PARAMS += -e 'EXCHANGE_RATES_SOURCE=$(EXCHANGE_RATES_SOURCE)' +ifdef EXCHANGE_RATES_MARKET_CAP_SOURCE + BLOCKSCOUT_CONTAINER_PARAMS += -e 'EXCHANGE_RATES_MARKET_CAP_SOURCE=$(EXCHANGE_RATES_MARKET_CAP_SOURCE)' +endif +ifdef EXCHANGE_RATES_PRICE_SOURCE + BLOCKSCOUT_CONTAINER_PARAMS += -e 'EXCHANGE_RATES_PRICE_SOURCE=$(EXCHANGE_RATES_PRICE_SOURCE)' endif ifdef EXCHANGE_RATES_COINGECKO_COIN_ID BLOCKSCOUT_CONTAINER_PARAMS += -e 'EXCHANGE_RATES_COINGECKO_COIN_ID=$(EXCHANGE_RATES_COINGECKO_COIN_ID)' @@ -561,9 +567,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 @@ -723,6 +735,15 @@ endif ifdef ACCOUNT_VERIFICATION_EMAIL_RESEND_INTERVAL BLOCKSCOUT_CONTAINER_PARAMS += -e 'ACCOUNT_VERIFICATION_EMAIL_RESEND_INTERVAL=$(ACCOUNT_VERIFICATION_EMAIL_RESEND_INTERVAL)' endif +ifdef INDEXER_TOKEN_INSTANCE_RETRY_BATCH_SIZE + BLOCKSCOUT_CONTAINER_PARAMS += -e 'INDEXER_TOKEN_INSTANCE_RETRY_BATCH_SIZE=$(INDEXER_TOKEN_INSTANCE_RETRY_BATCH_SIZE)' +endif +ifdef INDEXER_TOKEN_INSTANCE_REALTIME_BATCH_SIZE + BLOCKSCOUT_CONTAINER_PARAMS += -e 'INDEXER_TOKEN_INSTANCE_REALTIME_BATCH_SIZE=$(INDEXER_TOKEN_INSTANCE_REALTIME_BATCH_SIZE)' +endif +ifdef INDEXER_TOKEN_INSTANCE_SANITIZE_BATCH_SIZE + BLOCKSCOUT_CONTAINER_PARAMS += -e 'INDEXER_TOKEN_INSTANCE_SANITIZE_BATCH_SIZE=$(INDEXER_TOKEN_INSTANCE_SANITIZE_BATCH_SIZE)' +endif HAS_BLOCKSCOUT_IMAGE := $(shell docker images | grep -sw "${BS_CONTAINER_IMAGE} ") build: 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 c2c9117105d6..962158f0d821 100644 --- a/mix.lock +++ b/mix.lock @@ -6,7 +6,7 @@ "accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm", "11b18c220bcc2eab63b5470c038ef10eb6783bcb1fcdb11aa4137defa5ac1bb8"}, "b58": {:hex, :b58, "1.0.3", "d300d6ae5a3de956a54b9e8220e924e4fee1a349de983df2340fe61e0e464202", [:mix], [], "hexpm", "af62a98a8661fd89978cf3a3a4b5b2ebe82209de6ac6164f0b112e36af72fc59"}, "bamboo": {:hex, :bamboo, "2.3.0", "d2392a2cabe91edf488553d3c70638b532e8db7b76b84b0a39e3dfe492ffd6fc", [:mix], [{:hackney, ">= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.4 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "dd0037e68e108fd04d0e8773921512c940e35d981e097b5793543e3b2f9cd3f6"}, - "bcrypt_elixir": {:hex, :bcrypt_elixir, "3.0.1", "9be815469e6bfefec40fa74658ecbbe6897acfb57614df1416eeccd4903f602c", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "486bb95efb645d1efc6794c1ddd776a186a9a713abf06f45708a6ce324fb96cf"}, + "bcrypt_elixir": {:hex, :bcrypt_elixir, "3.1.0", "0b110a9a6c619b19a7f73fa3004aa11d6e719a67e672d1633dc36b6b2290a0f7", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2ad2acb5a8bc049e8d5aa267802631912bb80d5f4110a178ae7999e69dca1bf7"}, "benchee": {:hex, :benchee, "1.1.0", "f3a43817209a92a1fade36ef36b86e1052627fd8934a8b937ac9ab3a76c43062", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}], "hexpm", "7da57d545003165a012b587077f6ba90b89210fd88074ce3c60ce239eb5e6d93"}, "benchee_csv": {:hex, :benchee_csv, "1.0.0", "0b3b9223290bfcb8003552705bec9bcf1a89b4a83b70bd686e45295c264f3d16", [:mix], [{:benchee, ">= 0.99.0 and < 2.0.0", [hex: :benchee, repo: "hexpm", optional: false]}, {:csv, "~> 2.0", [hex: :csv, repo: "hexpm", optional: false]}], "hexpm", "cdefb804c021dcf7a99199492026584be9b5a21d6644ac0d01c81c5d97c520d5"}, "briefly": {:git, "https://github.com/CargoSense/briefly.git", "678a3763e72a7d1f23ac71b209b96bd199bffbbb", []}, @@ -15,13 +15,13 @@ "bypass": {:hex, :bypass, "2.1.0", "909782781bf8e20ee86a9cabde36b259d44af8b9f38756173e8f5e2e1fabb9b1", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "d9b5df8fa5b7a6efa08384e9bbecfe4ce61c77d28a4282f79e02f1ef78d96b80"}, "castore": {:hex, :castore, "1.0.3", "7130ba6d24c8424014194676d608cb989f62ef8039efd50ff4b3f33286d06db8", [:mix], [], "hexpm", "680ab01ef5d15b161ed6a95449fac5c6b8f60055677a8e79acf01b27baa4390b"}, "cbor": {:hex, :cbor, "1.0.1", "39511158e8ea5a57c1fcb9639aaa7efde67129678fee49ebbda780f6f24959b0", [:mix], [], "hexpm", "5431acbe7a7908f17f6a9cd43311002836a34a8ab01876918d8cfb709cd8b6a2"}, - "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, + "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, "cldr_utils": {:hex, :cldr_utils, "2.24.1", "5ff8c8c55f96666228827bcf85a23d632022def200566346545d01d15e4c30dc", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "1820300531b5b849d0bc468e5a87cd64f8f2c5191916f548cbe69b2efc203780"}, "cloak": {:hex, :cloak, "1.1.2", "7e0006c2b0b98d976d4f559080fabefd81f0e0a50a3c4b621f85ceeb563e80bb", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "940d5ac4fcd51b252930fd112e319ea5ae6ab540b722f3ca60a85666759b9585"}, "cloak_ecto": {:hex, :cloak_ecto, "1.2.0", "e86a3df3bf0dc8980f70406bcb0af2858bac247d55494d40bc58a152590bd402", [:mix], [{:cloak, "~> 1.1.1", [hex: :cloak, repo: "hexpm", optional: false]}, {:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm", "8bcc677185c813fe64b786618bd6689b1707b35cd95acaae0834557b15a0c62f"}, "coerce": {:hex, :coerce, "1.0.1", "211c27386315dc2894ac11bc1f413a0e38505d808153367bd5c6e75a4003d096", [:mix], [], "hexpm", "b44a691700f7a1a15b4b7e2ff1fa30bebd669929ac8aa43cffe9e2f8bf051cf1"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, - "comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"}, + "comeonin": {:hex, :comeonin, "5.4.0", "246a56ca3f41d404380fc6465650ddaa532c7f98be4bda1b4656b3a37cc13abe", [:mix], [], "hexpm", "796393a9e50d01999d56b7b8420ab0481a7538d0caf80919da493b4a6e51faf1"}, "con_cache": {:hex, :con_cache, "1.0.0", "6405e2bd5d5005334af72939432783562a8c35a196c2e63108fe10bb97b366e6", [:mix], [], "hexpm", "4d1f5cb1a67f3c1a468243dc98d10ac83af7f3e33b7e7c15999dc2c9bc0a551e"}, "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, "cors_plug": {:hex, :cors_plug, "3.0.3", "7c3ac52b39624bc616db2e937c282f3f623f25f8d550068b6710e58d04a0e330", [:mix], [{:plug, "~> 1.13", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3f2d759e8c272ed3835fab2ef11b46bddab8c1ab9528167bd463b6452edf830d"}, @@ -35,37 +35,37 @@ "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "decorator": {:hex, :decorator, "1.4.0", "a57ac32c823ea7e4e67f5af56412d12b33274661bb7640ec7fc882f8d23ac419", [:mix], [], "hexpm", "0a07cedd9083da875c7418dea95b78361197cf2bf3211d743f6f7ce39656597f"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, - "dialyxir": {:hex, :dialyxir, "1.3.0", "fd1672f0922b7648ff9ce7b1b26fcf0ef56dda964a459892ad15f6b4410b5284", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "00b2a4bcd6aa8db9dcb0b38c1225b7277dca9bc370b6438715667071a304696f"}, + "dialyxir": {:hex, :dialyxir, "1.4.1", "a22ed1e7bd3a3e3f197b68d806ef66acb61ee8f57b3ac85fc5d57354c5482a93", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "84b795d6d7796297cca5a3118444b80c7d94f7ce247d49886e7c291e1ae49801"}, "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"}, - "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"}, + "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.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, "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"}, + "ex_abi": {:hex, :ex_abi, "0.6.2", "a33d0df94efd54d6879d20ab8cb6561432f13cbb1e912801d2e97ef50f795e9d", [:mix], [{:ex_keccak, "~> 0.7.3", [hex: :ex_keccak, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e901c580a2491b19a6c27891c6d40cd8fe41d996c8c2ec02d5dd4c5a86239bc6"}, "ex_cldr": {:hex, :ex_cldr, "2.37.2", "c45041534ec60af367c4c1af02a608576118044fe3c441c782fd424061d6b517", [:mix], [{:cldr_utils, "~> 2.21", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: true]}], "hexpm", "c8467b1d5080716ace6621703b6656cb2f9545572a54b341da900791a0cf92ba"}, "ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.15.0", "aadd34e91cfac7ef6b03fe8f47f8c6fa8c5daf3f89b5d9fee64ec545ded839cf", [:mix], [{:ex_cldr, "~> 2.34", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "0521316396c66877a2d636219767560bb2397c583341fcb154ecf9f3000e6ff8"}, "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_numbers": {:hex, :ex_cldr_numbers, "2.32.2", "5e0e3031d3f54b51fe7078a7a94592987b70b06d631bdc88813b222dc5a8b1bd", [: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", "91257684a9c4d6abdf738f0cc5671837de876e69552e8bd4bc5fa1bfd5817713"}, "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_doc": {:hex, :ex_doc, "0.30.6", "5f8b54854b240a2b55c9734c4b1d0dd7bdd41f71a095d42a70445c03cf05a281", [: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", "bd48f2ddacf4e482c727f9293d9498e0881597eae6ddc3d9562bd7923375109f"}, "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_keccak": {:hex, :ex_keccak, "0.7.3", "33298f97159f6b0acd28f6e96ce5ea975a0f4a19f85fe615b4f4579b88b24d06", [:mix], [{:rustler, ">= 0.0.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.6.1", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "4c5e6d9d5f77b64ab48769a0166a9814180d40ced68ed74ce60a5174ab55b3fc"}, "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"}, - "ex_secp256k1": {:hex, :ex_secp256k1, "0.7.0", "fa555152e8680c1e2df09ccc2884eccd25d8bc328c630b0b8952abe3745edc8f", [:mix], [{:rustler, ">= 0.0.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.6", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "2a8754c7b3d83bbcab4a188ed033254ca5c6ac252188ced0d382ca343096bf73"}, + "ex_secp256k1": {:hex, :ex_secp256k1, "0.7.2", "33398c172813b90fab9ab75c12b98d16cfab472c6dcbde832b13c45ce1c01947", [:mix], [{:rustler, ">= 0.0.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.6", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "f3b1bf56e6992e28b9d86e3bf741a4aca3e641052eb47d13ae4f5f4d4944bdaf"}, "ex_utils": {:hex, :ex_utils, "0.1.7", "2c133e0bcdc49a858cf8dacf893308ebc05bc5fba501dc3d2935e65365ec0bf3", [:mix], [], "hexpm", "66d4fe75285948f2d1e69c2a5ddd651c398c813574f8d36a9eef11dc20356ef6"}, "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.2", "90830764b7a428c07bb1475628b4e020f34a081605b9484df6b7b3fe7e32ee9c", [: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", "2ca98dabbfd10215cb2072fc5c83d91f6d1e1a7b5e576300c0ec6825ac97a070"}, + "exvcr": {:hex, :exvcr, "0.14.4", "1aa5fe7d3f10b117251c158f8d28b39f7fc73d0a7628b2d0b75bf8cfb1111576", [: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", "4e600568c02ed29d46bc2e2c74927d172ba06658aa8b14705c0207363c44cc94"}, "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"}, - "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"}, + "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.2", "d7ff544ddae5e1cb49e9cf7fa4e356d7f41b283989a1c304bfc47a8cc1cf966f", [:rebar3], [{:certifi, "~>2.12.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.4.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", "af94d5c9f97857db257090a4a10e5426ecb6f4918aa5cc666798566ae14b65fd"}, "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"}, "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"}, @@ -98,7 +98,7 @@ "oauth2": {:hex, :oauth2, "2.0.1", "70729503e05378697b958919bb2d65b002ba6b28c8112328063648a9348aaa3f", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "c64e20d4d105bcdbcbe03170fb530d0eddc3a3e6b135a87528a22c8aecf74c52"}, "optimal": {:hex, :optimal, "0.3.6", "46bbf52fbbbd238cda81e02560caa84f93a53c75620f1fe19e81e4ae7b07d1dd", [:mix], [], "hexpm", "1a06ea6a653120226b35b283a1cd10039550f2c566edcdec22b29316d73640fd"}, "parallel_stream": {:hex, :parallel_stream, "1.1.0", "f52f73eb344bc22de335992377413138405796e0d0ad99d995d9977ac29f1ca9", [:mix], [], "hexpm", "684fd19191aedfaf387bbabbeb8ff3c752f0220c8112eb907d797f4592d6e871"}, - "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, + "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "phoenix": {:hex, :phoenix, "1.5.14", "2d5db884be496eefa5157505ec0134e66187cb416c072272420c5509d67bf808", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "207f1aa5520320cbb7940d7ff2dde2342162cf513875848f88249ea0ba02fef7"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.2", "b21bd01fdeffcfe2fab49e4942aa938b6d3e89e93a480d4aee58085560a0bc0d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "70242edd4601d50b69273b057ecf7b684644c19ee750989fd555625ae4ce8f5d"}, "phoenix_html": {:hex, :phoenix_html, "3.0.4", "232d41884fe6a9c42d09f48397c175cd6f0d443aaa34c7424da47604201df2e1", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "ce17fd3cf815b2ed874114073e743507704b1f5288bb03c304a77458485efc8b"}, @@ -109,7 +109,7 @@ "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, - "postgrex": {:hex, :postgrex, "0.17.2", "a3ec9e3239d9b33f1e5841565c4eb200055c52cc0757a22b63ca2d529bbe764c", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "80a918a9e9531d39f7bd70621422f3ebc93c01618c645f2d91306f50041ed90c"}, + "postgrex": {:hex, :postgrex, "0.17.3", "c92cda8de2033a7585dae8c61b1d420a1a1322421df84da9a82a6764580c503d", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "946cf46935a4fdca7a81448be76ba3503cff082df42c6ec1ff16a4bdfbfb098d"}, "prometheus": {:hex, :prometheus, "4.10.0", "792adbf0130ff61b5fa8826f013772af24b6e57b984445c8d602c8a0355704a1", [:mix, :rebar3], [{:quantile_estimator, "~> 0.2.1", [hex: :quantile_estimator, repo: "hexpm", optional: false]}], "hexpm", "2a99bb6dce85e238c7236fde6b0064f9834dc420ddbd962aac4ea2a3c3d59384"}, "prometheus_ecto": {:hex, :prometheus_ecto, "1.4.3", "3dd4da1812b8e0dbee81ea58bb3b62ed7588f2eae0c9e97e434c46807ff82311", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm", "8d66289f77f913b37eda81fd287340c17e61a447549deb28efc254532b2bed82"}, "prometheus_ex": {:git, "https://github.com/lanodan/prometheus.ex", "31f7fbe4b71b79ba27efc2a5085746c4011ceb8f", [branch: "fix/elixir-1.14"]}, @@ -123,10 +123,10 @@ "ratio": {:hex, :ratio, "2.4.2", "c8518f3536d49b1b00d88dd20d49f8b11abb7819638093314a6348139f14f9f9", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:numbers, "~> 5.2.0", [hex: :numbers, repo: "hexpm", optional: false]}], "hexpm", "441ef6f73172a3503de65ccf1769030997b0d533b1039422f1e5e0e0b4cbf89e"}, "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"}, + "rustler_precompiled": {:hex, :rustler_precompiled, "0.6.3", "f838d94bc35e1844973ee7266127b156fdc962e9e8b7ff666c8fb4fed7964d23", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "e18ecca3669a7454b3a2be75ae6c3ef01d550bc9a8cf5fbddcfff843b881d7c6"}, + "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_datadog": {:hex, :spandex_datadog, "1.4.0", "0594b9655b0af00ab9137122616bc0208b68ceec01e9916ab13d6fbb33dcce35", [:mix], [{:msgpax, "~> 2.2.1 or ~> 2.3", [hex: :msgpax, repo: "hexpm", optional: false]}, {:spandex, "~> 3.2", [hex: :spandex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "360f8e1b4db238c1749c4872b1697b096429927fa42b8858d0bb782067380123"}, "spandex_ecto": {:hex, :spandex_ecto, "0.7.0", "259ad2feb7c834e774ec623f99c0fbacca8d60a73be212f92b75e37f853c81be", [:mix], [{:spandex, "~> 2.2 or ~> 3.0", [hex: :spandex, repo: "hexpm", optional: false]}], "hexpm", "c64784be79d95538013b7c60828830411c5c7aff1f4e8d66dfe564b3c83b500e"}, "spandex_phoenix": {:hex, :spandex_phoenix, "1.1.0", "9cff829d05258dd49a227c56711b19b69a8fd5d4873d8e9a92a4f4097e7322ab", [:mix], [{:phoenix, "~> 1.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.3", [hex: :plug, repo: "hexpm", optional: false]}, {:spandex, "~> 2.2 or ~> 3.0", [hex: :spandex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "265fe05c1736485fbb75d66ef7576682ebf6428c391dd54d22217f612fd4ddad"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, @@ -139,7 +139,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,