From aee79da124c56e05c64f337afcd367e1cdc45640 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop Date: Fri, 22 Sep 2023 13:53:35 +0300 Subject: [PATCH 001/224] Don't override transaction status --- CHANGELOG.md | 2 + .../import/runner/internal_transactions.ex | 33 ++++++++++++++-- .../runner/internal_transactions_test.exs | 38 ++++--------------- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53cba9522b71..0a707ea38114 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ ### Fixes +- [#8513](https://github.com/blockscout/blockscout/pull/8513) - Don't override transaction status + ### Chore - [#8504](https://github.com/blockscout/blockscout/pull/8504) - Deploy new UI through Makefile diff --git a/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex b/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex index 8b26d9b2c811..5ca6ef92c67a 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex @@ -311,7 +311,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do from( t in Transaction, where: t.block_hash in ^pending_block_hashes, - select: map(t, [:hash, :block_hash, :block_number, :cumulative_gas_used]), + select: map(t, [:hash, :block_hash, :block_number, :cumulative_gas_used, :status]), # Enforce Transaction ShareLocks order (see docs: sharelocks.md) order_by: [asc: t.hash], lock: "FOR NO KEY UPDATE" @@ -514,6 +514,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do timeout, timestamps, first_trace, + transaction_from_db, transaction_receipt_from_node ) @@ -525,7 +526,8 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do transaction_hashes_iterator, timeout, timestamps, - first_trace + first_trace, + transaction_from_db ) true -> @@ -539,6 +541,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do timeout, timestamps, first_trace, + transaction_from_db, transaction_receipt_from_node ) end @@ -579,6 +582,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do end end + # credo:disable-for-next-line defp update_transactions_inner( repo, valid_internal_transactions, @@ -587,6 +591,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do timeout, timestamps, first_trace, + transaction_from_db, transaction_receipt_from_node \\ nil ) do valid_internal_transactions_count = Enum.count(valid_internal_transactions) @@ -595,6 +600,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do set = generate_transaction_set_to_update( first_trace, + transaction_from_db, transaction_receipt_from_node, timestamps, txs_with_error_in_internal_txs @@ -628,19 +634,20 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do def generate_transaction_set_to_update( first_trace, + transaction_from_db, transaction_receipt_from_node, timestamps, txs_with_error_in_internal_txs ) do default_set = [ created_contract_address_hash: first_trace.created_contract_address_hash, - error: first_trace.error, - status: first_trace.status, updated_at: timestamps.updated_at ] set = default_set + |> put_status_in_update_set(first_trace, transaction_from_db) + |> put_error_in_update_set(first_trace, transaction_from_db, transaction_receipt_from_node) |> Keyword.put_new(:block_hash, first_trace.block_hash) |> Keyword.put_new(:block_number, first_trace.block_number) |> Keyword.put_new(:index, transaction_receipt_from_node && transaction_receipt_from_node.transaction_index) @@ -665,6 +672,24 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do filtered_set end + defp put_status_in_update_set(update_set, first_trace, %{status: nil}), + do: Keyword.put_new(update_set, :status, first_trace.status) + + defp put_status_in_update_set(update_set, _first_trace, _transaction_from_db), do: update_set + + defp put_error_in_update_set(update_set, first_trace, _transaction_from_db, %{status: :error}), + do: Keyword.put_new(update_set, :error, first_trace.error) + + defp put_error_in_update_set(update_set, first_trace, %{status: :error}, _transaction_receipt_from_node), + do: Keyword.put_new(update_set, :error, first_trace.error) + + defp put_error_in_update_set(update_set, first_trace, _transaction_from_db, _transaction_receipt_from_node) do + case update_set[:status] do + :error -> Keyword.put_new(update_set, :error, first_trace.error) + _ -> update_set + end + end + defp remove_consensus_of_invalid_blocks(repo, invalid_block_numbers) do minimal_block = EthereumJSONRPC.first_block_to_fetch(:trace_first_block) diff --git a/apps/explorer/test/explorer/chain/import/runner/internal_transactions_test.exs b/apps/explorer/test/explorer/chain/import/runner/internal_transactions_test.exs index 033c2b8cab3b..ffe02c5dc7df 100644 --- a/apps/explorer/test/explorer/chain/import/runner/internal_transactions_test.exs +++ b/apps/explorer/test/explorer/chain/import/runner/internal_transactions_test.exs @@ -6,7 +6,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do alias Explorer.Chain.Import.Runner.InternalTransactions describe "run/1" do - test "transaction's status becomes :error when its internal_transaction has an error" do + test "transaction's status doesn't become :error when its internal_transaction has an error" do transaction = insert(:transaction) |> with_block(status: :ok) insert(:pending_block_operation, block_hash: transaction.block_hash, block_number: transaction.block_number) @@ -19,7 +19,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do assert {:ok, _} = run_internal_transactions([internal_transaction_changes]) - assert :error == Repo.get(Transaction, transaction.hash).status + assert :ok == Repo.get(Transaction, transaction.hash).status end test "transaction's has_error_in_internal_txs become true when its internal_transaction (where index != 0) has an error" do @@ -61,7 +61,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do assert {:ok, _} = run_internal_transactions([internal_transaction_changes]) tx = Repo.get(Transaction, transaction.hash) - assert :error == tx.status + assert :ok == tx.status assert false == tx.has_error_in_internal_txs end @@ -90,7 +90,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do assert false == tx.has_error_in_internal_txs end - test "simple coin transfer's status becomes :error when its internal_transaction has an error" do + test "simple coin transfer's status doesn't become :error when its internal_transaction has an error" do transaction = insert(:transaction) |> with_block(status: :ok) insert(:pending_block_operation, block_hash: transaction.block_hash, block_number: transaction.block_number) @@ -104,10 +104,10 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do assert {:ok, _} = run_internal_transactions([internal_transaction_changes]) - assert :error == Repo.get(Transaction, transaction.hash).status + assert :ok == Repo.get(Transaction, transaction.hash).status end - test "for block with 2 simple coin transfer's statuses become :error when its both internal_transactions has an error" do + test "for block with 2 simple coin transfer's statuses doesn't become :error even when its both internal_transactions has an error" do a_block = insert(:block, number: 1000) transaction1 = insert(:transaction) |> with_block(a_block, status: :ok) transaction2 = insert(:transaction) |> with_block(a_block, status: :ok) @@ -128,31 +128,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do assert {:ok, _} = run_internal_transactions([internal_transaction_changes_1, internal_transaction_changes_2]) - assert :error == Repo.get(Transaction, transaction1.hash).status - assert :error == Repo.get(Transaction, transaction2.hash).status - end - - test "for block with 2 simple coin transfer's only status become :error for tx where internal_transactions has an error" do - a_block = insert(:block, number: 1000) - transaction1 = insert(:transaction) |> with_block(a_block, status: :ok) - transaction2 = insert(:transaction) |> with_block(a_block, status: :ok) - insert(:pending_block_operation, block_hash: a_block.hash, block_number: a_block.number) - - assert :ok == transaction1.status - assert :ok == transaction2.status - - index = 0 - error = "Out of gas" - - internal_transaction_changes_1 = - make_internal_transaction_changes_for_simple_coin_transfers(transaction1, index, error) - - internal_transaction_changes_2 = - make_internal_transaction_changes_for_simple_coin_transfers(transaction2, index, nil) - - assert {:ok, _} = run_internal_transactions([internal_transaction_changes_1, internal_transaction_changes_2]) - - assert :error == Repo.get(Transaction, transaction1.hash).status + assert :ok == Repo.get(Transaction, transaction1.hash).status assert :ok == Repo.get(Transaction, transaction2.hash).status end From 7afe8e19b59923ac4bf1b065c6e3a0f0a0ed9d45 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop Date: Fri, 21 Jul 2023 14:10:46 +0400 Subject: [PATCH 002/224] Add CoinBalance fetcher init query limit --- CHANGELOG.md | 1 + apps/explorer/lib/explorer/chain.ex | 10 +++++++++- apps/indexer/lib/indexer/fetcher/coin_balance.ex | 3 ++- config/runtime.exs | 4 +++- docker-compose/envs/common-blockscout.env | 1 + 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a707ea38114..649eee411014 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - [#8180](https://github.com/blockscout/blockscout/pull/8180) - Deposits and Withdrawals for Polygon Edge +- [#7996](https://github.com/blockscout/blockscout/pull/7996) - Add CoinBalance fetcher init query limit ### Fixes diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 3c4239b7ce0f..6aa532de03e1 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2232,7 +2232,7 @@ defmodule Explorer.Chain do ) query - |> add_fetcher_limit(limited?) + |> add_coin_balances_fetcher_limit(limited?) |> Repo.stream_reduce(initial, reducer) end @@ -6315,6 +6315,14 @@ defmodule Explorer.Chain do limit(query, ^token_balances_fetcher_limit) end + defp add_coin_balances_fetcher_limit(query, false), do: query + + defp add_coin_balances_fetcher_limit(query, true) do + coin_balances_fetcher_limit = Application.get_env(:indexer, :coin_balances_fetcher_init_limit) + + limit(query, ^coin_balances_fetcher_limit) + end + def put_has_token_transfers_to_tx(query, true), do: query def put_has_token_transfers_to_tx(query, false) do diff --git a/apps/indexer/lib/indexer/fetcher/coin_balance.ex b/apps/indexer/lib/indexer/fetcher/coin_balance.ex index ce80805e5d13..26d5d399624a 100644 --- a/apps/indexer/lib/indexer/fetcher/coin_balance.ex +++ b/apps/indexer/lib/indexer/fetcher/coin_balance.ex @@ -69,7 +69,8 @@ defmodule Indexer.Fetcher.CoinBalance do address_fields |> entry() |> reducer.(acc) - end + end, + true ) final diff --git a/config/runtime.exs b/config/runtime.exs index 54b268988fc6..2d51abb6198b 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -431,7 +431,9 @@ config :indexer, hide_indexing_progress_alert: ConfigHelper.parse_bool_env_var("INDEXER_HIDE_INDEXING_PROGRESS_ALERT"), fetcher_init_limit: ConfigHelper.parse_integer_env_var("INDEXER_FETCHER_INIT_QUERY_LIMIT", 100), token_balances_fetcher_init_limit: - ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_BALANCES_FETCHER_INIT_QUERY_LIMIT", 100_000) + ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_BALANCES_FETCHER_INIT_QUERY_LIMIT", 100_000), + coin_balances_fetcher_init_limit: + ConfigHelper.parse_integer_env_var("INDEXER_COIN_BALANCES_FETCHER_INIT_QUERY_LIMIT", 2000) config :indexer, Indexer.Supervisor, enabled: !ConfigHelper.parse_bool_env_var("DISABLE_INDEXER") diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 39ad8823cd83..5d8c29b3c563 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -139,6 +139,7 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_REALTIME_FETCHER_MAX_GAP= # INDEXER_FETCHER_INIT_QUERY_LIMIT= # INDEXER_TOKEN_BALANCES_FETCHER_INIT_QUERY_LIMIT= +# INDEXER_COIN_BALANCES_FETCHER_INIT_QUERY_LIMIT= # INDEXER_DISABLE_WITHDRAWALS_FETCHER= # WITHDRAWALS_FIRST_BLOCK= # TOKEN_ID_MIGRATION_FIRST_BLOCK= From 9f07e5a3646bb70eaef567d6d3c8ee1ea30c9cf6 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 25 Sep 2023 15:13:16 +0300 Subject: [PATCH 003/224] Move PolygonEdge-related migration to the corresponding repository --- CHANGELOG.md | 1 + .../20230731130103_modify_collated_gas_price_constraint.exs | 0 2 files changed, 1 insertion(+) rename apps/explorer/priv/{repo => polygon_edge}/migrations/20230731130103_modify_collated_gas_price_constraint.exs (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a707ea38114..b3ba42ee4077 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ ### Chore +- [#8529](https://github.com/blockscout/blockscout/pull/8529) - Move PolygonEdge-related migration to the corresponding ecto repository - [#8504](https://github.com/blockscout/blockscout/pull/8504) - Deploy new UI through Makefile - [#8501](https://github.com/blockscout/blockscout/pull/8501) - Conceal secondary ports in docker compose setup diff --git a/apps/explorer/priv/repo/migrations/20230731130103_modify_collated_gas_price_constraint.exs b/apps/explorer/priv/polygon_edge/migrations/20230731130103_modify_collated_gas_price_constraint.exs similarity index 100% rename from apps/explorer/priv/repo/migrations/20230731130103_modify_collated_gas_price_constraint.exs rename to apps/explorer/priv/polygon_edge/migrations/20230731130103_modify_collated_gas_price_constraint.exs From 776ff695bb974d9118294bcd0b670a1acf22fbfe Mon Sep 17 00:00:00 2001 From: Andrey Atapin Date: Mon, 25 Sep 2023 21:12:21 +0500 Subject: [PATCH 004/224] Fix empty TransferBatch event handling (#7959) * Fix empty TransferBatch event handling * changelog * format * migration * fixed changelog --------- Co-authored-by: Victor Baranov --- CHANGELOG.md | 1 + ...elete_erc_1155_tt_with_empty_token_ids.exs | 9 +++ .../lib/indexer/transform/token_transfers.ex | 58 +++++++++++-------- .../transform/token_transfers_test.exs | 22 +++++++ 4 files changed, 66 insertions(+), 24 deletions(-) create mode 100644 apps/explorer/priv/repo/migrations/20230719160318_delete_erc_1155_tt_with_empty_token_ids.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index 440ba02ff718..890e43e8a61e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Fixes +- [#7959](https://github.com/blockscout/blockscout/pull/7959) - Fix empty batch transfers handling - [#8513](https://github.com/blockscout/blockscout/pull/8513) - Don't override transaction status ### Chore diff --git a/apps/explorer/priv/repo/migrations/20230719160318_delete_erc_1155_tt_with_empty_token_ids.exs b/apps/explorer/priv/repo/migrations/20230719160318_delete_erc_1155_tt_with_empty_token_ids.exs new file mode 100644 index 000000000000..190fdf8e8023 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20230719160318_delete_erc_1155_tt_with_empty_token_ids.exs @@ -0,0 +1,9 @@ +defmodule Explorer.Repo.Migrations.DeleteErc1155TtWithEmptyTokenIds do + use Ecto.Migration + + def change do + execute(""" + DELETE from token_transfers USING tokens WHERE token_transfers.token_contract_address_hash = tokens.contract_address_hash AND tokens.type = 'ERC-1155' AND (token_transfers.token_ids IS NULL OR ARRAY_LENGTH(token_transfers.token_ids, 1) = 0) ; + """) + end +end diff --git a/apps/indexer/lib/indexer/transform/token_transfers.ex b/apps/indexer/lib/indexer/transform/token_transfers.ex index dae077901adc..fc6fb6a6cfe7 100644 --- a/apps/indexer/lib/indexer/transform/token_transfers.ex +++ b/apps/indexer/lib/indexer/transform/token_transfers.ex @@ -125,17 +125,23 @@ defmodule Indexer.Transform.TokenTransfers do end defp do_parse(log, %{tokens: tokens, token_transfers: token_transfers} = acc, type \\ :erc20_erc721) do - {token, token_transfer} = + parse_result = if type != :erc1155 do parse_params(log) else parse_erc1155_params(log) end - %{ - tokens: [token | tokens], - token_transfers: [token_transfer | token_transfers] - } + case parse_result do + {token, token_transfer} -> + %{ + tokens: [token | tokens], + token_transfers: [token_transfer | token_transfers] + } + + nil -> + acc + end rescue e in [FunctionClauseError, MatchError] -> Logger.error(fn -> @@ -271,25 +277,29 @@ defmodule Indexer.Transform.TokenTransfers do ) do [token_ids, values] = decode_data(data, [{:array, {:uint, 256}}, {:array, {:uint, 256}}]) - token_transfer = %{ - block_number: log.block_number, - block_hash: log.block_hash, - log_index: log.index, - from_address_hash: truncate_address_hash(third_topic), - to_address_hash: truncate_address_hash(fourth_topic), - token_contract_address_hash: log.address_hash, - transaction_hash: log.transaction_hash, - token_type: "ERC-1155", - token_ids: token_ids, - amounts: values - } - - token = %{ - contract_address_hash: log.address_hash, - type: "ERC-1155" - } - - {token, token_transfer} + if token_ids == [] || values == [] do + nil + else + token_transfer = %{ + block_number: log.block_number, + block_hash: log.block_hash, + log_index: log.index, + from_address_hash: truncate_address_hash(third_topic), + to_address_hash: truncate_address_hash(fourth_topic), + token_contract_address_hash: log.address_hash, + transaction_hash: log.transaction_hash, + token_type: "ERC-1155", + token_ids: token_ids, + amounts: values + } + + token = %{ + contract_address_hash: log.address_hash, + type: "ERC-1155" + } + + {token, token_transfer} + end end def parse_erc1155_params(%{third_topic: third_topic, fourth_topic: fourth_topic, data: data} = log) do diff --git a/apps/indexer/test/indexer/transform/token_transfers_test.exs b/apps/indexer/test/indexer/transform/token_transfers_test.exs index f416733ea13b..8833474acccc 100644 --- a/apps/indexer/test/indexer/transform/token_transfers_test.exs +++ b/apps/indexer/test/indexer/transform/token_transfers_test.exs @@ -263,6 +263,28 @@ defmodule Indexer.Transform.TokenTransfersTest do } end + test "parses erc1155 batch token transfer with empty ids/values" do + log = %{ + address_hash: "0x598AF04C88122FA4D1e08C5da3244C39F10D4F14", + block_number: 9_065_059, + data: + "0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + first_topic: "0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb", + secon_topic: "0x81D0caF80E9bFfD9bF9c641ab964feB9ef69069e", + third_topic: "0x598AF04C88122FA4D1e08C5da3244C39F10D4F14", + fourth_topic: "0x0000000000000000000000000000000000000000", + index: 6, + transaction_hash: "0xa6ad6588edb4abd8ca45f30d2f026ba20b68a3002a5870dbd30cc3752568483b", + block_hash: "0x61b720e40f8c521edd77a52cabce556c18b18b198f78e361f310003386ff1f02", + type: "mined" + } + + assert TokenTransfers.parse([log]) == %{ + token_transfers: [], + tokens: [] + } + end + test "logs error with unrecognized token transfer format" do log = %{ address_hash: "0x58Ab73CB79c8275628E0213742a85B163fE0A9Fb", From 0e2b4cb384d2de58eef32994adfc93d996662dc0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 18:11:58 +0000 Subject: [PATCH 005/224] Bump ex_cldr_units from 3.16.2 to 3.16.3 Bumps [ex_cldr_units](https://github.com/elixir-cldr/cldr_units) from 3.16.2 to 3.16.3. - [Release notes](https://github.com/elixir-cldr/cldr_units/releases) - [Changelog](https://github.com/elixir-cldr/cldr_units/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-cldr/cldr_units/commits/v3.16.3) --- updated-dependencies: - dependency-name: ex_cldr_units dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- mix.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.lock b/mix.lock index 2ed2ed4fa812..d68e1450d98b 100644 --- a/mix.lock +++ b/mix.lock @@ -36,7 +36,7 @@ "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "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"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.36", "487ea8ef9bdc659f085e6e654f3c3feea1d36ac3943edf9d2ef6c98de9174c13", [:mix], [], "hexpm", "a524e395634bdcf60a616efe77fd79561bec2e930d8b82745df06ab4e844400a"}, "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.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"}, @@ -44,9 +44,9 @@ "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_lists": {:hex, :ex_cldr_lists, "2.10.1", "67795eb28f8534a36cbdfeeaea6d3ee587eeac7eafff71968dd046c215d4ec42", [: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", "da826148d95c7a1889fd847ae5704c141747bad43a5a44431ae97bced57b0f93"}, "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_cldr_units": {:hex, :ex_cldr_units, "3.16.3", "41344ed9f6d5c69baeb4d13dadc80310ca511626f738af2e69ae53dd40fb28a5", [: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", "889dd56084821723ce82c4639d62cfc3513a9ca1ffb9063bcc83e7b3de5bc35b"}, "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.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"}, From 35cd3cbb37d89f1e24ad3248943349ed9c9e83f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 18:46:09 +0000 Subject: [PATCH 006/224] Bump eslint from 8.49.0 to 8.50.0 in /apps/block_scout_web/assets Bumps [eslint](https://github.com/eslint/eslint) from 8.49.0 to 8.50.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.49.0...v8.50.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- apps/block_scout_web/assets/package-lock.json | 30 +++++++++---------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 5a24c565ee7a..9def044259bc 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -78,7 +78,7 @@ "copy-webpack-plugin": "^11.0.0", "css-loader": "^5.2.7", "css-minimizer-webpack-plugin": "^5.0.1", - "eslint": "^8.49.0", + "eslint": "^8.50.0", "eslint-config-standard": "^17.1.0", "eslint-plugin-import": "^2.28.1", "eslint-plugin-node": "^11.1.0", @@ -2101,9 +2101,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", - "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", + "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -7190,15 +7190,15 @@ } }, "node_modules/eslint": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", - "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz", + "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.49.0", + "@eslint/js": "8.50.0", "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -19112,9 +19112,9 @@ } }, "@eslint/js": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", - "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", + "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", "dev": true }, "@ethereumjs/common": { @@ -23001,15 +23001,15 @@ } }, "eslint": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", - "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz", + "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.49.0", + "@eslint/js": "8.50.0", "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 3f5de715aafd..fc5678e9ebc2 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -90,7 +90,7 @@ "copy-webpack-plugin": "^11.0.0", "css-loader": "^5.2.7", "css-minimizer-webpack-plugin": "^5.0.1", - "eslint": "^8.49.0", + "eslint": "^8.50.0", "eslint-config-standard": "^17.1.0", "eslint-plugin-import": "^2.28.1", "eslint-plugin-node": "^11.1.0", From 194bfbf7e93a3a6167031487f4532b94c1ab95e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 18:46:48 +0000 Subject: [PATCH 007/224] Bump sweetalert2 from 11.7.28 to 11.7.29 in /apps/block_scout_web/assets Bumps [sweetalert2](https://github.com/sweetalert2/sweetalert2) from 11.7.28 to 11.7.29. - [Release notes](https://github.com/sweetalert2/sweetalert2/releases) - [Changelog](https://github.com/sweetalert2/sweetalert2/blob/main/CHANGELOG.md) - [Commits](https://github.com/sweetalert2/sweetalert2/compare/v11.7.28...v11.7.29) --- updated-dependencies: - dependency-name: sweetalert2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- apps/block_scout_web/assets/package-lock.json | 14 +++++++------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 5a24c565ee7a..f75b0982ae12 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -61,7 +61,7 @@ "redux": "^4.2.1", "stream-browserify": "^3.0.0", "stream-http": "^3.1.1", - "sweetalert2": "^11.7.28", + "sweetalert2": "^11.7.29", "urijs": "^1.19.11", "url": "^0.11.3", "util": "^0.12.5", @@ -15937,9 +15937,9 @@ } }, "node_modules/sweetalert2": { - "version": "11.7.28", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.28.tgz", - "integrity": "sha512-9895DVuYTDlV4Hx4IJablFKMdSqzwpy0PKycztbO4cXnOeVMmw55weOq4gcZAh3/tAyunCKjApFDrlSAcswwcA==", + "version": "11.7.29", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.29.tgz", + "integrity": "sha512-oXl//RcQGDWUZqU3fB6zq4qjHQYelIQ9FaJWucsH2tNkN9YGfk0XCAa836rfnV9mNOsaI18gWgIn0NFp0iXAcw==", "funding": { "type": "individual", "url": "https://github.com/sponsors/limonte" @@ -29614,9 +29614,9 @@ } }, "sweetalert2": { - "version": "11.7.28", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.28.tgz", - "integrity": "sha512-9895DVuYTDlV4Hx4IJablFKMdSqzwpy0PKycztbO4cXnOeVMmw55weOq4gcZAh3/tAyunCKjApFDrlSAcswwcA==" + "version": "11.7.29", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.29.tgz", + "integrity": "sha512-oXl//RcQGDWUZqU3fB6zq4qjHQYelIQ9FaJWucsH2tNkN9YGfk0XCAa836rfnV9mNOsaI18gWgIn0NFp0iXAcw==" }, "symbol-tree": { "version": "3.2.4", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 3f5de715aafd..2858c9bc631f 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -73,7 +73,7 @@ "redux": "^4.2.1", "stream-browserify": "^3.0.0", "stream-http": "^3.1.1", - "sweetalert2": "^11.7.28", + "sweetalert2": "^11.7.29", "urijs": "^1.19.11", "url": "^0.11.3", "util": "^0.12.5", From f8a1ff96232448cfacc8d4c18d0fc3b94edc9f32 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 18:47:14 +0000 Subject: [PATCH 008/224] Bump @babel/core from 7.22.20 to 7.23.0 in /apps/block_scout_web/assets Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.22.20 to 7.23.0. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.0/packages/babel-core) --- updated-dependencies: - dependency-name: "@babel/core" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- apps/block_scout_web/assets/package-lock.json | 180 ++++++++++-------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 98 insertions(+), 84 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 5a24c565ee7a..c3fd23dd559c 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -71,7 +71,7 @@ "xss": "^1.0.14" }, "devDependencies": { - "@babel/core": "^7.22.20", + "@babel/core": "^7.23.0", "@babel/preset-env": "^7.22.20", "autoprefixer": "^10.4.16", "babel-loader": "^9.1.3", @@ -249,21 +249,21 @@ } }, "node_modules/@babel/core": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.20.tgz", - "integrity": "sha512-Y6jd1ahLubuYweD/zJH+vvOY141v4f9igNQAQ+MBgq9JlHS2iTsZKn1aMsb3vGccZsXI16VzTBw52Xx0DWmtnA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz", + "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.22.15", + "@babel/generator": "^7.23.0", "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.22.20", - "@babel/helpers": "^7.22.15", - "@babel/parser": "^7.22.16", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.0", + "@babel/parser": "^7.23.0", "@babel/template": "^7.22.15", - "@babel/traverse": "^7.22.20", - "@babel/types": "^7.22.19", - "convert-source-map": "^1.7.0", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", @@ -277,12 +277,17 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "node_modules/@babel/generator": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", - "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dependencies": { - "@babel/types": "^7.22.15", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -407,12 +412,12 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -453,9 +458,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.20.tgz", - "integrity": "sha512-dLT7JVWIUUxKOs1UnJUBR3S70YK+pKX6AbJgB2vMIvEkZkrfJDbYDJesnPshtKV4LhDOR3Oc5YULeDizRek+5A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", + "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-module-imports": "^7.22.15", @@ -597,13 +602,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz", - "integrity": "sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==", + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz", + "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==", "dependencies": { "@babel/template": "^7.22.15", - "@babel/traverse": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -623,9 +628,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.16", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", - "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -1938,18 +1943,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.20.tgz", - "integrity": "sha512-eU260mPZbU7mZ0N+X10pxXhQFMGTeLb9eFS0mxehS8HZp9o1uSnFeWQuG1UPrlxgA7QoUzFhOnilHDp0AXCyHw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", + "integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", "dependencies": { "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.22.15", + "@babel/generator": "^7.23.0", "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.22.5", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.16", - "@babel/types": "^7.22.19", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1958,12 +1963,12 @@ } }, "node_modules/@babel/types": { - "version": "7.22.19", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.19.tgz", - "integrity": "sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.19", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -5759,6 +5764,7 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, "dependencies": { "safe-buffer": "~5.1.1" } @@ -17822,33 +17828,40 @@ "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==" }, "@babel/core": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.20.tgz", - "integrity": "sha512-Y6jd1ahLubuYweD/zJH+vvOY141v4f9igNQAQ+MBgq9JlHS2iTsZKn1aMsb3vGccZsXI16VzTBw52Xx0DWmtnA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz", + "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==", "requires": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.22.15", + "@babel/generator": "^7.23.0", "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.22.20", - "@babel/helpers": "^7.22.15", - "@babel/parser": "^7.22.16", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.0", + "@babel/parser": "^7.23.0", "@babel/template": "^7.22.15", - "@babel/traverse": "^7.22.20", - "@babel/types": "^7.22.19", - "convert-source-map": "^1.7.0", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" + }, + "dependencies": { + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + } } }, "@babel/generator": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", - "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "requires": { - "@babel/types": "^7.22.15", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -17945,12 +17958,12 @@ "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" }, "@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "requires": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { @@ -17979,9 +17992,9 @@ } }, "@babel/helper-module-transforms": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.20.tgz", - "integrity": "sha512-dLT7JVWIUUxKOs1UnJUBR3S70YK+pKX6AbJgB2vMIvEkZkrfJDbYDJesnPshtKV4LhDOR3Oc5YULeDizRek+5A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", + "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", "requires": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-module-imports": "^7.22.15", @@ -18078,13 +18091,13 @@ } }, "@babel/helpers": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz", - "integrity": "sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==", + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz", + "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==", "requires": { "@babel/template": "^7.22.15", - "@babel/traverse": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0" } }, "@babel/highlight": { @@ -18098,9 +18111,9 @@ } }, "@babel/parser": { - "version": "7.22.16", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", - "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==" + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.22.15", @@ -18986,29 +18999,29 @@ } }, "@babel/traverse": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.20.tgz", - "integrity": "sha512-eU260mPZbU7mZ0N+X10pxXhQFMGTeLb9eFS0mxehS8HZp9o1uSnFeWQuG1UPrlxgA7QoUzFhOnilHDp0AXCyHw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", + "integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", "requires": { "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.22.15", + "@babel/generator": "^7.23.0", "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.22.5", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.16", - "@babel/types": "^7.22.19", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.22.19", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.19.tgz", - "integrity": "sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "requires": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.19", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -21921,6 +21934,7 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, "requires": { "safe-buffer": "~5.1.1" } diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 3f5de715aafd..2b1ae390193f 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -83,7 +83,7 @@ "xss": "^1.0.14" }, "devDependencies": { - "@babel/core": "^7.22.20", + "@babel/core": "^7.23.0", "@babel/preset-env": "^7.22.20", "autoprefixer": "^10.4.16", "babel-loader": "^9.1.3", From ceb9bab651a921f7691de050602df45d23b6aee4 Mon Sep 17 00:00:00 2001 From: Nikita Pozdniakov Date: Fri, 22 Sep 2023 14:29:43 +0300 Subject: [PATCH 009/224] Handle ':error.types/0 is undefined' error --- apps/explorer/lib/explorer/chain/transaction.ex | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index c40d87769f14..82967c0c0634 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -756,12 +756,12 @@ defmodule Explorer.Chain.Transaction do end defp find_and_decode(abi, data, hash) do - result = - abi - |> ABI.parse_specification() - |> ABI.find_and_decode(data) - - {:ok, result} + with {%FunctionSelector{}, _mapping} = result <- + abi + |> ABI.parse_specification() + |> ABI.find_and_decode(data) do + {:ok, result} + end rescue e -> Logger.warn(fn -> From 3d483fcdab61b01bfce8b1ef7a81ab0ea1d7647b Mon Sep 17 00:00:00 2001 From: Nikita Pozdniakov Date: Fri, 22 Sep 2023 14:33:11 +0300 Subject: [PATCH 010/224] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 890e43e8a61e..12b41373fb44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Fixes +- [#8515](https://github.com/blockscout/blockscout/pull/8515) - Fix `:error.types/0 is undefined` warning - [#7959](https://github.com/blockscout/blockscout/pull/7959) - Fix empty batch transfers handling - [#8513](https://github.com/blockscout/blockscout/pull/8513) - Don't override transaction status From 71d5ab847768a05847013d006fbc884f11ac0721 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 26 Sep 2023 12:26:58 +0300 Subject: [PATCH 011/224] New issue template --- .github/ISSUE_TEMPLATE/bug_report.yml | 115 ++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 11 +++ CHANGELOG.md | 1 + 3 files changed, 127 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000000..f3545b6d54da --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,115 @@ +name: Bug Report +description: File a bug report +labels: [ "triage" ] +body: + - type: markdown + attributes: + value: | + Thanks for reporting a bug 🐛! + + Please search open/closed issues before submitting. Someone might have had the similar problem before 😉! + + - type: textarea + id: description + attributes: + label: Description + description: A brief description of the issue. + validations: + required: true + + - type: dropdown + id: installation-type + attributes: + label: Type of the installation + description: How the application has been deployed. + options: + - Docker-compose + - Manual from the source code + - Helm charts + - Docker + validations: + required: true + + - type: dropdown + id: archive-node-type + attributes: + label: Type of the JSON RPC archive node + description: Which type of archive node is used. + options: + - Erigon + - Geth + - Nethermind + - Reth + - PolygonEdge + - Besu + - OpenEthereum + - Other + validations: + required: true + + - type: dropdown + id: chain-type + attributes: + label: Type of the chain + description: Type of the chain. + options: + - L1 + - L2 + - Other + + - type: input + id: link + attributes: + label: Link to the page + description: The link to the page where the issue occurs. + placeholder: https://eth.blockscout.com + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: | + Explain how to reproduce the issue in the development environment. + + - type: input + id: backend-version + attributes: + label: Backend version + description: The release version of the backend or branch/commit. + placeholder: v5.2.3 + validations: + required: true + + - type: input + id: frontend-version + attributes: + label: Frontend version + description: The release version of the frontend or branch/commit. + placeholder: v1.11.1 + + - type: input + id: elixir-version + attributes: + label: Elixir & Erlang/OTP versions + description: Elixir & Erlang/OTP versions. + placeholder: Elixir 1.14.5 (compiled with Erlang/OTP 25) + validations: + required: true + + - type: dropdown + id: operating-system + attributes: + label: Operating system + description: The operating system this issue occurred with. + options: + - macOS + - Windows + - Linux + - Other + + - type: textarea + id: additional-information + attributes: + label: Additional information + description: | + Use this section to provide any additional information you might have (e.g screenshots or screencasts). \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000000..e72f610f24bb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: true +contact_links: + - name: Feature Request + url: https://blockscout.canny.io/feature-requests + about: Request a feature or enhancement + - name: Ask a question + url: https://github.com/orgs/blockscout/discussions + about: Ask questions and discuss topics with other community members + - name: Join our Discord Server + url: https://discord.gg/blockscout + about: The official Blockscout Discord community \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 12b41373fb44..a2264c17d3a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ### Chore +- [#8536](https://github.com/blockscout/blockscout/pull/8536) - New issue template - [#8529](https://github.com/blockscout/blockscout/pull/8529) - Move PolygonEdge-related migration to the corresponding ecto repository - [#8504](https://github.com/blockscout/blockscout/pull/8504) - Deploy new UI through Makefile - [#8501](https://github.com/blockscout/blockscout/pull/8501) - Conceal secondary ports in docker compose setup From 1d43bb7dc2e5fa8078e60091c231034e16daed33 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 26 Sep 2023 13:11:56 +0300 Subject: [PATCH 012/224] Fix issue template --- .github/ISSUE_TEMPLATE/bug_report.yml | 36 +++++++++++++-------------- CHANGELOG.md | 2 +- ISSUE_TEMPLATE.md | 22 ---------------- 3 files changed, 19 insertions(+), 41 deletions(-) delete mode 100644 ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index f3545b6d54da..8f563f127a40 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -22,11 +22,11 @@ body: attributes: label: Type of the installation description: How the application has been deployed. - options: - - Docker-compose - - Manual from the source code - - Helm charts - - Docker + options: + - Docker-compose + - Manual from the source code + - Helm charts + - Docker validations: required: true @@ -35,15 +35,15 @@ body: attributes: label: Type of the JSON RPC archive node description: Which type of archive node is used. - options: - - Erigon - - Geth - - Nethermind - - Reth - - PolygonEdge - - Besu - - OpenEthereum - - Other + options: + - Erigon + - Geth + - Nethermind + - Reth + - PolygonEdge + - Besu + - OpenEthereum + - Other validations: required: true @@ -52,10 +52,10 @@ body: attributes: label: Type of the chain description: Type of the chain. - options: - - L1 - - L2 - - Other + options: + - L1 + - L2 + - Other - type: input id: link diff --git a/CHANGELOG.md b/CHANGELOG.md index a2264c17d3a0..20262f45c04f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ ### Chore -- [#8536](https://github.com/blockscout/blockscout/pull/8536) - New issue template +- [#8536](https://github.com/blockscout/blockscout/pull/8536), [#8537](https://github.com/blockscout/blockscout/pull/8537) - New issue template - [#8529](https://github.com/blockscout/blockscout/pull/8529) - Move PolygonEdge-related migration to the corresponding ecto repository - [#8504](https://github.com/blockscout/blockscout/pull/8504) - Deploy new UI through Makefile - [#8501](https://github.com/blockscout/blockscout/pull/8501) - Conceal secondary ports in docker compose setup diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md deleted file mode 100644 index 1e5bff013634..000000000000 --- a/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,22 +0,0 @@ -*Describe your issue here.* - -### Environment - -* Deployment type (Manual/Docker/Docker-compose): -* Elixir & Erlang/OTP versions (`elixir -version`): -* Node JS version (`node -v`): -* Operating System: -* Blockscout Version/branch/commit: -* Archive node type && version (Erigon/Geth/Nethermind/Ganache/?): - -### Steps to reproduce - -*Tell us how to reproduce this issue. ❤️ if you can push up a branch to your fork with a regression test we can run to reproduce locally.* - -### Expected behaviour - -*Tell us what should happen.* - -### Actual behaviour - -*Tell us what happens instead.* From ab05ca165b4fcab95c3b9e6c20699100919bf088 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 26 Sep 2023 13:59:13 +0300 Subject: [PATCH 013/224] Disable blank issues creation --- .github/ISSUE_TEMPLATE/config.yml | 2 +- CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index e72f610f24bb..439bca677db3 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,4 @@ -blank_issues_enabled: true +blank_issues_enabled: false contact_links: - name: Feature Request url: https://blockscout.canny.io/feature-requests diff --git a/CHANGELOG.md b/CHANGELOG.md index 20262f45c04f..1ce93962006f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ ### Chore -- [#8536](https://github.com/blockscout/blockscout/pull/8536), [#8537](https://github.com/blockscout/blockscout/pull/8537) - New issue template +- [#8536](https://github.com/blockscout/blockscout/pull/8536), [#8537](https://github.com/blockscout/blockscout/pull/8537), [#8540](https://github.com/blockscout/blockscout/pull/8540) - New issue template - [#8529](https://github.com/blockscout/blockscout/pull/8529) - Move PolygonEdge-related migration to the corresponding ecto repository - [#8504](https://github.com/blockscout/blockscout/pull/8504) - Deploy new UI through Makefile - [#8501](https://github.com/blockscout/blockscout/pull/8501) - Conceal secondary ports in docker compose setup From f854ebab43188a63514183831b5ded521af8f849 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 18:24:55 +0000 Subject: [PATCH 014/224] Bump sweetalert2 from 11.7.29 to 11.7.31 in /apps/block_scout_web/assets Bumps [sweetalert2](https://github.com/sweetalert2/sweetalert2) from 11.7.29 to 11.7.31. - [Release notes](https://github.com/sweetalert2/sweetalert2/releases) - [Changelog](https://github.com/sweetalert2/sweetalert2/blob/main/CHANGELOG.md) - [Commits](https://github.com/sweetalert2/sweetalert2/compare/v11.7.29...v11.7.31) --- updated-dependencies: - dependency-name: sweetalert2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- apps/block_scout_web/assets/package-lock.json | 14 +++++++------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 169345582b10..4a7e289372d2 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -61,7 +61,7 @@ "redux": "^4.2.1", "stream-browserify": "^3.0.0", "stream-http": "^3.1.1", - "sweetalert2": "^11.7.29", + "sweetalert2": "^11.7.31", "urijs": "^1.19.11", "url": "^0.11.3", "util": "^0.12.5", @@ -15943,9 +15943,9 @@ } }, "node_modules/sweetalert2": { - "version": "11.7.29", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.29.tgz", - "integrity": "sha512-oXl//RcQGDWUZqU3fB6zq4qjHQYelIQ9FaJWucsH2tNkN9YGfk0XCAa836rfnV9mNOsaI18gWgIn0NFp0iXAcw==", + "version": "11.7.31", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.31.tgz", + "integrity": "sha512-AVNODg41LqhC241UHTanW2ZQdh0ziiw+Gc3Dmy1EuCFP+g09P3mBEhvauU88J3JLEX6UpTfhp7iok9t8uhEdQQ==", "funding": { "type": "individual", "url": "https://github.com/sponsors/limonte" @@ -29628,9 +29628,9 @@ } }, "sweetalert2": { - "version": "11.7.29", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.29.tgz", - "integrity": "sha512-oXl//RcQGDWUZqU3fB6zq4qjHQYelIQ9FaJWucsH2tNkN9YGfk0XCAa836rfnV9mNOsaI18gWgIn0NFp0iXAcw==" + "version": "11.7.31", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.31.tgz", + "integrity": "sha512-AVNODg41LqhC241UHTanW2ZQdh0ziiw+Gc3Dmy1EuCFP+g09P3mBEhvauU88J3JLEX6UpTfhp7iok9t8uhEdQQ==" }, "symbol-tree": { "version": "3.2.4", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 9f2a5e9f0ad8..beeae5ef9b42 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -73,7 +73,7 @@ "redux": "^4.2.1", "stream-browserify": "^3.0.0", "stream-http": "^3.1.1", - "sweetalert2": "^11.7.29", + "sweetalert2": "^11.7.31", "urijs": "^1.19.11", "url": "^0.11.3", "util": "^0.12.5", From 1f0c46d97bc9525a72731eeced84eb347db88127 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 19:03:44 +0000 Subject: [PATCH 015/224] Bump briefly from `678a376` to `51dfe7f` Bumps [briefly](https://github.com/CargoSense/briefly) from `678a376` to `51dfe7f`. - [Release notes](https://github.com/CargoSense/briefly/releases) - [Commits](https://github.com/CargoSense/briefly/compare/678a3763e72a7d1f23ac71b209b96bd199bffbbb...51dfe7fbe0f897ea2a921d9af120762392aca6a1) --- updated-dependencies: - dependency-name: briefly dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index d68e1450d98b..58711856c2ce 100644 --- a/mix.lock +++ b/mix.lock @@ -8,7 +8,7 @@ "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", []}, + "briefly": {:git, "https://github.com/CargoSense/briefly.git", "51dfe7fbe0f897ea2a921d9af120762392aca6a1", []}, "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, "bureaucrat": {:hex, :bureaucrat, "0.2.9", "d98e4d2b9bdbf22e4a45c2113ce8b38b5b63278506c6ff918e3b943a4355d85b", [:mix], [{:inflex, ">= 1.10.0", [hex: :inflex, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.2.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, ">= 1.0.0", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 1.5 or ~> 2.0 or ~> 3.0 or ~> 4.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "111c8dd84382a62e1026ae011d592ceee918553e5203fe8448d9ba6ccbdfff7d"}, "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"}, From 2e8d9060c5dd9a6522d64c9eb6c165df392a3a9b Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Wed, 27 Sep 2023 14:19:17 +0300 Subject: [PATCH 016/224] Add block_type to search results (#8530) * Add block_type to search results * Changelog --- CHANGELOG.md | 1 + .../views/api/v2/search_view.ex | 22 ++++++++++-- .../api/v2/search_controller_test.exs | 36 +++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ce93962006f..120b6f856d56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- [#8530](https://github.com/blockscout/blockscout/pull/8530) - Add `block_type` to search results - [#8180](https://github.com/blockscout/blockscout/pull/8180) - Deposits and Withdrawals for Polygon Edge - [#7996](https://github.com/blockscout/blockscout/pull/7996) - Add CoinBalance fetcher init query limit 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 3663e2f96e80..0376e04f423e 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 @@ -1,7 +1,8 @@ defmodule BlockScoutWeb.API.V2.SearchView do use BlockScoutWeb, :view - alias BlockScoutWeb.Endpoint + alias BlockScoutWeb.{BlockView, Endpoint} + alias Explorer.Chain alias Explorer.Chain.{Address, Block, Hash, Transaction} def render("search_results.json", %{search_results: search_results, next_page_params: next_page_params}) do @@ -53,12 +54,21 @@ defmodule BlockScoutWeb.API.V2.SearchView do def prepare_search_result(%{type: "block"} = search_result) do block_hash = hash_to_string(search_result.block_hash) + {:ok, block} = + Chain.hash_to_block(hash(search_result.block_hash), + necessity_by_association: %{ + :nephews => :optional + }, + api?: true + ) + %{ "type" => search_result.type, "block_number" => search_result.block_number, "block_hash" => block_hash, "url" => block_path(Endpoint, :show, block_hash), - "timestamp" => search_result.timestamp + "timestamp" => search_result.timestamp, + "block_type" => block |> BlockView.block_type() |> String.downcase() } end @@ -76,6 +86,14 @@ defmodule BlockScoutWeb.API.V2.SearchView do 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 hash(%Hash{} = hash), do: hash + + defp hash(bytes), + do: %Hash{ + byte_count: 32, + bytes: bytes + } + defp redirect_search_results(%Address{} = item) do %{"type" => "address", "parameter" => Address.checksum(item.hash)} end 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 145963c99d32..6804f778f0f7 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 @@ -28,6 +28,7 @@ defmodule BlockScoutWeb.API.V2.SearchControllerTest do item = Enum.at(response["items"], 0) assert item["type"] == "block" + assert item["block_type"] == "block" assert item["block_number"] == block.number assert item["block_hash"] == to_string(block.hash) assert item["url"] =~ to_string(block.hash) @@ -47,6 +48,38 @@ defmodule BlockScoutWeb.API.V2.SearchControllerTest do assert item["timestamp"] == block.timestamp |> to_string() |> String.replace(" ", "T") end + test "search reorg", %{conn: conn} do + block = insert(:block, consensus: false) + + request = get(conn, "/api/v2/search?q=#{block.hash}") + assert response = json_response(request, 200) + + assert Enum.count(response["items"]) == 1 + assert response["next_page_params"] == nil + + item = Enum.at(response["items"], 0) + + assert item["type"] == "block" + assert item["block_type"] == "reorg" + assert item["block_number"] == block.number + assert item["block_hash"] == to_string(block.hash) + assert item["url"] =~ to_string(block.hash) + + request = get(conn, "/api/v2/search?q=#{block.number}") + assert response = json_response(request, 200) + + assert Enum.count(response["items"]) == 1 + assert response["next_page_params"] == nil + + item = Enum.at(response["items"], 0) + + assert item["type"] == "block" + assert item["block_type"] == "reorg" + assert item["block_number"] == block.number + assert item["block_hash"] == to_string(block.hash) + assert item["url"] =~ to_string(block.hash) + end + test "search address", %{conn: conn} do address = insert(:address) name = insert(:unique_address_name, address: address) @@ -326,6 +359,9 @@ defmodule BlockScoutWeb.API.V2.SearchControllerTest do 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) + + assert response |> Enum.filter(fn x -> x["block_type"] == "block" end) |> Enum.count() == 1 + assert response |> Enum.filter(fn x -> x["block_type"] == "reorg" end) |> Enum.count() == 1 end test "returns empty list and don't crash", %{conn: conn} do From daed959d8f75fe5a663fbf37e4d57c58f454de14 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Wed, 27 Sep 2023 14:54:42 +0300 Subject: [PATCH 017/224] =?UTF-8?q?Add=20owner=5Faddress=5Fhash=20and=20ot?= =?UTF-8?q?her=20fields=20to=20token=5Finstances;=20Move=20toke=E2=80=A6?= =?UTF-8?q?=20(#8386)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add owner_address_hash and other fields to token_instances; Move token instances' insert into synchronous block import stage * Add TokenInstanceOwnerAddressMigration process * Add TokenInstanceOwnerAddressMigration.Helper tests * Add envs to Makefile and .env file * Process review comments * Process review comments --- CHANGELOG.md | 1 + apps/explorer/config/config.exs | 2 + apps/explorer/config/runtime/test.exs | 2 + apps/explorer/lib/explorer/application.ex | 1 + apps/explorer/lib/explorer/chain.ex | 61 ++++++- .../explorer/chain/import/runner/blocks.ex | 172 +++++++++++++++++- .../chain/import/runner/token_instances.ex | 106 +++++++++++ .../chain/import/stage/block_following.ex | 3 +- .../lib/explorer/chain/token/instance.ex | 21 ++- .../lib/explorer/chain/token_transfer.ex | 10 +- .../helper.ex | 77 ++++++++ .../supervisor.ex | 26 +++ .../worker.ex | 51 ++++++ ...dd_token_ids_to_address_token_balances.exs | 13 ++ .../chain/import/runner/blocks_test.exs | 160 ++++++++++++++++ apps/explorer/test/explorer/chain_test.exs | 35 +--- .../helper_test.exs | 121 ++++++++++++ apps/explorer/test/support/factory.ex | 6 + apps/indexer/lib/indexer/block/fetcher.ex | 5 +- .../fetcher/token_instance/legacy_sanitize.ex | 58 ++++++ .../fetcher/token_instance/realtime.ex | 2 +- .../fetcher/token_instance/sanitize.ex | 4 +- apps/indexer/lib/indexer/supervisor.ex | 2 + .../transform/address_token_balances.ex | 13 -- .../lib/indexer/transform/token_instances.ex | 88 +++++++++ .../transform/address_token_balances_test.exs | 16 +- config/runtime.exs | 11 ++ docker-compose/envs/common-blockscout.env | 7 +- 28 files changed, 1011 insertions(+), 63 deletions(-) create mode 100644 apps/explorer/lib/explorer/chain/import/runner/token_instances.ex create mode 100644 apps/explorer/lib/explorer/token_instance_owner_address_migration/helper.ex create mode 100644 apps/explorer/lib/explorer/token_instance_owner_address_migration/supervisor.ex create mode 100644 apps/explorer/lib/explorer/token_instance_owner_address_migration/worker.ex create mode 100644 apps/explorer/priv/repo/migrations/20230818094455_add_token_ids_to_address_token_balances.exs create mode 100644 apps/explorer/test/explorer/token_instance_owner_address_migration/helper_test.exs create mode 100644 apps/indexer/lib/indexer/fetcher/token_instance/legacy_sanitize.ex create mode 100644 apps/indexer/lib/indexer/transform/token_instances.ex diff --git a/CHANGELOG.md b/CHANGELOG.md index 120b6f856d56..0696b53b234f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- [#8386](https://github.com/blockscout/blockscout/pull/8386) - Add `owner_address_hash` to the `token_instances` - [#8530](https://github.com/blockscout/blockscout/pull/8530) - Add `block_type` to search results - [#8180](https://github.com/blockscout/blockscout/pull/8180) - Deposits and Withdrawals for Polygon Edge - [#7996](https://github.com/blockscout/blockscout/pull/7996) - Add CoinBalance fetcher init query limit diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index 8bfc263b536d..feb7ca93e8b3 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -110,6 +110,8 @@ config :explorer, Explorer.Counters.BlockPriorityFeeCounter, config :explorer, Explorer.TokenTransferTokenIdMigration.Supervisor, enabled: true +config :explorer, Explorer.TokenInstanceOwnerAddressMigration.Supervisor, enabled: true + config :explorer, Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand, enabled: true config :explorer, Explorer.Chain.Fetcher.FetchValidatorInfoOnDemand, enabled: true diff --git a/apps/explorer/config/runtime/test.exs b/apps/explorer/config/runtime/test.exs index 0afa030023b1..bef121b2c1be 100644 --- a/apps/explorer/config/runtime/test.exs +++ b/apps/explorer/config/runtime/test.exs @@ -30,6 +30,8 @@ config :explorer, Explorer.Tracer, disabled?: false config :explorer, Explorer.TokenTransferTokenIdMigration.Supervisor, enabled: false +config :explorer, Explorer.TokenInstanceOwnerAddressMigration.Supervisor, enabled: false + config :explorer, realtime_events_sender: Explorer.Chain.Events.SimpleSender diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index beb6f4082408..23c5f2548f18 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -119,6 +119,7 @@ defmodule Explorer.Application do configure(TokenTransferTokenIdMigration.Supervisor), configure(Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand), configure(Explorer.Chain.Fetcher.FetchValidatorInfoOnDemand), + configure(Explorer.TokenInstanceOwnerAddressMigration.Supervisor), sc_microservice_configure(Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand) ] |> List.flatten() diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 6aa532de03e1..e7e493c80386 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -567,7 +567,7 @@ defmodule Explorer.Chain do select: log, inner_join: block in Block, on: block.hash == log.block_hash, - where: block.consensus + where: block.consensus == true ) preloaded_query = @@ -4391,12 +4391,15 @@ defmodule Explorer.Chain do |> Repo.stream_reduce(initial, reducer) end - @spec stream_unfetched_token_instances( + @doc """ + Finds all token instances (pairs of contract_address_hash and token_id) which was met in token transfers but has no corresponding entry in token_instances table + """ + @spec stream_not_inserted_token_instances( initial :: accumulator, reducer :: (entry :: map(), accumulator -> accumulator) ) :: {:ok, accumulator} when accumulator: term() - def stream_unfetched_token_instances(initial, reducer) when is_function(reducer, 2) do + def stream_not_inserted_token_instances(initial, reducer) when is_function(reducer, 2) do nft_tokens = from( token in Token, @@ -4438,6 +4441,24 @@ defmodule Explorer.Chain do Repo.stream_reduce(distinct_query, initial, reducer) end + @doc """ + Finds all token instances where metadata never tried to fetch + """ + @spec stream_token_instances_with_unfetched_metadata( + initial :: accumulator, + reducer :: (entry :: map(), accumulator -> accumulator) + ) :: {:ok, accumulator} + when accumulator: term() + def stream_token_instances_with_unfetched_metadata(initial, reducer) when is_function(reducer, 2) do + Instance + |> where([instance], is_nil(instance.error) and is_nil(instance.metadata)) + |> select([instance], %{ + contract_address_hash: instance.token_contract_address_hash, + token_id: instance.token_id + }) + |> Repo.stream_reduce(initial, reducer) + end + @spec stream_token_instances_with_error( initial :: accumulator, reducer :: (entry :: map(), accumulator -> accumulator), @@ -4696,18 +4717,37 @@ defmodule Explorer.Chain do end @doc """ - Expects map of change params. Inserts using on_conflict: :replace_all + Expects map of change params. Inserts using on_conflict: `token_instance_metadata_on_conflict/0` + !!! Supposed to be used ONLY for import of `metadata` or `error`. """ @spec upsert_token_instance(map()) :: {:ok, Instance.t()} | {:error, Ecto.Changeset.t()} def upsert_token_instance(params) do changeset = Instance.changeset(%Instance{}, params) Repo.insert(changeset, - on_conflict: :replace_all, + on_conflict: token_instance_metadata_on_conflict(), conflict_target: [:token_id, :token_contract_address_hash] ) end + defp token_instance_metadata_on_conflict do + from( + token_instance in Instance, + update: [ + set: [ + metadata: fragment("EXCLUDED.metadata"), + error: fragment("EXCLUDED.error"), + owner_updated_at_block: token_instance.owner_updated_at_block, + owner_updated_at_log_index: token_instance.owner_updated_at_log_index, + owner_address_hash: token_instance.owner_address_hash, + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", token_instance.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", token_instance.updated_at) + ] + ], + where: is_nil(token_instance.metadata) + ) + end + @doc """ Inserts list of token instances via upsert_token_instance/1. """ @@ -4809,6 +4849,17 @@ defmodule Explorer.Chain do select_repo(options).exists?(query) end + @spec token_instance_with_unfetched_metadata?(non_neg_integer, Hash.Address.t(), [api?]) :: boolean + def token_instance_with_unfetched_metadata?(token_id, token_contract_address, options \\ []) do + Instance + |> where([instance], is_nil(instance.error) and is_nil(instance.metadata)) + |> where( + [instance], + instance.token_id == ^token_id and instance.token_contract_address_hash == ^token_contract_address + ) + |> select_repo(options).exists?() + end + defp fetch_coin_balances(address, paging_options) do address.hash |> CoinBalance.fetch_coin_balances(paging_options) diff --git a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex index ea533119015e..57fc87602367 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex @@ -8,11 +8,22 @@ defmodule Explorer.Chain.Import.Runner.Blocks do import Ecto.Query, only: [from: 2, where: 3, subquery: 1] alias Ecto.{Changeset, Multi, Repo} - alias Explorer.Chain.{Address, Block, Import, PendingBlockOperation, Transaction} + + alias Explorer.Chain.{ + Address, + Block, + Import, + PendingBlockOperation, + Token, + Token.Instance, + TokenTransfer, + Transaction + } + alias Explorer.Chain.Block.Reward alias Explorer.Chain.Import.Runner alias Explorer.Chain.Import.Runner.Address.CurrentTokenBalances - alias Explorer.Chain.Import.Runner.Tokens + alias Explorer.Chain.Import.Runner.{TokenInstances, Tokens} alias Explorer.Prometheus.Instrumenter alias Explorer.Repo, as: ExplorerRepo alias Explorer.Utility.MissingRangesManipulator @@ -177,6 +188,14 @@ defmodule Explorer.Chain.Import.Runner.Blocks do :derive_address_current_token_balances ) end) + |> Multi.run(:update_token_instances_owner, fn repo, %{derive_transaction_forks: transactions} -> + Instrumenter.block_import_stage_runner( + fn -> update_token_instances_owner(repo, transactions, insert_options) end, + :address_referencing, + :blocks, + :update_token_instances_owner + ) + end) |> Multi.run(:blocks_update_token_holder_counts, fn repo, %{ delete_address_current_token_balances: deleted, @@ -577,6 +596,155 @@ defmodule Explorer.Chain.Import.Runner.Blocks do {:ok, derived_address_current_token_balances} end + defp update_token_instances_owner(_, [], _), do: {:ok, []} + + defp update_token_instances_owner(repo, forked_transaction_hashes, options) do + forked_transaction_hashes + |> forked_token_transfers_query() + |> repo.all() + |> process_forked_token_transfers(repo, options) + end + + defp process_forked_token_transfers([], _, _), do: {:ok, []} + + defp process_forked_token_transfers(token_transfers, repo, options) do + changes_initial = + Enum.reduce(token_transfers, %{}, fn tt, acc -> + Map.put_new(acc, {tt.token_contract_address_hash, tt.token_id}, %{ + token_contract_address_hash: tt.token_contract_address_hash, + token_id: tt.token_id, + owner_address_hash: tt.from, + owner_updated_at_block: -1, + owner_updated_at_log_index: -1 + }) + end) + + non_consensus_block_numbers = token_transfers |> Enum.map(fn tt -> tt.block_number end) |> Enum.uniq() + + filtered_query = TokenTransfer.only_consensus_transfers_query() + + base_query = + from(token_transfer in subquery(filtered_query), + select: %{ + token_contract_address_hash: token_transfer.token_contract_address_hash, + token_id: fragment("(?)[1]", token_transfer.token_ids), + block_number: max(token_transfer.block_number) + }, + group_by: [token_transfer.token_contract_address_hash, fragment("(?)[1]", token_transfer.token_ids)] + ) + + historical_token_transfers_query = + Enum.reduce(token_transfers, base_query, fn tt, acc -> + from(token_transfer in acc, + or_where: + token_transfer.token_contract_address_hash == ^tt.token_contract_address_hash and + fragment("? @> ARRAY[?::decimal]", token_transfer.token_ids, ^tt.token_id) and + token_transfer.block_number < ^tt.block_number and + token_transfer.block_number not in ^non_consensus_block_numbers + ) + end) + + refs_to_token_transfers = + from(historical_tt in subquery(historical_token_transfers_query), + inner_join: tt in subquery(filtered_query), + on: + tt.token_contract_address_hash == historical_tt.token_contract_address_hash and + tt.block_number == historical_tt.block_number and + fragment("? @> ARRAY[?::decimal]", tt.token_ids, historical_tt.token_id), + select: %{ + token_contract_address_hash: tt.token_contract_address_hash, + token_id: historical_tt.token_id, + log_index: max(tt.log_index), + block_number: tt.block_number + }, + group_by: [tt.token_contract_address_hash, historical_tt.token_id, tt.block_number] + ) + + derived_token_transfers_query = + from(tt in filtered_query, + inner_join: tt_1 in subquery(refs_to_token_transfers), + on: tt_1.log_index == tt.log_index and tt_1.block_number == tt.block_number + ) + + changes = + derived_token_transfers_query + |> repo.all() + |> Enum.reduce(changes_initial, fn tt, acc -> + token_id = List.first(tt.token_ids) + current_key = {tt.token_contract_address_hash, token_id} + + params = %{ + token_contract_address_hash: tt.token_contract_address_hash, + token_id: token_id, + owner_address_hash: tt.to_address_hash, + owner_updated_at_block: tt.block_number, + owner_updated_at_log_index: tt.log_index + } + + Map.put( + acc, + current_key, + Enum.max_by([acc[current_key], params], fn %{ + owner_updated_at_block: block_number, + owner_updated_at_log_index: log_index + } -> + {block_number, log_index} + end) + ) + end) + |> Map.values() + + TokenInstances.insert( + repo, + changes, + options + |> Map.put(:timestamps, Import.timestamps()) + |> Map.put(:on_conflict, token_instances_on_conflict()) + ) + end + + defp forked_token_transfers_query(forked_transaction_hashes) do + from(token_transfer in TokenTransfer, + where: token_transfer.transaction_hash in ^forked_transaction_hashes, + inner_join: token in Token, + on: token.contract_address_hash == token_transfer.token_contract_address_hash, + where: token.type == "ERC-721", + inner_join: instance in Instance, + on: + fragment("? @> ARRAY[?::decimal]", token_transfer.token_ids, instance.token_id) and + instance.token_contract_address_hash == token_transfer.token_contract_address_hash, + # per one token instance we will have only one token transfer + where: + token_transfer.block_number == instance.owner_updated_at_block and + token_transfer.log_index == instance.owner_updated_at_log_index, + select: %{ + from: token_transfer.from_address_hash, + to: token_transfer.to_address_hash, + token_id: instance.token_id, + token_contract_address_hash: token_transfer.token_contract_address_hash, + block_number: token_transfer.block_number, + log_index: token_transfer.log_index + } + ) + end + + defp token_instances_on_conflict do + from( + token_instance in Instance, + update: [ + set: [ + metadata: token_instance.metadata, + error: token_instance.error, + owner_updated_at_block: fragment("EXCLUDED.owner_updated_at_block"), + owner_updated_at_log_index: fragment("EXCLUDED.owner_updated_at_log_index"), + owner_address_hash: fragment("EXCLUDED.owner_address_hash"), + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", token_instance.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", token_instance.updated_at) + ] + ] + ) + end + defp derive_address_current_token_balances_grouped_query(deleted_address_current_token_balances) do initial_query = from(tb in Address.TokenBalance, diff --git a/apps/explorer/lib/explorer/chain/import/runner/token_instances.ex b/apps/explorer/lib/explorer/chain/import/runner/token_instances.ex new file mode 100644 index 000000000000..0fb38962a755 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/token_instances.ex @@ -0,0 +1,106 @@ +defmodule Explorer.Chain.Import.Runner.TokenInstances do + @moduledoc """ + Bulk imports `t:Explorer.Chain.TokenInstances.t/0`. + """ + + require Ecto.Query + + alias Ecto.{Changeset, Multi, Repo} + alias Explorer.Chain.Import + alias Explorer.Chain.Token.Instance, as: TokenInstance + alias Explorer.Prometheus.Instrumenter + + import Ecto.Query, only: [from: 2] + + @behaviour Import.Runner + + # milliseconds + @timeout 60_000 + + @type imported :: [TokenInstance.t()] + + @impl Import.Runner + def ecto_schema_module, do: TokenInstance + + @impl Import.Runner + def option_key, do: :token_instances + + @impl Import.Runner + def imported_table_row do + %{ + value_type: "[#{ecto_schema_module()}.t()]", + value_description: "List of `t:#{ecto_schema_module()}.t/0`s" + } + end + + @impl Import.Runner + def run(multi, changes_list, %{timestamps: timestamps} = options) do + insert_options = + options + |> Map.get(option_key(), %{}) + |> Map.take(~w(on_conflict timeout)a) + |> Map.put_new(:timeout, @timeout) + |> Map.put(:timestamps, timestamps) + + Multi.run(multi, :token_instances, fn repo, _ -> + Instrumenter.block_import_stage_runner( + fn -> insert(repo, changes_list, insert_options) end, + :block_referencing, + :token_instances, + :token_instances + ) + end) + end + + @impl Import.Runner + def timeout, do: @timeout + + @spec insert(Repo.t(), [map()], %{ + optional(:on_conflict) => Import.Runner.on_conflict(), + required(:timeout) => timeout, + required(:timestamps) => Import.timestamps() + }) :: + {:ok, [TokenInstance.t()]} + | {:error, [Changeset.t()]} + def insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = options) when is_list(changes_list) do + on_conflict = Map.get_lazy(options, :on_conflict, &default_on_conflict/0) + + # Guarantee the same import order to avoid deadlocks + ordered_changes_list = Enum.sort_by(changes_list, &{&1.token_contract_address_hash, &1.token_id}) + + {:ok, _} = + Import.insert_changes_list( + repo, + ordered_changes_list, + conflict_target: [:token_contract_address_hash, :token_id], + on_conflict: on_conflict, + for: TokenInstance, + returning: true, + timeout: timeout, + timestamps: timestamps + ) + end + + defp default_on_conflict do + from( + token_instance in TokenInstance, + update: [ + set: [ + metadata: token_instance.metadata, + error: token_instance.error, + owner_updated_at_block: fragment("EXCLUDED.owner_updated_at_block"), + owner_updated_at_log_index: fragment("EXCLUDED.owner_updated_at_log_index"), + owner_address_hash: fragment("EXCLUDED.owner_address_hash"), + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", token_instance.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", token_instance.updated_at) + ] + ], + where: + fragment("EXCLUDED.owner_address_hash IS NOT NULL") and fragment("EXCLUDED.owner_updated_at_block IS NOT NULL") and + (fragment("EXCLUDED.owner_updated_at_block > ?", token_instance.owner_updated_at_block) or + (fragment("EXCLUDED.owner_updated_at_block = ?", token_instance.owner_updated_at_block) and + fragment("EXCLUDED.owner_updated_at_log_index >= ?", token_instance.owner_updated_at_log_index)) or + is_nil(token_instance.owner_updated_at_block) or is_nil(token_instance.owner_address_hash)) + ) + end +end diff --git a/apps/explorer/lib/explorer/chain/import/stage/block_following.ex b/apps/explorer/lib/explorer/chain/import/stage/block_following.ex index 65a7c076039c..8abbf9f79b7d 100644 --- a/apps/explorer/lib/explorer/chain/import/stage/block_following.ex +++ b/apps/explorer/lib/explorer/chain/import/stage/block_following.ex @@ -15,7 +15,8 @@ defmodule Explorer.Chain.Import.Stage.BlockFollowing do do: [ Runner.Block.SecondDegreeRelations, Runner.Block.Rewards, - Runner.Address.CurrentTokenBalances + Runner.Address.CurrentTokenBalances, + Runner.TokenInstances ] @impl Stage diff --git a/apps/explorer/lib/explorer/chain/token/instance.ex b/apps/explorer/lib/explorer/chain/token/instance.ex index ea91796e3214..c1b5bcd5f4ff 100644 --- a/apps/explorer/lib/explorer/chain/token/instance.ex +++ b/apps/explorer/lib/explorer/chain/token/instance.ex @@ -5,7 +5,7 @@ defmodule Explorer.Chain.Token.Instance do use Explorer.Schema - alias Explorer.Chain.{Address, Hash, Token, TokenTransfer} + alias Explorer.Chain.{Address, Block, Hash, Token, TokenTransfer} alias Explorer.Chain.Token.Instance alias Explorer.PagingOptions @@ -20,7 +20,10 @@ defmodule Explorer.Chain.Token.Instance do token_id: non_neg_integer(), token_contract_address_hash: Hash.Address.t(), metadata: map() | nil, - error: String.t() + error: String.t(), + owner_address_hash: Hash.Address.t(), + owner_updated_at_block: Block.block_number(), + owner_updated_at_log_index: non_neg_integer() } @primary_key false @@ -28,8 +31,10 @@ defmodule Explorer.Chain.Token.Instance do field(:token_id, :decimal, primary_key: true) field(:metadata, :map) field(:error, :string) + field(:owner_updated_at_block, :integer) + field(:owner_updated_at_log_index, :integer) - belongs_to(:owner, Address, references: :hash, define_field: false) + belongs_to(:owner, Address, foreign_key: :owner_address_hash, references: :hash, type: Hash.Address) belongs_to( :token, @@ -45,7 +50,15 @@ defmodule Explorer.Chain.Token.Instance do def changeset(%Instance{} = instance, params \\ %{}) do instance - |> cast(params, [:token_id, :metadata, :token_contract_address_hash, :error]) + |> cast(params, [ + :token_id, + :metadata, + :token_contract_address_hash, + :error, + :owner_address_hash, + :owner_updated_at_block, + :owner_updated_at_log_index + ]) |> validate_required([:token_id, :token_contract_address_hash]) |> foreign_key_constraint(:token_contract_address_hash) end diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index ff17c0f768cb..1c5a15123f51 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -47,7 +47,7 @@ defmodule Explorer.Chain.TokenTransfer do * `:token_id` - ID of the token (applicable to ERC-721 tokens) * `:transaction` - The `t:Explorer.Chain.Transaction.t/0` ledger * `:transaction_hash` - Transaction foreign key - * `:log_index` - Index of the corresponding `t:Explorer.Chain.Log.t/0` in the transaction. + * `:log_index` - Index of the corresponding `t:Explorer.Chain.Log.t/0` in the block. * `:amounts` - Tokens transferred amounts in case of batched transfer in ERC-1155 * `:token_ids` - IDs of the tokens (applicable to ERC-1155 tokens) """ @@ -360,4 +360,12 @@ defmodule Explorer.Chain.TokenTransfer do end def filter_by_type(query, _), do: query + + def only_consensus_transfers_query do + from(token_transfer in __MODULE__, + inner_join: block in Block, + on: token_transfer.block_hash == block.hash, + where: block.consensus == true + ) + end end diff --git a/apps/explorer/lib/explorer/token_instance_owner_address_migration/helper.ex b/apps/explorer/lib/explorer/token_instance_owner_address_migration/helper.ex new file mode 100644 index 000000000000..7b0a092245f0 --- /dev/null +++ b/apps/explorer/lib/explorer/token_instance_owner_address_migration/helper.ex @@ -0,0 +1,77 @@ +defmodule Explorer.TokenInstanceOwnerAddressMigration.Helper do + @moduledoc """ + Auxiliary functions for TokenInstanceOwnerAddressMigration.{Worker and Supervisor} + """ + import Ecto.Query, + only: [ + from: 2 + ] + + alias Explorer.{Chain, Repo} + alias Explorer.Chain.Token.Instance + alias Explorer.Chain.{SmartContract, TokenTransfer} + + {:ok, burn_address_hash} = Chain.string_to_address_hash(SmartContract.burn_address_hash_string()) + @burn_address_hash burn_address_hash + + @spec filtered_token_instances_query(non_neg_integer()) :: Ecto.Query.t() + def filtered_token_instances_query(limit) do + from(instance in Instance, + where: is_nil(instance.owner_address_hash), + inner_join: token in assoc(instance, :token), + where: token.type == "ERC-721", + limit: ^limit, + select: %{token_id: instance.token_id, token_contract_address_hash: instance.token_contract_address_hash} + ) + end + + @spec fetch_and_insert([map]) :: + {:error, :timeout | [map]} + | {:ok, + %{ + :token_instances => [Instance.t()] + }} + | {:error, any, any, map} + def fetch_and_insert(batch) do + changes = + Enum.map(batch, fn %{token_id: token_id, token_contract_address_hash: token_contract_address_hash} -> + token_transfer_query = + from(tt in TokenTransfer.only_consensus_transfers_query(), + where: + tt.token_contract_address_hash == ^token_contract_address_hash and + fragment("? @> ARRAY[?::decimal]", tt.token_ids, ^token_id), + order_by: [desc: tt.block_number, desc: tt.log_index], + limit: 1, + select: %{ + token_contract_address_hash: tt.token_contract_address_hash, + token_ids: tt.token_ids, + to_address_hash: tt.to_address_hash, + block_number: tt.block_number, + log_index: tt.log_index + } + ) + + token_transfer = + Repo.one(token_transfer_query) || + %{to_address_hash: @burn_address_hash, block_number: -1, log_index: -1} + + %{ + token_contract_address_hash: token_contract_address_hash, + token_id: token_id, + token_type: "ERC-721", + owner_address_hash: token_transfer.to_address_hash, + owner_updated_at_block: token_transfer.block_number, + owner_updated_at_log_index: token_transfer.log_index + } + end) + + Chain.import(%{token_instances: %{params: changes}}) + end + + @spec unfilled_token_instances_exists? :: boolean + def unfilled_token_instances_exists? do + 1 + |> filtered_token_instances_query() + |> Repo.exists?() + end +end diff --git a/apps/explorer/lib/explorer/token_instance_owner_address_migration/supervisor.ex b/apps/explorer/lib/explorer/token_instance_owner_address_migration/supervisor.ex new file mode 100644 index 000000000000..5bb8532fdf5a --- /dev/null +++ b/apps/explorer/lib/explorer/token_instance_owner_address_migration/supervisor.ex @@ -0,0 +1,26 @@ +defmodule Explorer.TokenInstanceOwnerAddressMigration.Supervisor do + @moduledoc """ + Supervisor for Explorer.TokenInstanceOwnerAddressMigration.Worker + """ + + use Supervisor + + alias Explorer.TokenInstanceOwnerAddressMigration.{Helper, Worker} + + def start_link(init_arg) do + Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__) + end + + @impl true + def init(_init_arg) do + if Helper.unfilled_token_instances_exists?() do + children = [ + {Worker, Application.get_env(:explorer, Explorer.TokenInstanceOwnerAddressMigration)} + ] + + Supervisor.init(children, strategy: :one_for_one) + else + :ignore + end + end +end diff --git a/apps/explorer/lib/explorer/token_instance_owner_address_migration/worker.ex b/apps/explorer/lib/explorer/token_instance_owner_address_migration/worker.ex new file mode 100644 index 000000000000..c0ad7c487997 --- /dev/null +++ b/apps/explorer/lib/explorer/token_instance_owner_address_migration/worker.ex @@ -0,0 +1,51 @@ +defmodule Explorer.TokenInstanceOwnerAddressMigration.Worker do + @moduledoc """ + GenServer for filling owner_address_hash, owner_updated_at_block and owner_updated_at_log_index + for ERC-721 token instances. Works in the following way + 1. Checks if there are some unprocessed nfts. + - if yes, then go to 2 stage + - if no, then shutdown + 2. Fetch `(concurrency * batch_size)` token instances, process them in `concurrency` tasks. + 3. Go to step 1 + """ + + use GenServer, restart: :transient + + alias Explorer.Repo + alias Explorer.TokenInstanceOwnerAddressMigration.Helper + + def start_link(concurrency: concurrency, batch_size: batch_size) do + GenServer.start_link(__MODULE__, %{concurrency: concurrency, batch_size: batch_size}, name: __MODULE__) + end + + @impl true + def init(opts) do + GenServer.cast(__MODULE__, :check_necessity) + + {:ok, opts} + end + + @impl true + def handle_cast(:check_necessity, state) do + if Helper.unfilled_token_instances_exists?() do + GenServer.cast(__MODULE__, :backfill) + {:noreply, state} + else + {:stop, :normal, state} + end + end + + @impl true + def handle_cast(:backfill, %{concurrency: concurrency, batch_size: batch_size} = state) do + (concurrency * batch_size) + |> Helper.filtered_token_instances_query() + |> Repo.all() + |> Enum.chunk_every(batch_size) + |> Enum.map(fn batch -> Task.async(fn -> Helper.fetch_and_insert(batch) end) end) + |> Task.await_many(:infinity) + + GenServer.cast(__MODULE__, :check_necessity) + + {:noreply, state} + end +end diff --git a/apps/explorer/priv/repo/migrations/20230818094455_add_token_ids_to_address_token_balances.exs b/apps/explorer/priv/repo/migrations/20230818094455_add_token_ids_to_address_token_balances.exs new file mode 100644 index 000000000000..a141276ab529 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20230818094455_add_token_ids_to_address_token_balances.exs @@ -0,0 +1,13 @@ +defmodule Explorer.Repo.Migrations.AddTokenIdsToAddressTokenBalances do + use Ecto.Migration + + def change do + alter table(:token_instances) do + add(:owner_address_hash, :bytea, null: true) + add(:owner_updated_at_block, :bigint, null: true) + add(:owner_updated_at_log_index, :integer, null: true) + end + + create(index(:token_instances, [:owner_address_hash])) + end +end diff --git a/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs b/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs index ab2d5bc46493..7e79c20916f3 100644 --- a/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs +++ b/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs @@ -368,6 +368,166 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do assert %{block_number: ^number, block_hash: ^hash} = Repo.one(PendingBlockOperation) end + + test "change instance owner if was token transfer in older blocks", + %{consensus_block: %{hash: block_hash, miner_hash: miner_hash, number: block_number}, options: options} do + block_number = block_number + 2 + consensus_block = insert(:block, %{hash: block_hash, number: block_number}) + + transaction = + :transaction + |> insert() + |> with_block(consensus_block) + + token_address = insert(:contract_address) + insert(:token, contract_address: token_address, type: "ERC-721") + id = Decimal.new(1) + + tt = + insert(:token_transfer, + token_ids: [id], + transaction: transaction, + token_contract_address: token_address, + block_number: block_number, + block: consensus_block, + log_index: 123 + ) + + %{hash: hash_1} = params_for(:block, consensus: true, miner_hash: miner_hash) + consensus_block_1 = insert(:block, %{hash: hash_1, number: block_number - 1}) + + transaction = + :transaction + |> insert() + |> with_block(consensus_block_1) + + for _ <- 0..10 do + insert(:token_transfer, + token_ids: [id], + transaction: transaction, + token_contract_address: tt.token_contract_address, + block_number: consensus_block_1.number, + block: consensus_block_1 + ) + end + + tt_1 = + insert(:token_transfer, + token_ids: [id], + transaction: transaction, + token_contract_address: tt.token_contract_address, + block_number: consensus_block_1.number, + block: consensus_block_1 + ) + + %{hash: hash_2} = params_for(:block, consensus: true, miner_hash: miner_hash) + consensus_block_2 = insert(:block, %{hash: hash_2, number: block_number - 2}) + + for _ <- 0..10 do + tx = + :transaction + |> insert() + |> with_block(consensus_block_2) + + insert(:token_transfer, + token_ids: [id], + transaction: tx, + token_contract_address: tt.token_contract_address, + block_number: consensus_block_2.number, + block: consensus_block_2 + ) + end + + instance = + insert(:token_instance, + token_contract_address_hash: token_address.hash, + token_id: id, + owner_updated_at_block: tt.block_number, + owner_updated_at_log_index: tt.log_index, + owner_address_hash: insert(:address).hash + ) + + block_params = + params_for(:block, hash: block_hash, miner_hash: miner_hash, number: block_number, consensus: false) + + %Ecto.Changeset{valid?: true, changes: block_changes} = Block.changeset(%Block{}, block_params) + changes_list = [block_changes] + error = instance.error + block_number = tt_1.block_number + log_index = tt_1.log_index + owner_address_hash = tt_1.to_address_hash + token_address_hash = token_address.hash + + assert {:ok, + %{ + update_token_instances_owner: [ + %Explorer.Chain.Token.Instance{ + token_id: ^id, + error: ^error, + owner_updated_at_block: ^block_number, + owner_updated_at_log_index: ^log_index, + owner_address_hash: ^owner_address_hash, + token_contract_address_hash: ^token_address_hash + } + ] + }} = Multi.new() |> Blocks.run(changes_list, options) |> Repo.transaction() + end + + test "change instance owner if there was no more token transfers", + %{consensus_block: %{hash: block_hash, miner_hash: miner_hash, number: block_number}, options: options} do + block_number = block_number + 1 + consensus_block = insert(:block, %{hash: block_hash, number: block_number}) + + transaction = + :transaction + |> insert() + |> with_block(consensus_block) + + token_address = insert(:contract_address) + insert(:token, contract_address: token_address, type: "ERC-721") + id = Decimal.new(1) + + tt = + insert(:token_transfer, + token_ids: [id], + transaction: transaction, + token_contract_address: token_address, + block_number: block_number, + block: consensus_block + ) + + instance = + insert(:token_instance, + token_contract_address_hash: token_address.hash, + token_id: id, + owner_updated_at_block: tt.block_number, + owner_updated_at_log_index: tt.log_index, + owner_address_hash: insert(:address).hash + ) + + block_params = + params_for(:block, hash: block_hash, miner_hash: miner_hash, number: block_number, consensus: false) + + %Ecto.Changeset{valid?: true, changes: block_changes} = Block.changeset(%Block{}, block_params) + changes_list = [block_changes] + error = instance.error + owner_address_hash = tt.from_address_hash + token_address_hash = token_address.hash + + assert {:ok, + %{ + update_token_instances_owner: [ + %Explorer.Chain.Token.Instance{ + token_id: ^id, + error: ^error, + owner_updated_at_block: -1, + owner_updated_at_log_index: -1, + owner_address_hash: ^owner_address_hash, + token_contract_address_hash: ^token_address_hash + } + ] + }} = Multi.new() |> Blocks.run(changes_list, options) |> Repo.transaction() + end end describe "lose_consensus/5" do diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 728d7adc2971..b7472db7120d 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -183,30 +183,6 @@ defmodule Explorer.ChainTest do assert result.token_contract_address_hash == token.contract_address_hash end - test "replaces existing token instance record" do - token = insert(:token) - - params = %{ - token_id: 1, - token_contract_address_hash: token.contract_address_hash, - metadata: %{uri: "http://example.com"} - } - - {:ok, _} = Chain.upsert_token_instance(params) - - params1 = %{ - token_id: 1, - token_contract_address_hash: token.contract_address_hash, - metadata: %{uri: "http://example1.com"} - } - - {:ok, result} = Chain.upsert_token_instance(params1) - - assert result.token_id == Decimal.new(1) - assert result.metadata == params1.metadata - assert result.token_contract_address_hash == token.contract_address_hash - end - test "fails to import with invalid params" do params = %{ token_id: 1, @@ -243,7 +219,8 @@ defmodule Explorer.ChainTest do insert(:token_instance, token_id: 1, token_contract_address_hash: token.contract_address_hash, - error: "no uri" + error: "no uri", + metadata: nil ) params = %{ @@ -4903,7 +4880,7 @@ defmodule Explorer.ChainTest do end end - describe "stream_unfetched_token_instances/2" do + describe "stream_not_inserted_token_instances/2" do test "reduces with given reducer and accumulator for ERC-721 token" do token_contract_address = insert(:contract_address) token = insert(:token, contract_address: token_contract_address, type: "ERC-721") @@ -4924,7 +4901,7 @@ defmodule Explorer.ChainTest do token_ids: [11] ) - assert {:ok, [result]} = Chain.stream_unfetched_token_instances([], &[&1 | &2]) + assert {:ok, [result]} = Chain.stream_not_inserted_token_instances([], &[&1 | &2]) assert result.token_id == List.first(token_transfer.token_ids) assert result.contract_address_hash == token_transfer.token_contract_address_hash end @@ -4948,7 +4925,7 @@ defmodule Explorer.ChainTest do token_ids: nil ) - assert {:ok, []} = Chain.stream_unfetched_token_instances([], &[&1 | &2]) + assert {:ok, []} = Chain.stream_not_inserted_token_instances([], &[&1 | &2]) end test "do not fetch records with token instances" do @@ -4976,7 +4953,7 @@ defmodule Explorer.ChainTest do token_contract_address_hash: token_transfer.token_contract_address_hash ) - assert {:ok, []} = Chain.stream_unfetched_token_instances([], &[&1 | &2]) + assert {:ok, []} = Chain.stream_not_inserted_token_instances([], &[&1 | &2]) end end diff --git a/apps/explorer/test/explorer/token_instance_owner_address_migration/helper_test.exs b/apps/explorer/test/explorer/token_instance_owner_address_migration/helper_test.exs new file mode 100644 index 000000000000..355d161259bb --- /dev/null +++ b/apps/explorer/test/explorer/token_instance_owner_address_migration/helper_test.exs @@ -0,0 +1,121 @@ +defmodule Explorer.TokenInstanceOwnerAddressMigration.HelperTest do + use Explorer.DataCase + + alias Explorer.{Chain, Repo} + alias Explorer.Chain.Token.Instance + alias Explorer.TokenInstanceOwnerAddressMigration.Helper + + {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + @burn_address_hash burn_address_hash + + describe "fetch_and_insert/2" do + test "successfully update owner of single token instance" do + token_address = insert(:contract_address) + insert(:token, contract_address: token_address, type: "ERC-721") + + instance = insert(:token_instance, token_contract_address_hash: token_address.hash) + + transaction = + :transaction + |> insert() + |> with_block() + + tt_1 = + insert(:token_transfer, + token_ids: [instance.token_id], + transaction: transaction, + token_contract_address: token_address + ) + + Helper.fetch_and_insert([ + %{token_id: instance.token_id, token_contract_address_hash: instance.token_contract_address_hash} + ]) + + owner_address = tt_1.to_address_hash + block_number = tt_1.block_number + log_index = tt_1.log_index + + assert %Instance{ + owner_address_hash: ^owner_address, + owner_updated_at_block: ^block_number, + owner_updated_at_log_index: ^log_index + } = + Repo.get_by(Instance, + token_id: instance.token_id, + token_contract_address_hash: instance.token_contract_address_hash + ) + end + + test "put placeholder value if tt absent in db" do + instance = insert(:token_instance) + + Helper.fetch_and_insert([ + %{token_id: instance.token_id, token_contract_address_hash: instance.token_contract_address_hash} + ]) + + assert %Instance{ + owner_address_hash: @burn_address_hash, + owner_updated_at_block: -1, + owner_updated_at_log_index: -1 + } = + Repo.get_by(Instance, + token_id: instance.token_id, + token_contract_address_hash: instance.token_contract_address_hash + ) + end + + test "update owners of token instances batch" do + instances = + for _ <- 0..5 do + token_address = insert(:contract_address) + insert(:token, contract_address: token_address, type: "ERC-721") + + instance = insert(:token_instance, token_contract_address_hash: token_address.hash) + + tt = + for _ <- 0..5 do + transaction = + :transaction + |> insert() + |> with_block() + + for _ <- 0..5 do + insert(:token_transfer, + token_ids: [instance.token_id], + transaction: transaction, + token_contract_address: token_address + ) + end + end + |> Enum.concat() + |> Enum.max_by(fn tt -> {tt.block_number, tt.log_index} end) + + %{ + token_id: instance.token_id, + token_contract_address_hash: instance.token_contract_address_hash, + owner_address_hash: tt.to_address_hash, + owner_updated_at_block: tt.block_number, + owner_updated_at_log_index: tt.log_index + } + end + + Helper.fetch_and_insert(instances) + + for ti <- instances do + owner_address = ti.owner_address_hash + block_number = ti.owner_updated_at_block + log_index = ti.owner_updated_at_log_index + + assert %Instance{ + owner_address_hash: ^owner_address, + owner_updated_at_block: ^block_number, + owner_updated_at_log_index: ^log_index + } = + Repo.get_by(Instance, + token_id: ti.token_id, + token_contract_address_hash: ti.token_contract_address_hash + ) + end + end + end +end diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index da2e13f6669d..b85a0fff6d82 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -863,6 +863,12 @@ defmodule Explorer.Factory do } end + def log_index_factory do + %{ + log_index: sequence("token_id", & &1) + } + end + def token_balance_factory do %TokenBalance{ address: build(:address), diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 4b0cf3aaa00b..5b5eaf0b8902 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -37,6 +37,7 @@ defmodule Indexer.Block.Fetcher do Addresses, AddressTokenBalances, MintTransfers, + TokenInstances, TokenTransfers, TransactionActions } @@ -183,6 +184,7 @@ defmodule Indexer.Block.Fetcher do address_token_balances = AddressTokenBalances.params_set(%{token_transfers_params: token_transfers}), transaction_actions = Enum.map(transaction_actions, fn action -> Map.put(action, :data, Map.delete(action.data, :block_number)) end), + token_instances = TokenInstances.params_set(%{token_transfers_params: token_transfers}), basic_import_options = %{ addresses: %{params: addresses}, address_coin_balances: %{params: coin_balances_params_set}, @@ -198,7 +200,8 @@ defmodule Indexer.Block.Fetcher do token_transfers: %{params: token_transfers}, tokens: %{on_conflict: :nothing, params: tokens}, transactions: %{params: transactions_with_receipts}, - withdrawals: %{params: withdrawals_params} + withdrawals: %{params: withdrawals_params}, + token_instances: %{params: token_instances} }, import_options = (if Application.get_env(:explorer, :chain_type) == "polygon_edge" do diff --git a/apps/indexer/lib/indexer/fetcher/token_instance/legacy_sanitize.ex b/apps/indexer/lib/indexer/fetcher/token_instance/legacy_sanitize.ex new file mode 100644 index 000000000000..1fe11e1d90dc --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/token_instance/legacy_sanitize.ex @@ -0,0 +1,58 @@ +defmodule Indexer.Fetcher.TokenInstance.LegacySanitize do + @moduledoc """ + This fetcher is stands for creating token instances which wasn't inserted yet and index meta for them. Legacy is because now we token instances inserted on block import and this fetcher is only for historical and unfetched for some reasons data + """ + + use Indexer.Fetcher, restart: :permanent + use Spandex.Decorators + + import Indexer.Fetcher.TokenInstance.Helper + + alias Explorer.Chain + alias Indexer.BufferedTask + + @behaviour BufferedTask + + @default_max_batch_size 10 + @default_max_concurrency 10 + @doc false + def child_spec([init_options, gen_server_options]) do + merged_init_opts = + defaults() + |> Keyword.merge(init_options) + |> Keyword.merge(state: []) + + Supervisor.child_spec({BufferedTask, [{__MODULE__, merged_init_opts}, gen_server_options]}, id: __MODULE__) + end + + @impl BufferedTask + def init(initial_acc, reducer, _) do + {:ok, acc} = + Chain.stream_not_inserted_token_instances(initial_acc, fn data, acc -> + reducer.(data, acc) + end) + + acc + end + + @impl BufferedTask + 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 + + defp defaults do + [ + flush_interval: :infinity, + max_concurrency: Application.get_env(:indexer, __MODULE__)[:concurrency] || @default_max_concurrency, + max_batch_size: Application.get_env(:indexer, __MODULE__)[:batch_size] || @default_max_batch_size, + poll: false, + task_supervisor: __MODULE__.TaskSupervisor + ] + end +end diff --git a/apps/indexer/lib/indexer/fetcher/token_instance/realtime.ex b/apps/indexer/lib/indexer/fetcher/token_instance/realtime.ex index 804c9258c851..14375b16018a 100644 --- a/apps/indexer/lib/indexer/fetcher/token_instance/realtime.ex +++ b/apps/indexer/lib/indexer/fetcher/token_instance/realtime.ex @@ -35,7 +35,7 @@ defmodule Indexer.Fetcher.TokenInstance.Realtime do 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) + Chain.token_instance_with_unfetched_metadata?(token_id, hash) end) |> batch_fetch_instances() diff --git a/apps/indexer/lib/indexer/fetcher/token_instance/sanitize.ex b/apps/indexer/lib/indexer/fetcher/token_instance/sanitize.ex index 1bc3a70bd1f1..f2cc5fa71d96 100644 --- a/apps/indexer/lib/indexer/fetcher/token_instance/sanitize.ex +++ b/apps/indexer/lib/indexer/fetcher/token_instance/sanitize.ex @@ -28,7 +28,7 @@ defmodule Indexer.Fetcher.TokenInstance.Sanitize do @impl BufferedTask def init(initial_acc, reducer, _) do {:ok, acc} = - Chain.stream_unfetched_token_instances(initial_acc, fn data, acc -> + Chain.stream_token_instances_with_unfetched_metadata(initial_acc, fn data, acc -> reducer.(data, acc) end) @@ -39,7 +39,7 @@ defmodule Indexer.Fetcher.TokenInstance.Sanitize do 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) + Chain.token_instance_with_unfetched_metadata?(token_id, hash) end) |> batch_fetch_instances() diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex index ccbeddca5045..732c71727a71 100644 --- a/apps/indexer/lib/indexer/supervisor.ex +++ b/apps/indexer/lib/indexer/supervisor.ex @@ -13,6 +13,7 @@ defmodule Indexer.Supervisor do alias Indexer.Block.Catchup, as: BlockCatchup alias Indexer.Block.Realtime, as: BlockRealtime + alias Indexer.Fetcher.TokenInstance.LegacySanitize, as: TokenInstanceLegacySanitize alias Indexer.Fetcher.TokenInstance.Realtime, as: TokenInstanceRealtime alias Indexer.Fetcher.TokenInstance.Retry, as: TokenInstanceRetry alias Indexer.Fetcher.TokenInstance.Sanitize, as: TokenInstanceSanitize @@ -115,6 +116,7 @@ defmodule Indexer.Supervisor do {TokenInstanceRealtime.Supervisor, [[memory_monitor: memory_monitor]]}, {TokenInstanceRetry.Supervisor, [[memory_monitor: memory_monitor]]}, {TokenInstanceSanitize.Supervisor, [[memory_monitor: memory_monitor]]}, + {TokenInstanceLegacySanitize.Supervisor, [[memory_monitor: memory_monitor]]}, configure(TransactionAction.Supervisor, [[memory_monitor: memory_monitor]]), {ContractCode.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, diff --git a/apps/indexer/lib/indexer/transform/address_token_balances.ex b/apps/indexer/lib/indexer/transform/address_token_balances.ex index cae8b2cbac7b..291783f6a2ba 100644 --- a/apps/indexer/lib/indexer/transform/address_token_balances.ex +++ b/apps/indexer/lib/indexer/transform/address_token_balances.ex @@ -11,7 +11,6 @@ defmodule Indexer.Transform.AddressTokenBalances do defp reducer({:token_transfers_params, token_transfers_params}, initial) when is_list(token_transfers_params) do token_transfers_params - |> ignore_burn_address_transfers_for_token_erc_721() |> Enum.reduce(initial, fn %{ block_number: block_number, from_address_hash: from_address_hash, @@ -31,10 +30,6 @@ defmodule Indexer.Transform.AddressTokenBalances do end) end - defp ignore_burn_address_transfers_for_token_erc_721(token_transfers_params) do - Enum.filter(token_transfers_params, &do_filter_burn_address/1) - end - 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 @@ -46,12 +41,4 @@ defmodule Indexer.Transform.AddressTokenBalances do token_type: token_type }) end - - def do_filter_burn_address(%{to_address_hash: unquote(burn_address_hash_string()), token_type: "ERC-721"}) do - false - end - - def do_filter_burn_address(_token_balance_param) do - true - end end diff --git a/apps/indexer/lib/indexer/transform/token_instances.ex b/apps/indexer/lib/indexer/transform/token_instances.ex new file mode 100644 index 000000000000..a9fb4372d2fd --- /dev/null +++ b/apps/indexer/lib/indexer/transform/token_instances.ex @@ -0,0 +1,88 @@ +defmodule Indexer.Transform.TokenInstances do + @moduledoc """ + Module extracts token instances from token transfers + """ + + def params_set(%{} = import_options) do + Enum.reduce(import_options, %{}, &reducer/2) + end + + defp reducer({:token_transfers_params, token_transfers_params}, initial) when is_list(token_transfers_params) do + token_transfers_params + |> Enum.reduce(initial, fn + %{ + block_number: block_number, + from_address_hash: from_address_hash, + to_address_hash: to_address_hash, + token_contract_address_hash: token_contract_address_hash, + token_ids: [_ | _] + } = tt, + acc + when is_integer(block_number) and + is_binary(from_address_hash) and + is_binary(to_address_hash) and is_binary(token_contract_address_hash) -> + transfer_to_instances(tt, acc) + + _, acc -> + acc + end) + |> Map.values() + end + + defp transfer_to_instances( + %{ + token_type: "ERC-721" = token_type, + to_address_hash: to_address_hash, + token_ids: [token_id], + token_contract_address_hash: token_contract_address_hash, + block_number: block_number, + log_index: log_index + }, + acc + ) do + params = %{ + token_contract_address_hash: token_contract_address_hash, + token_id: token_id, + token_type: token_type, + owner_address_hash: to_address_hash, + owner_updated_at_block: block_number, + owner_updated_at_log_index: log_index + } + + current_key = {token_contract_address_hash, token_id} + + Map.put( + acc, + current_key, + Enum.max_by( + [ + params, + acc[current_key] || params + ], + fn %{ + owner_updated_at_block: owner_updated_at_block, + owner_updated_at_log_index: owner_updated_at_log_index + } -> + {owner_updated_at_block, owner_updated_at_log_index} + end + ) + ) + end + + defp transfer_to_instances( + %{ + token_type: _token_type, + token_ids: [_ | _] = token_ids, + token_contract_address_hash: token_contract_address_hash + }, + acc + ) do + Enum.reduce(token_ids, acc, fn id, sub_acc -> + Map.put(sub_acc, {token_contract_address_hash, id}, %{ + token_contract_address_hash: token_contract_address_hash, + token_id: id, + token_type: "ERC-1155" + }) + end) + end +end diff --git a/apps/indexer/test/indexer/transform/address_token_balances_test.exs b/apps/indexer/test/indexer/transform/address_token_balances_test.exs index 04111ff26215..b70e2f84fe09 100644 --- a/apps/indexer/test/indexer/transform/address_token_balances_test.exs +++ b/apps/indexer/test/indexer/transform/address_token_balances_test.exs @@ -66,7 +66,7 @@ defmodule Indexer.Transform.AddressTokenBalancesTest do ]) end - test "does not set params when the to_address_hash is the burn address for the Token ERC-721" do + test "does set params when the to_address_hash is the burn address for the Token ERC-721" do block_number = 1 from_address_hash = "0x5b8410f67eb8040bb1cd1e8a4ff9d5f6ce678a15" to_address_hash = "0x0000000000000000000000000000000000000000" @@ -77,12 +77,22 @@ defmodule Indexer.Transform.AddressTokenBalancesTest do from_address_hash: from_address_hash, to_address_hash: to_address_hash, token_contract_address_hash: token_contract_address_hash, - token_type: "ERC-721" + token_type: "ERC-721", + token_ids: [1] } params_set = AddressTokenBalances.params_set(%{token_transfers_params: [token_transfer_params]}) - assert MapSet.size(params_set) == 0 + assert params_set == + MapSet.new([ + %{ + address_hash: "0x5b8410f67eb8040bb1cd1e8a4ff9d5f6ce678a15", + block_number: 1, + token_contract_address_hash: "0xe18035bf8712672935fdb4e5e431b1a0183d2dfc", + token_id: 1, + token_type: "ERC-721" + } + ]) end end end diff --git a/config/runtime.exs b/config/runtime.exs index 2d51abb6198b..e83d0489d833 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -412,6 +412,10 @@ config :explorer, Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand, config :explorer, Explorer.Chain.Cache.MinMissingBlockNumber, enabled: !ConfigHelper.parse_bool_env_var("DISABLE_INDEXER") +config :explorer, Explorer.TokenInstanceOwnerAddressMigration, + concurrency: ConfigHelper.parse_integer_env_var("TOKEN_INSTANCE_OWNER_MIGRATION_CONCURRENCY", 5), + batch_size: ConfigHelper.parse_integer_env_var("TOKEN_INSTANCE_OWNER_MIGRATION_BATCH_SIZE", 50) + ############### ### Indexer ### ############### @@ -502,6 +506,9 @@ config :indexer, Indexer.Fetcher.TokenInstance.Retry.Supervisor, config :indexer, Indexer.Fetcher.TokenInstance.Sanitize.Supervisor, disabled?: ConfigHelper.parse_bool_env_var("INDEXER_DISABLE_TOKEN_INSTANCE_SANITIZE_FETCHER") +config :indexer, Indexer.Fetcher.TokenInstance.LegacySanitize.Supervisor, + disabled?: ConfigHelper.parse_bool_env_var("INDEXER_DISABLE_TOKEN_INSTANCE_LEGACY_SANITIZE_FETCHER", "true") + config :indexer, Indexer.Fetcher.EmptyBlocksSanitizer, batch_size: ConfigHelper.parse_integer_env_var("INDEXER_EMPTY_BLOCKS_SANITIZER_BATCH_SIZE", 100) @@ -532,6 +539,10 @@ config :indexer, Indexer.Fetcher.TokenInstance.Sanitize, 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.TokenInstance.LegacySanitize, + concurrency: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_INSTANCE_LEGACY_SANITIZE_CONCURRENCY", 10), + batch_size: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_INSTANCE_LEGACY_SANITIZE_BATCH_SIZE", 10) + config :indexer, Indexer.Fetcher.InternalTransaction, batch_size: ConfigHelper.parse_integer_env_var("INDEXER_INTERNAL_TRANSACTIONS_BATCH_SIZE", 10), concurrency: ConfigHelper.parse_integer_env_var("INDEXER_INTERNAL_TRANSACTIONS_CONCURRENCY", 4), diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 5d8c29b3c563..6211113d22f6 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -100,6 +100,7 @@ DISABLE_REALTIME_INDEXER=false INDEXER_DISABLE_TOKEN_INSTANCE_REALTIME_FETCHER=false INDEXER_DISABLE_TOKEN_INSTANCE_RETRY_FETCHER=false INDEXER_DISABLE_TOKEN_INSTANCE_SANITIZE_FETCHER=false +INDEXER_DISABLE_TOKEN_INSTANCE_LEGACY_SANITIZE_FETCHER=false INDEXER_DISABLE_PENDING_TRANSACTIONS_FETCHER=false INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_CATCHUP_BLOCKS_BATCH_SIZE= @@ -113,6 +114,7 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_TOKEN_INSTANCE_RETRY_CONCURRENCY= # INDEXER_TOKEN_INSTANCE_REALTIME_CONCURRENCY= # INDEXER_TOKEN_INSTANCE_SANITIZE_CONCURRENCY= +# INDEXER_TOKEN_INSTANCE_LEGACY_SANITIZE_CONCURRENCY=10 # INDEXER_COIN_BALANCES_BATCH_SIZE= # INDEXER_COIN_BALANCES_CONCURRENCY= # INDEXER_RECEIPTS_BATCH_SIZE= @@ -230,4 +232,7 @@ EIP_1559_ELASTICITY_MULTIPLIER=2 # INDEXER_TOKEN_INSTANCE_RETRY_BATCH_SIZE=10 # INDEXER_TOKEN_INSTANCE_REALTIME_BATCH_SIZE=1 # INDEXER_TOKEN_INSTANCE_SANITIZE_BATCH_SIZE=10 -API_V2_ENABLED=true \ No newline at end of file +API_V2_ENABLED=true +# INDEXER_TOKEN_INSTANCE_LEGACY_SANITIZE_BATCH_SIZE=10 +# TOKEN_INSTANCE_OWNER_MIGRATION_CONCURRENCY=5 +# TOKEN_INSTANCE_OWNER_MIGRATION_BATCH_SIZE=50 From 5adcca011cc82858e848da4aa7bd490610df06ca Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Wed, 27 Sep 2023 17:17:43 +0300 Subject: [PATCH 018/224] Add CHAIN_TYPE build arg to Dockerfile --- CHANGELOG.md | 1 + docker/Dockerfile | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0696b53b234f..9394771c1a57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Fixes +- [#8552](https://github.com/blockscout/blockscout/pull/8552) - Add CHAIN_TYPE build arg to Dockerfile - [#8515](https://github.com/blockscout/blockscout/pull/8515) - Fix `:error.types/0 is undefined` warning - [#7959](https://github.com/blockscout/blockscout/pull/7959) - Fix empty batch transfers handling - [#8513](https://github.com/blockscout/blockscout/pull/8513) - Don't override transaction status diff --git a/docker/Dockerfile b/docker/Dockerfile index aba6de879ff1..ba1ccd8f85d6 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -18,6 +18,8 @@ ARG MIXPANEL_TOKEN ARG MIXPANEL_URL ARG AMPLITUDE_API_KEY ARG AMPLITUDE_URL +ARG CHAIN_TYPE +ENV CHAIN_TYPE=${CHAIN_TYPE} # Cache elixir deps ADD mix.exs mix.lock ./ @@ -59,6 +61,8 @@ FROM bitwalker/alpine-elixir-phoenix:1.14 ARG RELEASE_VERSION ENV RELEASE_VERSION=${RELEASE_VERSION} +ARG CHAIN_TYPE +ENV CHAIN_TYPE=${CHAIN_TYPE} ARG BLOCKSCOUT_VERSION ENV BLOCKSCOUT_VERSION=${BLOCKSCOUT_VERSION} From 88a618d7c0599ef1b87e9fdbe56b58a6a119e2f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:29:22 +0000 Subject: [PATCH 019/224] Bump @amplitude/analytics-browser in /apps/block_scout_web/assets Bumps [@amplitude/analytics-browser](https://github.com/amplitude/Amplitude-TypeScript) from 2.3.1 to 2.3.2. - [Release notes](https://github.com/amplitude/Amplitude-TypeScript/releases) - [Commits](https://github.com/amplitude/Amplitude-TypeScript/compare/@amplitude/analytics-browser@2.3.1...@amplitude/analytics-browser@2.3.2) --- updated-dependencies: - dependency-name: "@amplitude/analytics-browser" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- apps/block_scout_web/assets/package-lock.json | 46 +++++++++---------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 4a7e289372d2..50f5ff5e635f 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -7,7 +7,7 @@ "name": "blockscout", "license": "GPL-3.0", "dependencies": { - "@amplitude/analytics-browser": "^2.3.1", + "@amplitude/analytics-browser": "^2.3.2", "@fortawesome/fontawesome-free": "^6.4.2", "@tarekraafat/autocomplete.js": "^10.2.7", "@walletconnect/web3-provider": "^1.8.0", @@ -116,15 +116,15 @@ } }, "node_modules/@amplitude/analytics-browser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.3.1.tgz", - "integrity": "sha512-sDX6Nupw6SSsTfaCYrrIwRys4Ins/H7TzmJimEqM4bEoKmM9ONkKFAAF+E9sQz9XvMr5iBBqbmb5Sd+u3f7SRA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.3.2.tgz", + "integrity": "sha512-Zjk/PeCc0PZuzWlsGaf/i6qkkofqJvToh41tPhkOIFvLJlv/3cHtB2WXEod9vj20m+XcNoBATbcFAQgl2ubTeA==", "dependencies": { "@amplitude/analytics-client-common": "^2.0.6", "@amplitude/analytics-core": "^2.0.5", "@amplitude/analytics-types": "^2.2.0", - "@amplitude/plugin-page-view-tracking-browser": "^2.0.11", - "@amplitude/plugin-web-attribution-browser": "^2.0.11", + "@amplitude/plugin-page-view-tracking-browser": "^2.0.12", + "@amplitude/plugin-web-attribution-browser": "^2.0.12", "tslib": "^2.4.1" } }, @@ -174,9 +174,9 @@ "integrity": "sha512-HThPlsX5KnxCorQnPVNQR8WZy0/PdgAzlT5n8bubjSmzPEWQgvn8bQzD48wYXoAkEZxlfYlaSnx7IxD5j3N1Dw==" }, "node_modules/@amplitude/plugin-page-view-tracking-browser": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.0.11.tgz", - "integrity": "sha512-GZ5JpyZOX6Ejif+m9vE/H8XbB41OkPOLDzIudd52vVlZN/o5Pmovb5UyDW+C9osNP7mra7YeB28vrJwqN18IbQ==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.0.12.tgz", + "integrity": "sha512-0QJJZocqQ+MltQcI4BkshQJcUOM9mIOiud0BnDPcwAbUjlzGVG+xm4KuOA3bjfGGcSF7I6mpnOnG3nPkX5k47A==", "dependencies": { "@amplitude/analytics-client-common": "^2.0.6", "@amplitude/analytics-types": "^2.2.0", @@ -189,9 +189,9 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/@amplitude/plugin-web-attribution-browser": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-attribution-browser/-/plugin-web-attribution-browser-2.0.11.tgz", - "integrity": "sha512-5NyjGq6FaNCU2ZBuaViaqf3/x9JQOghA40gjNoGSpsogeZZbxdpZDDMWbE3Zq5+ruTGbwed6IqlrFkT3NUQyZw==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-attribution-browser/-/plugin-web-attribution-browser-2.0.12.tgz", + "integrity": "sha512-Ii5lzsixf+2E9f+K2Knk3q/O9tYkoFDm0E9KfPhvkNM27mRLtJ1/YTfvmW4otaYrnPwcvl2Y5E9tzATVrmIelw==", "dependencies": { "@amplitude/analytics-client-common": "^2.0.6", "@amplitude/analytics-core": "^2.0.5", @@ -17695,15 +17695,15 @@ "dev": true }, "@amplitude/analytics-browser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.3.1.tgz", - "integrity": "sha512-sDX6Nupw6SSsTfaCYrrIwRys4Ins/H7TzmJimEqM4bEoKmM9ONkKFAAF+E9sQz9XvMr5iBBqbmb5Sd+u3f7SRA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.3.2.tgz", + "integrity": "sha512-Zjk/PeCc0PZuzWlsGaf/i6qkkofqJvToh41tPhkOIFvLJlv/3cHtB2WXEod9vj20m+XcNoBATbcFAQgl2ubTeA==", "requires": { "@amplitude/analytics-client-common": "^2.0.6", "@amplitude/analytics-core": "^2.0.5", "@amplitude/analytics-types": "^2.2.0", - "@amplitude/plugin-page-view-tracking-browser": "^2.0.11", - "@amplitude/plugin-web-attribution-browser": "^2.0.11", + "@amplitude/plugin-page-view-tracking-browser": "^2.0.12", + "@amplitude/plugin-web-attribution-browser": "^2.0.12", "tslib": "^2.4.1" }, "dependencies": { @@ -17759,9 +17759,9 @@ "integrity": "sha512-HThPlsX5KnxCorQnPVNQR8WZy0/PdgAzlT5n8bubjSmzPEWQgvn8bQzD48wYXoAkEZxlfYlaSnx7IxD5j3N1Dw==" }, "@amplitude/plugin-page-view-tracking-browser": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.0.11.tgz", - "integrity": "sha512-GZ5JpyZOX6Ejif+m9vE/H8XbB41OkPOLDzIudd52vVlZN/o5Pmovb5UyDW+C9osNP7mra7YeB28vrJwqN18IbQ==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.0.12.tgz", + "integrity": "sha512-0QJJZocqQ+MltQcI4BkshQJcUOM9mIOiud0BnDPcwAbUjlzGVG+xm4KuOA3bjfGGcSF7I6mpnOnG3nPkX5k47A==", "requires": { "@amplitude/analytics-client-common": "^2.0.6", "@amplitude/analytics-types": "^2.2.0", @@ -17776,9 +17776,9 @@ } }, "@amplitude/plugin-web-attribution-browser": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-attribution-browser/-/plugin-web-attribution-browser-2.0.11.tgz", - "integrity": "sha512-5NyjGq6FaNCU2ZBuaViaqf3/x9JQOghA40gjNoGSpsogeZZbxdpZDDMWbE3Zq5+ruTGbwed6IqlrFkT3NUQyZw==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-attribution-browser/-/plugin-web-attribution-browser-2.0.12.tgz", + "integrity": "sha512-Ii5lzsixf+2E9f+K2Knk3q/O9tYkoFDm0E9KfPhvkNM27mRLtJ1/YTfvmW4otaYrnPwcvl2Y5E9tzATVrmIelw==", "requires": { "@amplitude/analytics-client-common": "^2.0.6", "@amplitude/analytics-core": "^2.0.5", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index beeae5ef9b42..398e36537eca 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -20,7 +20,7 @@ }, "dependencies": { "@fortawesome/fontawesome-free": "^6.4.2", - "@amplitude/analytics-browser": "^2.3.1", + "@amplitude/analytics-browser": "^2.3.2", "@tarekraafat/autocomplete.js": "^10.2.7", "@walletconnect/web3-provider": "^1.8.0", "assert": "^2.1.0", From 394de45bdc1108f4e7c4a6e8964e076808e464f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 21:42:11 +0000 Subject: [PATCH 020/224] Bump get-func-name from 2.0.0 to 2.0.2 in /apps/block_scout_web/assets Bumps [get-func-name](https://github.com/chaijs/get-func-name) from 2.0.0 to 2.0.2. - [Release notes](https://github.com/chaijs/get-func-name/releases) - [Commits](https://github.com/chaijs/get-func-name/commits/v2.0.2) --- updated-dependencies: - dependency-name: get-func-name dependency-type: indirect ... Signed-off-by: dependabot[bot] --- apps/block_scout_web/assets/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 50f5ff5e635f..273257e62ab4 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -8969,9 +8969,9 @@ } }, "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "engines": { "node": "*" } @@ -24457,9 +24457,9 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==" }, "get-intrinsic": { "version": "1.2.1", From 4167926ed3d2a9a1d82755c5067059aebe63aee6 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Thu, 28 Sep 2023 11:42:11 +0300 Subject: [PATCH 021/224] Apply new issue template to the footer --- .github/ISSUE_TEMPLATE/bug_report.yml | 29 +++-------- CHANGELOG.md | 2 +- .../templates/layout/_footer.html.eex | 2 +- .../lib/block_scout_web/views/layout_view.ex | 52 ++++++------------- 4 files changed, 24 insertions(+), 61 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 8f563f127a40..bcd36c1e76b6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -30,32 +30,21 @@ body: validations: required: true - - type: dropdown + - type: input id: archive-node-type attributes: label: Type of the JSON RPC archive node description: Which type of archive node is used. - options: - - Erigon - - Geth - - Nethermind - - Reth - - PolygonEdge - - Besu - - OpenEthereum - - Other + placeholder: "Erigon/Geth/Nethermind/Reth/PolygonEdge/Besu/OpenEthereum/..." validations: required: true - - type: dropdown + - type: input id: chain-type attributes: label: Type of the chain description: Type of the chain. - options: - - L1 - - L2 - - Other + placeholder: L1/L2/... - type: input id: link @@ -96,16 +85,12 @@ body: validations: required: true - - type: dropdown - id: operating-system + - type: input + id: os-version attributes: label: Operating system description: The operating system this issue occurred with. - options: - - macOS - - Windows - - Linux - - Other + placeholder: Linux/macOS/Windows - type: textarea id: additional-information diff --git a/CHANGELOG.md b/CHANGELOG.md index 9394771c1a57..cf2d1373cfc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ ### Chore -- [#8536](https://github.com/blockscout/blockscout/pull/8536), [#8537](https://github.com/blockscout/blockscout/pull/8537), [#8540](https://github.com/blockscout/blockscout/pull/8540) - New issue template +- [#8536](https://github.com/blockscout/blockscout/pull/8536), [#8537](https://github.com/blockscout/blockscout/pull/8537), [#8540](https://github.com/blockscout/blockscout/pull/8540), [#8557](https://github.com/blockscout/blockscout/pull/8557) - New issue template - [#8529](https://github.com/blockscout/blockscout/pull/8529) - Move PolygonEdge-related migration to the corresponding ecto repository - [#8504](https://github.com/blockscout/blockscout/pull/8504) - Deploy new UI through Makefile - [#8501](https://github.com/blockscout/blockscout/pull/8501) - Conceal secondary ports in docker compose setup diff --git a/apps/block_scout_web/lib/block_scout_web/templates/layout/_footer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/layout/_footer.html.eex index 0f82cf278d16..ff88dd8c8fdc 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/layout/_footer.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/layout/_footer.html.eex @@ -38,7 +38,7 @@