From f8f6c78c7c9e1de763d3af815e0dbdba9bb2c2a7 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Wed, 17 Jul 2024 13:55:20 +0400 Subject: [PATCH 001/363] feat: Add memory metrics for OnDemand fetchers (#10425) --- apps/indexer/lib/indexer/memory/monitor.ex | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/indexer/lib/indexer/memory/monitor.ex b/apps/indexer/lib/indexer/memory/monitor.ex index 6fefce04a1b8..b48bb0b73ca3 100644 --- a/apps/indexer/lib/indexer/memory/monitor.ex +++ b/apps/indexer/lib/indexer/memory/monitor.ex @@ -208,7 +208,7 @@ defmodule Indexer.Memory.Monitor do @megabytes_divisor 2 ** 20 defp set_metrics(%__MODULE__{shrinkable_set: shrinkable_set}) do total_memory = - Enum.reduce(shrinkable_set, 0, fn pid, acc -> + Enum.reduce(shrinkable_set ++ on_demand_fetchers(), 0, fn pid, acc -> memory = memory(pid) / @megabytes_divisor name = name(pid) @@ -220,6 +220,15 @@ defmodule Indexer.Memory.Monitor do Instrumenter.set_memory_consumed(:total, total_memory) end + defp on_demand_fetchers do + Enum.flat_map([Indexer.Application, Indexer.Supervisor, Explorer.Supervisor], fn supervisor -> + supervisor + |> Supervisor.which_children() + |> Enum.filter(fn {name, _, _, _} -> is_atom(name) and String.contains?(to_string(name), "OnDemand") end) + |> Enum.map(fn {_, pid, _, _} -> pid end) + end) + end + defp name(pid) do case Process.info(pid, :registered_name) do {:registered_name, name} when is_atom(name) -> From 168067cff797b5cd93d3c25d75680b3e52aa2bc5 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Wed, 17 Jul 2024 17:08:26 +0400 Subject: [PATCH 002/363] fix: Fix on-demand fetchers metrics (#10431) * fix: Fix on-demand fetchers metrics * Fix monitor metrics --- apps/indexer/lib/indexer/memory/monitor.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/indexer/lib/indexer/memory/monitor.ex b/apps/indexer/lib/indexer/memory/monitor.ex index b48bb0b73ca3..c907cda6b59a 100644 --- a/apps/indexer/lib/indexer/memory/monitor.ex +++ b/apps/indexer/lib/indexer/memory/monitor.ex @@ -208,7 +208,7 @@ defmodule Indexer.Memory.Monitor do @megabytes_divisor 2 ** 20 defp set_metrics(%__MODULE__{shrinkable_set: shrinkable_set}) do total_memory = - Enum.reduce(shrinkable_set ++ on_demand_fetchers(), 0, fn pid, acc -> + Enum.reduce(Enum.to_list(shrinkable_set) ++ on_demand_fetchers(), 0, fn pid, acc -> memory = memory(pid) / @megabytes_divisor name = name(pid) @@ -221,7 +221,9 @@ defmodule Indexer.Memory.Monitor do end defp on_demand_fetchers do - Enum.flat_map([Indexer.Application, Indexer.Supervisor, Explorer.Supervisor], fn supervisor -> + [Indexer.Application, Indexer.Supervisor, Explorer.Supervisor] + |> Enum.reject(&is_nil(Process.whereis(&1))) + |> Enum.flat_map(fn supervisor -> supervisor |> Supervisor.which_children() |> Enum.filter(fn {name, _, _, _} -> is_atom(name) and String.contains?(to_string(name), "OnDemand") end) From 6f8b48f2c1e658d01f3ade8e682ef64bad73e885 Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Fri, 19 Jul 2024 10:52:48 +0200 Subject: [PATCH 003/363] fix: race condition in cache tests (#10441) --- apps/explorer/lib/explorer/chain/cache/address_sum.ex | 2 +- .../lib/explorer/chain/cache/address_sum_minus_burnt.ex | 2 +- .../lib/explorer/chain/cache/background_migrations.ex | 8 ++++---- apps/explorer/lib/explorer/chain/cache/block.ex | 2 +- .../explorer/lib/explorer/chain/cache/gas_price_oracle.ex | 2 +- apps/explorer/lib/explorer/chain/cache/gas_usage.ex | 2 +- .../lib/explorer/chain/cache/pending_block_operation.ex | 2 +- apps/explorer/lib/explorer/chain/cache/transaction.ex | 2 +- apps/explorer/lib/explorer/chain/ordered_cache.ex | 2 +- apps/explorer/test/explorer/chain/cache/block_test.exs | 2 +- .../test/explorer/chain/cache/transaction_test.exs | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/cache/address_sum.ex b/apps/explorer/lib/explorer/chain/cache/address_sum.ex index 16da91ba0b9a..8e2d67f381aa 100644 --- a/apps/explorer/lib/explorer/chain/cache/address_sum.ex +++ b/apps/explorer/lib/explorer/chain/cache/address_sum.ex @@ -28,7 +28,7 @@ defmodule Explorer.Chain.Cache.AddressSum do # If this gets called it means an async task was requested, but none exists # so a new one needs to be launched {:ok, task} = - Task.start(fn -> + Task.start_link(fn -> try do result = Etherscan.fetch_sum_coin_total_supply() diff --git a/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex b/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex index 3af99f580a83..6e8dfa9289e9 100644 --- a/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex +++ b/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex @@ -28,7 +28,7 @@ defmodule Explorer.Chain.Cache.AddressSumMinusBurnt do # If this gets called it means an async task was requested, but none exists # so a new one needs to be launched {:ok, task} = - Task.start(fn -> + Task.start_link(fn -> try do result = Etherscan.fetch_sum_coin_total_supply_minus_burnt() diff --git a/apps/explorer/lib/explorer/chain/cache/background_migrations.ex b/apps/explorer/lib/explorer/chain/cache/background_migrations.ex index c4449853b290..3d716b7da808 100644 --- a/apps/explorer/lib/explorer/chain/cache/background_migrations.ex +++ b/apps/explorer/lib/explorer/chain/cache/background_migrations.ex @@ -22,7 +22,7 @@ defmodule Explorer.Chain.Cache.BackgroundMigrations do } defp handle_fallback(:transactions_denormalization_finished) do - Task.start(fn -> + Task.start_link(fn -> set_transactions_denormalization_finished(TransactionsDenormalization.migration_finished?()) end) @@ -30,7 +30,7 @@ defmodule Explorer.Chain.Cache.BackgroundMigrations do end defp handle_fallback(:tb_token_type_finished) do - Task.start(fn -> + Task.start_link(fn -> set_tb_token_type_finished(AddressTokenBalanceTokenType.migration_finished?()) end) @@ -38,7 +38,7 @@ defmodule Explorer.Chain.Cache.BackgroundMigrations do end defp handle_fallback(:ctb_token_type_finished) do - Task.start(fn -> + Task.start_link(fn -> set_ctb_token_type_finished(AddressCurrentTokenBalanceTokenType.migration_finished?()) end) @@ -46,7 +46,7 @@ defmodule Explorer.Chain.Cache.BackgroundMigrations do end defp handle_fallback(:tt_denormalization_finished) do - Task.start(fn -> + Task.start_link(fn -> set_tt_denormalization_finished(TokenTransferTokenType.migration_finished?()) end) diff --git a/apps/explorer/lib/explorer/chain/cache/block.ex b/apps/explorer/lib/explorer/chain/cache/block.ex index 672f80f76e2a..edb97c43d794 100644 --- a/apps/explorer/lib/explorer/chain/cache/block.ex +++ b/apps/explorer/lib/explorer/chain/cache/block.ex @@ -67,7 +67,7 @@ defmodule Explorer.Chain.Cache.Block do # If this gets called it means an async task was requested, but none exists # so a new one needs to be launched {:ok, task} = - Task.start(fn -> + Task.start_link(fn -> try do result = fetch_count_consensus_block() diff --git a/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex b/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex index 5716fa113bfc..12c615dad3e6 100644 --- a/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex +++ b/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex @@ -321,7 +321,7 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do # If this gets called it means an async task was requested, but none exists # so a new one needs to be launched {:ok, task} = - Task.start(fn -> + Task.start_link(fn -> try do {result, acc} = get_average_gas_price(num_of_blocks(), safelow(), average(), fast()) diff --git a/apps/explorer/lib/explorer/chain/cache/gas_usage.ex b/apps/explorer/lib/explorer/chain/cache/gas_usage.ex index f671f2bf5377..6a632c97dd24 100644 --- a/apps/explorer/lib/explorer/chain/cache/gas_usage.ex +++ b/apps/explorer/lib/explorer/chain/cache/gas_usage.ex @@ -49,7 +49,7 @@ defmodule Explorer.Chain.Cache.GasUsage do # If this gets called it means an async task was requested, but none exists # so a new one needs to be launched {:ok, task} = - Task.start(fn -> + Task.start_link(fn -> try do result = fetch_sum_gas_used() diff --git a/apps/explorer/lib/explorer/chain/cache/pending_block_operation.ex b/apps/explorer/lib/explorer/chain/cache/pending_block_operation.ex index 6980087afa79..7dccbe06ca0b 100644 --- a/apps/explorer/lib/explorer/chain/cache/pending_block_operation.ex +++ b/apps/explorer/lib/explorer/chain/cache/pending_block_operation.ex @@ -46,7 +46,7 @@ defmodule Explorer.Chain.Cache.PendingBlockOperation do # If this gets called it means an async task was requested, but none exists # so a new one needs to be launched {:ok, task} = - Task.start(fn -> + Task.start_link(fn -> try do result = Repo.aggregate(PendingBlockOperation, :count, timeout: :infinity) diff --git a/apps/explorer/lib/explorer/chain/cache/transaction.ex b/apps/explorer/lib/explorer/chain/cache/transaction.ex index bad41fe32e1e..6ac24f537e6a 100644 --- a/apps/explorer/lib/explorer/chain/cache/transaction.ex +++ b/apps/explorer/lib/explorer/chain/cache/transaction.ex @@ -47,7 +47,7 @@ defmodule Explorer.Chain.Cache.Transaction do # If this gets called it means an async task was requested, but none exists # so a new one needs to be launched {:ok, task} = - Task.start(fn -> + Task.start_link(fn -> try do result = Repo.aggregate(Transaction, :count, :hash, timeout: :infinity) diff --git a/apps/explorer/lib/explorer/chain/ordered_cache.ex b/apps/explorer/lib/explorer/chain/ordered_cache.ex index c6235d33b85a..9d2e797cddb6 100644 --- a/apps/explorer/lib/explorer/chain/ordered_cache.ex +++ b/apps/explorer/lib/explorer/chain/ordered_cache.ex @@ -290,7 +290,7 @@ defmodule Explorer.Chain.OrderedCache do # Different updates cannot interfere with the removed element because # if this was scheduled for removal it means it is too old, so following # updates cannot insert it in the future. - Task.start(fn -> + Task.start_link(fn -> Process.sleep(100) if is_list(key) do diff --git a/apps/explorer/test/explorer/chain/cache/block_test.exs b/apps/explorer/test/explorer/chain/cache/block_test.exs index 70b258f3d048..39a5da93ee59 100644 --- a/apps/explorer/test/explorer/chain/cache/block_test.exs +++ b/apps/explorer/test/explorer/chain/cache/block_test.exs @@ -22,7 +22,7 @@ defmodule Explorer.Chain.Cache.BlockTest do _result = Block.get_count() - Process.sleep(2000) + Process.sleep(1000) updated_value = Block.get_count() diff --git a/apps/explorer/test/explorer/chain/cache/transaction_test.exs b/apps/explorer/test/explorer/chain/cache/transaction_test.exs index 29f484a41a46..0be603a33b05 100644 --- a/apps/explorer/test/explorer/chain/cache/transaction_test.exs +++ b/apps/explorer/test/explorer/chain/cache/transaction_test.exs @@ -22,7 +22,7 @@ defmodule Explorer.Chain.Cache.TransactionTest do _result = Transaction.get_count() - Process.sleep(2000) + Process.sleep(1000) updated_value = Transaction.get_count() From cfd3da54036f3badd25bcac14ba753b73f19a48e Mon Sep 17 00:00:00 2001 From: Markus Osterlund / robriks <80549215+robriks@users.noreply.github.com> Date: Fri, 19 Jul 2024 06:08:39 -0400 Subject: [PATCH 004/363] doc: Move note in README.md higher for visibility (#10450) The note detailing `docker compose` use for Docker's compose v2 plugin should be higher in the README since the document's first bash snippet includes `docker-compose up --build` which uses the "docker-compose" syntax. This way the relevant note is readily visible above the first mention of "docker-compose" so skimmers trying to get the explorer up and running are immediately informed of the difference --- docker-compose/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose/README.md b/docker-compose/README.md index 6d750a1444e3..1283ede3d4d9 100644 --- a/docker-compose/README.md +++ b/docker-compose/README.md @@ -10,6 +10,8 @@ Runs Blockscout locally in Docker containers with [docker-compose](https://githu ## Building Docker containers from source +**Note**: in all below examples, you can use `docker compose` instead of `docker-compose`, if compose v2 plugin is installed in Docker. + ```bash cd ./docker-compose docker-compose up --build @@ -38,8 +40,6 @@ and 5 containers for microservices (written in Rust): The repo contains built-in configs for different JSON RPC clients without need to build the image. -**Note**: in all below examples, you can use `docker compose` instead of `docker-compose`, if compose v2 plugin is installed in Docker. - | __JSON RPC Client__ | __Docker compose launch command__ | | -------- | ------- | | Erigon | `docker-compose -f erigon.yml up -d` | From 23adf212005c4730d309e7d84efaf8a55f53fc59 Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Fri, 19 Jul 2024 15:52:01 +0300 Subject: [PATCH 005/363] fix: include internal transactions in state change (#10210) * fix: include internal transactions in state change * Process review comments * Do not take into account first trace * Process review comment --- .../api/v2/transaction_controller.ex | 9 +- .../transaction_state_controller.ex | 9 +- .../models/transaction_state_helper.ex | 106 +++++++++----- .../api/v2/transaction_controller_test.exs | 138 +++++++++++++++++- .../chain/transaction/state_change.ex | 134 +++++++++++------ 5 files changed, 296 insertions(+), 100 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex index 21bc5e7cb149..485411b11593 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex @@ -460,14 +460,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do """ @spec state_changes(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} def state_changes(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do - with {:ok, transaction, _transaction_hash} <- - validate_transaction(transaction_hash_string, params, - necessity_by_association: - Map.merge(@transaction_necessity_by_association, %{ - [block: [miner: [:names, :smart_contract, :proxy_implementations]]] => :optional - }), - api?: true - ) do + with {:ok, transaction, _transaction_hash} <- validate_transaction(transaction_hash_string, params, api?: true) do state_changes_plus_next_page = transaction |> TransactionStateHelper.state_changes(params |> paging_options() |> Keyword.merge(api?: true)) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex index 7ea2d6e74e6c..f978e7647b28 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex @@ -25,14 +25,7 @@ defmodule BlockScoutWeb.TransactionStateController do def index(conn, %{"transaction_id" => transaction_hash_string, "type" => "JSON"} = params) do with {:ok, transaction_hash} <- Chain.string_to_transaction_hash(transaction_hash_string), {:ok, transaction} <- - Chain.hash_to_transaction( - transaction_hash, - necessity_by_association: %{ - [block: :miner] => :optional, - from_address: :optional, - to_address: :optional - } - ), + Chain.hash_to_transaction(transaction_hash), {:ok, false} <- AccessHelper.restricted_access?(to_string(transaction.from_address_hash), params), {:ok, false} <- diff --git a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex index 1502e9b2148c..8d492ed1841e 100644 --- a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex @@ -7,8 +7,8 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] alias Explorer.Chain.Transaction.StateChange - alias Explorer.{Chain, PagingOptions} - alias Explorer.Chain.{Block, BlockNumberHelper, Transaction, Wei} + alias Explorer.{Chain, PagingOptions, Repo} + alias Explorer.Chain.{BlockNumberHelper, Transaction, Wei} alias Explorer.Chain.Cache.StateChanges alias Indexer.Fetcher.OnDemand.CoinBalance, as: CoinBalanceOnDemand alias Indexer.Fetcher.OnDemand.TokenBalance, as: TokenBalanceOnDemand @@ -16,9 +16,15 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do {:ok, burn_address_hash} = Chain.string_to_address_hash(burn_address_hash_string()) @burn_address_hash burn_address_hash + @doc """ + This function takes transaction, fetches all the transactions before this one from the same block + together with internal transactions and token transfers and calculates native coin and token + balances before and after this transaction. + """ + @spec state_changes(Transaction.t(), [Chain.paging_options() | Chain.api?()]) :: [StateChange.t()] def state_changes(transaction, options \\ []) - def state_changes(%Transaction{block: %Block{}} = transaction, options) do + def state_changes(%Transaction{} = transaction, options) do paging_options = Keyword.get(options, :paging_options, default_paging_options()) {offset} = paging_options.key || {0} @@ -48,53 +54,82 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do state_changes end - defp do_state_changes(%Transaction{block: %Block{} = block} = transaction, options) do - transaction_hash = transaction.hash - - full_options = [ - necessity_by_association: %{ - [from_address: :smart_contract] => :optional, - [to_address: :smart_contract] => :optional, - [from_address: :names] => :optional, - [to_address: :names] => :optional, - from_address: :required, - to_address: :required - }, - # we need to consider all token transfers in block to show whole state change of transaction - paging_options: %PagingOptions{key: nil, page_size: nil}, - api?: Keyword.get(options, :api?, false) - ] - - token_transfers = Chain.transaction_to_token_transfers(transaction_hash, full_options) - + defp do_state_changes(%Transaction{} = transaction, options) do block_txs = - Chain.block_to_transactions(block.hash, - necessity_by_association: %{}, + transaction.block_hash + |> Chain.block_to_transactions( paging_options: %PagingOptions{key: nil, page_size: nil}, api?: Keyword.get(options, :api?, false) ) + |> Enum.filter(&(&1.index <= transaction.index)) + |> Repo.preload([:token_transfers, :internal_transactions]) + + transaction = + block_txs + |> Enum.find(&(&1.hash == transaction.hash)) + |> Repo.preload( + token_transfers: [ + from_address: [:names, :smart_contract, :proxy_implementations], + to_address: [:names, :smart_contract, :proxy_implementations] + ], + internal_transactions: [ + from_address: [:names, :smart_contract, :proxy_implementations], + to_address: [:names, :smart_contract, :proxy_implementations] + ], + block: [miner: [:names, :smart_contract, :proxy_implementations]], + from_address: [:names, :smart_contract, :proxy_implementations], + to_address: [:names, :smart_contract, :proxy_implementations] + ) - previous_block_number = BlockNumberHelper.previous_block_number(block.number) + previous_block_number = BlockNumberHelper.previous_block_number(transaction.block_number) - from_before_block = coin_balance(transaction.from_address_hash, previous_block_number, options) - to_before_block = coin_balance(transaction.to_address_hash, previous_block_number, options) - miner_before_block = coin_balance(block.miner_hash, previous_block_number, options) + coin_balances_before_block = transaction_to_coin_balances(transaction, previous_block_number, options) - {from_before_tx, to_before_tx, miner_before_tx} = - StateChange.coin_balances_before(transaction, block_txs, from_before_block, to_before_block, miner_before_block) + coin_balances_before_tx = StateChange.coin_balances_before(transaction, block_txs, coin_balances_before_block) - native_coin_entries = StateChange.native_coin_entries(transaction, from_before_tx, to_before_tx, miner_before_tx) + native_coin_entries = StateChange.native_coin_entries(transaction, coin_balances_before_tx) token_balances_before = - token_transfers - |> Enum.reduce(%{}, &token_transfers_to_balances_reducer(&1, &2, options)) + transaction.token_transfers + |> Enum.reduce(%{}, &token_transfers_to_balances_reducer(&1, &2, previous_block_number, options)) |> StateChange.token_balances_before(transaction, block_txs) - tokens_entries = StateChange.token_entries(token_transfers, token_balances_before) + tokens_entries = StateChange.token_entries(transaction.token_transfers, token_balances_before) native_coin_entries ++ tokens_entries end + defp transaction_to_coin_balances(transaction, previous_block_number, options) do + Enum.reduce( + transaction.internal_transactions, + %{ + transaction.from_address_hash => + {transaction.from_address, coin_balance(transaction.from_address_hash, previous_block_number, options)}, + transaction.to_address_hash => + {transaction.to_address, coin_balance(transaction.to_address_hash, previous_block_number, options)}, + transaction.block.miner_hash => + {transaction.block.miner, coin_balance(transaction.block.miner_hash, previous_block_number, options)} + }, + &internal_transaction_to_coin_balances(&1, previous_block_number, options, &2) + ) + end + + defp internal_transaction_to_coin_balances(internal_transaction, previous_block_number, options, acc) do + if internal_transaction.value |> Wei.to(:wei) |> Decimal.positive?() do + acc + |> Map.put_new_lazy(internal_transaction.from_address_hash, fn -> + {internal_transaction.from_address, + coin_balance(internal_transaction.from_address_hash, previous_block_number, options)} + end) + |> Map.put_new_lazy(internal_transaction.to_address_hash, fn -> + {internal_transaction.to_address, + coin_balance(internal_transaction.to_address_hash, previous_block_number, options)} + end) + else + acc + end + end + defp coin_balance(address_hash, _block_number, _options) when is_nil(address_hash) do %Wei{value: Decimal.new(0)} end @@ -145,11 +180,10 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do end end - defp token_transfers_to_balances_reducer(transfer, balances, options) do + defp token_transfers_to_balances_reducer(transfer, balances, prev_block, options) do from = transfer.from_address to = transfer.to_address token_hash = transfer.token_contract_address_hash - prev_block = BlockNumberHelper.previous_block_number(transfer.block_number) balances |> case do diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs index 7b3a9652bbf6..f28b729e2983 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs @@ -5,7 +5,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do alias BlockScoutWeb.Models.UserFromAuth alias Explorer.Account.WatchlistAddress - alias Explorer.Chain.{Address, InternalTransaction, Log, Token, TokenTransfer, Transaction} + alias Explorer.Chain.{Address, InternalTransaction, Log, Token, TokenTransfer, Transaction, Wei} alias Explorer.Repo setup do @@ -965,6 +965,142 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do assert response = json_response(request, 200) assert Enum.count(response["items"]) == 3 end + + test "does not include internal transaction with index 0", %{conn: conn} do + block_before = insert(:block) + + transaction = + :transaction + |> insert() + |> with_block(status: :ok) + + internal_transaction_from = insert(:address) + internal_transaction_to = insert(:address) + + insert(:internal_transaction, + transaction: transaction, + index: 0, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 0, + value: %Wei{value: Decimal.new(7)}, + from_address_hash: internal_transaction_from.hash, + from_address: internal_transaction_from, + to_address_hash: internal_transaction_to.hash, + to_address: internal_transaction_to + ) + + insert(:address_coin_balance, + address: transaction.from_address, + address_hash: transaction.from_address_hash, + block_number: block_before.number + ) + + insert(:address_coin_balance, + address: transaction.to_address, + address_hash: transaction.to_address_hash, + block_number: block_before.number + ) + + insert(:address_coin_balance, + address: transaction.block.miner, + address_hash: transaction.block.miner_hash, + block_number: block_before.number + ) + + insert(:address_coin_balance, + address: internal_transaction_from, + address_hash: internal_transaction_from.hash, + block_number: block_before.number + ) + + insert(:address_coin_balance, + address: internal_transaction_to, + address_hash: internal_transaction_to.hash, + block_number: block_before.number + ) + + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/state-changes") + + assert response = json_response(request, 200) + assert Enum.count(response["items"]) == 3 + end + + test "return entries from internal transaction", %{conn: conn} do + block_before = insert(:block) + + transaction = + :transaction + |> insert() + |> with_block(status: :ok) + + internal_transaction_from = insert(:address) + internal_transaction_to = insert(:address) + + insert(:internal_transaction, + transaction: transaction, + index: 0, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 0, + value: %Wei{value: Decimal.new(7)}, + from_address_hash: internal_transaction_from.hash, + from_address: internal_transaction_from, + to_address_hash: internal_transaction_to.hash, + to_address: internal_transaction_to + ) + + insert(:internal_transaction, + transaction: transaction, + index: 1, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 1, + value: %Wei{value: Decimal.new(7)}, + from_address_hash: internal_transaction_from.hash, + from_address: internal_transaction_from, + to_address_hash: internal_transaction_to.hash, + to_address: internal_transaction_to + ) + + insert(:address_coin_balance, + address: transaction.from_address, + address_hash: transaction.from_address_hash, + block_number: block_before.number + ) + + insert(:address_coin_balance, + address: transaction.to_address, + address_hash: transaction.to_address_hash, + block_number: block_before.number + ) + + insert(:address_coin_balance, + address: transaction.block.miner, + address_hash: transaction.block.miner_hash, + block_number: block_before.number + ) + + insert(:address_coin_balance, + address: internal_transaction_from, + address_hash: internal_transaction_from.hash, + block_number: block_before.number + ) + + insert(:address_coin_balance, + address: internal_transaction_to, + address_hash: internal_transaction_to.hash, + block_number: block_before.number + ) + + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/state-changes") + + assert response = json_response(request, 200) + assert Enum.count(response["items"]) == 5 + end end if Application.compile_env(:explorer, :chain_type) == :stability do diff --git a/apps/explorer/lib/explorer/chain/transaction/state_change.ex b/apps/explorer/lib/explorer/chain/transaction/state_change.ex index 3d5f0f7f8b5e..9bdf9222b6d1 100644 --- a/apps/explorer/lib/explorer/chain/transaction/state_change.ex +++ b/apps/explorer/lib/explorer/chain/transaction/state_change.ex @@ -4,14 +4,14 @@ defmodule Explorer.Chain.Transaction.StateChange do """ alias Explorer.Chain - alias Explorer.Chain.{Hash, TokenTransfer, Transaction, Wei} + alias Explorer.Chain.{Address, Block, Hash, InternalTransaction, TokenTransfer, Transaction, Wei} alias Explorer.Chain.Transaction.StateChange defstruct [:coin_or_token_transfers, :address, :token_id, :balance_before, :balance_after, :balance_diff, :miner?] @type t :: %__MODULE__{ coin_or_token_transfers: :coin | [TokenTransfer.t()], - address: Hash.Address.t(), + address: Address.t(), token_id: nil | non_neg_integer(), balance_before: Wei.t() | Decimal.t(), balance_after: Wei.t() | Decimal.t(), @@ -19,35 +19,49 @@ defmodule Explorer.Chain.Transaction.StateChange do miner?: boolean() } - def coin_balances_before(tx, block_txs, from_before, to_before, miner_before) do + @type coin_balances_map :: %{Hash.Address.t() => {Address.t(), Wei.t()}} + + @zero_wei %Wei{value: Decimal.new(0)} + + @spec coin_balances_before(Transaction.t(), [Transaction.t()], coin_balances_map()) :: coin_balances_map() + def coin_balances_before(tx, block_txs, coin_balances_before_block) do block = tx.block block_txs |> Enum.reduce_while( - {from_before, to_before, miner_before}, - fn block_tx, {block_from, block_to, block_miner} = state -> + coin_balances_before_block, + fn block_tx, acc -> if block_tx.index < tx.index do - {:cont, - {update_coin_balance_from_tx(tx.from_address_hash, block_tx, block_from, block), - update_coin_balance_from_tx(tx.to_address_hash, block_tx, block_to, block), - update_coin_balance_from_tx(tx.block.miner_hash, block_tx, block_miner, block)}} + {:cont, update_coin_balances_from_tx(acc, block_tx, block)} else # txs ordered by index ascending, so we can halt after facing index greater or equal than index of our tx - {:halt, state} + {:halt, acc} end end ) end - def update_coin_balance_from_tx(address_hash, tx, balance, block) do - from = tx.from_address_hash - to = tx.to_address_hash - miner = block.miner_hash + @spec update_coin_balances_from_tx(coin_balances_map(), Transaction.t(), Block.t()) :: coin_balances_map() + def update_coin_balances_from_tx(coin_balances, tx, block) do + coin_balances = + coin_balances + |> update_balance(tx.from_address_hash, &Wei.sub(&1, from_loss(tx))) + |> update_balance(tx.to_address_hash, &Wei.sum(&1, to_profit(tx))) + |> update_balance(block.miner_hash, &Wei.sum(&1, miner_profit(tx, block))) + + if error?(tx) do + coin_balances + else + tx.internal_transactions |> Enum.reduce(coin_balances, &update_coin_balances_from_internal_tx(&1, &2)) + end + end + + defp update_coin_balances_from_internal_tx(%InternalTransaction{index: 0}, coin_balances), do: coin_balances - balance - |> (&if(address_hash == from, do: Wei.sub(&1, from_loss(tx)), else: &1)).() - |> (&if(address_hash == to, do: Wei.sum(&1, to_profit(tx)), else: &1)).() - |> (&if(address_hash == miner, do: Wei.sum(&1, miner_profit(tx, block)), else: &1)).() + defp update_coin_balances_from_internal_tx(internal_tx, coin_balances) do + coin_balances + |> update_balance(internal_tx.from_address_hash, &Wei.sub(&1, from_loss(internal_tx))) + |> update_balance(internal_tx.to_address_hash, &Wei.sum(&1, to_profit(internal_tx))) end def token_balances_before(balances_before, tx, block_txs) do @@ -65,11 +79,11 @@ defmodule Explorer.Chain.Transaction.StateChange do ) end - def do_update_token_balances_from_token_transfers( - token_transfers, - balances_map, - include_transfers \\ :no - ) do + defp do_update_token_balances_from_token_transfers( + token_transfers, + balances_map, + include_transfers \\ :no + ) do Enum.reduce( token_transfers, balances_map, @@ -139,7 +153,12 @@ defmodule Explorer.Chain.Transaction.StateChange do end) end - def from_loss(tx) do + @doc """ + Returns the balance change of from address of a transaction + or an internal transaction. + """ + @spec from_loss(Transaction.t() | InternalTransaction.t()) :: Wei.t() + def from_loss(%Transaction{} = tx) do {_, fee} = Transaction.fee(tx, :wei) if error?(tx) do @@ -149,7 +168,16 @@ defmodule Explorer.Chain.Transaction.StateChange do end end - def to_profit(tx) do + def from_loss(%InternalTransaction{} = tx) do + tx.value + end + + @doc """ + Returns the balance change of to address of a transaction + or an internal transaction. + """ + @spec to_profit(Transaction.t() | InternalTransaction.t()) :: Wei.t() + def to_profit(%Transaction{} = tx) do if error?(tx) do %Wei{value: 0} else @@ -157,6 +185,10 @@ defmodule Explorer.Chain.Transaction.StateChange do end end + def to_profit(%InternalTransaction{} = tx) do + tx.value + end + defp miner_profit(tx, block) do base_fee_per_gas = block.base_fee_per_gas || %Wei{value: Decimal.new(0)} max_priority_fee_per_gas = tx.max_priority_fee_per_gas || tx.gas_price @@ -196,35 +228,30 @@ defmodule Explorer.Chain.Transaction.StateChange do } end - def native_coin_entries(transaction, from_before_tx, to_before_tx, miner_before_tx) do + @doc """ + Returns the list of native coin state changes of a transaction, including state changes from the internal transactions, + taking into account state changes from previous transactions in the same block. + """ + @spec native_coin_entries(Transaction.t(), coin_balances_map()) :: [t()] + def native_coin_entries(transaction, coin_balances_before_tx) do block = transaction.block - from_hash = transaction.from_address_hash - to_hash = transaction.to_address_hash - miner_hash = block.miner_hash + coin_balances_after_tx = update_coin_balances_from_tx(coin_balances_before_tx, transaction, block) - from_coin_entry = - if from_hash not in [to_hash, miner_hash] do - from = transaction.from_address - from_after_tx = update_coin_balance_from_tx(from_hash, transaction, from_before_tx, block) - coin_entry(from, from_before_tx, from_after_tx) - end + coin_balances_before_tx + |> Enum.reduce([], fn {address_hash, {address, coin_balance_before}}, acc -> + {_, coin_balance_after} = coin_balances_after_tx[address_hash] + coin_entry = coin_entry(address, coin_balance_before, coin_balance_after, address_hash == block.miner_hash) - to_coin_entry = - if not is_nil(to_hash) and to_hash != miner_hash do - to = transaction.to_address - to_after = update_coin_balance_from_tx(to_hash, transaction, to_before_tx, block) - coin_entry(to, to_before_tx, to_after) + if coin_entry do + [coin_entry | acc] + else + acc end - - miner = block.miner - miner_after = update_coin_balance_from_tx(miner_hash, transaction, miner_before_tx, block) - miner_entry = coin_entry(miner, miner_before_tx, miner_after, true) - - [from_coin_entry, to_coin_entry, miner_entry] |> Enum.reject(&is_nil/1) + end) end - defp coin_entry(address, balance_before, balance_after, miner? \\ false) do + defp coin_entry(address, balance_before, balance_after, miner?) do diff = Wei.sub(balance_after, balance_before) if has_diff?(diff) do @@ -240,6 +267,19 @@ defmodule Explorer.Chain.Transaction.StateChange do end end + defp update_balance(coin_balances, address_hash, _update_function) when is_nil(address_hash), + do: coin_balances + + defp update_balance(coin_balances, address_hash, update_function) do + if Map.has_key?(coin_balances, address_hash) do + Map.update(coin_balances, address_hash, @zero_wei, fn {address, balance} -> + {address, update_function.(balance)} + end) + else + coin_balances + end + end + def token_entries(token_transfers, token_balances_before) do token_balances_after = do_update_token_balances_from_token_transfers( From 4c36b8a82cb9c2f69021c9034eb54883abd1809a Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Fri, 19 Jul 2024 17:36:22 +0200 Subject: [PATCH 006/363] fix: code compiler test (#10454) --- .../smart_contract/solidity/code_compiler_test.exs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs index ed80cb12191d..cef3b5465ea7 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs @@ -14,6 +14,13 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do describe "run/2" do setup do + configuration = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, enabled: false) + + on_exit(fn -> + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, configuration) + end) + {:ok, contract_code_info: Factory.contract_code_info(), contract_code_info_modern_compiler: Factory.contract_code_info_modern_compiler()} From 158720ea2059448b24c7e4f4fc987aa59fe6750a Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 19 Jul 2024 18:38:12 +0300 Subject: [PATCH 007/363] fix: Missing clauses in MetadataPreloader functions (#10439) * Missing clause in MetadataPreloader.alter_address * Update clause for alter_address * Add nil clause for item_to_address_hash_strings --- .../lib/explorer/chain/address/metadata_preloader.ex | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/address/metadata_preloader.ex b/apps/explorer/lib/explorer/chain/address/metadata_preloader.ex index 762af23c3f6e..2ee81f28438d 100644 --- a/apps/explorer/lib/explorer/chain/address/metadata_preloader.ex +++ b/apps/explorer/lib/explorer/chain/address/metadata_preloader.ex @@ -127,6 +127,8 @@ defmodule Explorer.Chain.Address.MetadataPreloader do end) end + defp item_to_address_hash_strings(nil), do: [] + defp item_to_address_hash_strings(%Transaction{ to_address_hash: to_address_hash, created_contract_address_hash: created_contract_address_hash, @@ -285,14 +287,16 @@ defmodule Explorer.Chain.Address.MetadataPreloader do alter_address(address, address.hash, names, field_to_put_info) end - defp alter_address(_, nil, _names, _field) do - nil - end + defp alter_address(address, nil, _names, _field), do: address defp alter_address(%NotLoaded{}, address_hash, names, field) do %{field => names[Address.checksum(address_hash)]} end + defp alter_address(nil, address_hash, names, field) do + %{field => names[Address.checksum(address_hash)]} + end + defp alter_address(%Address{} = address, address_hash, names, :ens_domain_name) do %Address{address | ens_domain_name: names[Address.checksum(address_hash)]} end From f87017793a73c397bb3b238e4d368cbd4f40d092 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 19 Jul 2024 18:38:55 +0300 Subject: [PATCH 008/363] Fix "key :bytes not found in: nil" issue (#10435) --- .../lib/explorer/smart_contract/helper.ex | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/apps/explorer/lib/explorer/smart_contract/helper.ex b/apps/explorer/lib/explorer/smart_contract/helper.ex index dd74830d16c6..38c2026c6159 100644 --- a/apps/explorer/lib/explorer/smart_contract/helper.ex +++ b/apps/explorer/lib/explorer/smart_contract/helper.ex @@ -44,10 +44,7 @@ defmodule Explorer.SmartContract.Helper do def add_contract_code_md5(%{address_hash: address_hash_string} = attrs) when is_binary(address_hash_string) do with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), {:ok, address} <- Chain.hash_to_address(address_hash) do - contract_code_md5 = contract_code_md5(address.contract_code.bytes) - - attrs - |> Map.put_new(:contract_code_md5, contract_code_md5) + attrs_extend_with_contract_code_md5(attrs, address) else _ -> attrs end @@ -56,14 +53,7 @@ defmodule Explorer.SmartContract.Helper do def add_contract_code_md5(%{address_hash: address_hash} = attrs) do case Chain.hash_to_address(address_hash) do {:ok, address} -> - if address.contract_code do - contract_code_md5 = contract_code_md5(address.contract_code.bytes) - - attrs - |> Map.put_new(:contract_code_md5, contract_code_md5) - else - attrs - end + attrs_extend_with_contract_code_md5(attrs, address) _ -> attrs @@ -78,6 +68,17 @@ defmodule Explorer.SmartContract.Helper do |> Base.encode16(case: :lower) end + defp attrs_extend_with_contract_code_md5(attrs, address) do + if address.contract_code do + contract_code_md5 = contract_code_md5(address.contract_code.bytes) + + attrs + |> Map.put_new(:contract_code_md5, contract_code_md5) + else + attrs + end + end + def sanitize_input(nil), do: nil def sanitize_input(input) do From b6634290b1c4046c4ff8c48c0faf0810c21ad4fb Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Tue, 23 Jul 2024 01:01:20 -0600 Subject: [PATCH 009/363] fix: avoid infinite loop during batch block range binary search (#10436) --- .../lib/indexer/fetcher/arbitrum/utils/rpc.ex | 206 +++++++++++++----- 1 file changed, 148 insertions(+), 58 deletions(-) diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex index e099815a173d..8fc1f2afd9c5 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex @@ -12,6 +12,8 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Rpc do @zero_hash "0000000000000000000000000000000000000000000000000000000000000000" @rpc_resend_attempts 20 + @default_binary_search_threshold 1000 + # outbox() @selector_outbox "ce11e6ab" # sequencerInbox() @@ -173,27 +175,13 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Rpc do EthereumJSONRPC.json_rpc_named_arguments() ) :: non_neg_integer() def get_block_number_for_keyset(sequencer_inbox_address, keyset_hash, json_rpc_named_arguments) do - [ - %{ - contract_address: sequencer_inbox_address, - method_id: @selector_get_keyset_creation_block, - args: [keyset_hash] - } - ] - |> IndexerHelper.read_contracts_with_retries( + read_contract_and_handle_result_as_integer( + sequencer_inbox_address, + @selector_get_keyset_creation_block, + [keyset_hash], @selector_sequencer_inbox_contract_abi, - json_rpc_named_arguments, - @rpc_resend_attempts + json_rpc_named_arguments ) - # Extracts the list of responses from the tuple returned by read_contracts_with_retries. - |> Kernel.elem(0) - # Retrieves the first response from the list of responses. The responses are in a list - # because read_contracts_with_retries accepts a list of method calls. - |> List.first() - # Extracts the result from the {status, result} tuple which is composed in EthereumJSONRPC.Encoder.decode_result. - |> Kernel.elem(1) - # Extracts the first decoded value from the result, which is a list, even if it contains only one value. - |> List.first() end # Calls getter functions on a rollup contract and collects their return values. @@ -220,6 +208,7 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Rpc do } end) |> IndexerHelper.read_contracts_with_retries(@rollup_contract_abi, json_rpc_named_arguments, @rpc_resend_attempts) + # Extracts the list of responses from the tuple returned by read_contracts_with_retries. |> Kernel.elem(0) |> Enum.zip(method_ids) |> Enum.reduce(%{}, fn {{:ok, [response]}, method_id}, retval -> @@ -473,12 +462,13 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Rpc do ) do opposite_block = do_binary_search_of_opposite_block( - initial_block - initial_step, + max(1, initial_block - initial_step), initial_step, required_batch_number, rollup_config, required_batch_number, - initial_block + initial_block, + %{} ) # the default direction for the block range exploration is chosen to be from the highest to lowest @@ -495,7 +485,8 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Rpc do # `findBatchContainingBlock` of the Node Interface contract to determine the # batch number of the inspected block and, based on the call result and the # previously inspected block, decides whether the opposite block is found or - # another iteration is required. + # another iteration is required. In order to avoid redundant RPC calls, the + # function uses a cache to store the batch numbers. # # Assumptions: # - The initial step is low enough to not jump more than one batch in a single @@ -515,9 +506,26 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Rpc do # - `prev_batch_number`: The number of the batch where the block was inspected # on the previous iteration. # - `prev_inspected_block`: The block number that was previously inspected. + # - `cache`: A map that stores the batch numbers for rollup blocks to avoid + # redundant RPC calls. + # - `iteration_threshold`: The maximum number of iterations allowed for the + # binary search to avoid infinite loops. # # Returns: - # - The block number of the opposite block in the rollup. + # - The block number of the opposite block in the rollup or raises an error if + # the iteration threshold is exceeded. + @spec do_binary_search_of_opposite_block( + non_neg_integer(), + integer(), + non_neg_integer(), + %{ + node_interface_address: EthereumJSONRPC.address(), + json_rpc_named_arguments: EthereumJSONRPC.json_rpc_named_arguments() + }, + non_neg_integer(), + non_neg_integer(), + %{non_neg_integer() => non_neg_integer()} + ) :: non_neg_integer() @spec do_binary_search_of_opposite_block( non_neg_integer(), integer(), @@ -527,6 +535,8 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Rpc do json_rpc_named_arguments: EthereumJSONRPC.json_rpc_named_arguments() }, non_neg_integer(), + non_neg_integer(), + %{non_neg_integer() => non_neg_integer()}, non_neg_integer() ) :: non_neg_integer() defp do_binary_search_of_opposite_block( @@ -535,78 +545,158 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Rpc do required_batch_number, %{node_interface_address: _, json_rpc_named_arguments: _} = rollup_config, prev_batch_number, - prev_inspected_block + prev_inspected_block, + cache, + iteration_threshold \\ @default_binary_search_threshold ) do - new_batch_number = + if iteration_threshold == 0 do + raise "Binary search iteration threshold exceeded" + end + + {new_batch_number, new_cache} = get_batch_number_for_rollup_block( rollup_config.node_interface_address, inspected_block, - rollup_config.json_rpc_named_arguments + rollup_config.json_rpc_named_arguments, + cache ) - next_block_to_inspect = max(1, inspected_block - step) + is_batch_repeated? = new_batch_number == prev_batch_number + + is_min_step_required_batch? = + abs(prev_inspected_block - inspected_block) == 1 and new_batch_number == required_batch_number + + new_step = + cond do + # The batch number is the same as the previous one, so there is no need to reduce step and + # the next iteration should continue in the same direction. + is_batch_repeated? -> + step + + # For the next two cases the batch number differs from one found in the previous iteration, + # so it is necessary to cut the step in half and change the direction of the search if the + # the next iteration assumed to move away from the required batch number. + step > 0 -> + adjust_step(step, new_batch_number <= required_batch_number) + + step < 0 -> + adjust_step(step, new_batch_number >= required_batch_number) + end + + if is_min_step_required_batch? and not is_batch_repeated? do + # The current step is the smallest possible, the inspected block in the required batch but + # the batch number is different from one found in the previous iteration. This means that + # the previous block was in the neighboring batch and the current block is in the boundary + # of the required batch. + + inspected_block + else + # Whether the required batch number is not reached yet, or there is uncertainty if the + # inspected block is in the boundary of the required batch: the current batch is the same + # as one found in the previous iteration or the step is not the smallest possible. + + next_block_to_inspect = max(1, inspected_block - new_step) - if new_batch_number == prev_batch_number do do_binary_search_of_opposite_block( next_block_to_inspect, - step, + new_step, required_batch_number, rollup_config, new_batch_number, - inspected_block + inspected_block, + new_cache, + iteration_threshold - 1 ) - else - if abs(prev_inspected_block - inspected_block) == 1 and new_batch_number == required_batch_number do - inspected_block - else - # credo:disable-for-next-line Credo.Check.Refactor.Nesting - new_step = if(abs(step) == 1, do: -step, else: -div(step, 2)) - - do_binary_search_of_opposite_block( - next_block_to_inspect, - new_step, - required_batch_number, - rollup_config, - new_batch_number, - inspected_block - ) - end + end + end + + # Adjusts the step size for the binary search based on the current step size and + # the need to change the direction of the search. + @spec adjust_step(integer(), boolean()) :: integer() + defp adjust_step(step, change_direction?) do + case {abs(step), change_direction?} do + {1, true} -> -step + {1, false} -> step + {_, true} -> -div(step, 2) + {_, false} -> div(step, 2) end end # Retrieves the batch number for a given rollup block by interacting with the - # node interface contract. It calls the `findBatchContainingBlock` method of - # the contract to find the batch containing the specified block number. + # Node Interface contract. + # + # This function calls the `findBatchContainingBlock` method of the Node Interface + # contract to find the batch containing the specified block number. In order to + # avoid redundant RPC calls, the function uses a cache to store the batch numbers. # # Parameters: # - `node_interface_address`: The address of the node interface contract. # - `block_number`: The rollup block number. # - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC # connection. + # - `cache`: A map that stores the batch numbers for rollup blocks to avoid + # redundant RPC calls. # # Returns: - # - The number of a batch containing the specified rollup block. + # `{batch_number, new_cache}`, where + # - `batch_number` - The number of a batch containing the specified rollup block. + # - `new_cache` - The updated cache with the new batch number. @spec get_batch_number_for_rollup_block( EthereumJSONRPC.address(), EthereumJSONRPC.block_number(), + EthereumJSONRPC.json_rpc_named_arguments(), + %{non_neg_integer() => non_neg_integer()} + ) :: {non_neg_integer(), %{non_neg_integer() => non_neg_integer()}} + defp get_batch_number_for_rollup_block(node_interface_address, block_number, json_rpc_named_arguments, cache) + + defp get_batch_number_for_rollup_block(_, block_number, _, cache) when is_map_key(cache, block_number) do + {Map.get(cache, block_number), cache} + end + + defp get_batch_number_for_rollup_block(node_interface_address, block_number, json_rpc_named_arguments, cache) do + batch_number = + read_contract_and_handle_result_as_integer( + node_interface_address, + @selector_find_batch_containing_block, + [block_number], + @node_interface_contract_abi, + json_rpc_named_arguments + ) + + {batch_number, Map.put(cache, block_number, batch_number)} + end + + # Calls one contract method and processes the result as an integer. + @spec read_contract_and_handle_result_as_integer( + EthereumJSONRPC.address(), + binary(), + [term()], + [map()], EthereumJSONRPC.json_rpc_named_arguments() ) :: non_neg_integer() - defp get_batch_number_for_rollup_block(node_interface_address, block_number, json_rpc_named_arguments) do + defp read_contract_and_handle_result_as_integer( + contract_address, + method_selector, + args, + abi, + json_rpc_named_arguments + ) do [ %{ - contract_address: node_interface_address, - method_id: @selector_find_batch_containing_block, - args: [block_number] + contract_address: contract_address, + method_id: method_selector, + args: args } ] - |> IndexerHelper.read_contracts_with_retries( - @node_interface_contract_abi, - json_rpc_named_arguments, - @rpc_resend_attempts - ) + |> IndexerHelper.read_contracts_with_retries(abi, json_rpc_named_arguments, @rpc_resend_attempts) + # Extracts the list of responses from the tuple returned by read_contracts_with_retries. |> Kernel.elem(0) + # Retrieves the first response from the list of responses. The responses are in a list + # because read_contracts_with_retries accepts a list of method calls. |> List.first() + # Extracts the result from the {status, result} tuple which is composed in EthereumJSONRPC.Encoder.decode_result. |> Kernel.elem(1) + # Extracts the first decoded value from the result, which is a list, even if it contains only one value. |> List.first() end From b140e9f5b980a058f799cbbe55ebf7b63ac104e9 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Tue, 23 Jul 2024 16:16:27 +0400 Subject: [PATCH 010/363] chore: Token transfers broadcast optimization (#10465) --- .../lib/block_scout_web/notifier.ex | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/notifier.ex b/apps/block_scout_web/lib/block_scout_web/notifier.ex index f364dfa64e00..a25f81212020 100644 --- a/apps/block_scout_web/lib/block_scout_web/notifier.ex +++ b/apps/block_scout_web/lib/block_scout_web/notifier.ex @@ -177,16 +177,13 @@ defmodule BlockScoutWeb.Notifier do def handle_event({:chain_event, :token_transfers, :realtime, all_token_transfers}) do all_token_transfers_full = all_token_transfers - |> Enum.map( - &(&1 - |> Repo.preload( - DenormalizationHelper.extend_transaction_preload([ - :token, - :transaction, - from_address: [:names, :smart_contract, :proxy_implementations], - to_address: [:names, :smart_contract, :proxy_implementations] - ]) - )) + |> Repo.preload( + DenormalizationHelper.extend_transaction_preload([ + :token, + :transaction, + from_address: [:names, :smart_contract, :proxy_implementations], + to_address: [:names, :smart_contract, :proxy_implementations] + ]) ) transfers_by_token = Enum.group_by(all_token_transfers_full, fn tt -> to_string(tt.token_contract_address_hash) end) From 83e848d5a4b2dc1062884bc412d9c3492b93aa09 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 23 Jul 2024 15:24:08 +0300 Subject: [PATCH 011/363] perf: Reduce LookUpSmartContractSourcesOnDemand fetcher footprint (#10457) * Reduce LookUpSmartContractSourcesOnDemand fetcher footprint * LookUpSmartContractSourcesOnDemand: reduce input payload to several params * Remove need_to_check_and_partially_verified and eligibility_for_sources_fetching from state --- apps/explorer/lib/explorer/chain.ex | 21 ++- ...ook_up_smart_contract_sources_on_demand.ex | 141 ++++++++++++------ .../lib/explorer/chain/smart_contract.ex | 4 +- .../smart_contract/solidity/publish_helper.ex | 4 +- 4 files changed, 117 insertions(+), 53 deletions(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 30ecb22f920b..f99aa779b120 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -1167,11 +1167,20 @@ defmodule Explorer.Chain do %{smart_contract: smart_contract} -> if smart_contract do CheckBytecodeMatchingOnDemand.trigger_check(address_result, smart_contract) - LookUpSmartContractSourcesOnDemand.trigger_fetch(address_result, smart_contract) + + LookUpSmartContractSourcesOnDemand.trigger_fetch( + to_string(address_result.hash), + address_result.contract_code, + smart_contract + ) SmartContract.check_and_update_constructor_args(address_result) else - LookUpSmartContractSourcesOnDemand.trigger_fetch(address_result, nil) + LookUpSmartContractSourcesOnDemand.trigger_fetch( + to_string(address_result.hash), + address_result.contract_code, + nil + ) {implementation_address_hashes, _} = Implementation.get_implementation( @@ -1190,7 +1199,13 @@ defmodule Explorer.Chain do end _ -> - LookUpSmartContractSourcesOnDemand.trigger_fetch(address_result, nil) + if address_result do + LookUpSmartContractSourcesOnDemand.trigger_fetch( + to_string(address_result.hash), + address_result.contract_code, + nil + ) + end address_result end diff --git a/apps/explorer/lib/explorer/chain/fetcher/look_up_smart_contract_sources_on_demand.ex b/apps/explorer/lib/explorer/chain/fetcher/look_up_smart_contract_sources_on_demand.ex index 47d4cb592098..1394f8aa0fc6 100644 --- a/apps/explorer/lib/explorer/chain/fetcher/look_up_smart_contract_sources_on_demand.ex +++ b/apps/explorer/lib/explorer/chain/fetcher/look_up_smart_contract_sources_on_demand.ex @@ -5,7 +5,7 @@ defmodule Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand do use GenServer - alias Explorer.Chain.{Address, Data, SmartContract} + alias Explorer.Chain.{Data, SmartContract} alias Explorer.Chain.Events.Publisher alias Explorer.SmartContract.EthBytecodeDBInterface alias Explorer.SmartContract.Solidity.Publisher, as: SolidityPublisher @@ -17,37 +17,41 @@ defmodule Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand do @cooldown_timeout 500 - def trigger_fetch(nil, _) do + def trigger_fetch(nil, _, _) do :ignore end - def trigger_fetch(address, %SmartContract{partially_verified: true}) do - GenServer.cast(__MODULE__, {:fetch, address}) + def trigger_fetch( + address_hash_string, + address_contract_code, + %SmartContract{partially_verified: true} + ) do + GenServer.cast(__MODULE__, {:check_eligibility, address_hash_string, address_contract_code, false}) end - def trigger_fetch(_address, %SmartContract{}) do + def trigger_fetch(_address_hash_string, _address_contract_code, %SmartContract{}) do :ignore end - def trigger_fetch(address, _) do - GenServer.cast(__MODULE__, {:fetch, address}) + def trigger_fetch(address_hash_string, address_contract_code, smart_contract) do + GenServer.cast(__MODULE__, {:check_eligibility, address_hash_string, address_contract_code, is_nil(smart_contract)}) end - defp fetch_sources(address, only_full?) do - Publisher.broadcast(%{eth_bytecode_db_lookup_started: [address.hash]}, :on_demand) + defp fetch_sources(address_hash_string, address_contract_code, only_full?) do + Publisher.broadcast(%{eth_bytecode_db_lookup_started: [address_hash_string]}, :on_demand) - creation_tx_input = contract_creation_input(address.hash) + creation_tx_input = contract_creation_input(address_hash_string) with {:ok, %{"sourceType" => type, "matchType" => match_type} = source} <- %{} - |> prepare_bytecode_for_microservice(creation_tx_input, Data.to_string(address.contract_code)) - |> EthBytecodeDBInterface.search_contract(address.hash), + |> prepare_bytecode_for_microservice(creation_tx_input, Data.to_string(address_contract_code)) + |> EthBytecodeDBInterface.search_contract(address_hash_string), :ok <- check_match_type(match_type, only_full?), - {:ok, _} <- process_contract_source(type, source, address.hash) do - Publisher.broadcast(%{smart_contract_was_verified: [address.hash]}, :on_demand) + {:ok, _} <- process_contract_source(type, source, address_hash_string) do + Publisher.broadcast(%{smart_contract_was_verified: [address_hash_string]}, :on_demand) else _ -> - Publisher.broadcast(%{smart_contract_was_not_verified: [address.hash]}, :on_demand) + Publisher.broadcast(%{smart_contract_was_not_verified: [address_hash_string]}, :on_demand) false end end @@ -73,26 +77,47 @@ defmodule Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand do end @impl true - def handle_cast({:fetch, address}, %{current_concurrency: counter, max_concurrency: max_concurrency} = state) + def handle_cast({:check_eligibility, address_hash_string, address_contract_code, nil_smart_contract?}, state) do + check_eligibility_for_sources_fetching(address_hash_string, address_contract_code, nil_smart_contract?, state) + end + + @impl true + def handle_cast( + {:fetch, address_hash_string, address_contract_code, need_to_check_and_partially_verified?}, + %{current_concurrency: counter, max_concurrency: max_concurrency} = state + ) when counter < max_concurrency do - handle_fetch_request(address, state) + handle_fetch_request(address_hash_string, address_contract_code, need_to_check_and_partially_verified?, state) end @impl true - def handle_cast({:fetch, _address} = request, %{current_concurrency: _counter} = state) do + def handle_cast( + {:fetch, _address_hash_string, _address_contract_code, _need_to_check_and_partially_verified?} = request, + %{current_concurrency: _counter} = state + ) do Process.send_after(self(), request, @cooldown_timeout) - {:noreply, state} end @impl true - def handle_info({:fetch, address}, %{current_concurrency: counter, max_concurrency: max_concurrency} = state) + def handle_info({:check_eligibility, address_hash_string, address_contract_code, nil_smart_contract?}, state) do + check_eligibility_for_sources_fetching(address_hash_string, address_contract_code, nil_smart_contract?, state) + end + + @impl true + def handle_info( + {:fetch, address_hash_string, address_contract_code, need_to_check_and_partially_verified?}, + %{current_concurrency: counter, max_concurrency: max_concurrency} = state + ) when counter < max_concurrency do - handle_fetch_request(address, state) + handle_fetch_request(address_hash_string, address_contract_code, need_to_check_and_partially_verified?, state) end @impl true - def handle_info({:fetch, _address} = request, state) do + def handle_info( + {:fetch, _address_hash_string, _address_contract_code, _need_to_check_and_partially_verified?} = request, + state + ) do Process.send_after(self(), request, @cooldown_timeout) {:noreply, state} end @@ -108,10 +133,10 @@ defmodule Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand do {:noreply, %{state | current_concurrency: counter - 1}} end - defp partially_verified?(%Address{smart_contract: nil}), do: nil + defp partially_verified?(_address_hash_string, true), do: nil - defp partially_verified?(%Address{hash: hash}) do - SmartContract.select_partially_verified_by_address_hash(hash) + defp partially_verified?(address_hash_string, _nil_smart_contract?) do + SmartContract.select_partially_verified_by_address_hash(address_hash_string) end defp check_interval(address_string) do @@ -129,16 +154,16 @@ defmodule Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand do end end - def process_contract_source("SOLIDITY", source, address_hash) do - SolidityPublisher.process_rust_verifier_response(source, address_hash, %{}, true, true, true) + def process_contract_source("SOLIDITY", source, address_hash_string) do + SolidityPublisher.process_rust_verifier_response(source, address_hash_string, %{}, true, true, true) end - def process_contract_source("VYPER", source, address_hash) do - VyperPublisher.process_rust_verifier_response(source, address_hash, %{}, true, true, true) + def process_contract_source("VYPER", source, address_hash_string) do + VyperPublisher.process_rust_verifier_response(source, address_hash_string, %{}, true, true, true) end - def process_contract_source("YUL", source, address_hash) do - SolidityPublisher.process_rust_verifier_response(source, address_hash, %{}, true, true, true) + def process_contract_source("YUL", source, address_hash_string) do + SolidityPublisher.process_rust_verifier_response(source, address_hash_string, %{}, true, true, true) end def process_contract_source(_, _source, _address_hash), do: false @@ -146,25 +171,49 @@ defmodule Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand do defp check_match_type("PARTIAL", true), do: :full_match_required defp check_match_type(_, _), do: :ok - defp handle_fetch_request(address, %{current_concurrency: counter} = state) do - need_to_check_and_partially_verified? = - check_interval(to_lowercase_string(address.hash)) && partially_verified?(address) - - diff = - if is_nil(need_to_check_and_partially_verified?) || need_to_check_and_partially_verified? do - Task.Supervisor.async_nolink(Explorer.GenesisDataTaskSupervisor, fn -> - fetch_sources(address, need_to_check_and_partially_verified?) - end) + defp handle_fetch_request( + address_hash_string, + address_contract_code, + need_to_check_and_partially_verified?, + %{ + current_concurrency: counter + } = state + ) do + Task.Supervisor.async_nolink(Explorer.GenesisDataTaskSupervisor, fn -> + fetch_sources(address_hash_string, address_contract_code, need_to_check_and_partially_verified?) + end) - :ets.insert(@cache_name, {to_lowercase_string(address.hash), DateTime.utc_now()}) + :ets.insert(@cache_name, {to_lowercase_string(address_hash_string), DateTime.utc_now()}) - 1 - else - 0 - end + diff = 1 {:noreply, %{state | current_concurrency: counter + diff}} end - defp to_lowercase_string(hash), do: hash |> to_string() |> String.downcase() + defp eligible_for_sources_fetching?(need_to_check_and_partially_verified?) do + is_nil(need_to_check_and_partially_verified?) || need_to_check_and_partially_verified? + end + + @spec stale_and_partially_verified?(String.t(), boolean()) :: boolean() | nil + defp stale_and_partially_verified?(address_hash_string, nil_smart_contract?) do + check_interval(to_lowercase_string(address_hash_string)) && + partially_verified?(address_hash_string, nil_smart_contract?) + end + + defp check_eligibility_for_sources_fetching(address_hash_string, address_contract_code, nil_smart_contract?, state) do + need_to_check_and_partially_verified? = stale_and_partially_verified?(address_hash_string, nil_smart_contract?) + + eligibility_for_sources_fetching = eligible_for_sources_fetching?(need_to_check_and_partially_verified?) + + if eligibility_for_sources_fetching do + GenServer.cast( + __MODULE__, + {:fetch, address_hash_string, address_contract_code, need_to_check_and_partially_verified?} + ) + end + + {:noreply, state} + end + + defp to_lowercase_string(address_hash_string), do: address_hash_string |> String.downcase() end diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index 9d44ed40bf13..71a2a0064fde 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -513,11 +513,11 @@ defmodule Explorer.Chain.SmartContract do Returns SmartContract by the given smart-contract address hash, if it is partially verified """ @spec select_partially_verified_by_address_hash(binary() | Hash.t(), keyword) :: boolean() | nil - def select_partially_verified_by_address_hash(address_hash, options \\ []) do + def select_partially_verified_by_address_hash(address_hash_string, options \\ []) do query = from( smart_contract in __MODULE__, - where: smart_contract.address_hash == ^address_hash, + where: smart_contract.address_hash == ^address_hash_string, select: smart_contract.partially_verified ) diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/publish_helper.ex b/apps/explorer/lib/explorer/smart_contract/solidity/publish_helper.ex index 4af55016fc73..c452a97f7e84 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/publish_helper.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/publish_helper.ex @@ -4,9 +4,9 @@ defmodule Explorer.SmartContract.Solidity.PublishHelper do """ alias Ecto.Changeset - alias Explorer.Chain.{Address, SmartContract} alias Explorer.Chain.Events.Publisher, as: EventsPublisher alias Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand + alias Explorer.Chain.SmartContract alias Explorer.SmartContract.Solidity.Publisher alias Explorer.ThirdPartyIntegrations.Sourcify @@ -149,7 +149,7 @@ defmodule Explorer.SmartContract.Solidity.PublishHelper do def check_and_verify(address_hash_string) do if Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour)[:eth_bytecode_db?] do - LookUpSmartContractSourcesOnDemand.trigger_fetch(%Address{hash: address_hash_string}, nil) + LookUpSmartContractSourcesOnDemand.trigger_fetch(address_hash_string, nil, nil) else if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do check_by_address_in_sourcify( From eb574f8961dd72e3e7e00f1a7cc52dfaa7ae1e6b Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Tue, 23 Jul 2024 16:40:16 +0400 Subject: [PATCH 012/363] chore: Move eth_bytecode_db_lookup_started event to smart contract related event handler (#10462) --- .../lib/block_scout_web/realtime_event_handler.ex | 1 - .../lib/block_scout_web/smart_contract_realtime_event_handler.ex | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex b/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex index c2aa239fb049..f77ae623f836 100644 --- a/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex +++ b/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex @@ -42,7 +42,6 @@ defmodule BlockScoutWeb.RealtimeEventHandler do Subscriber.to(:changed_bytecode, :on_demand) Subscriber.to(:fetched_bytecode, :on_demand) Subscriber.to(:fetched_token_instance_metadata, :on_demand) - Subscriber.to(:eth_bytecode_db_lookup_started, :on_demand) Subscriber.to(:zkevm_confirmed_batches, :realtime) # Does not come from the indexer Subscriber.to(:exchange_rate) diff --git a/apps/block_scout_web/lib/block_scout_web/smart_contract_realtime_event_handler.ex b/apps/block_scout_web/lib/block_scout_web/smart_contract_realtime_event_handler.ex index e94e8a3bb9bb..73c222d0699c 100644 --- a/apps/block_scout_web/lib/block_scout_web/smart_contract_realtime_event_handler.ex +++ b/apps/block_scout_web/lib/block_scout_web/smart_contract_realtime_event_handler.ex @@ -17,6 +17,7 @@ defmodule BlockScoutWeb.SmartContractRealtimeEventHandler do Subscriber.to(:contract_verification_result, :on_demand) Subscriber.to(:smart_contract_was_verified, :on_demand) Subscriber.to(:smart_contract_was_not_verified, :on_demand) + Subscriber.to(:eth_bytecode_db_lookup_started, :on_demand) {:ok, []} end From 30d01e48ffe405a338b57cccf8a216dfd3099a21 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 23 Jul 2024 18:54:23 +0300 Subject: [PATCH 013/363] doc: refine PR template --- PULL_REQUEST_TEMPLATE.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 6e15223f201f..ef743c30f6d6 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -7,12 +7,15 @@ ## Changelog ### Enhancements + *Things you added that don't break anything. Regression tests for Bug Fixes count as Enhancements.* ### Bug Fixes + *Things you changed that fix bugs. If a fixes a bug, but in so doing adds a new requirement, removes code, or requires a database reset and reindex, the breaking part of the change should be added to Incompatible Changes below also.* ### Incompatible Changes + *Things you broke while doing Enhancements and Bug Fixes. Breaking changes include (1) adding new requirements and (2) removing code. Renaming counts as (2) because a rename is a removal followed by an add.* ## Upgrading @@ -21,9 +24,9 @@ ## Checklist for your Pull Request (PR) - - [ ] If I added new functionality, I added tests covering it. - - [ ] If I fixed a bug, I added a regression test to prevent the bug from silently reappearing again. - - [ ] I checked whether I should update the docs and did so by submitting a PR to https://github.com/blockscout/docs - - [ ] If I added/changed/removed ENV var, I submitted a PR to https://github.com/blockscout/docs to update the list of env vars at https://github.com/blockscout/docs/blob/master/for-developers/information-and-settings/env-variables.md and I updated the version to `master` in the Version column. Changes will be reflected in this table: https://docs.blockscout.com/for-developers/information-and-settings/env-variables. - - [ ] If I added new DB indices, I checked, that they are not redundant with PGHero or other tools. - - [ ] If I added/removed chain type, I modified the Github CI matrix and PR labels accordingly. +- [ ] If I added new functionality, I added tests covering it. +- [ ] If I fixed a bug, I added a regression test to prevent the bug from silently reappearing again. +- [ ] I checked whether I should update the docs and did so by submitting a PR to [docs repository](https://github.com/blockscout/docs). +- [ ] If I added/changed/removed ENV var, I submitted a PR to [docs repository](https://github.com/blockscout/docs) to update the list of [env vars](https://github.com/blockscout/docs/blob/master/for-developers/information-and-settings/env-variables.md) and I updated the version to `master` in the Version column. If I removed variable, I added it to [Deprecated ENV Variables](https://github.com/blockscout/docs/blob/master/for-developers/information-and-settings/env-variables/deprecated-env-variables/README.md) page. After merging docs PR, changes will be reflected in these [pages](https://docs.blockscout.com/for-developers/information-and-settings/env-variables). +- [ ] If I added new DB indices, I checked, that they are not redundant, with PGHero or other tools. +- [ ] If I added/removed chain type, I modified the Github CI matrix and PR labels accordingly. From f1752d972d57d54118649ac7cdf9dee1079872b3 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Wed, 24 Jul 2024 12:48:43 +0400 Subject: [PATCH 014/363] fix: Add no overlapping constraint to missing_block_ranges (#10449) --- ...3_add_no_overlap_index_to_missing_block_ranges.exs | 11 +++++++++++ .../block/catchup/missing_ranges_collector_test.exs | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 apps/explorer/priv/repo/migrations/20240718150123_add_no_overlap_index_to_missing_block_ranges.exs diff --git a/apps/explorer/priv/repo/migrations/20240718150123_add_no_overlap_index_to_missing_block_ranges.exs b/apps/explorer/priv/repo/migrations/20240718150123_add_no_overlap_index_to_missing_block_ranges.exs new file mode 100644 index 000000000000..7f569610b1cb --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20240718150123_add_no_overlap_index_to_missing_block_ranges.exs @@ -0,0 +1,11 @@ +defmodule Explorer.Repo.Migrations.AddNoOverlapIndexToMissingBlockRanges do + use Ecto.Migration + + def change do + create( + constraint(:missing_block_ranges, :missing_block_ranges_no_overlap, + exclude: ~s|gist (int4range(to_number, from_number, '[]') WITH &&)| + ) + ) + end +end diff --git a/apps/indexer/test/indexer/block/catchup/missing_ranges_collector_test.exs b/apps/indexer/test/indexer/block/catchup/missing_ranges_collector_test.exs index 77449d9004b3..a53a7d3063bc 100644 --- a/apps/indexer/test/indexer/block/catchup/missing_ranges_collector_test.exs +++ b/apps/indexer/test/indexer/block/catchup/missing_ranges_collector_test.exs @@ -38,9 +38,9 @@ defmodule Indexer.Block.Catchup.MissingRangesCollectorTest do Application.put_env(:indexer, :last_block, 200) insert(:missing_block_range, from_number: 250, to_number: 220) - insert(:missing_block_range, from_number: 220, to_number: 190) + insert(:missing_block_range, from_number: 219, to_number: 190) insert(:missing_block_range, from_number: 120, to_number: 90) - insert(:missing_block_range, from_number: 90, to_number: 80) + insert(:missing_block_range, from_number: 89, to_number: 80) MissingRangesCollector.start_link([]) Process.sleep(500) From 536960363ac16e5f6a8f4c2f01c435913f39d3af Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Wed, 24 Jul 2024 02:49:30 -0600 Subject: [PATCH 015/363] feat: missing Arbitrum batches re-discovery (#10446) * functionality to re-discover missed batches * Proper request for safe block --- .../lib/explorer/chain/arbitrum/reader.ex | 88 ++++++ .../arbitrum/tracking_batches_statuses.ex | 82 ++++- .../lib/indexer/fetcher/arbitrum/utils/db.ex | 128 ++++++++ .../fetcher/arbitrum/workers/new_batches.ex | 282 +++++++++++++++++- config/runtime.exs | 3 +- docker-compose/envs/common-blockscout.env | 1 + 6 files changed, 551 insertions(+), 33 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/arbitrum/reader.ex b/apps/explorer/lib/explorer/chain/arbitrum/reader.ex index 1fd6623f26ee..62d5ce681062 100644 --- a/apps/explorer/lib/explorer/chain/arbitrum/reader.ex +++ b/apps/explorer/lib/explorer/chain/arbitrum/reader.ex @@ -1061,4 +1061,92 @@ defmodule Explorer.Chain.Arbitrum.Reader do keyset -> {:ok, {keyset.batch_number, keyset.data}} end end + + @doc """ + Retrieves the batch numbers of missing L1 batches within a specified range. + + This function constructs a query to find the batch numbers of L1 batches that + are missing within the given range of batch numbers. It uses a right join with + a generated series to identify batch numbers that do not exist in the + `arbitrum_l1_batches` table. + + ## Parameters + - `start_batch_number`: The starting batch number of the search range. + - `end_batch_number`: The ending batch number of the search range. + + ## Returns + - A list of batch numbers in ascending order that are missing within the specified range. + """ + @spec find_missing_batches(non_neg_integer(), non_neg_integer()) :: [non_neg_integer()] + def find_missing_batches(start_batch_number, end_batch_number) + when is_integer(start_batch_number) and is_integer(end_batch_number) and end_batch_number >= start_batch_number do + query = + from(batch in L1Batch, + right_join: + missing_range in fragment( + """ + ( + SELECT distinct b1.number + FROM generate_series((?)::integer, (?)::integer) AS b1(number) + WHERE NOT EXISTS + (SELECT 1 FROM arbitrum_l1_batches b2 WHERE b2.number=b1.number) + ORDER BY b1.number DESC + ) + """, + ^start_batch_number, + ^end_batch_number + ), + on: batch.number == missing_range.number, + select: missing_range.number, + order_by: missing_range.number, + distinct: missing_range.number + ) + + query + |> Repo.all(timeout: :infinity) + end + + @doc """ + Retrieves L1 block numbers for the given list of batch numbers. + + This function finds the numbers of L1 blocks that include L1 transactions + associated with batches within the specified list of batch numbers. + + ## Parameters + - `batch_numbers`: A list of batch numbers for which to retrieve the L1 block numbers. + + ## Returns + - A map where the keys are batch numbers and the values are corresponding L1 block numbers. + """ + @spec get_l1_blocks_of_batches_by_numbers([non_neg_integer()]) :: %{non_neg_integer() => FullBlock.block_number()} + def get_l1_blocks_of_batches_by_numbers(batch_numbers) when is_list(batch_numbers) do + query = + from(batch in L1Batch, + join: l1tx in assoc(batch, :commitment_transaction), + where: batch.number in ^batch_numbers, + select: {batch.number, l1tx.block_number} + ) + + query + |> Repo.all(timeout: :infinity) + |> Enum.reduce(%{}, fn {batch_number, l1_block_number}, acc -> + Map.put(acc, batch_number, l1_block_number) + end) + end + + @doc """ + Retrieves the minimum and maximum batch numbers of L1 batches. + + ## Returns + - A tuple containing the minimum and maximum batch numbers or `{nil, nil}` if no batches are found. + """ + @spec get_min_max_batch_numbers() :: {non_neg_integer(), non_neg_integer()} | {nil | nil} + def get_min_max_batch_numbers do + query = + from(batch in L1Batch, + select: {min(batch.number), max(batch.number)} + ) + + Repo.one(query) + end end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/tracking_batches_statuses.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/tracking_batches_statuses.ex index 05e101696e53..2b90b6d523f3 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/tracking_batches_statuses.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/tracking_batches_statuses.ex @@ -1,11 +1,12 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do @moduledoc """ - Manages the tracking and updating of the statuses of rollup batches, confirmations, and cross-chain message executions for an Arbitrum rollup. + Manages the tracking and updating of the statuses of rollup batches, + confirmations, and cross-chain message executions for an Arbitrum rollup. This module orchestrates the workflow for discovering new and historical - batches of rollup transactions, confirmations of rollup blocks, and - executions of L2-to-L1 messages. It ensures the accurate tracking and - updating of the rollup process stages. + batches of rollup transactions, confirmations of rollup blocks, and executions + of L2-to-L1 messages. It ensures the accurate tracking and updating of the + rollup process stages. The fetcher's operation cycle begins with the `:init_worker` message, which establishes the initial state with the necessary configuration. @@ -14,12 +15,13 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do specific messages: - `:check_new_batches`: Discovers new batches of rollup transactions and updates their statuses. - - `:check_new_confirmations`: Identifies new confirmations of rollup blocks - to update their statuses. - - `:check_new_executions`: Finds new executions of L2-to-L1 messages to + - `:check_new_confirmations`: Identifies new confirmations of rollup blocks to update their statuses. + - `:check_new_executions`: Finds new executions of L2-to-L1 messages to update + their statuses. - `:check_historical_batches`: Processes historical batches of rollup transactions. + - `:check_missing_batches`: Inspects for missing batches of rollup transactions. - `:check_historical_confirmations`: Handles historical confirmations of rollup blocks. - `:check_historical_executions`: Manages historical executions of L2-to-L1 @@ -28,12 +30,12 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do transactions, confirming the blocks and messages involved. Discovery of rollup transaction batches is executed by requesting logs on L1 - that correspond to the `SequencerBatchDelivered` event emitted by the - Arbitrum `SequencerInbox` contract. + that correspond to the `SequencerBatchDelivered` event emitted by the Arbitrum + `SequencerInbox` contract. Discovery of rollup block confirmations is executed by requesting logs on L1 - that correspond to the `SendRootUpdated` event emitted by the Arbitrum - `Outbox` contract. + that correspond to the `SendRootUpdated` event emitted by the Arbitrum `Outbox` + contract. Discovery of the L2-to-L1 message executions occurs by requesting logs on L1 that correspond to the `OutBoxTransactionExecuted` event emitted by the @@ -90,6 +92,7 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do finalized_confirmations = config_tracker[:finalized_confirmations] confirmation_batches_depth = config_tracker[:confirmation_batches_depth] new_batches_limit = config_tracker[:new_batches_limit] + missing_batches_range = config_tracker[:missing_batches_range] node_interface_address = config_tracker[:node_interface_contract] Process.send(self(), :init_worker, []) @@ -113,6 +116,7 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do l1_start_block: l1_start_block, l1_rollup_init_block: l1_rollup_init_block, new_batches_limit: new_batches_limit, + missing_batches_range: missing_batches_range, messages_to_blocks_shift: messages_to_blocks_shift, confirmation_batches_depth: confirmation_batches_depth, node_interface_address: node_interface_address @@ -179,6 +183,8 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do new_executions_start_block = Db.l1_block_to_discover_latest_execution(l1_start_block) historical_executions_end_block = Db.l1_block_to_discover_earliest_execution(l1_start_block - 1) + {lowest_batch, missing_batches_end_batch} = Db.get_min_max_batch_numbers() + Process.send(self(), :check_new_batches, []) new_state = @@ -188,7 +194,8 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do Map.merge(state.config, %{ l1_start_block: l1_start_block, l1_outbox_address: outbox_address, - l1_sequencer_inbox_address: sequencer_inbox_address + l1_sequencer_inbox_address: sequencer_inbox_address, + lowest_batch: lowest_batch }) ) |> Map.put( @@ -200,7 +207,8 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do historical_confirmations_end_block: nil, historical_confirmations_start_block: nil, new_executions_start_block: new_executions_start_block, - historical_executions_end_block: historical_executions_end_block + historical_executions_end_block: historical_executions_end_block, + missing_batches_end_batch: missing_batches_end_batch }) ) @@ -320,8 +328,8 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do # status of the L2-to-L1 messages included in the corresponding rollup blocks is # also updated. # - # After processing, it immediately transitions to checking historical - # confirmations of rollup blocks by sending the `:check_historical_confirmations` + # After processing, it immediately transitions to inspecting for missing batches + # of rollup blocks by sending the `:check_missing_batches` # message. # # ## Parameters @@ -336,7 +344,7 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do def handle_info(:check_historical_batches, state) do {handle_duration, {:ok, start_block}} = :timer.tc(&NewBatches.discover_historical_batches/1, [state]) - Process.send(self(), :check_historical_confirmations, []) + Process.send(self(), :check_missing_batches, []) new_data = Map.merge(state.data, %{ @@ -347,6 +355,48 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do {:noreply, %{state | data: new_data}} end + # Initiates the process of inspecting for missing batches of rollup transactions. + # + # This function inspects the database for missing batches within the calculated + # batch range. If a missing batch is identified, the L1 block range to look up + # for the transaction that committed the batch is built based on the neighboring + # batches. Then logs within the block range are fetched to get the batch data. + # After discovery, the linkage between batches and the corresponding rollup + # blocks and transactions is built. The status of the L2-to-L1 messages included + # in the corresponding rollup blocks is also updated. + # + # After processing, it immediately transitions to checking historical + # confirmations of rollup blocks by sending the `:check_historical_confirmations` + # message. + # + # ## Parameters + # - `:check_missing_batches`: The message that triggers the function. + # - `state`: The current state of the fetcher, containing configuration and data + # needed for inspection of the missed batches. + # + # ## Returns + # - `{:noreply, new_state}`: Where `new_state` is updated with the new end batch + # for the next iteration of missing batches inspection. + @impl GenServer + def handle_info(:check_missing_batches, state) do + # At the moment of the very first fetcher running, no batches were found yet + new_data = + if is_nil(state.config.lowest_batch) do + state.data + else + {handle_duration, {:ok, start_batch}} = :timer.tc(&NewBatches.inspect_for_missing_batches/1, [state]) + + Map.merge(state.data, %{ + duration: increase_duration(state.data, handle_duration), + missing_batches_end_batch: start_batch - 1 + }) + end + + Process.send(self(), :check_historical_confirmations, []) + + {:noreply, %{state | data: new_data}} + end + # Initiates the process of discovering and handling historical confirmations of rollup blocks. # # This function fetches logs within the calculated range to identify the diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex index 703b0693fe43..1c304e623b9c 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex @@ -688,6 +688,134 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do |> Enum.map(&logs_to_map/1) end + @doc """ + Retrieves L1 block ranges that could be used to re-discover missing batches + within a specified range of batch numbers. + + This function identifies the L1 block ranges corresponding to missing L1 batches + within the given range of batch numbers. It first finds the missing batches, + then determines their neighboring ranges, and finally maps these ranges to the + corresponding L1 block numbers. + + ## Parameters + - `start_batch_number`: The starting batch number of the search range. + - `end_batch_number`: The ending batch number of the search range. + - `block_for_batch_0`: The L1 block number corresponding to the batch number 0. + + ## Returns + - A list of tuples, each containing a start and end L1 block number for the + ranges corresponding to the missing batches. + + ## Examples + + Example #1 + - Within the range from 1 to 10, the missing batch is 2. The L1 block for the + batch #1 is 10, and the L1 block for the batch #3 is 31. + - The output will be `[{11, 30}]`. + + Example #2 + - Within the range from 1 to 10, the missing batches are 2 and 6, and + - The L1 block for the batch #1 is 10. + - The L1 block for the batch #3 is 31. + - The L1 block for the batch #5 is 64. + - The L1 block for the batch #7 is 90. + - The output will be `[{11, 30}, {65, 89}]`. + + Example #3 + - Within the range from 1 to 10, the missing batches are 2 and 4, and + - The L1 block for the batch #1 is 10. + - The L1 block for the batch #3 is 31. + - The L1 block for the batch #5 is 64. + - The output will be `[{11, 30}, {32, 63}]`. + """ + @spec get_l1_block_ranges_for_missing_batches(non_neg_integer(), non_neg_integer(), FullBlock.block_number()) :: [ + {FullBlock.block_number(), FullBlock.block_number()} + ] + def get_l1_block_ranges_for_missing_batches(start_batch_number, end_batch_number, block_for_batch_0) + when is_integer(start_batch_number) and is_integer(end_batch_number) and end_batch_number >= start_batch_number do + # credo:disable-for-lines:4 Credo.Check.Refactor.PipeChainStart + neighbors_of_missing_batches = + Reader.find_missing_batches(start_batch_number, end_batch_number) + |> list_to_chunks() + |> chunks_to_neighbor_ranges() + + if neighbors_of_missing_batches == [] do + [] + else + l1_blocks = + neighbors_of_missing_batches + |> Enum.reduce(MapSet.new(), fn {start_batch, end_batch}, acc -> + acc + |> MapSet.put(start_batch) + |> MapSet.put(end_batch) + end) + # To avoid error in getting L1 block for the batch 0 + |> MapSet.delete(0) + |> MapSet.to_list() + |> Reader.get_l1_blocks_of_batches_by_numbers() + # It is safe to add the block for the batch 0 even if the batch 1 is missing + |> Map.put(0, block_for_batch_0) + + neighbors_of_missing_batches + |> Enum.map(fn {start_batch, end_batch} -> + {l1_blocks[start_batch] + 1, l1_blocks[end_batch] - 1} + end) + end + end + + # Splits a list into chunks of consecutive numbers, e.g., [1, 2, 3, 5, 6, 8] becomes [[1, 2, 3], [5, 6], [8]]. + @spec list_to_chunks([non_neg_integer()]) :: [[non_neg_integer()]] + defp list_to_chunks(list) do + chunk_fun = fn current, acc -> + case acc do + [] -> + {:cont, [current]} + + [last | _] = acc when current == last + 1 -> + {:cont, [current | acc]} + + acc -> + {:cont, Enum.reverse(acc), [current]} + end + end + + after_fun = fn acc -> + case acc do + # Special case to handle the situation when the initial list is empty + [] -> {:cont, []} + _ -> {:cont, Enum.reverse(acc), []} + end + end + + list + |> Enum.chunk_while([], chunk_fun, after_fun) + end + + # Converts chunks of elements into neighboring ranges, e.g., [[1, 2], [4]] becomes [{0, 3}, {3, 5}]. + @spec chunks_to_neighbor_ranges([[non_neg_integer()]]) :: [{non_neg_integer(), non_neg_integer()}] + defp chunks_to_neighbor_ranges([]), do: [] + + defp chunks_to_neighbor_ranges(list_of_chunks) do + list_of_chunks + |> Enum.map(fn current -> + case current do + [one_element] -> {one_element - 1, one_element + 1} + chunk -> {List.first(chunk) - 1, List.last(chunk) + 1} + end + end) + end + + @doc """ + Retrieves the minimum and maximum batch numbers of L1 batches. + + ## Returns + - A tuple containing the minimum and maximum batch numbers or `{nil, nil}` if no batches are found. + """ + @spec get_min_max_batch_numbers() :: {non_neg_integer(), non_neg_integer()} | {nil | nil} + def get_min_max_batch_numbers do + Reader.get_min_max_batch_numbers() + end + @doc """ Returns 32-byte signature of the event `L2ToL1Tx` """ diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex index 8a222a8d6a99..cf768c041622 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex @@ -44,6 +44,8 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # keccak256("SequencerBatchDelivered(uint256,bytes32,bytes32,bytes32,uint256,(uint64,uint64,uint64,uint64),uint8)") @event_sequencer_batch_delivered "0x7394f4a19a13c7b92b5bb71033245305946ef78452f7b4986ac1390b5df4ebd7" + @max_depth_for_safe_block 1000 + @doc """ Discovers and imports new batches of rollup transactions within the current L1 block range. @@ -91,7 +93,11 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do :node_interface_address => binary(), optional(any()) => any() }, - :data => %{:new_batches_start_block => non_neg_integer(), optional(any()) => any()}, + :data => %{ + :new_batches_start_block => non_neg_integer(), + :historical_batches_end_block => non_neg_integer(), + optional(any()) => any() + }, optional(any()) => any() }) :: {:ok, non_neg_integer()} def discover_new_batches( @@ -104,7 +110,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do new_batches_limit: new_batches_limit, node_interface_address: node_interface_address }, - data: %{new_batches_start_block: start_block} + data: %{new_batches_start_block: start_block, historical_batches_end_block: historical_batches_end_block} } = _state ) do # Requesting the "latest" block instead of "safe" allows to catch new batches @@ -116,21 +122,62 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do Rpc.get_resend_attempts() ) + {safe_chain_block, _} = IndexerHelper.get_safe_block(l1_rpc_config.json_rpc_named_arguments) + + # max() cannot be used here since l1_rpc_config.logs_block_range must not + # be taken into account to identify if it is L3 or not + safe_block = + if safe_chain_block < latest_block + 1 - @max_depth_for_safe_block do + # The case of L3, the safe block is too far behind the latest block, + # therefore it is assumed that there is no so deep re-orgs there. + latest_block + 1 - min(@max_depth_for_safe_block, l1_rpc_config.logs_block_range) + else + safe_chain_block + end + + # It is necessary to re-visit some amount of the previous blocks to ensure that + # no batches are missed due to reorgs. The amount of blocks to re-visit depends + # either on the current safe block but must not exceed @max_depth_for_safe_block + # (or L1 RPC max block range for getting logs) since on L3 chains the safe block + # could be too far behind the latest block. + # At the same time it does not make sense to re-visit blocks that will be + # re-visited by the historical batches discovery process. + # If the new batches discovery process does not reach the chain head previously + # no need to re-visit the blocks. + safe_start_block = max(min(start_block, safe_block), historical_batches_end_block + 1) + end_block = min(start_block + l1_rpc_config.logs_block_range - 1, latest_block) - if start_block <= end_block do - log_info("Block range for new batches discovery: #{start_block}..#{end_block}") + if safe_start_block <= end_block do + log_info("Block range for new batches discovery: #{safe_start_block}..#{end_block}") - discover( - sequencer_inbox_address, - start_block, - end_block, - new_batches_limit, - messages_to_blocks_shift, - l1_rpc_config, - node_interface_address, - rollup_rpc_config - ) + # Since with taking the safe block into account, the range safe_start_block..end_block + # could be larger than L1 RPC max block range for getting logs, it is necessary to + # divide the range into the chunks + safe_start_block + |> Stream.unfold(fn + current when current > end_block -> + nil + + current -> + next = min(current + l1_rpc_config.logs_block_range - 1, end_block) + {current, next + 1} + end) + |> Stream.each(fn chunk_start -> + chunk_end = min(chunk_start + l1_rpc_config.logs_block_range - 1, end_block) + + discover( + sequencer_inbox_address, + chunk_start, + chunk_end, + new_batches_limit, + messages_to_blocks_shift, + l1_rpc_config, + node_interface_address, + rollup_rpc_config + ) + end) + |> Stream.run() {:ok, end_block} else @@ -187,7 +234,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do :node_interface_address => binary(), optional(any()) => any() }, - :data => %{:historical_batches_end_block => any(), optional(any()) => any()}, + :data => %{:historical_batches_end_block => non_neg_integer(), optional(any()) => any()}, optional(any()) => any() }) :: {:ok, non_neg_integer()} def discover_historical_batches( @@ -226,6 +273,107 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do end end + @doc """ + Inspects and imports missing batches within a specified range of batch numbers. + + This function first finds the missing batches, then determines their + neighboring ranges, maps these ranges to the corresponding L1 block numbers, + and for every such range it retrieves logs representing the + SequencerBatchDelivered events emitted by the SequencerInbox contract. + These logs are processed to identify the batches and their details. The + function then constructs comprehensive data structures for batches, + lifecycle transactions, rollup blocks, and rollup transactions. Additionally, + it identifies L2-to-L1 messages that have been committed within these batches + and updates their status. All discovered and processed data are then imported + into the database. + + ## Parameters + - A map containing: + - `config`: Configuration settings including the L1 rollup initialization block, + RPC configurations, SequencerInbox address, a shift for the message + to block number mapping, a limit for new batches discovery, and the + max size of the range for missing batches inspection. + - `data`: Contains the ending batch number for the missing batches inspection. + + ## Returns + - `{:ok, start_batch}`: On successful inspection of the given batch range, where + `start_batch` is the calculated starting batch for the inspected range, + indicating the need to consider another batch range in the next iteration of + missing batch inspection. + - `{:ok, lowest_batch}`: If the discovery process has been finished, indicating + that all batches up to the rollup origins have been checked and no further + action is needed. + """ + @spec inspect_for_missing_batches(%{ + :config => %{ + :l1_rollup_init_block => non_neg_integer(), + :l1_rpc => %{ + :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), + :logs_block_range => non_neg_integer(), + optional(any()) => any() + }, + :l1_sequencer_inbox_address => binary(), + :lowest_batch => non_neg_integer(), + :messages_to_blocks_shift => non_neg_integer(), + :missing_batches_range => non_neg_integer(), + :new_batches_limit => non_neg_integer(), + :node_interface_address => binary(), + :rollup_rpc => %{ + :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), + :chunk_size => non_neg_integer(), + optional(any()) => any() + }, + optional(any()) => any() + }, + :data => %{:missing_batches_end_batch => non_neg_integer(), optional(any()) => any()}, + optional(any()) => any() + }) :: {:ok, non_neg_integer()} + def inspect_for_missing_batches( + %{ + config: %{ + l1_rpc: l1_rpc_config, + rollup_rpc: rollup_rpc_config, + l1_sequencer_inbox_address: sequencer_inbox_address, + messages_to_blocks_shift: messages_to_blocks_shift, + l1_rollup_init_block: l1_rollup_init_block, + new_batches_limit: new_batches_limit, + missing_batches_range: missing_batches_range, + lowest_batch: lowest_batch, + node_interface_address: node_interface_address + }, + data: %{missing_batches_end_batch: end_batch} + } = _state + ) + when not is_nil(lowest_batch) and not is_nil(end_batch) do + # No need to inspect for missing batches below the lowest batch + # since it is assumed that they are picked up by historical batches + # discovery process + if end_batch > lowest_batch do + start_batch = max(lowest_batch, end_batch - missing_batches_range + 1) + + log_info("Batch range for missing batches inspection: #{start_batch}..#{end_batch}") + + l1_block_ranges_for_missing_batches = + Db.get_l1_block_ranges_for_missing_batches(start_batch, end_batch, l1_rollup_init_block - 1) + + unless l1_block_ranges_for_missing_batches == [] do + discover_missing_batches( + sequencer_inbox_address, + l1_block_ranges_for_missing_batches, + new_batches_limit, + messages_to_blocks_shift, + l1_rpc_config, + node_interface_address, + rollup_rpc_config + ) + end + + {:ok, start_batch} + else + {:ok, lowest_batch} + end + end + # Initiates the discovery process for batches within a specified block range. # # Invokes the actual discovery process for new batches by calling `do_discover` @@ -243,6 +391,24 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # # ## Returns # - N/A + @spec discover( + binary(), + non_neg_integer(), + non_neg_integer(), + non_neg_integer(), + non_neg_integer(), + %{ + :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), + :chunk_size => non_neg_integer(), + optional(any()) => any() + }, + binary(), + %{ + :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), + :chunk_size => non_neg_integer(), + optional(any()) => any() + } + ) :: any() defp discover( sequencer_inbox_address, start_block, @@ -282,6 +448,24 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # # ## Returns # - N/A + @spec discover_historical( + binary(), + non_neg_integer(), + non_neg_integer(), + non_neg_integer(), + non_neg_integer(), + %{ + :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), + :chunk_size => non_neg_integer(), + optional(any()) => any() + }, + binary(), + %{ + :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), + :chunk_size => non_neg_integer(), + optional(any()) => any() + } + ) :: any() defp discover_historical( sequencer_inbox_address, start_block, @@ -304,6 +488,72 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do ) end + # Initiates the discovery process for missing batches within specified block ranges. + # + # This function divides each L1 block range into chunks to call `discover_historical` + # for every chunk to discover missing batches. + # + # ## Parameters + # - `sequencer_inbox_address`: The SequencerInbox contract address. + # - `l1_block_ranges`: The L1 block ranges to look for missing batches. + # - `new_batches_limit`: Limit of new batches to process in one iteration. + # - `messages_to_blocks_shift`: Shift value for message to block number mapping. + # - `l1_rpc_config`: Configuration for L1 RPC calls. + # - `node_interface_address`: The address of the NodeInterface contract on the rollup. + # - `rollup_rpc_config`: Configuration for rollup RPC calls. + # + # ## Returns + # - N/A + @spec discover_missing_batches( + binary(), + [{non_neg_integer(), non_neg_integer()}], + non_neg_integer(), + non_neg_integer(), + %{ + :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), + :logs_block_range => non_neg_integer(), + :chunk_size => non_neg_integer(), + optional(any()) => any() + }, + binary(), + %{ + :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), + :chunk_size => non_neg_integer(), + optional(any()) => any() + } + ) :: :ok + defp discover_missing_batches( + sequencer_inbox_address, + l1_block_ranges, + new_batches_limit, + messages_to_blocks_shift, + l1_rpc_config, + node_interface_address, + rollup_rpc_config + ) do + Enum.each(l1_block_ranges, fn {start_block, end_block} -> + Enum.each(0..div(end_block - start_block, l1_rpc_config.logs_block_range), fn i -> + start_block = start_block + i * l1_rpc_config.logs_block_range + end_block = min(start_block + l1_rpc_config.logs_block_range - 1, end_block) + + log_info("Block range for missing batches discovery: #{start_block}..#{end_block}") + + # `do_discover` is not used here to demonstrate the need to fetch batches + # which are already historical + discover_historical( + sequencer_inbox_address, + start_block, + end_block, + new_batches_limit, + messages_to_blocks_shift, + l1_rpc_config, + node_interface_address, + rollup_rpc_config + ) + end) + end) + end + # Performs the discovery of new or historical batches within a specified block range, # processing and importing the relevant data into the database. # @@ -346,7 +596,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do :chunk_size => non_neg_integer(), optional(any()) => any() } - ) :: :ok + ) :: any() defp do_discover( sequencer_inbox_address, start_block, diff --git a/config/runtime.exs b/config/runtime.exs index 3d972e2009a9..3c91ccbc4a11 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -899,7 +899,8 @@ config :indexer, Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses, finalized_confirmations: ConfigHelper.parse_bool_env_var("INDEXER_ARBITRUM_CONFIRMATIONS_TRACKING_FINALIZED", "true"), new_batches_limit: ConfigHelper.parse_integer_env_var("INDEXER_ARBITRUM_NEW_BATCHES_LIMIT", 10), node_interface_contract: - ConfigHelper.safe_get_env("INDEXER_ARBITRUM_NODE_INTERFACE_CONTRACT", "0x00000000000000000000000000000000000000C8") + ConfigHelper.safe_get_env("INDEXER_ARBITRUM_NODE_INTERFACE_CONTRACT", "0x00000000000000000000000000000000000000C8"), + missing_batches_range: ConfigHelper.parse_integer_env_var("INDEXER_ARBITRUM_MISSING_BATCHES_RANGE", 10000) config :indexer, Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses.Supervisor, enabled: ConfigHelper.parse_bool_env_var("INDEXER_ARBITRUM_BATCHES_TRACKING_ENABLED") diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index e37f225202be..f2dcbd3c4605 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -239,6 +239,7 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_ARBITRUM_BATCHES_TRACKING_ENABLED= # INDEXER_ARBITRUM_BATCHES_TRACKING_RECHECK_INTERVAL= # INDEXER_ARBITRUM_NEW_BATCHES_LIMIT= +# INDEXER_ARBITRUM_MISSING_BATCHES_RANGE= # INDEXER_ARBITRUM_BATCHES_TRACKING_MESSAGES_TO_BLOCKS_SHIFT= # INDEXER_ARBITRUM_CONFIRMATIONS_TRACKING_FINALIZED= # INDEXER_ARBITRUM_BATCHES_TRACKING_L1_FINALIZATION_CHECK_ENABLED= From 98f299beea632e4e2ae31688a9bf341758633327 Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Wed, 24 Jul 2024 02:49:56 -0600 Subject: [PATCH 016/363] feat: revisited approach to catchup missed Arbitrum messages (#10374) * DB query to identify missed messages * DB query to identify missed L2-L1 messages * reworked approach to stop missed messages discovery * initial version of improved backfiller * documentation updated * new env var and new way to identify if rollup synced * format, credo, spelling issues fixed * proper spec * missing comments added * missed env variable * Unified queries in the functions with similar functionality * extra space removed --- .../lib/explorer/chain/arbitrum/reader.ex | 192 ++++++++++++--- .../explorer/utility/missing_block_range.ex | 33 ++- .../20240628210148_add_index_for_messages.exs | 14 ++ .../arbitrum/rollup_messages_catchup.ex | 142 ++++++----- .../lib/indexer/fetcher/arbitrum/utils/db.ex | 223 ++++++++++++----- .../workers/historical_messages_on_l2.ex | 225 ++++++++++-------- config/runtime.exs | 6 +- cspell.json | 1 + docker-compose/envs/common-blockscout.env | 3 +- 9 files changed, 561 insertions(+), 278 deletions(-) create mode 100644 apps/explorer/priv/arbitrum/migrations/20240628210148_add_index_for_messages.exs diff --git a/apps/explorer/lib/explorer/chain/arbitrum/reader.ex b/apps/explorer/lib/explorer/chain/arbitrum/reader.ex index 62d5ce681062..e944d588cabb 100644 --- a/apps/explorer/lib/explorer/chain/arbitrum/reader.ex +++ b/apps/explorer/lib/explorer/chain/arbitrum/reader.ex @@ -3,7 +3,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do Contains read functions for Arbitrum modules. """ - import Ecto.Query, only: [from: 2, limit: 2, order_by: 2, subquery: 1, where: 2, where: 3] + import Ecto.Query, only: [dynamic: 2, from: 2, limit: 2, order_by: 2, select: 3, subquery: 1, where: 2, where: 3] import Explorer.Chain, only: [select_repo: 1] alias Explorer.Chain.Arbitrum.{ @@ -19,7 +19,9 @@ defmodule Explorer.Chain.Arbitrum.Reader do alias Explorer.{Chain, PagingOptions, Repo} alias Explorer.Chain.Block, as: FullBlock - alias Explorer.Chain.{Hash, Transaction} + alias Explorer.Chain.{Hash, Log, Transaction} + + @to_l2_messages_transaction_types [100, 105] @doc """ Retrieves the number of the latest L1 block where an L1-to-L2 message was discovered. @@ -62,50 +64,49 @@ defmodule Explorer.Chain.Arbitrum.Reader do end @doc """ - Retrieves the number of the earliest rollup block where an L2-to-L1 message was discovered. + Retrieves the rollup block number of the first missed L2-to-L1 message. + + The function identifies missing messages by checking logs for the specified + L2-to-L1 event and verifying if there are corresponding entries in the messages + table. A message is considered missed if there is a log entry without a + matching message record. + + ## Parameters + - `arbsys_contract`: The address of the Arbitrum system contract. + - `l2_to_l1_event`: The event identifier for L2-to-L1 messages. ## Returns - - The number of rollup block, or `nil` if no L2-to-L1 messages are found. + - The block number of the first missed L2-to-L1 message, or `nil` if no missed + messages are found. """ - @spec rollup_block_of_earliest_discovered_message_from_l2() :: FullBlock.block_number() | nil - def rollup_block_of_earliest_discovered_message_from_l2 do - query = - from(msg in Message, - select: msg.originating_transaction_block_number, - where: msg.direction == :from_l2 and not is_nil(msg.originating_transaction_block_number), - order_by: [asc: msg.originating_transaction_block_number], - limit: 1 - ) - - query + @spec rollup_block_of_first_missed_message_from_l2(binary(), binary()) :: FullBlock.block_number() | nil + def rollup_block_of_first_missed_message_from_l2(arbsys_contract, l2_to_l1_event) do + # credo:disable-for-lines:5 Credo.Check.Refactor.PipeChainStart + missed_messages_from_l2_query(arbsys_contract, l2_to_l1_event) + |> order_by(desc: :block_number) + |> limit(1) + |> select([log], log.block_number) |> Repo.one() end @doc """ - Retrieves the number of the earliest rollup block where a completed L1-to-L2 message was discovered. + Retrieves the rollup block number of the first missed L1-to-L2 message. + + The function identifies missing messages by checking transactions of specific + types that are supposed to contain L1-to-L2 messages and verifying if there are + corresponding entries in the messages table. A message is considered missed if + there is a transaction without a matching message record. ## Returns - - The block number of the rollup block, or `nil` if no completed L1-to-L2 messages are found, - or if the rollup transaction that emitted the corresponding message has not been indexed yet. + - The block number of the first missed L1-to-L2 message, or `nil` if no missed + messages are found. """ - @spec rollup_block_of_earliest_discovered_message_to_l2() :: FullBlock.block_number() | nil - def rollup_block_of_earliest_discovered_message_to_l2 do - completion_tx_subquery = - from(msg in Message, - select: msg.completion_transaction_hash, - where: msg.direction == :to_l2 and not is_nil(msg.completion_transaction_hash), - order_by: [asc: msg.message_id], - limit: 1 - ) - - query = - from(tx in Transaction, - select: tx.block_number, - where: tx.hash == subquery(completion_tx_subquery), - limit: 1 - ) - - query + @spec rollup_block_of_first_missed_message_to_l2() :: FullBlock.block_number() | nil + def rollup_block_of_first_missed_message_to_l2 do + missed_messages_to_l2_query() + |> order_by(desc: :block_number) + |> limit(1) + |> select([rollup_tx], rollup_tx.block_number) |> Repo.one() end @@ -799,6 +800,125 @@ defmodule Explorer.Chain.Arbitrum.Reader do select_repo(options).all(query) end + @doc """ + Retrieves the transaction hashes for missed L1-to-L2 messages within a specified + block range. + + The function identifies missed messages by checking transactions of specific + types that are supposed to contain L1-to-L2 messages and verifying if there are + corresponding entries in the messages table. A message is considered missed if + there is a transaction without a matching message record within the specified + block range. + + ## Parameters + - `start_block`: The starting block number of the range. + - `end_block`: The ending block number of the range. + + ## Returns + - A list of transaction hashes for missed L1-to-L2 messages. + """ + @spec transactions_for_missed_messages_to_l2(non_neg_integer(), non_neg_integer()) :: [Hash.t()] + def transactions_for_missed_messages_to_l2(start_block, end_block) do + missed_messages_to_l2_query() + |> where([rollup_tx], rollup_tx.block_number >= ^start_block and rollup_tx.block_number <= ^end_block) + |> order_by(desc: :block_timestamp) + |> select([rollup_tx], rollup_tx.hash) + |> Repo.all() + end + + # Constructs a query to retrieve missed L1-to-L2 messages. + # + # The function constructs a query to identify missing messages by checking + # transactions of specific types that are supposed to contain L1-to-L2 + # messages and verifying if there are corresponding entries in the messages + # table. A message is considered missed if there is a transaction without a + # matching message record. + # + # ## Returns + # - A query to retrieve missed L1-to-L2 messages. + @spec missed_messages_to_l2_query() :: Ecto.Query.t() + defp missed_messages_to_l2_query do + from(rollup_tx in Transaction, + left_join: msg in Message, + on: rollup_tx.hash == msg.completion_transaction_hash and msg.direction == :to_l2, + where: rollup_tx.type in @to_l2_messages_transaction_types and is_nil(msg.completion_transaction_hash) + ) + end + + @doc """ + Retrieves the logs for missed L2-to-L1 messages within a specified block range. + + The function identifies missed messages by checking logs for the specified + L2-to-L1 event and verifying if there are corresponding entries in the messages + table. A message is considered missed if there is a log entry without a + matching message record within the specified block range. + + ## Parameters + - `start_block`: The starting block number of the range. + - `end_block`: The ending block number of the range. + - `arbsys_contract`: The address of the Arbitrum system contract. + - `l2_to_l1_event`: The event identifier for L2-to-L1 messages. + + ## Returns + - A list of logs for missed L2-to-L1 messages. + """ + @spec logs_for_missed_messages_from_l2(non_neg_integer(), non_neg_integer(), binary(), binary()) :: [Log.t()] + def logs_for_missed_messages_from_l2(start_block, end_block, arbsys_contract, l2_to_l1_event) do + # credo:disable-for-lines:5 Credo.Check.Refactor.PipeChainStart + missed_messages_from_l2_query(arbsys_contract, l2_to_l1_event, start_block, end_block) + |> where([log, msg], log.block_number >= ^start_block and log.block_number <= ^end_block) + |> order_by(desc: :block_number, desc: :index) + |> select([log], log) + |> Repo.all() + end + + # Constructs a query to retrieve missed L2-to-L1 messages. + # + # The function constructs a query to identify missing messages by checking logs + # for the specified L2-to-L1 and verifying if there are corresponding entries + # in the messages table within a given block range, or among all messages if no + # block range is provided. A message is considered missed if there is a log + # entry without a matching message record. + # + # ## Parameters + # - `arbsys_contract`: The address hash of the Arbitrum system contract. + # - `l2_to_l1_event`: The event identifier for L2 to L1 messages. + # - `start_block`: The starting block number for the search range (optional). + # - `end_block`: The ending block number for the search range (optional). + # + # ## Returns + # - A query to retrieve missed L2-to-L1 messages. + @spec missed_messages_from_l2_query(binary(), binary(), non_neg_integer() | nil, non_neg_integer() | nil) :: + Ecto.Query.t() + defp missed_messages_from_l2_query(arbsys_contract, l2_to_l1_event, start_block \\ nil, end_block \\ nil) do + # It is assumed that all the messages from the same transaction are handled + # atomically so there is no need to check the message_id for each log entry. + # Otherwise, the join condition must be extended with + # fragment("encode(l0.fourth_topic, 'hex') = LPAD(TO_HEX(a1.message_id::BIGINT), 64, '0')") + base_condition = + dynamic([log, msg], log.transaction_hash == msg.originating_transaction_hash and msg.direction == :from_l2) + + join_condition = + if is_nil(start_block) or is_nil(end_block) do + base_condition + else + dynamic( + [_, msg], + ^base_condition and + msg.originating_transaction_block_number >= ^start_block and + msg.originating_transaction_block_number <= ^end_block + ) + end + + from(log in Log, + left_join: msg in Message, + on: ^join_condition, + where: + log.address_hash == ^arbsys_contract and log.first_topic == ^l2_to_l1_event and + is_nil(msg.originating_transaction_hash) + ) + end + @doc """ Retrieves the total count of rollup batches indexed up to the current moment. diff --git a/apps/explorer/lib/explorer/utility/missing_block_range.ex b/apps/explorer/lib/explorer/utility/missing_block_range.ex index ac8c8a39e5a5..9bcc6c9224d6 100644 --- a/apps/explorer/lib/explorer/utility/missing_block_range.ex +++ b/apps/explorer/lib/explorer/utility/missing_block_range.ex @@ -9,6 +9,10 @@ defmodule Explorer.Utility.MissingBlockRange do @default_returning_batch_size 10 + @typedoc """ + * `from_number`: The lower bound of the block range. + * `to_number`: The upper bound of the block range. + """ typed_schema "missing_block_ranges" do field(:from_number, :integer) field(:to_number, :integer) @@ -139,7 +143,8 @@ defmodule Explorer.Utility.MissingBlockRange do ## Returns - Returns `nil` if no intersecting ranges are found, or an `Explorer.Utility.MissingBlockRange` instance of the first intersecting range otherwise. """ - @spec intersects_with_range(Block.block_number(), Block.block_number()) :: nil | Explorer.Utility.MissingBlockRange + @spec intersects_with_range(Block.block_number(), Block.block_number()) :: + nil | Explorer.Utility.MissingBlockRange.t() def intersects_with_range(lower_number, higher_number) when is_integer(lower_number) and lower_number >= 0 and is_integer(higher_number) and lower_number <= higher_number do @@ -182,7 +187,19 @@ defmodule Explorer.Utility.MissingBlockRange do defp update_to_number_or_delete_range(%{from_number: from} = range, to) when to > from, do: Repo.delete(range) defp update_to_number_or_delete_range(range, to), do: update_range(range, %{to_number: to}) - defp get_range_by_block_number(number) do + @doc """ + Fetches the range of blocks that includes the given block number if it falls + within any of the ranges that need to be (re)fetched. + + ## Parameters + - `number`: The block number to check against the missing block ranges. + + ## Returns + - A single range record of `Explorer.Utility.MissingBlockRange` that includes + the given block number, or `nil` if no such range is found. + """ + @spec get_range_by_block_number(Block.block_number()) :: nil | Explorer.Utility.MissingBlockRange.t() + def get_range_by_block_number(number) do number |> include_bound_query() |> Repo.one() @@ -250,6 +267,18 @@ defmodule Explorer.Utility.MissingBlockRange do from(r in query, where: r.to_number > ^upper_bound) end + @doc """ + Constructs a query to check if a given block number falls within any of the + ranges of blocks that need to be (re)fetched. + + ## Parameters + - `bound`: The block number to check against the missing block ranges. + + ## Returns + - A query that can be used to find ranges where the given block number is + within the `from_number` and `to_number` bounds. + """ + @spec include_bound_query(Block.block_number()) :: Ecto.Query.t() def include_bound_query(bound) do from(r in __MODULE__, where: r.from_number >= ^bound, where: r.to_number <= ^bound) end diff --git a/apps/explorer/priv/arbitrum/migrations/20240628210148_add_index_for_messages.exs b/apps/explorer/priv/arbitrum/migrations/20240628210148_add_index_for_messages.exs new file mode 100644 index 000000000000..9a62a7c41248 --- /dev/null +++ b/apps/explorer/priv/arbitrum/migrations/20240628210148_add_index_for_messages.exs @@ -0,0 +1,14 @@ +defmodule Explorer.Repo.Arbitrum.Migrations.AddIndexForMessages do + use Ecto.Migration + + def change do + # name of the index is specified explicitly because the default index name is cut and not unique + create( + index( + :arbitrum_crosslevel_messages, + [:direction, :originating_transaction_block_number, :originating_transaction_hash], + name: :arbitrum_crosslevel_messages_dir_block_hash + ) + ) + end +end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/rollup_messages_catchup.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/rollup_messages_catchup.ex index cbd7cf085353..a0ec6e0c8ace 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/rollup_messages_catchup.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/rollup_messages_catchup.ex @@ -1,51 +1,54 @@ defmodule Indexer.Fetcher.Arbitrum.RollupMessagesCatchup do @moduledoc """ - Manages the catch-up process for historical rollup messages between Layer 1 (L1) and Layer 2 (L2) within the Arbitrum network. - - This module aims to discover historical messages that were not captured by the block - fetcher or the catch-up block fetcher. This situation arises during the upgrade of an - existing instance of BlockScout (BS) that already has indexed blocks but lacks - a crosschain messages discovery mechanism. Therefore, it becomes necessary to traverse - the already indexed blocks to extract crosschain messages contained within them. - - The fetcher's operation cycle consists of five phases, initiated by sending specific - messages: - - `:wait_for_new_block`: Waits for the block fetcher to index new blocks before - proceeding with message discovery. - - `:init_worker`: Sets up the initial parameters for the message discovery process, - identifying the ending blocks for the search. - - `:historical_msg_from_l2` and `:historical_msg_to_l2`: Manage the discovery and - processing of messages sent from L2 to L1 and from L1 to L2, respectively. - - `:plan_next_iteration`: Schedules the next iteration of the catch-up process. - - Workflow diagram of the fetcher state changes: - - wait_for_new_block - | - V - init_worker - | - V - |-> historical_msg_from_l2 -> historical_msg_to_l2 -> plan_next_iteration ->| - |---------------------------------------------------------------------------| - - `historical_msg_from_l2` discovers L2-to-L1 messages by analyzing logs from already - indexed rollup transactions. Logs representing the `L2ToL1Tx` event are utilized - to construct messages. The current rollup state, including information about - committed batches and confirmed blocks, is used to assign the appropriate status - to the messages before importing them into the database. - - `historical_msg_to_l2` discovers L1-to-L2 messages by requesting rollup - transactions through RPC. Transactions containing a `requestId` in their body are - utilized to construct messages. These messages are marked as `:relayed`, indicating - that they have been successfully received on L2 and are considered completed, and - are then imported into the database. This approach is adopted because it parallels - the action of re-indexing existing transactions to include Arbitrum-specific fields, - which are absent in the currently indexed transactions. However, permanently adding - these fields to the database model for the sake of historical message catch-up is - impractical. Therefore, to avoid the extensive process of re-indexing and to - minimize changes to the database schema, fetching the required data directly from - an external node via RPC is preferred for historical message discovery. + Manages the catch-up process for historical rollup messages between Layer 1 (L1) + and Layer 2 (L2) within the Arbitrum network. + + This module aims to discover historical messages that were not captured by the + block fetcher or the catch-up block fetcher. This situation arises during the + upgrade of an existing instance of BlockScout (BS) that already has indexed + blocks but lacks a crosschain messages discovery mechanism. Therefore, it + becomes necessary to traverse the already indexed blocks to extract crosschain + messages contained within them. + + The fetcher's operation cycle consists of five phases, initiated by sending + specific messages: + - `:wait_for_new_block`: Waits for the block fetcher to index new blocks before + proceeding with message discovery. + - `:init_worker`: Sets up the initial parameters for the message discovery + process, identifying the ending blocks for the search. + - `:historical_msg_from_l2` and `:historical_msg_to_l2`: Manage the discovery + and processing of messages sent from L2 to L1 and from L1 to L2, respectively. + - `:plan_next_iteration`: Schedules the next iteration of the catch-up process. + + Workflow diagram of the fetcher state changes: + + wait_for_new_block + | + V + init_worker + | + V + |-> historical_msg_from_l2 -> historical_msg_to_l2 -> plan_next_iteration ->| + |---------------------------------------------------------------------------| + + `historical_msg_from_l2` discovers L2-to-L1 messages by analyzing logs from + already indexed rollup transactions. Logs representing the `L2ToL1Tx` event are + utilized to construct messages. The current rollup state, including information + about committed batches and confirmed blocks, is used to assign the appropriate + status to the messages before importing them into the database. + + `historical_msg_to_l2` discovers in the database transactions that could be + responsible for L1-to-L2 messages and then re-requests these transactions + through RPC. Results are utilized to construct messages. These messages are + marked as `:relayed`, indicating that they have been successfully received on + L2 and are considered completed, and are then imported into the database. This + approach is adopted because it parallels the action of re-indexing existing + transactions to include Arbitrum-specific fields, which are absent in the + currently indexed transactions. However, permanently adding these fields to the + database model for the sake of historical message catch-up is impractical. + Therefore, to avoid the extensive process of re-indexing and to minimize changes + to the database schema, fetching the required data directly from an external + node via RPC is preferred for historical message discovery. """ use GenServer @@ -89,8 +92,7 @@ defmodule Indexer.Fetcher.Arbitrum.RollupMessagesCatchup do config_tracker = Application.get_all_env(:indexer)[__MODULE__] recheck_interval = config_tracker[:recheck_interval] - messages_to_l2_blocks_depth = config_tracker[:messages_to_l2_blocks_depth] - messages_from_l2_blocks_depth = config_tracker[:messages_to_l1_blocks_depth] + missed_messages_blocks_depth = config_tracker[:missed_messages_blocks_depth] Process.send(self(), :wait_for_new_block, []) @@ -104,8 +106,7 @@ defmodule Indexer.Fetcher.Arbitrum.RollupMessagesCatchup do }, json_l2_rpc_named_arguments: args[:json_rpc_named_arguments], recheck_interval: recheck_interval, - messages_to_l2_blocks_depth: messages_to_l2_blocks_depth, - messages_from_l2_blocks_depth: messages_from_l2_blocks_depth + missed_messages_blocks_depth: missed_messages_blocks_depth }, data: %{} }} @@ -167,9 +168,9 @@ defmodule Indexer.Fetcher.Arbitrum.RollupMessagesCatchup do end # Sets the initial parameters for discovering historical messages. This function - # calculates the end blocks for both L1-to-L2 and L2-to-L1 message discovery - # processes based on th earliest messages already indexed. If no messages are - # available, the block number before the latest indexed block will be used. + # inspects the database for missed messages and, if any are found, identifies the + # end blocks of the ranges for both L1-to-L2 and L2-to-L1 messages. If no missed + # messages are found, the block number before the latest indexed block will be used. # These end blocks are used to initiate the discovery process in subsequent iterations. # # After identifying the initial values, the function immediately transitions to @@ -178,15 +179,22 @@ defmodule Indexer.Fetcher.Arbitrum.RollupMessagesCatchup do # # ## Parameters # - `:init_worker`: The message that triggers the handler. - # - `state`: The current state of the fetcher. + # - `state`: The current state of the fetcher containing the first rollup block + # number and the number of the most recent block indexed. # # ## Returns # - `{:noreply, new_state}` where the end blocks for both L1-to-L2 and L2-to-L1 # message discovery are established. @impl GenServer - def handle_info(:init_worker, %{data: _} = state) do - historical_msg_from_l2_end_block = Db.rollup_block_to_discover_missed_messages_from_l2(state.data.new_block - 1) - historical_msg_to_l2_end_block = Db.rollup_block_to_discover_missed_messages_to_l2(state.data.new_block - 1) + def handle_info( + :init_worker, + %{config: %{rollup_rpc: %{first_block: rollup_first_block}}, data: %{new_block: just_received_block}} = state + ) do + historical_msg_from_l2_end_block = + Db.rollup_block_to_discover_missed_messages_from_l2(just_received_block, rollup_first_block) + + historical_msg_to_l2_end_block = + Db.rollup_block_to_discover_missed_messages_to_l2(just_received_block, rollup_first_block) Process.send(self(), :historical_msg_from_l2, []) @@ -205,7 +213,8 @@ defmodule Indexer.Fetcher.Arbitrum.RollupMessagesCatchup do # # This function uses the results from the previous iteration to set the end block # for the current message discovery iteration. It identifies the start block and - # requests rollup logs within the specified range to explore `L2ToL1Tx` events. + # requests rollup logs within the specified range to explore `L2ToL1Tx` events + # that have no matching records in the cross-level messages table. # Discovered events are used to compose messages to be stored in the database. # Before being stored in the database, each message is assigned the appropriate # status based on the current state of the rollup. @@ -251,17 +260,18 @@ defmodule Indexer.Fetcher.Arbitrum.RollupMessagesCatchup do # Processes the next iteration of historical L1-to-L2 message discovery. # - # This function uses the results from the previous iteration to set the end block for - # the current message discovery iteration. It identifies the start block and requests - # rollup blocks within the specified range through RPC to explore transactions - # containing a `requestId` in their body. This RPC request is necessary because the + # This function uses the results from the previous iteration to set the end block + # for the current message discovery iteration. It identifies the start block and + # inspects the database for transactions within the block range that could contain + # missing messages. Then it requests rollup transactions through RPC to extract the + # `requestId` for every transaction. This RPC request is necessary because the # `requestId` field is not present in the transaction model of already indexed - # transactions in the database. The discovered transactions are then used to construct - # messages, which are subsequently stored in the database. These imported messages are - # marked as `:relayed`, signifying that they represent completed actions from L1 to L2. + # transactions in the database. Results are used to construct messages, which are + # subsequently stored in the database. These imported messages are marked as + # `:relayed`, signifying that they represent completed actions from L1 to L2. # # After importing the messages, the function immediately switches to the process - # of choosing a delay prior to the next iteration of historical messages discovery + # of choosing a delay prior to the next iteration of historical message discovery # by sending the `:plan_next_iteration` message. # # ## Parameters diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex index 1c304e623b9c..66fe5b7035b7 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex @@ -5,13 +5,13 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do import Ecto.Query, only: [from: 2] - import Indexer.Fetcher.Arbitrum.Utils.Logging, only: [log_warning: 1] + import Indexer.Fetcher.Arbitrum.Utils.Logging, only: [log_warning: 1, log_info: 1] alias Explorer.{Chain, Repo} alias Explorer.Chain.Arbitrum alias Explorer.Chain.Arbitrum.Reader alias Explorer.Chain.Block, as: FullBlock - alias Explorer.Chain.{Data, Hash, Log} + alias Explorer.Chain.{Data, Hash} alias Explorer.Utility.MissingBlockRange @@ -218,52 +218,79 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do end @doc """ - Determines the rollup block number to start searching for missed messages originating from L2. + Determines the rollup block number to discover missed L2-to-L1 messages within + a specified range. + + The function checks for the first missed L2-to-L1 message and whether historical + block fetching is still in progress. If no missed messages are found and + historical fetching is complete, it returns the block number just before the + first rollup block. Otherwise, it returns the appropriate block number based on + the findings. ## Parameters - - `value_if_nil`: The default value to return if no messages originating from L2 have been found. + - `initial_value`: The initial block number to start the further search of the + missed messages from if no missed messages are found and historical blocks + are not fetched yet. + - `rollup_first_block`: The block number of the first rollup block. ## Returns - - The rollup block number just before the earliest discovered message from L2, - or `value_if_nil` if no messages from L2 are found. + - The block number of the first missed L2-to-L1 message. """ - @spec rollup_block_to_discover_missed_messages_from_l2(nil | FullBlock.block_number()) :: + @spec rollup_block_to_discover_missed_messages_from_l2(FullBlock.block_number(), FullBlock.block_number()) :: nil | FullBlock.block_number() - def rollup_block_to_discover_missed_messages_from_l2(value_if_nil \\ nil) - when (is_integer(value_if_nil) and value_if_nil >= 0) or is_nil(value_if_nil) do - case Reader.rollup_block_of_earliest_discovered_message_from_l2() do - nil -> - log_warning("No messages from L2 found in DB") - value_if_nil + def rollup_block_to_discover_missed_messages_from_l2(initial_value, rollup_first_block) do + arbsys_contract = Application.get_env(:indexer, Indexer.Fetcher.Arbitrum.Messaging)[:arbsys_contract] - value -> - value - 1 + with {:block, nil} <- + {:block, Reader.rollup_block_of_first_missed_message_from_l2(arbsys_contract, @l2_to_l1_event)}, + {:synced, true} <- {:synced, rollup_synced?()} do + log_info("No missed messages from L2 found") + rollup_first_block - 1 + else + {:block, value} -> + log_info("First missed message from L2 found in block #{value}") + value + + {:synced, false} -> + log_info("No missed messages from L2 found but historical blocks fetching still in progress") + initial_value end end @doc """ - Determines the rollup block number to start searching for missed messages originating to L2. + Determines the rollup block number to discover missed L1-to-L2 messages within + a specified range. + + The function checks for the first missed L1-to-L2 message and whether historical + block fetching is still in progress. If no missed messages are found and + historical fetching is complete, it returns the block number just before the + first rollup block. Otherwise, it returns the appropriate block number based on + the findings. ## Parameters - - `value_if_nil`: The default value to return if no messages originating to L2 have been found. + - `initial_value`: The initial block number to start the further search of the + missed messages from if no missed messages are found and historical blocks + are not fetched yet. + - `rollup_first_block`: The block number of the first rollup block. ## Returns - - The rollup block number just before the earliest discovered message to L2, - or `value_if_nil` if no messages to L2 are found. + - The block number of the first missed L1-to-L2 message. """ - @spec rollup_block_to_discover_missed_messages_to_l2(nil | FullBlock.block_number()) :: nil | FullBlock.block_number() - def rollup_block_to_discover_missed_messages_to_l2(value_if_nil \\ nil) - when (is_integer(value_if_nil) and value_if_nil >= 0) or is_nil(value_if_nil) do - case Reader.rollup_block_of_earliest_discovered_message_to_l2() do - nil -> - # In theory it could be a situation when when the earliest message points - # to a completion transaction which is not indexed yet. In this case, this - # warning will occur. - log_warning("No completed messages to L2 found in DB") - value_if_nil + @spec rollup_block_to_discover_missed_messages_to_l2(FullBlock.block_number(), FullBlock.block_number()) :: + nil | FullBlock.block_number() + def rollup_block_to_discover_missed_messages_to_l2(initial_value, rollup_first_block) do + with {:block, nil} <- {:block, Reader.rollup_block_of_first_missed_message_to_l2()}, + {:synced, true} <- {:synced, rollup_synced?()} do + log_info("No missed messages to L2 found") + rollup_first_block - 1 + else + {:block, value} -> + log_info("First missed message to L2 found in block #{value}") + value - value -> - value - 1 + {:synced, false} -> + log_info("No missed messages to L2 found but historical blocks fetching still in progress") + initial_value end end @@ -371,7 +398,11 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do - A list of `Explorer.Chain.Block` instances containing detailed information for each block number in the input list. Returns an empty list if no blocks are found for the given numbers. """ - @spec rollup_blocks(maybe_improper_list(FullBlock.block_number(), [])) :: [FullBlock.t()] + @spec rollup_blocks([FullBlock.block_number()]) :: [FullBlock.t()] + def rollup_blocks(list_of_block_numbers) + + def rollup_blocks([]), do: [] + def rollup_blocks(list_of_block_numbers) when is_list(list_of_block_numbers) do query = @@ -641,50 +672,64 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do end @doc """ - Retrieves all rollup logs in the range of blocks from `start_block` to `end_block` - corresponding to the `L2ToL1Tx` event emitted by the ArbSys contract. + Retrieves the transaction hashes as strings for missed L1-to-L2 messages within + a specified block range. + + The function identifies missed messages by checking transactions of specific + types that are supposed to contain L1-to-L2 messages and verifying if there are + corresponding entries in the messages table. A message is considered missed if + there is a transaction without a matching message record within the specified + block range. ## Parameters - - `start_block`: The starting block number of the range from which to - retrieve the transaction logs containing L2-to-L1 messages. + - `start_block`: The starting block number of the range. - `end_block`: The ending block number of the range. ## Returns - - A list of log maps for the `L2ToL1Tx` event where binary values for hashes - and data are decoded into hex strings, containing detailed information about - each event within the specified block range. Returns an empty list if no - relevant logs are found. + - A list of transaction hashes as strings for missed L1-to-L2 messages. """ - @spec l2_to_l1_logs(FullBlock.block_number(), FullBlock.block_number()) :: [ + @spec transactions_for_missed_messages_to_l2(non_neg_integer(), non_neg_integer()) :: [String.t()] + def transactions_for_missed_messages_to_l2(start_block, end_block) do + # credo:disable-for-lines:2 Credo.Check.Refactor.PipeChainStart + Reader.transactions_for_missed_messages_to_l2(start_block, end_block) + |> Enum.map(&Hash.to_string/1) + end + + @doc """ + Retrieves the logs for missed L2-to-L1 messages within a specified block range + and converts them to maps. + + The function identifies missed messages by checking logs for the specified + L2-to-L1 event and verifying if there are corresponding entries in the messages + table. A message is considered missed if there is a log entry without a + matching message record within the specified block range. + + ## Parameters + - `start_block`: The starting block number of the range. + - `end_block`: The ending block number of the range. + + ## Returns + - A list of maps representing the logs for missed L2-to-L1 messages. + """ + @spec logs_for_missed_messages_from_l2(non_neg_integer(), non_neg_integer()) :: [ %{ - data: String, + data: String.t(), index: non_neg_integer(), - first_topic: String, - second_topic: String, - third_topic: String, - fourth_topic: String, - address_hash: String, - transaction_hash: String, - block_hash: String, + first_topic: String.t(), + second_topic: String.t(), + third_topic: String.t(), + fourth_topic: String.t(), + address_hash: String.t(), + transaction_hash: String.t(), + block_hash: String.t(), block_number: FullBlock.block_number() } ] - def l2_to_l1_logs(start_block, end_block) - when is_integer(start_block) and start_block >= 0 and - is_integer(end_block) and start_block <= end_block do + def logs_for_missed_messages_from_l2(start_block, end_block) do arbsys_contract = Application.get_env(:indexer, Indexer.Fetcher.Arbitrum.Messaging)[:arbsys_contract] - query = - from(log in Log, - where: - log.block_number >= ^start_block and - log.block_number <= ^end_block and - log.address_hash == ^arbsys_contract and - log.first_topic == ^@l2_to_l1_event - ) - - query - |> Repo.all(timeout: :infinity) + # credo:disable-for-lines:2 Credo.Check.Refactor.PipeChainStart + Reader.logs_for_missed_messages_from_l2(start_block, end_block, arbsys_contract, @l2_to_l1_event) |> Enum.map(&logs_to_map/1) end @@ -870,11 +915,42 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do not Enum.empty?(Reader.get_anytrust_keyset(keyset_hash)) end - @spec get_da_info_by_batch_number(non_neg_integer()) :: map() | nil + @doc """ + Retrieves Data Availability (DA) information for a specific Arbitrum batch number. + + This function queries the database for DA information stored in the + `DaMultiPurposeRecord`. It specifically looks for records where + the `data_type` is 0, which corresponds to batch-specific DA information. + + ## Parameters + - `batch_number`: The Arbitrum batch number. + + ## Returns + - A map containing the DA information for the specified batch number. This map + corresponds to the `data` field of the `DaMultiPurposeRecord`. + - An empty map (`%{}`) if no DA information is found for the given batch number. + """ + @spec get_da_info_by_batch_number(non_neg_integer()) :: map() def get_da_info_by_batch_number(batch_number) do Reader.get_da_info_by_batch_number(batch_number) end + # Checks if the rollup is synced by verifying if the block after the first block exists in the database. + @spec rollup_synced?() :: boolean() + defp rollup_synced? do + # Since zero block does not have any useful data, it make sense to consider + # the block just after it + rollup_tail = Application.get_all_env(:indexer)[:first_block] + 1 + + query = + from( + block in FullBlock, + where: block.number == ^rollup_tail and block.consensus == true + ) + + if(is_nil(query |> Repo.one()), do: false, else: true) + end + @spec lifecycle_transaction_to_map(Arbitrum.LifecycleTransaction.t()) :: Arbitrum.LifecycleTransaction.to_import() defp lifecycle_transaction_to_map(tx) do [:id, :hash, :block_number, :timestamp, :status] @@ -918,6 +994,25 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do |> db_record_to_map(log, true) end + # Converts an Arbitrum-related database record to a map with specified keys and optional encoding. + # + # This function is used to transform various Arbitrum-specific database records + # (such as LifecycleTransaction, BatchBlock, or Message) into maps containing + # only the specified keys. It's particularly useful for preparing data for + # import or further processing of Arbitrum blockchain data. + # + # Parameters: + # - `required_keys`: A list of atoms representing the keys to include in the + # output map. + # - `record`: The database record or struct to be converted. + # - `encode`: Boolean flag to determine if Hash and Data types should be + # encoded to strings (default: false). When true, Hash and Data are + # converted to string representations; otherwise, their raw bytes are used. + # + # Returns: + # - A map containing only the required keys from the input record. Hash and + # Data types are either encoded to strings or left as raw bytes based on + # the `encode` parameter. @spec db_record_to_map([atom()], map(), boolean()) :: map() defp db_record_to_map(required_keys, record, encode \\ false) do required_keys |> Enum.reduce(%{}, fn key, record_as_map -> diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex index 449c2fa7bb6c..94457cd56941 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex @@ -1,24 +1,27 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do @moduledoc """ - Handles the discovery and processing of historical messages between Layer 1 (L1) and Layer 2 (L2) within an Arbitrum rollup. + Handles the discovery and processing of historical messages between Layer 1 (L1) and Layer 2 (L2) within an Arbitrum rollup. - L1-to-L2 messages are discovered by requesting rollup transactions through RPC. - This is necessary because some Arbitrum-specific fields are not included in the - already indexed transactions within the database. + ## L1-to-L2 Messages + L1-to-L2 messages are discovered by first inspecting the database to identify + potentially missed messages. Then, rollup transactions are requested through RPC + to fetch the necessary data. This is required because some Arbitrum-specific fields, + such as the `requestId`, are not included in the already indexed transactions within + the database. + ## L2-to-L1 Messages L2-to-L1 messages are discovered by analyzing the logs of already indexed rollup transactions. """ - import Indexer.Fetcher.Arbitrum.Utils.Logging, only: [log_warning: 1, log_info: 1] + import Indexer.Fetcher.Arbitrum.Utils.Logging, only: [log_warning: 1, log_info: 1, log_debug: 1] - alias EthereumJSONRPC.Block.ByNumber, as: BlockByNumber alias EthereumJSONRPC.Transaction, as: TransactionByRPC alias Explorer.Chain alias Indexer.Fetcher.Arbitrum.Messaging - alias Indexer.Fetcher.Arbitrum.Utils.{Db, Logging, Rpc} + alias Indexer.Fetcher.Arbitrum.Utils.{Db, Rpc} require Logger @@ -34,25 +37,31 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do ## Parameters - `end_block`: The ending block number up to which the discovery should occur. - If `nil` or lesser than the indexer first block, the function - returns with no action taken. + If `nil` or less than the indexer's first block, the function returns with no + action taken. - `state`: Contains the operational configuration, including the depth of - blocks to consider for the starting point of message discovery. + blocks to consider for the starting point of message discovery and the + first block of the rollup chain. ## Returns - - `{:ok, nil}`: If `end_block` is `nil`, indicating no discovery action was required. - - `{:ok, rollup_first_block}`: If `end_block` is lesser than the indexer first block, - indicating that the "genesis" of the block chain was reached. + - `{:ok, nil}`: If `end_block` is `nil`, indicating no discovery action was + required. + - `{:ok, rollup_first_block}`: If `end_block` is less than the indexer's first + block, indicating that the "genesis" of the blockchain was reached. - `{:ok, start_block}`: Upon successful discovery of historical messages, where - `start_block` indicates the necessity to consider another block range in the next - iteration of message discovery. + `start_block` indicates the necessity to consider another block range in the + next iteration of message discovery. - `{:ok, end_block + 1}`: If the required block range is not fully indexed, - indicating that the next iteration of message discovery should start with the same - block range. + indicating that the next iteration of message discovery should start with the + same block range. """ @spec discover_historical_messages_from_l2(nil | integer(), %{ :config => %{ - :messages_to_l2_blocks_depth => non_neg_integer(), + :missed_messages_blocks_depth => non_neg_integer(), + :rollup_rpc => %{ + :first_block => non_neg_integer(), + optional(any()) => any() + }, optional(any()) => any() }, optional(any()) => any() @@ -72,13 +81,13 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do end_block, %{ config: %{ - messages_from_l2_blocks_depth: messages_from_l2_blocks_depth, + missed_messages_blocks_depth: missed_messages_blocks_depth, rollup_rpc: %{first_block: rollup_first_block} } } = _state ) when is_integer(end_block) do - start_block = max(rollup_first_block, end_block - messages_from_l2_blocks_depth + 1) + start_block = max(rollup_first_block, end_block - missed_messages_blocks_depth + 1) if Db.indexed_blocks?(start_block, end_block) do do_discover_historical_messages_from_l2(start_block, end_block) @@ -106,11 +115,12 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do # # ## Returns # - `{:ok, start_block}`: A tuple indicating successful processing, returning the initial - # starting block number. + # starting block number. + @spec do_discover_historical_messages_from_l2(non_neg_integer(), non_neg_integer()) :: {:ok, non_neg_integer()} defp do_discover_historical_messages_from_l2(start_block, end_block) do log_info("Block range for discovery historical messages from L2: #{start_block}..#{end_block}") - logs = Db.l2_to_l1_logs(start_block, end_block) + logs = Db.logs_for_missed_messages_from_l2(start_block, end_block) unless logs == [] do messages = @@ -126,35 +136,40 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do @doc """ Initiates the discovery of historical messages sent from L1 to L2 up to a specified block number. - This function orchestrates the process of discovering historical L1-to-L2 messages within - a given rollup block range, based on the existence of the `requestId` field in the rollup - transaction body. Transactions are requested through RPC because already indexed - transactions from the database cannot be utilized; the `requestId` field is not included - in the transaction model. The function ensures that the block range has been indexed - before proceeding with message discovery and import. The imported messages are marked as - `:relayed`, as they represent completed actions from L1 to L2. + This function orchestrates the process of discovering historical L1-to-L2 + messages within a given rollup block range, based on the existence of the + `requestId` field in the rollup transaction body. The initial list of + transactions that could contain the messages is received from the database, and + then their bodies are re-requested through RPC because already indexed + transactions from the database cannot be utilized; the `requestId` field is not + included in the transaction model. The function ensures that the block range + has been indexed before proceeding with message discovery and import. The + imported messages are marked as `:relayed`, as they represent completed actions + from L1 to L2. ## Parameters - - `end_block`: The ending block number for the discovery operation. - If `nil` or lesser than the indexer first block, the function - returns with no action taken. - - `state`: The current state of the operation, containing configuration parameters - including `messages_to_l2_blocks_depth`, `chunk_size`, and JSON RPC connection - settings. + - `end_block`: The ending block number for the discovery operation. If `nil` or + less than the indexer's first block, the function returns with no action + taken. + - `state`: The current state of the operation, containing configuration + parameters including the depth of blocks to consider for the starting point + of message discovery, size of chunk to make request to RPC, and JSON RPC + connection settings. ## Returns - `{:ok, nil}`: If `end_block` is `nil`, indicating no action was necessary. - - `{:ok, rollup_first_block}`: If `end_block` is lesser than the indexer first block, - indicating that the "genesis" of the block chain was reached. - - `{:ok, start_block}`: On successful completion of historical message discovery, where - `start_block` indicates the necessity to consider another block range in the next - iteration of message discovery. - - `{:ok, end_block + 1}`: If the required block range is not fully indexed, indicating - that the next iteration of message discovery should start with the same block range. + - `{:ok, rollup_first_block}`: If `end_block` is less than the indexer's first + block, indicating that the "genesis" of the blockchain was reached. + - `{:ok, start_block}`: On successful completion of historical message + discovery, where `start_block` indicates the necessity to consider another + block range in the next iteration of message discovery. + - `{:ok, end_block + 1}`: If the required block range is not fully indexed, + indicating that the next iteration of message discovery should start with the + same block range. """ @spec discover_historical_messages_to_l2(nil | integer(), %{ :config => %{ - :messages_to_l2_blocks_depth => non_neg_integer(), + :missed_messages_blocks_depth => non_neg_integer(), :rollup_rpc => %{ :chunk_size => non_neg_integer(), :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), @@ -177,10 +192,10 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do def discover_historical_messages_to_l2( end_block, - %{config: %{messages_to_l2_blocks_depth: _, rollup_rpc: %{first_block: _}} = config} = _state + %{config: %{missed_messages_blocks_depth: _, rollup_rpc: %{first_block: _}} = config} = _state ) when is_integer(end_block) do - start_block = max(config.rollup_rpc.first_block, end_block - config.messages_to_l2_blocks_depth + 1) + start_block = max(config.rollup_rpc.first_block, end_block - config.missed_messages_blocks_depth + 1) # Although indexing blocks is not necessary to determine the completion of L1-to-L2 messages, # for database consistency, it is preferable to delay marking these messages as completed. @@ -195,22 +210,36 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do end end - # The function iterates through the block range in chunks, making RPC calls to fetch rollup block - # data and extract transactions. Each transaction is filtered for L1-to-L2 messages based on - # existence of `requestId` field in the transaction body, and then imported into the database. - # The imported messages are marked as `:relayed` as they represent completed actions from L1 to L2. + # Discovers and processes historical messages sent from L1 to L2 within a + # specified rollup block range. # - # Already indexed transactions from the database cannot be used because the `requestId` field is - # not included in the transaction model. + # This function identifies which of already indexed transactions within the + # block range contains L1-to-L2 messages and makes RPC calls to fetch + # transaction data. These transactions are then processed to construct proper + # message structures, which are imported into the database. The imported + # messages are marked as `:relayed` as they represent completed actions from L1 + # to L2. + # + # Note: Already indexed transactions from the database cannot be used because + # the `requestId` field is not included in the transaction model. # # ## Parameters # - `start_block`: The starting block number for the discovery range. # - `end_block`: The ending block number for the discovery range. - # - `config`: The configuration map containing settings for RPC communication and chunk size. + # - `config`: The configuration map containing settings for RPC communication + # and chunk size. # # ## Returns - # - `{:ok, start_block}`: A tuple indicating successful processing, returning the initial - # starting block number. + # - `{:ok, start_block}`: A tuple indicating successful processing, returning + # the initial starting block number. + @spec do_discover_historical_messages_to_l2(non_neg_integer(), non_neg_integer(), %{ + :rollup_rpc => %{ + :chunk_size => non_neg_integer(), + :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), + optional(any()) => any() + }, + optional(any()) => any() + }) :: {:ok, non_neg_integer()} defp do_discover_historical_messages_to_l2( start_block, end_block, @@ -218,68 +247,56 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do ) do log_info("Block range for discovery historical messages to L2: #{start_block}..#{end_block}") - {messages, _} = - start_block..end_block - |> Enum.chunk_every(chunk_size) - |> Enum.reduce({[], 0}, fn chunk, {messages_acc, chunks_counter} -> - Logging.log_details_chunk_handling( - "Collecting rollup data", - {"block", "blocks"}, - chunk, - chunks_counter, - end_block - start_block + 1 - ) - - # Since DB does not contain the field RequestId specific to Arbitrum - # all transactions will be requested from the rollup RPC endpoint. - # The catchup process intended to be run once and only for the BS instance - # which are already exist, so it does not make sense to introduce - # the new field in DB - requests = build_block_by_number_requests(chunk) - - messages = - requests - |> Rpc.make_chunked_request(json_rpc_named_arguments, "eth_getBlockByNumber") - |> get_transactions() - |> Enum.map(fn tx -> - tx - |> TransactionByRPC.to_elixir() - |> TransactionByRPC.elixir_to_params() - end) - |> Messaging.filter_l1_to_l2_messages(false) - - {messages ++ messages_acc, chunks_counter + length(chunk)} - end) - - unless messages == [] do + transactions = Db.transactions_for_missed_messages_to_l2(start_block, end_block) + transactions_length = length(transactions) + + if transactions_length > 0 do + log_debug("#{transactions_length} historical messages to L2 discovered") + + messages = + transactions + |> Enum.chunk_every(chunk_size) + |> Enum.reduce([], fn chunk, messages_acc -> + # Since DB does not contain the field RequestId specific to Arbitrum + # all transactions will be requested from the rollup RPC endpoint. + # The catchup process intended to be run once and only for the BS instance + # which are already exist, so it does not make sense to introduce + # the new field in DB + requests = build_transaction_requests(chunk) + + messages = + requests + |> Rpc.make_chunked_request(json_rpc_named_arguments, "eth_getTransactionByHash") + |> Enum.map(&transaction_json_to_map/1) + |> Messaging.filter_l1_to_l2_messages(false) + + messages ++ messages_acc + end) + log_info("#{length(messages)} completions of L1-to-L2 messages will be imported") + import_to_db(messages) end - import_to_db(messages) - {:ok, start_block} end - # Constructs a list of `eth_getBlockByNumber` requests for a given list of block numbers. - defp build_block_by_number_requests(block_numbers) do - block_numbers - |> Enum.reduce([], fn block_num, requests_list -> + # Constructs a list of `eth_getTransactionByHash` requests for a given list of transaction hashes. + defp build_transaction_requests(tx_hashes) do + tx_hashes + |> Enum.reduce([], fn tx_hash, requests_list -> [ - BlockByNumber.request(%{ - id: block_num, - number: block_num - }) + Rpc.transaction_by_hash_request(%{id: 0, hash: tx_hash}) | requests_list ] end) end - # Aggregates transactions from a list of blocks, combining them into a single list. - defp get_transactions(blocks_by_rpc) do - blocks_by_rpc - |> Enum.reduce([], fn block_by_rpc, txs -> - block_by_rpc["transactions"] ++ txs - end) + # Transforms a JSON transaction object into a map. + @spec transaction_json_to_map(%{String.t() => any()}) :: map() + defp transaction_json_to_map(transaction_json) do + transaction_json + |> TransactionByRPC.to_elixir() + |> TransactionByRPC.elixir_to_params() end # Imports a list of messages into the database. diff --git a/config/runtime.exs b/config/runtime.exs index 3c91ccbc4a11..e5de09cf0cb0 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -907,10 +907,8 @@ config :indexer, Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses.Supervisor, config :indexer, Indexer.Fetcher.Arbitrum.RollupMessagesCatchup, recheck_interval: ConfigHelper.parse_time_env_var("INDEXER_ARBITRUM_MISSED_MESSAGES_RECHECK_INTERVAL", "1h"), - messages_to_l2_blocks_depth: - ConfigHelper.parse_integer_env_var("INDEXER_ARBITRUM_MISSED_MESSAGES_TO_L2_BLOCK_DEPTH", 50), - messages_to_l1_blocks_depth: - ConfigHelper.parse_integer_env_var("INDEXER_ARBITRUM_MISSED_MESSAGES_TO_L1_BLOCK_DEPTH", 1000) + missed_messages_blocks_depth: + ConfigHelper.parse_integer_env_var("INDEXER_ARBITRUM_MISSED_MESSAGES_BLOCKS_DEPTH", 10000) config :indexer, Indexer.Fetcher.Arbitrum.RollupMessagesCatchup.Supervisor, enabled: ConfigHelper.parse_bool_env_var("INDEXER_ARBITRUM_BRIDGE_MESSAGES_TRACKING_ENABLED") diff --git a/cspell.json b/cspell.json index 2f572a7d60e4..91a90a03e643 100644 --- a/cspell.json +++ b/cspell.json @@ -285,6 +285,7 @@ "lkve", "llhauc", "loggable", + "LPAD", "LUKSO", "luxon", "mabi", diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index f2dcbd3c4605..02cccd8d4c22 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -246,8 +246,7 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_ARBITRUM_BRIDGE_MESSAGES_TRACKING_ENABLED= # INDEXER_ARBITRUM_TRACKING_MESSAGES_ON_L1_RECHECK_INTERVAL= # INDEXER_ARBITRUM_MISSED_MESSAGES_RECHECK_INTERVAL= -# INDEXER_ARBITRUM_MISSED_MESSAGES_TO_L2_BLOCK_DEPTH= -# INDEXER_ARBITRUM_MISSED_MESSAGES_TO_L1_BLOCK_DEPTH= +# INDEXER_ARBITRUM_MISSED_MESSAGES_BLOCKS_DEPTH= # INDEXER_REALTIME_FETCHER_MAX_GAP= # INDEXER_FETCHER_INIT_QUERY_LIMIT= # INDEXER_TOKEN_BALANCES_FETCHER_INIT_QUERY_LIMIT= From f2b10cc6b4b32f6a83d9408187e4d649148e470c Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 24 Jul 2024 12:15:03 +0300 Subject: [PATCH 017/363] chore: disable public metrics by default, set 1 day as default period of update (#10469) --- apps/explorer/lib/explorer/chain/metrics.ex | 24 +++---- .../lib/explorer/chain/metrics/queries.ex | 70 ++++++++++--------- .../lib/explorer/prometheus/instrumenter.ex | 56 +++++++-------- config/runtime.exs | 4 +- docker-compose/envs/common-blockscout.env | 3 +- 5 files changed, 82 insertions(+), 75 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/metrics.ex b/apps/explorer/lib/explorer/chain/metrics.ex index 9031ed8cb5dc..b90e925e42d0 100644 --- a/apps/explorer/lib/explorer/chain/metrics.ex +++ b/apps/explorer/lib/explorer/chain/metrics.ex @@ -13,13 +13,13 @@ defmodule Explorer.Chain.Metrics do @interval :timer.hours(1) @options [timeout: 60_000, api?: true] @metrics_list [ - :weekly_success_transactions_number, - :weekly_deployed_smart_contracts_number, - :weekly_verified_smart_contracts_number, - :weekly_new_addresses_number, - :weekly_new_tokens_number, - :weekly_new_token_transfers_number, - :weekly_simplified_active_addresses_number + :success_transactions_number, + :deployed_smart_contracts_number, + :verified_smart_contracts_number, + :new_addresses_number, + :new_tokens_number, + :new_token_transfers_number, + :simplified_active_addresses_number ] @spec start_link(term()) :: GenServer.on_start() @@ -28,11 +28,11 @@ defmodule Explorer.Chain.Metrics do end def init(_) do - if Application.get_env(:explorer, __MODULE__)[:disabled?] do - :ignore - else + if Application.get_env(:explorer, __MODULE__)[:enabled] do send(self(), :set_metrics) {:ok, %{}} + else + :ignore end end @@ -69,12 +69,12 @@ defmodule Explorer.Chain.Metrics do defp set_handler_metric(metric) do func = String.to_atom(to_string(metric) <> "_query") - weekly_transactions_count = + transactions_count = Queries |> apply(func, []) |> select_repo(@options).one() - apply(Instrumenter, metric, [weekly_transactions_count]) + apply(Instrumenter, metric, [transactions_count]) end defp schedule_next_run do diff --git a/apps/explorer/lib/explorer/chain/metrics/queries.ex b/apps/explorer/lib/explorer/chain/metrics/queries.ex index 41b0c628b7ab..c68b829422b7 100644 --- a/apps/explorer/lib/explorer/chain/metrics/queries.ex +++ b/apps/explorer/lib/explorer/chain/metrics/queries.ex @@ -31,18 +31,18 @@ defmodule Explorer.Chain.Metrics.Queries do @doc """ Retrieves the query for fetching the number of successful transactions in a week. """ - @spec weekly_success_transactions_number_query() :: Ecto.Query.t() - def weekly_success_transactions_number_query do + @spec success_transactions_number_query() :: Ecto.Query.t() + def success_transactions_number_query do if DenormalizationHelper.transactions_denormalization_finished?() do Transaction - |> where([tx], tx.block_timestamp >= ago(7, "day")) + |> where([tx], tx.block_timestamp >= ago(^update_period_hours(), "hour")) |> where([tx], tx.block_consensus == true) |> where([tx], tx.status == ^1) |> select([tx], count(tx.hash)) else Transaction |> join(:inner, [tx], block in assoc(tx, :block)) - |> where([tx, block], block.timestamp >= ago(7, "day")) + |> where([tx, block], block.timestamp >= ago(^update_period_hours(), "hour")) |> where([tx, block], block.consensus == true) |> where([tx, block], tx.status == ^1) |> select([tx, block], count(tx.hash)) @@ -52,13 +52,13 @@ defmodule Explorer.Chain.Metrics.Queries do @doc """ Retrieves the query for the number of smart contracts deployed in the current week. """ - @spec weekly_deployed_smart_contracts_number_query() :: Ecto.Query.t() - def weekly_deployed_smart_contracts_number_query do + @spec deployed_smart_contracts_number_query() :: Ecto.Query.t() + def deployed_smart_contracts_number_query do transactions_query = if DenormalizationHelper.transactions_denormalization_finished?() do Transaction |> where([tx], not is_nil(tx.created_contract_address_hash)) - |> where([tx], tx.block_timestamp >= ago(7, "day")) + |> where([tx], tx.block_timestamp >= ago(^update_period_hours(), "hour")) |> where([tx], tx.block_consensus == true) |> where([tx], tx.status == ^1) |> select([tx], tx.created_contract_address_hash) @@ -67,7 +67,7 @@ defmodule Explorer.Chain.Metrics.Queries do |> join(:inner, [tx], block in assoc(tx, :block)) |> where([tx], not is_nil(tx.created_contract_address_hash)) |> where([tx, block], block.consensus == true) - |> where([tx, block], block.timestamp >= ago(7, "day")) + |> where([tx, block], block.timestamp >= ago(^update_period_hours(), "hour")) |> where([tx, block], tx.status == ^1) |> select([tx, block], tx.created_contract_address_hash) end @@ -77,7 +77,7 @@ defmodule Explorer.Chain.Metrics.Queries do # InternalTransaction # |> join(:inner, [it], transaction in assoc(it, :transaction)) # |> where([it, tx], not is_nil(it.created_contract_address_hash)) - # |> where([it, tx], tx.block_timestamp >= ago(7, "day")) + # |> where([it, tx], tx.block_timestamp >= ago(^update_period_hours(), "hour")) # |> where([it, tx], tx.block_consensus == true) # |> where([it, tx], tx.status == ^1) # |> select([it, tx], it.created_contract_address_hash) @@ -97,41 +97,41 @@ defmodule Explorer.Chain.Metrics.Queries do @doc """ Retrieves the query for the number of verified smart contracts in the current week. """ - @spec weekly_verified_smart_contracts_number_query() :: Ecto.Query.t() - def weekly_verified_smart_contracts_number_query do + @spec verified_smart_contracts_number_query() :: Ecto.Query.t() + def verified_smart_contracts_number_query do SmartContract - |> where([sc], sc.inserted_at >= ago(7, "day")) + |> where([sc], sc.inserted_at >= ago(^update_period_hours(), "hour")) |> select([sc], count(sc.address_hash)) end @doc """ Retrieves the query for the number of new addresses in the current week. """ - @spec weekly_new_addresses_number_query() :: Ecto.Query.t() - def weekly_new_addresses_number_query do + @spec new_addresses_number_query() :: Ecto.Query.t() + def new_addresses_number_query do Address - |> where([a], a.inserted_at >= ago(7, "day")) + |> where([a], a.inserted_at >= ago(^update_period_hours(), "hour")) |> select([a], count(a.hash)) end @doc """ Retrieves the query for the number of new tokens detected in the current week. """ - @spec weekly_new_tokens_number_query() :: Ecto.Query.t() - def weekly_new_tokens_number_query do + @spec new_tokens_number_query() :: Ecto.Query.t() + def new_tokens_number_query do Token - |> where([token], token.inserted_at >= ago(7, "day")) + |> where([token], token.inserted_at >= ago(^update_period_hours(), "hour")) |> select([token], count(token.contract_address_hash)) end @doc """ Retrieves the query for the number of new token transfers detected in the current week. """ - @spec weekly_new_token_transfers_number_query() :: Ecto.Query.t() - def weekly_new_token_transfers_number_query do + @spec new_token_transfers_number_query() :: Ecto.Query.t() + def new_token_transfers_number_query do TokenTransfer |> join(:inner, [tt], block in Block, on: block.number == tt.block_number) - |> where([tt, block], block.timestamp >= ago(7, "day")) + |> where([tt, block], block.timestamp >= ago(^update_period_hours(), "hour")) |> where([tt, block], block.consensus == true) |> select([tt, block], fragment("COUNT(*)")) end @@ -139,17 +139,17 @@ defmodule Explorer.Chain.Metrics.Queries do @doc """ Retrieves the query for the number of addresses initiated transactions in the current week. """ - @spec weekly_simplified_active_addresses_number_query() :: Ecto.Query.t() - def weekly_simplified_active_addresses_number_query do + @spec simplified_active_addresses_number_query() :: Ecto.Query.t() + def simplified_active_addresses_number_query do if DenormalizationHelper.transactions_denormalization_finished?() do Transaction - |> where([tx], tx.block_timestamp >= ago(7, "day")) + |> where([tx], tx.block_timestamp >= ago(^update_period_hours(), "hour")) |> where([tx], tx.block_consensus == true) |> select([tx], fragment("COUNT(DISTINCT(?))", tx.from_address_hash)) else Transaction |> join(:inner, [tx], block in assoc(tx, :block)) - |> where([tx, block], block.timestamp >= ago(7, "day")) + |> where([tx, block], block.timestamp >= ago(^update_period_hours(), "hour")) |> where([tx, block], block.consensus == true) |> select([tx], fragment("COUNT(DISTINCT(?))", tx.from_address_hash)) end @@ -159,12 +159,12 @@ defmodule Explorer.Chain.Metrics.Queries do Retrieves the query for the number of active EOA and smart-contract addresses (from/to/contract participated in transactions, internal transactions, token transfers) in the current week. This query is currently unused since the very low performance: it doesn't return results in 1 hour. """ - @spec weekly_active_addresses_number_query() :: Ecto.Query.t() - def weekly_active_addresses_number_query do + @spec active_addresses_number_query() :: Ecto.Query.t() + def active_addresses_number_query do transactions_query = if DenormalizationHelper.transactions_denormalization_finished?() do Transaction - |> where([tx], tx.block_timestamp >= ago(7, "day")) + |> where([tx], tx.block_timestamp >= ago(^update_period_hours(), "hour")) |> where([tx], tx.block_consensus == true) |> distinct(true) |> select([tx], %{ @@ -179,7 +179,7 @@ defmodule Explorer.Chain.Metrics.Queries do else Transaction |> join(:inner, [tx], block in assoc(tx, :block)) - |> where([tx, block], block.timestamp >= ago(7, "day")) + |> where([tx, block], block.timestamp >= ago(^update_period_hours(), "hour")) |> where([tx, block], block.consensus == true) |> distinct(true) |> select([tx, block], %{ @@ -197,7 +197,7 @@ defmodule Explorer.Chain.Metrics.Queries do if DenormalizationHelper.transactions_denormalization_finished?() do InternalTransaction |> join(:inner, [it], transaction in assoc(it, :transaction)) - |> where([it, tx], tx.block_timestamp >= ago(7, "day")) + |> where([it, tx], tx.block_timestamp >= ago(^update_period_hours(), "hour")) |> where([it, tx], tx.block_consensus == true) |> where([it, tx], tx.status == ^1) |> select([it, tx], %{ @@ -214,7 +214,7 @@ defmodule Explorer.Chain.Metrics.Queries do InternalTransaction |> join(:inner, [it], transaction in assoc(it, :transaction)) |> join(:inner, [tx], block in assoc(tx, :block)) - |> where([it, tx, block], tx.block_timestamp >= ago(7, "day")) + |> where([it, tx, block], tx.block_timestamp >= ago(^update_period_hours(), "hour")) |> where([it, tx, block], block.consensus == true) |> where([it, tx, block], tx.status == ^1) |> select([it, tx, block], %{ @@ -233,7 +233,7 @@ defmodule Explorer.Chain.Metrics.Queries do if DenormalizationHelper.transactions_denormalization_finished?() do TokenTransfer |> join(:inner, [tt], transaction in assoc(tt, :transaction)) - |> where([tt, tx], tx.block_timestamp >= ago(7, "day")) + |> where([tt, tx], tx.block_timestamp >= ago(^update_period_hours(), "hour")) |> where([tt, tx], tx.block_consensus == true) |> where([tt, tx], tx.status == ^1) |> select([tt, tx], %{ @@ -245,7 +245,7 @@ defmodule Explorer.Chain.Metrics.Queries do TokenTransfer |> join(:inner, [tt], transaction in assoc(tt, :transaction)) |> join(:inner, [tx], block in assoc(tx, :block)) - |> where([tt, tx, block], tx.block_timestamp >= ago(7, "day")) + |> where([tt, tx, block], tx.block_timestamp >= ago(^update_period_hours(), "hour")) |> where([tt, tx, block], block.consensus == true) |> where([tt, tx, block], tx.status == ^1) |> select([tt, tx, block], %{ @@ -266,4 +266,8 @@ defmodule Explorer.Chain.Metrics.Queries do select: fragment("COUNT(DISTINCT ?)", q.address_hash) ) end + + defp update_period_hours do + Application.get_env(:explorer, Explorer.Chain.Metrics)[:update_period_hours] + end end diff --git a/apps/explorer/lib/explorer/prometheus/instrumenter.ex b/apps/explorer/lib/explorer/prometheus/instrumenter.ex index ca3d619db93e..9eed93f68ffd 100644 --- a/apps/explorer/lib/explorer/prometheus/instrumenter.ex +++ b/apps/explorer/lib/explorer/prometheus/instrumenter.ex @@ -14,45 +14,45 @@ defmodule Explorer.Prometheus.Instrumenter do ] @gauge [ - name: :weekly_success_transactions_number, - help: "Number of successful transactions in the last 7 days", + name: :success_transactions_number, + help: "Number of successful transactions in the period (default is 1 day)", registry: :public ] @gauge [ - name: :weekly_deployed_smart_contracts_number, + name: :deployed_smart_contracts_number, help: - "Number of deployed smart-contracts (smart-contracts from internal transactions are not accounted) in the last 7 days", + "Number of deployed smart-contracts (smart-contracts from internal transactions are not accounted) in the period (default is 1 day)", registry: :public ] @gauge [ - name: :weekly_verified_smart_contracts_number, - help: "Number of verified smart-contracts in the last 7 days", + name: :verified_smart_contracts_number, + help: "Number of verified smart-contracts in the period (default is 1 day)", registry: :public ] @gauge [ - name: :weekly_new_addresses_number, - help: "Number of new wallet addresses in the last 7 days", + name: :new_addresses_number, + help: "Number of new wallet addresses in the period (default is 1 day)", registry: :public ] @gauge [ - name: :weekly_new_tokens_number, - help: "Number of new tokens detected in the last 7 days", + name: :new_tokens_number, + help: "Number of new tokens detected in the period (default is 1 day)", registry: :public ] @gauge [ - name: :weekly_new_token_transfers_number, - help: "Number of new token transfers detected in the last 7 days", + name: :new_token_transfers_number, + help: "Number of new token transfers detected in the period (default is 1 day)", registry: :public ] @gauge [ - name: :weekly_active_addresses_number, - help: "Number of active EOA addresses (participated in transactions in to/from) in the last 7 days", + name: :active_addresses_number, + help: "Number of active EOA addresses (participated in transactions in to/from) in the period (default is 1 day)", registry: :public ] @@ -64,31 +64,31 @@ defmodule Explorer.Prometheus.Instrumenter do result end - def weekly_success_transactions_number(number) do - Gauge.set([name: :weekly_success_transactions_number, registry: :public], number) + def success_transactions_number(number) do + Gauge.set([name: :success_transactions_number, registry: :public], number) end - def weekly_deployed_smart_contracts_number(number) do - Gauge.set([name: :weekly_deployed_smart_contracts_number, registry: :public], number) + def deployed_smart_contracts_number(number) do + Gauge.set([name: :deployed_smart_contracts_number, registry: :public], number) end - def weekly_verified_smart_contracts_number(number) do - Gauge.set([name: :weekly_verified_smart_contracts_number, registry: :public], number) + def verified_smart_contracts_number(number) do + Gauge.set([name: :verified_smart_contracts_number, registry: :public], number) end - def weekly_new_addresses_number(number) do - Gauge.set([name: :weekly_new_addresses_number, registry: :public], number) + def new_addresses_number(number) do + Gauge.set([name: :new_addresses_number, registry: :public], number) end - def weekly_new_tokens_number(number) do - Gauge.set([name: :weekly_new_tokens_number, registry: :public], number) + def new_tokens_number(number) do + Gauge.set([name: :new_tokens_number, registry: :public], number) end - def weekly_new_token_transfers_number(number) do - Gauge.set([name: :weekly_new_token_transfers_number, registry: :public], number) + def new_token_transfers_number(number) do + Gauge.set([name: :new_token_transfers_number, registry: :public], number) end - def weekly_simplified_active_addresses_number(number) do - Gauge.set([name: :weekly_active_addresses_number, registry: :public], number) + def simplified_active_addresses_number(number) do + Gauge.set([name: :active_addresses_number, registry: :public], number) end end diff --git a/config/runtime.exs b/config/runtime.exs index e5de09cf0cb0..29feadfdd4fb 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -596,7 +596,9 @@ config :explorer, Explorer.Chain.TokenTransfer, whitelisted_weth_contracts: ConfigHelper.parse_list_env_var("WHITELISTED_WETH_CONTRACTS", ""), weth_token_transfers_filtering_enabled: ConfigHelper.parse_bool_env_var("WETH_TOKEN_TRANSFERS_FILTERING_ENABLED") -config :explorer, Explorer.Chain.Metrics, disabled?: ConfigHelper.parse_bool_env_var("METRICS_DISABLE_PUBLIC", "false") +config :explorer, Explorer.Chain.Metrics, + enabled: ConfigHelper.parse_bool_env_var("PUBLIC_METRICS_ENABLED", "false"), + update_period_hours: ConfigHelper.parse_integer_env_var("PUBLIC_METRICS_UPDATE_PERIOD_HOURS", 24) ############### ### Indexer ### diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 02cccd8d4c22..3f880b70890a 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -398,4 +398,5 @@ TENDERLY_CHAIN_PATH= # WHITELISTED_WETH_CONTRACTS= # SANITIZE_INCORRECT_WETH_BATCH_SIZE=100 # SANITIZE_INCORRECT_WETH_CONCURRENCY=1 -# METRICS_DISABLE_PUBLIC= \ No newline at end of file +# PUBLIC_METRICS_ENABLED= +# PUBLIC_METRICS_UPDATE_PERIOD_HOURS= \ No newline at end of file From 0156ed4cf6ed5dc61b1dd46c38e5e440c77cd5f8 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 24 Jul 2024 13:45:33 +0300 Subject: [PATCH 018/363] chore: Add test for broadcasting fetched_bytecode message (#10244) * Test for broadcasting fetched_bytecode message * Process review comment --- .../api/v2/address_controller_test.exs | 52 +++++++++++++++++++ .../api/v2/token_controller_test.exs | 2 - 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs index fd127eab9215..d8feb0c0ecc9 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs @@ -7,6 +7,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do alias BlockScoutWeb.Models.UserFromAuth alias Explorer.{Chain, Repo, TestHelper} alias Explorer.Chain.Address.Counters + alias Explorer.Chain.Events.Subscriber alias Explorer.Chain.{ Address, @@ -24,6 +25,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do alias Explorer.Account.WatchlistAddress alias Explorer.Chain.Address.CurrentTokenBalance + alias Indexer.Fetcher.OnDemand.ContractCode, as: ContractCodeOnDemand alias Plug.Conn import Explorer.Chain, only: [hash_to_lower_case_string: 1] @@ -36,6 +38,18 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do setup :verify_on_exit! + setup %{json_rpc_named_arguments: json_rpc_named_arguments} do + mocked_json_rpc_named_arguments = Keyword.put(json_rpc_named_arguments, :transport, EthereumJSONRPC.Mox) + + start_supervised!({Task.Supervisor, name: Indexer.TaskSupervisor}) + + start_supervised!({ContractCodeOnDemand, [mocked_json_rpc_named_arguments, [name: ContractCodeOnDemand]]}) + + %{json_rpc_named_arguments: mocked_json_rpc_named_arguments} + + :ok + end + defp topic(topic_hex_string) do {:ok, topic} = Explorer.Chain.Hash.Full.cast(topic_hex_string) topic @@ -284,6 +298,44 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert response["watchlist_address_id"] == watchlist_address.id end + + test "broadcasts fetched_bytecode event", %{conn: conn} do + address = insert(:address) + address_hash = address.hash + string_address_hash = to_string(address.hash) + + contract_code = "0x6080" + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: id, + jsonrpc: "2.0", + method: "eth_getCode", + params: [^string_address_hash, "latest"] + } + ], + _ -> + {:ok, [%{id: id, result: contract_code}]} + end) + + topic = "addresses:#{address_hash}" + + {:ok, _reply, _socket} = + BlockScoutWeb.UserSocketV2 + |> socket("no_id", %{}) + |> subscribe_and_join(topic) + + request = get(conn, "/api/v2/addresses/#{address.hash}") + assert _response = json_response(request, 200) + + assert_receive %Phoenix.Socket.Message{ + payload: %{fetched_bytecode: ^contract_code}, + event: "fetched_bytecode", + topic: ^topic + }, + :timer.seconds(1) + end end describe "/addresses/{address_hash}/counters" do diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs index 814c55959fef..bf1a85281f1b 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs @@ -5,8 +5,6 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do import Mox - alias BlockScoutWeb.Notifier - alias Explorer.{Repo, TestHelper} alias Explorer.Chain.{Address, Token, Token.Instance, TokenTransfer} From 0c7e9bd421cdfeaffcb773f252880fb4928280ed Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Wed, 24 Jul 2024 15:16:20 +0200 Subject: [PATCH 019/363] fix: mud api format fixes (#10362) * fix: mud api format fixes * fix: invalid filter format * feat: preload ens & metadata for mud worlds --- .../controllers/api/v2/mud_controller.ex | 47 ++++++++++++++----- .../block_scout_web/views/api/v2/mud_view.ex | 17 +++++-- apps/explorer/lib/explorer/chain/mud.ex | 4 ++ 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex index 8f9d0810841a..6cbba16d32dc 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex @@ -10,7 +10,11 @@ defmodule BlockScoutWeb.API.V2.MudController do import BlockScoutWeb.PagingHelper, only: [mud_records_sorting: 1] - alias Explorer.Chain.{Data, Hash, Mud, Mud.Schema.FieldSchema, Mud.Table} + import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1] + import Explorer.MicroserviceInterfaces.Metadata, only: [maybe_preload_metadata: 1] + + alias Explorer.Chain + alias Explorer.Chain.{Address, Data, Hash, Mud, Mud.Schema.FieldSchema, Mud.Table} action_fallback(BlockScoutWeb.API.V2.FallbackController) @@ -25,6 +29,18 @@ defmodule BlockScoutWeb.API.V2.MudController do |> Mud.worlds_list() |> split_list_by_page() + world_addresses = + worlds + |> Chain.hashes_to_addresses( + necessity_by_association: %{ + :names => :optional, + :smart_contract => :optional, + :proxy_implementations => :optional + }, + api?: true + ) + |> Enum.into(%{}, &{&1.hash, &1}) + next_page_params = next_page_params(next_page, worlds, conn.query_params, fn item -> %{"world" => item} @@ -32,7 +48,14 @@ defmodule BlockScoutWeb.API.V2.MudController do conn |> put_status(200) - |> render(:worlds, %{worlds: worlds, next_page_params: next_page_params}) + |> render(:worlds, %{ + worlds: + worlds + |> Enum.map(fn world -> Map.get(world_addresses, world, %Address{hash: world}) end) + |> maybe_preload_ens() + |> maybe_preload_metadata(), + next_page_params: next_page_params + }) end @doc """ @@ -190,7 +213,7 @@ defmodule BlockScoutWeb.API.V2.MudController do ns |> String.pad_trailing(14, <<0>>) _ -> - nil + :error end end @@ -225,17 +248,19 @@ defmodule BlockScoutWeb.API.V2.MudController do <<1::256>> "0x" <> hex -> - bin = Base.decode16!(hex, case: :mixed) - # addresses are padded to 32 bytes with zeros on the right - if FieldSchema.type_of(schema.key_schema, field_idx) == 97 do - <<0::size(256 - byte_size(bin) * 8), bin::binary>> - else - <> + with {:ok, bin} <- Base.decode16(hex, case: :mixed) do + # addresses are padded to 32 bytes with zeros on the right + if FieldSchema.type_of(schema.key_schema, field_idx) == 97 do + <<0::size(256 - byte_size(bin) * 8), bin::binary>> + else + <> + end end dec -> - num = dec |> Integer.parse() |> elem(0) - <> + with {num, _} <- Integer.parse(dec) do + <> + end end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/mud_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/mud_view.ex index 128c748bb994..935646c129ad 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/mud_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/mud_view.ex @@ -1,7 +1,8 @@ defmodule BlockScoutWeb.API.V2.MudView do use BlockScoutWeb, :view - alias Explorer.Chain.{Mud, Mud.Table} + alias BlockScoutWeb.API.V2.Helper + alias Explorer.Chain.{Address, Mud, Mud.Table} @doc """ Function to render GET requests to `/api/v2/mud/worlds` endpoint. @@ -9,7 +10,7 @@ defmodule BlockScoutWeb.API.V2.MudView do @spec render(String.t(), map()) :: map() def render("worlds.json", %{worlds: worlds, next_page_params: next_page_params}) do %{ - items: worlds, + items: worlds |> Enum.map(&prepare_world_for_list/1), next_page_params: next_page_params } end @@ -18,9 +19,7 @@ defmodule BlockScoutWeb.API.V2.MudView do Function to render GET requests to `/api/v2/mud/worlds/count` endpoint. """ def render("count.json", %{count: count}) do - %{ - count: count - } + count end @doc """ @@ -62,6 +61,14 @@ defmodule BlockScoutWeb.API.V2.MudView do } end + defp prepare_world_for_list(%Address{} = address) do + %{ + "address" => Helper.address_with_info(address, address.hash), + "tx_count" => address.transactions_count, + "coin_balance" => if(address.fetched_coin_balance, do: address.fetched_coin_balance.value) + } + end + defp format_record(nil, _schema, _blocks), do: nil defp format_record(record, schema, blocks) do diff --git a/apps/explorer/lib/explorer/chain/mud.ex b/apps/explorer/lib/explorer/chain/mud.ex index c9832d90b4ee..1ee67ee95c08 100644 --- a/apps/explorer/lib/explorer/chain/mud.ex +++ b/apps/explorer/lib/explorer/chain/mud.ex @@ -154,6 +154,8 @@ defmodule Explorer.Chain.Mud do defp filter_tables_by_namespace(query, nil), do: query + defp filter_tables_by_namespace(query, :error), do: query |> where([tb], false) + defp filter_tables_by_namespace(query, namespace) do query |> where([tb], fragment("substring(? FROM 3 FOR 14)", tb.key0) == ^namespace) end @@ -220,6 +222,8 @@ defmodule Explorer.Chain.Mud do defp filter_records(query, _key_name, nil), do: query + defp filter_records(query, _key_name, :error), do: query |> where([r], false) + defp filter_records(query, :key0, key), do: query |> where([r], r.key0 == ^key) defp filter_records(query, :key1, key), do: query |> where([r], r.key1 == ^key) From c17d53063b450573dc1a166d81176c911280c866 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Wed, 24 Jul 2024 21:15:54 +0400 Subject: [PATCH 020/363] fix: Add missing condition to fetch_min_missing_block_cache (#10478) --- apps/explorer/lib/explorer/chain.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index f99aa779b120..57383ee02001 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2284,7 +2284,7 @@ defmodule Explorer.Chain do (SELECT b1.number FROM generate_series((?)::integer, (?)::integer) AS b1(number) WHERE NOT EXISTS - (SELECT 1 FROM blocks b2 WHERE b2.number=b1.number AND b2.consensus)) + (SELECT 1 FROM blocks b2 WHERE b2.number=b1.number AND b2.consensus AND NOT b2.refetch_needed)) """, ^from_block_number, ^to_block_number From df084cf6395dfef1565a908360c40d941d186b9d Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 25 Jul 2024 09:57:55 +0300 Subject: [PATCH 021/363] chore: Manage Solidityscan platform id via runtime variable (#10473) * Manage Solidityscan platform id via runtime variable * Extend common-blockscout.env --- .../lib/explorer/third_party_integrations/solidityscan.ex | 7 +++++-- config/runtime.exs | 1 + docker-compose/envs/common-blockscout.env | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/explorer/lib/explorer/third_party_integrations/solidityscan.ex b/apps/explorer/lib/explorer/third_party_integrations/solidityscan.ex index 27a53f5b6a2e..fc34f0339fee 100644 --- a/apps/explorer/lib/explorer/third_party_integrations/solidityscan.ex +++ b/apps/explorer/lib/explorer/third_party_integrations/solidityscan.ex @@ -6,7 +6,6 @@ defmodule Explorer.ThirdPartyIntegrations.SolidityScan do require Logger alias Explorer.Helper - @blockscout_platform_id "16" @recv_timeout 60_000 @doc """ @@ -37,12 +36,16 @@ defmodule Explorer.ThirdPartyIntegrations.SolidityScan do defp base_url(address_hash_string) do if chain_id() && api_key() do - "https://api.solidityscan.com/api/v1/quickscan/#{@blockscout_platform_id}/#{chain_id()}/#{address_hash_string}" + "https://api.solidityscan.com/api/v1/quickscan/#{platform_id()}/#{chain_id()}/#{address_hash_string}" else nil end end + defp platform_id do + Application.get_env(:explorer, __MODULE__)[:platform_id] + end + defp chain_id do Application.get_env(:explorer, __MODULE__)[:chain_id] end diff --git a/config/runtime.exs b/config/runtime.exs index 29feadfdd4fb..b4bfedcc9a89 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -457,6 +457,7 @@ config :explorer, Explorer.ThirdPartyIntegrations.Sourcify, repo_url: System.get_env("SOURCIFY_REPO_URL") || "https://repo.sourcify.dev/contracts" config :explorer, Explorer.ThirdPartyIntegrations.SolidityScan, + platform_id: System.get_env("SOLIDITYSCAN_PLATFORM_ID", "16"), chain_id: System.get_env("SOLIDITYSCAN_CHAIN_ID"), api_key: System.get_env("SOLIDITYSCAN_API_TOKEN") diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 3f880b70890a..709233596e83 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -378,6 +378,7 @@ SOURCIFY_SERVER_URL= SOURCIFY_REPO_URL= SHOW_TENDERLY_LINK=false TENDERLY_CHAIN_PATH= +# SOLIDITYSCAN_PLATFORM_ID= # SOLIDITYSCAN_CHAIN_ID= # SOLIDITYSCAN_API_TOKEN= # NOVES_FI_BASE_API_URL= From 709b0e6730c2c4c061f35d19be19718e311428b1 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Thu, 25 Jul 2024 12:54:41 +0400 Subject: [PATCH 022/363] chore: Add recon dependency (#10486) --- mix.exs | 3 ++- mix.lock | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index f3a29e2a0e40..f4633edf03ab 100644 --- a/mix.exs +++ b/mix.exs @@ -97,7 +97,8 @@ defmodule BlockScout.Mixfile do {:tesla, "~> 1.11.1"}, # Documentation {:ex_doc, "~> 0.34.1", only: :dev, runtime: false}, - {:number, "~> 1.0.3"} + {:number, "~> 1.0.3"}, + {:recon, "~> 2.5"} ] end end diff --git a/mix.lock b/mix.lock index 4fd99527594f..ddbed22d9441 100644 --- a/mix.lock +++ b/mix.lock @@ -122,6 +122,7 @@ "que": {:hex, :que, "0.10.1", "788ed0ec92ed69bdf9cfb29bf41a94ca6355b8d44959bd0669cf706e557ac891", [:mix], [{:ex_utils, "~> 0.1.6", [hex: :ex_utils, repo: "hexpm", optional: false]}, {:memento, "~> 0.3.0", [hex: :memento, repo: "hexpm", optional: false]}], "hexpm", "a737b365253e75dbd24b2d51acc1d851049e87baae08cd0c94e2bc5cd65088d5"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "ratio": {:hex, :ratio, "2.4.2", "c8518f3536d49b1b00d88dd20d49f8b11abb7819638093314a6348139f14f9f9", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:numbers, "~> 5.2.0", [hex: :numbers, repo: "hexpm", optional: false]}], "hexpm", "441ef6f73172a3503de65ccf1769030997b0d533b1039422f1e5e0e0b4cbf89e"}, + "recon": {:hex, :recon, "2.5.5", "c108a4c406fa301a529151a3bb53158cadc4064ec0c5f99b03ddb8c0e4281bdf", [:mix, :rebar3], [], "hexpm", "632a6f447df7ccc1a4a10bdcfce71514412b16660fe59deca0fcf0aa3c054404"}, "redix": {:hex, :redix, "1.5.1", "a2386971e69bf23630fb3a215a831b5478d2ee7dc9ea7ac811ed89186ab5d7b7", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:nimble_options, "~> 0.5.0 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "85224eb2b683c516b80d472eb89b76067d5866913bf0be59d646f550de71f5c4"}, "remote_ip": {:hex, :remote_ip, "1.2.0", "fb078e12a44414f4cef5a75963c33008fe169b806572ccd17257c208a7bc760f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2ff91de19c48149ce19ed230a81d377186e4412552a597d6a5137373e5877cb7"}, "rustler_precompiled": {:hex, :rustler_precompiled, "0.7.1", "ecadf02cc59a0eccbaed6c1937303a5827fbcf60010c541595e6d3747d3d0f9f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "b9e4657b99a1483ea31502e1d58c464bedebe9028808eda45c3a429af4550c66"}, From f5515b5cfe81c77bc6f5404aed9ba2238c0f3be6 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:55:26 +0300 Subject: [PATCH 023/363] feat: Add INDEXER_DISABLE_REPLACED_TRANSACTION_FETCHER env (#10485) * feat: Add INDEXER_DISABLE_REPLACED_TRANSACTION_FETCHER env * Add new env to envs/common-blockscout.env --- apps/indexer/config/config.exs | 2 -- config/runtime.exs | 3 +++ docker-compose/envs/common-blockscout.env | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/indexer/config/config.exs b/apps/indexer/config/config.exs index 426ea5cc1839..5ce9ab28a24e 100644 --- a/apps/indexer/config/config.exs +++ b/apps/indexer/config/config.exs @@ -5,8 +5,6 @@ import Config config :indexer, ecto_repos: [Explorer.Repo] -# config :indexer, Indexer.Fetcher.ReplacedTransaction.Supervisor, disabled?: true - config :indexer, Indexer.Tracer, service: :indexer, adapter: SpandexDatadog.Adapter, diff --git a/config/runtime.exs b/config/runtime.exs index b4bfedcc9a89..671cfcd0a02d 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -709,6 +709,9 @@ config :indexer, Indexer.Block.Realtime.Supervisor, config :indexer, Indexer.Block.Catchup.Supervisor, enabled: !ConfigHelper.parse_bool_env_var("DISABLE_CATCHUP_INDEXER") +config :indexer, Indexer.Fetcher.ReplacedTransaction.Supervisor, + disabled?: ConfigHelper.parse_bool_env_var("INDEXER_DISABLE_REPLACED_TRANSACTION_FETCHER") + config :indexer, Indexer.Fetcher.TokenInstance.Realtime.Supervisor, disabled?: ConfigHelper.parse_bool_env_var("INDEXER_DISABLE_TOKEN_INSTANCE_REALTIME_FETCHER") diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 709233596e83..1d7d0f2509a0 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -156,6 +156,7 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_DISABLE_BLOCK_REWARD_FETCHER= # INDEXER_DISABLE_EMPTY_BLOCKS_SANITIZER= # INDEXER_DISABLE_WITHDRAWALS_FETCHER= +# INDEXER_DISABLE_REPLACED_TRANSACTION_FETCHER= # INDEXER_CATCHUP_BLOCKS_BATCH_SIZE= # INDEXER_CATCHUP_BLOCKS_CONCURRENCY= # INDEXER_CATCHUP_BLOCK_INTERVAL= From 368355be33c3b372382c3aec166518357fed5aa2 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Thu, 25 Jul 2024 14:45:47 +0400 Subject: [PATCH 024/363] fix: Move recon dep to explorer mix.exs (#10487) --- apps/explorer/mix.exs | 3 ++- mix.exs | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index 931d487de3fd..43f8c93bed70 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -120,7 +120,8 @@ defmodule Explorer.Mixfile do {:hammer_backend_redis, "~> 6.1"}, {:logger_json, "~> 5.1"}, {:typed_ecto_schema, "~> 0.4.1", runtime: false}, - {:ueberauth, "~> 0.7"} + {:ueberauth, "~> 0.7"}, + {:recon, "~> 2.5"} ] end diff --git a/mix.exs b/mix.exs index f4633edf03ab..f3a29e2a0e40 100644 --- a/mix.exs +++ b/mix.exs @@ -97,8 +97,7 @@ defmodule BlockScout.Mixfile do {:tesla, "~> 1.11.1"}, # Documentation {:ex_doc, "~> 0.34.1", only: :dev, runtime: false}, - {:number, "~> 1.0.3"}, - {:recon, "~> 2.5"} + {:number, "~> 1.0.3"} ] end end From 48089a65d3820020bd45c44dcc570b922d751cac Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 25 Jul 2024 18:29:27 +0300 Subject: [PATCH 025/363] chore: Return ex_abi core lib dependency (#10470) * chore: Return ex_abi core lib dependency * Add ex_keccak dep explicitly --- apps/block_scout_web/mix.exs | 1 + apps/ethereum_jsonrpc/mix.exs | 5 ++--- apps/explorer/mix.exs | 1 + apps/indexer/mix.exs | 1 + mix.lock | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs index 5db0828bffa4..7d458bf16b58 100644 --- a/apps/block_scout_web/mix.exs +++ b/apps/block_scout_web/mix.exs @@ -97,6 +97,7 @@ defmodule BlockScoutWeb.Mixfile do {:ex_cldr, "~> 2.38"}, {:ex_cldr_numbers, "~> 2.33"}, {:ex_cldr_units, "~> 3.17"}, + {:ex_keccak, "~> 0.7.5"}, {:cldr_utils, "~> 2.3"}, {:ex_machina, "~> 2.1", only: [:test]}, {:explorer, in_umbrella: true}, diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs index 2dbbca47f4f7..063d8aff882c 100644 --- a/apps/ethereum_jsonrpc/mix.exs +++ b/apps/ethereum_jsonrpc/mix.exs @@ -62,6 +62,7 @@ defmodule EthereumJsonrpc.MixProject do {:credo, "~> 1.5", only: :test, runtime: false}, # Static Type Checking {:dialyxir, "~> 1.1", only: [:dev, :test], runtime: false}, + {:ex_keccak, "~> 0.7.5"}, # JSONRPC HTTP Post calls {:httpoison, "~> 2.0"}, # Decode/Encode JSON for JSONRPC @@ -77,9 +78,7 @@ defmodule EthereumJsonrpc.MixProject do # Convert unix timestamps in JSONRPC to DateTimes {:timex, "~> 3.7.1"}, # Encode/decode function names and arguments - # todo: return to hex.pm once https://github.com/poanetwork/ex_abi/pull/170 is merged - # {:ex_abi, "~> 0.4"}, - {:ex_abi, git: "https://github.com/fedor-ivn/ex_abi", branch: "fix-type-decoder-huge-lists", override: true}, + {:ex_abi, "~> 0.8"}, # `:verify_fun` for `Socket.Web.connect` {:ssl_verify_fun, "~> 1.1"}, # `EthereumJSONRPC.WebSocket` diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index 43f8c93bed70..534148a77a37 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -78,6 +78,7 @@ defmodule Explorer.Mixfile do {:ecto_sql, "~> 3.3"}, # JSONRPC access to query smart contracts {:ethereum_jsonrpc, in_umbrella: true}, + {:ex_keccak, "~> 0.7.5"}, # Data factory for testing {:ex_machina, "~> 2.3", only: [:test]}, {:exvcr, "~> 0.10", only: :test}, diff --git a/apps/indexer/mix.exs b/apps/indexer/mix.exs index 74226a9065e0..e01d7be44d89 100644 --- a/apps/indexer/mix.exs +++ b/apps/indexer/mix.exs @@ -53,6 +53,7 @@ defmodule Indexer.MixProject do {:ethereum_jsonrpc, in_umbrella: true}, # Brotli compression/decompression {:brotli, "~> 0.3.2"}, + {:ex_keccak, "~> 0.7.5"}, # RLP encoding {:ex_rlp, "~> 0.6.0"}, # Importing to database diff --git a/mix.lock b/mix.lock index ddbed22d9441..cea29a9fdf80 100644 --- a/mix.lock +++ b/mix.lock @@ -42,7 +42,7 @@ "ecto_sql": {:hex, :ecto_sql, "3.11.3", "4eb7348ff8101fbc4e6bbc5a4404a24fecbe73a3372d16569526b0cf34ebc195", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16 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", "e5f36e3d736b99c7fee3e631333b8394ade4bafe9d96d35669fca2d81c2be928"}, "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, - "ex_abi": {:git, "https://github.com/fedor-ivn/ex_abi", "44b9f9bcf98203ca26923d396e7621b6f0970f2c", [branch: "fix-type-decoder-huge-lists"]}, + "ex_abi": {:hex, :ex_abi, "0.8.0", "bb08827bd8d71dbb311c69ac55a008669dfabe2ce5b58d65f97c08c0aba60ec6", [:mix], [{:ex_keccak, "~> 0.7.5", [hex: :ex_keccak, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "bbdae12c186aeeb4c53dd7c7c57f457923602db315aa1f66d7427467c8ad77af"}, "ex_cldr": {:hex, :ex_cldr, "2.39.2", "4a3a77797da8f900369822ea9353adfa035a5bbbbfff09b2d3d1b6fa461768e3", [:mix], [{:cldr_utils, "~> 2.25", [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", "02fd8913ef28d1b2a4190fd8016c2dec1f2291c9ce56c17d7649848c0261a6eb"}, "ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.16.1", "29317f533cb5ec046d04523256cca4090291e9157028f28731395149b06ff8b2", [:mix], [{:ex_cldr, "~> 2.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "095d5e973bf0ee066dd1153990d10cb6fa6d8ff0e028295bdce7a7821c70a0e4"}, "ex_cldr_lists": {:hex, :ex_cldr_lists, "2.11.0", "1d39e75f0e493ccc95adfc85c55b4ca34f0771626350ce326d9ab8813d91444e", [: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", "8132b30a5506ae8a09e5c9a21c23fd60c8837ce6c3a1de9966d813eb78951695"}, From f59191935fdb24f1a555b9d09f83f51c9b9bcc74 Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Fri, 26 Jul 2024 13:13:26 +0300 Subject: [PATCH 026/363] fix: extend block search range for `getblocknobytime` method (#10475) * fix: extend block search range for `getblocknobytime` method * perf: utlize `blocks_timestamp_index` to reduce search space of a query * fix: always return block number from db if block timestamp is equal given timestamp * fix: use `:before` to found last block for the day * refactor: order blocks by ascending timestamp * chore: fix formatting --- .../api/rpc/block_controller_test.exs | 32 +++++++++++ apps/explorer/lib/explorer/chain.ex | 55 +++++++++++++------ .../chain/transaction/history/historian.ex | 2 +- .../transaction/history/historian_test.exs | 24 ++++---- apps/explorer/test/explorer/chain_test.exs | 12 ++++ 5 files changed, 94 insertions(+), 31 deletions(-) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/block_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/block_controller_test.exs index 3060f17102c3..b5a21440c73a 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/block_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/block_controller_test.exs @@ -344,6 +344,38 @@ defmodule BlockScoutWeb.API.RPC.BlockControllerTest do schema = resolve_getblockreward_schema() assert :ok = ExJsonSchema.Validator.validate(schema, response) end + + test "returns any nearest block within arbitrary range of time", %{conn: conn} do + timestamp_string = "1617020209" + {:ok, timestamp} = Chain.param_to_block_timestamp(timestamp_string) + block = insert(:block, timestamp: timestamp) + + {timestamp_int, _} = Integer.parse(timestamp_string) + + timestamp_in_the_past_str = + (timestamp_int - 2 * 60) + |> to_string() + + expected_result = %{ + "blockNumber" => "#{block.number}" + } + + assert response = + conn + |> get("/api", %{ + "module" => "block", + "action" => "getblocknobytime", + "timestamp" => "#{timestamp_in_the_past_str}", + "closest" => "after" + }) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + schema = resolve_getblockreward_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + end end defp resolve_getblockreward_schema() do diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 57383ee02001..7991bf07f564 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2500,18 +2500,21 @@ defmodule Explorer.Chain do end @doc """ - Finds the block number closest to a given timestamp, with a one-minute buffer, optionally - adjusting based on whether the block should be before or after the timestamp. + Finds the block number closest to a given timestamp, optionally adjusting + based on whether the block should be before or after the timestamp. ## Parameters - - `given_timestamp`: The timestamp for which the closest block number is being sought. - - `closest`: A direction indicator (`:before` or `:after`) specifying whether the block number - returned should be before or after the given timestamp. - - `from_api`: A boolean flag indicating whether to use the replica database or the primary one - for the query. + - `given_timestamp`: The timestamp for which the closest block number is + being sought. + - `closest`: A direction indicator (`:before` or `:after`) specifying + whether the block number returned should be before or after the + given timestamp. + - `from_api`: A boolean flag indicating whether to use the replica database + or the primary one for the query. ## Returns - - `{:ok, block_number}` where `block_number` is the block number closest to the specified timestamp. + - `{:ok, block_number}` where `block_number` is the block number closest to + the specified timestamp. - `{:error, :not_found}` if no block is found within the specified criteria. """ @spec timestamp_to_block_number(DateTime.t(), :before | :after, boolean()) :: @@ -2519,19 +2522,35 @@ defmodule Explorer.Chain do def timestamp_to_block_number(given_timestamp, closest, from_api) do {:ok, t} = Timex.format(given_timestamp, "%Y-%m-%d %H:%M:%S", :strftime) - inner_query = + consensus_blocks_query = from( block in Block, - where: block.consensus == true, - where: - fragment("? <= TO_TIMESTAMP(?, 'YYYY-MM-DD HH24:MI:SS') + (1 * interval '1 minute')", block.timestamp, ^t), - where: - fragment("? >= TO_TIMESTAMP(?, 'YYYY-MM-DD HH24:MI:SS') - (1 * interval '1 minute')", block.timestamp, ^t) + where: block.consensus == true ) + gt_timestamp_query = + from( + block in consensus_blocks_query, + where: fragment("? >= TO_TIMESTAMP(?, 'YYYY-MM-DD HH24:MI:SS')", block.timestamp, ^t), + order_by: [asc: block.timestamp], + limit: 1, + select: block + ) + + lt_timestamp_query = + from( + block in consensus_blocks_query, + where: fragment("? <= TO_TIMESTAMP(?, 'YYYY-MM-DD HH24:MI:SS')", block.timestamp, ^t), + order_by: [desc: block.timestamp], + limit: 1, + select: block + ) + + union_query = lt_timestamp_query |> subquery() |> union(^gt_timestamp_query) + query = from( - block in subquery(inner_query), + block in subquery(union_query), select: block, order_by: fragment("abs(extract(epoch from (? - TO_TIMESTAMP(?, 'YYYY-MM-DD HH24:MI:SS'))))", block.timestamp, ^t), @@ -2564,11 +2583,11 @@ defmodule Explorer.Chain do end :after -> - if DateTime.compare(timestamp, given_timestamp) == :lt || + if DateTime.compare(timestamp, given_timestamp) == :gt || DateTime.compare(timestamp, given_timestamp) == :eq do - BlockNumberHelper.next_block_number(number) - else number + else + BlockNumberHelper.next_block_number(number) end end end diff --git a/apps/explorer/lib/explorer/chain/transaction/history/historian.ex b/apps/explorer/lib/explorer/chain/transaction/history/historian.ex index de731e61c872..bb6e25ab624a 100644 --- a/apps/explorer/lib/explorer/chain/transaction/history/historian.ex +++ b/apps/explorer/lib/explorer/chain/transaction/history/historian.ex @@ -38,7 +38,7 @@ defmodule Explorer.Chain.Transaction.History.Historian do from_api = false with {:ok, min_block} <- Chain.timestamp_to_block_number(earliest, :after, from_api), - {:ok, max_block} <- Chain.timestamp_to_block_number(latest, :after, from_api) do + {:ok, max_block} <- Chain.timestamp_to_block_number(latest, :before, from_api) do record = min_block |> compile_records_in_range(max_block) diff --git a/apps/explorer/test/explorer/chain/transaction/history/historian_test.exs b/apps/explorer/test/explorer/chain/transaction/history/historian_test.exs index 39c49953251c..6a8015dda77e 100644 --- a/apps/explorer/test/explorer/chain/transaction/history/historian_test.exs +++ b/apps/explorer/test/explorer/chain/transaction/history/historian_test.exs @@ -18,14 +18,14 @@ defmodule Explorer.Chain.Transaction.History.HistorianTest do describe "compile_records/1" do test "fetches transactions, total gas, total fee from blocks mined in the past num_days" do blocks = [ + # 1970-01-02 00:00:00 + insert(:block, timestamp: DateTime.from_unix!(days_to_secs(1))), + # 1970-01-03 00:00:60 insert(:block, timestamp: DateTime.from_unix!(days_to_secs(2) + 60)), # 1970-01-03 04:00:00 - insert(:block, timestamp: DateTime.from_unix!(days_to_secs(2) + 4 * 60 * 60)), - - # 1970-01-02 00:00:00 - insert(:block, timestamp: DateTime.from_unix!(days_to_secs(1))) + insert(:block, timestamp: DateTime.from_unix!(days_to_secs(2) + 4 * 60 * 60)) ] transaction_1 = insert(:transaction) |> with_block(Enum.at(blocks, 0), status: :ok) @@ -38,31 +38,31 @@ defmodule Explorer.Chain.Transaction.History.HistorianTest do assert {:ok, ^expected} = Historian.compile_records(1) - total_gas_used_1 = Decimal.add(transaction_1.gas_used, transaction_2.gas_used) + total_gas_used_01_03 = Decimal.add(transaction_2.gas_used, transaction_3.gas_used) %Explorer.Chain.Wei{value: transaction_1_gas_price_value} = transaction_1.gas_price %Explorer.Chain.Wei{value: transaction_2_gas_price_value} = transaction_2.gas_price %Explorer.Chain.Wei{value: transaction_3_gas_price_value} = transaction_3.gas_price - total_fee_1 = + total_fee_01_03 = Decimal.add( - Decimal.mult(transaction_1.gas_used, transaction_1_gas_price_value), - Decimal.mult(transaction_2.gas_used, transaction_2_gas_price_value) + Decimal.mult(transaction_2.gas_used, transaction_2_gas_price_value), + Decimal.mult(transaction_3.gas_used, transaction_3_gas_price_value) ) - total_fee_3 = Decimal.mult(transaction_3.gas_used, transaction_3_gas_price_value) + total_fee_01_02 = Decimal.mult(transaction_1.gas_used, transaction_1_gas_price_value) expected = [ %{date: ~D[1970-01-04], number_of_transactions: 0, gas_used: 0, total_fee: 0}, - %{date: ~D[1970-01-03], gas_used: total_gas_used_1, number_of_transactions: 2, total_fee: total_fee_1} + %{date: ~D[1970-01-03], gas_used: total_gas_used_01_03, number_of_transactions: 2, total_fee: total_fee_01_03} ] assert {:ok, ^expected} = Historian.compile_records(2) expected = [ %{date: ~D[1970-01-04], number_of_transactions: 0, gas_used: 0, total_fee: 0}, - %{date: ~D[1970-01-03], gas_used: total_gas_used_1, number_of_transactions: 2, total_fee: total_fee_1}, - %{date: ~D[1970-01-02], gas_used: transaction_3.gas_used, number_of_transactions: 1, total_fee: total_fee_3} + %{date: ~D[1970-01-03], gas_used: total_gas_used_01_03, number_of_transactions: 2, total_fee: total_fee_01_03}, + %{date: ~D[1970-01-02], gas_used: transaction_1.gas_used, number_of_transactions: 1, total_fee: total_fee_01_02} ] assert {:ok, ^expected} = Historian.compile_records(3) diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 482d520c6373..3f242e726724 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -4335,4 +4335,16 @@ defmodule Explorer.ChainTest do } end end + + describe "timestamp_to_block_number/3" do + test "returns correct block number when given timestamp is equal to block timestamp" do + timestamp = DateTime.from_unix!(60 * 60 * 24 * 1, :second) + block = insert(:block, timestamp: timestamp) + expected = {:ok, block.number} + + assert ^expected = Chain.timestamp_to_block_number(timestamp, :after, true) + + assert ^expected = Chain.timestamp_to_block_number(timestamp, :before, true) + end + end end From c6ff374d8ca83742b42f5896886194ce532bccce Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Mon, 29 Jul 2024 12:52:35 +0300 Subject: [PATCH 027/363] feat: Backfiller for omitted WETH transfers (#10466) * feat: Backfiller for omitted WETH transfers * todo: add token balance update * done RestoreOmittedWETHTransfers migrator * Remove dbg * remove dbg * Fix credo * Process review comments --- apps/explorer/config/config.exs | 1 + apps/explorer/config/runtime/test.exs | 1 + apps/explorer/lib/explorer/application.ex | 2 + apps/explorer/lib/explorer/chain.ex | 16 +- apps/explorer/lib/explorer/chain/log.ex | 28 +- apps/explorer/lib/explorer/helper.ex | 7 + .../restore_omitted_weth_transfers.ex | 269 ++++++++++++++++++ .../lib/indexer/transform/token_transfers.ex | 7 +- config/runtime.exs | 5 + docker-compose/envs/common-blockscout.env | 3 + 10 files changed, 329 insertions(+), 10 deletions(-) create mode 100644 apps/explorer/lib/explorer/migrator/restore_omitted_weth_transfers.ex diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index 03977df83bae..27a109720e01 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -125,6 +125,7 @@ config :explorer, Explorer.Migrator.TokenTransferTokenType, enabled: true config :explorer, Explorer.Migrator.SanitizeIncorrectWETHTokenTransfers, enabled: true config :explorer, Explorer.Migrator.TransactionBlockConsensus, enabled: true config :explorer, Explorer.Migrator.TokenTransferBlockConsensus, enabled: true +config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, enabled: true config :explorer, Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand, enabled: true diff --git a/apps/explorer/config/runtime/test.exs b/apps/explorer/config/runtime/test.exs index 56c02a846467..1dae56dfe007 100644 --- a/apps/explorer/config/runtime/test.exs +++ b/apps/explorer/config/runtime/test.exs @@ -47,6 +47,7 @@ config :explorer, Explorer.Migrator.TokenTransferTokenType, enabled: false config :explorer, Explorer.Migrator.SanitizeIncorrectWETHTokenTransfers, enabled: false config :explorer, Explorer.Migrator.TransactionBlockConsensus, enabled: false config :explorer, Explorer.Migrator.TokenTransferBlockConsensus, enabled: false +config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, 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 c82ebcb201bb..a92c10a8c131 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -57,6 +57,7 @@ defmodule Explorer.Application do Supervisor.child_spec({Task.Supervisor, name: Explorer.LookUpSmartContractSourcesTaskSupervisor}, id: LookUpSmartContractSourcesTaskSupervisor ), + Supervisor.child_spec({Task.Supervisor, name: Explorer.WETHMigratorSupervisor}, id: WETHMigratorSupervisor), Explorer.SmartContract.SolcDownloader, Explorer.SmartContract.VyperDownloader, {Registry, keys: :duplicate, name: Registry.ChainEvents, id: Registry.ChainEvents}, @@ -140,6 +141,7 @@ defmodule Explorer.Application do configure(Explorer.Migrator.SanitizeIncorrectWETHTokenTransfers), configure(Explorer.Migrator.TransactionBlockConsensus), configure(Explorer.Migrator.TokenTransferBlockConsensus), + configure(Explorer.Migrator.RestoreOmittedWETHTransfers), configure_chain_type_dependent_process(Explorer.Chain.Cache.StabilityValidatorsCounters, :stability) ] |> List.flatten() diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 7991bf07f564..391899bb38b8 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -3810,8 +3810,8 @@ defmodule Explorer.Chain do end end - @spec token_from_address_hash_exists?(Hash.Address.t(), [api?]) :: boolean() - def token_from_address_hash_exists?(%Hash{byte_count: unquote(Hash.Address.byte_count())} = hash, options) do + @spec token_from_address_hash_exists?(Hash.Address.t() | String.t(), [api?]) :: boolean() + def token_from_address_hash_exists?(hash, options) do query = from( t in Token, @@ -4595,6 +4595,18 @@ defmodule Explorer.Chain do |> TypeDecoder.decode_raw(types) end + @spec get_token_types([String.t()]) :: [{Hash.Address.t(), String.t()}] + def get_token_types(hashes) do + query = + from( + token in Token, + where: token.contract_address_hash in ^hashes, + select: {token.contract_address_hash, token.type} + ) + + Repo.all(query) + end + @spec get_token_type(Hash.Address.t()) :: String.t() | nil def get_token_type(hash) do query = diff --git a/apps/explorer/lib/explorer/chain/log.ex b/apps/explorer/lib/explorer/chain/log.ex index 46510f8e349f..7147f2098693 100644 --- a/apps/explorer/lib/explorer/chain/log.ex +++ b/apps/explorer/lib/explorer/chain/log.ex @@ -6,8 +6,8 @@ defmodule Explorer.Chain.Log do require Logger alias ABI.{Event, FunctionSelector} - alias Explorer.Chain - alias Explorer.Chain.{Address, Block, ContractMethod, Data, Hash, Log, Transaction} + alias Explorer.{Chain, Repo} + alias Explorer.Chain.{Address, Block, ContractMethod, Data, Hash, Log, TokenTransfer, Transaction} alias Explorer.Chain.SmartContract.Proxy alias Explorer.SmartContract.SigProviderInterface @@ -353,4 +353,28 @@ defmodule Explorer.Chain.Log do |> Chain.join_associations(necessity_by_association) |> Chain.select_repo(options).all() end + + @doc """ + Streams unfetched WETH token transfers. + Returns `{:ok, any()} | {:error, any()}` (return spec taken from Ecto.Repo.transaction/2) + Expects each_fun, a function to be called on each fetched log. It should accept log and return anything (return value will be discarded anyway) + """ + @spec stream_unfetched_weth_token_transfers((Log.t() -> any())) :: {:ok, any()} | {:error, any()} + def stream_unfetched_weth_token_transfers(each_fun) do + env = Application.get_env(:explorer, Explorer.Chain.TokenTransfer) + + __MODULE__ + |> where([log], log.address_hash in ^env[:whitelisted_weth_contracts]) + |> where( + [log], + log.first_topic == ^TokenTransfer.weth_deposit_signature() or + log.first_topic == ^TokenTransfer.weth_withdrawal_signature() + ) + |> join(:left, [log], tt in TokenTransfer, + on: log.block_hash == tt.block_hash and log.transaction_hash == tt.transaction_hash and log.index == tt.log_index + ) + |> where([log, tt], is_nil(tt.transaction_hash)) + |> select([log], log) + |> Repo.stream_each(each_fun) + end end diff --git a/apps/explorer/lib/explorer/helper.ex b/apps/explorer/lib/explorer/helper.ex index bc09fc0bc422..5f33572cfa89 100644 --- a/apps/explorer/lib/explorer/helper.ex +++ b/apps/explorer/lib/explorer/helper.ex @@ -8,6 +8,7 @@ defmodule Explorer.Helper do alias Explorer.Chain.Data import Ecto.Query, only: [where: 3] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] @max_safe_integer round(:math.pow(2, 63)) - 1 @@ -181,4 +182,10 @@ defmodule Explorer.Helper do true -> :eq end end + + def truncate_address_hash(nil), do: burn_address_hash_string() + + def truncate_address_hash("0x000000000000000000000000" <> truncated_hash) do + "0x#{truncated_hash}" + end end diff --git a/apps/explorer/lib/explorer/migrator/restore_omitted_weth_transfers.ex b/apps/explorer/lib/explorer/migrator/restore_omitted_weth_transfers.ex new file mode 100644 index 000000000000..edc89f2c50b8 --- /dev/null +++ b/apps/explorer/lib/explorer/migrator/restore_omitted_weth_transfers.ex @@ -0,0 +1,269 @@ +defmodule Explorer.Migrator.RestoreOmittedWETHTransfers do + @moduledoc """ + Inserts missed WETH token transfers + """ + + use GenServer, restart: :transient + + alias Explorer.{Chain, Helper} + alias Explorer.Chain.{Log, TokenTransfer} + alias Explorer.Migrator.MigrationStatus + + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + + require Logger + + @enqueue_busy_waiting_timeout 500 + @migration_timeout 250 + @migration_name "restore_omitted_weth_transfers" + + def start_link(_) do + GenServer.start_link(__MODULE__, :ok, name: __MODULE__) + end + + @impl true + def init(_) do + {:ok, %{}, {:continue, :check_env}} + end + + @impl true + def handle_continue(:check_env, state) do + list = Application.get_env(:explorer, Explorer.Chain.TokenTransfer)[:whitelisted_weth_contracts] + + cond do + Enum.empty?(list) -> + {:stop, :normal, state} + + check_token_types(list) -> + {:noreply, %{}, {:continue, :check_migration_status}} + + true -> + Logger.error("Stopping") + {:stop, :normal, state} + end + end + + @impl true + def handle_continue(:check_migration_status, state) do + case MigrationStatus.get_status(@migration_name) do + "completed" -> + {:stop, :normal, state} + + _ -> + MigrationStatus.set_status(@migration_name, "started") + {:noreply, %{}, {:continue, :ok}} + end + end + + @impl true + def handle_continue(:ok, _state) do + %{ref: ref} = + Task.async(fn -> + Log.stream_unfetched_weth_token_transfers(&enqueue_if_queue_is_not_full/1) + end) + + to_insert = + Application.get_env(:explorer, Explorer.Chain.TokenTransfer)[:whitelisted_weth_contracts] + |> Enum.map(fn contract_address_hash_string -> + if !Chain.token_from_address_hash_exists?(contract_address_hash_string, []) do + %{ + contract_address_hash: contract_address_hash_string, + type: "ERC-20" + } + end + end) + |> Enum.reject(&is_nil/1) + + if !Enum.empty?(to_insert) do + Chain.import(%{tokens: %{params: to_insert}}) + end + + Process.send_after(self(), :migrate, @migration_timeout) + + {:noreply, %{queue: [], current_concurrency: 0, stream_ref: ref, stream_is_over: false}} + end + + defp enqueue_if_queue_is_not_full(log) do + if GenServer.call(__MODULE__, :not_full?) do + GenServer.cast(__MODULE__, {:append_to_queue, log}) + else + :timer.sleep(@enqueue_busy_waiting_timeout) + + enqueue_if_queue_is_not_full(log) + end + end + + @impl true + def handle_call(:not_full?, _from, %{queue: queue} = state) do + {:reply, Enum.count(queue) < max_queue_size(), state} + end + + @impl true + def handle_cast({:append_to_queue, log}, %{queue: queue} = state) do + {:noreply, %{state | queue: [log | queue]}} + end + + @impl true + def handle_info(:migrate, %{queue: [], stream_is_over: true, current_concurrency: current_concurrency} = state) do + if current_concurrency > 0 do + {:noreply, state} + else + Logger.info("RestoreOmittedWETHTransfers migration is complete.") + + MigrationStatus.set_status(@migration_name, "completed") + {:stop, :normal, state} + end + end + + # fetch token balances + @impl true + def handle_info(:migrate, %{queue: queue, current_concurrency: current_concurrency} = state) do + if Enum.count(queue) > 0 and current_concurrency < concurrency() do + to_take = batch_size() * (concurrency() - current_concurrency) + {to_process, remainder} = Enum.split(queue, to_take) + + spawned_tasks = + to_process + |> Enum.chunk_every(batch_size()) + |> Enum.map(fn batch -> + run_task(batch) + end) + + if Enum.empty?(remainder) do + Process.send_after(self(), :migrate, migration_timeout()) + else + Process.send(self(), :migrate, []) + end + + {:noreply, %{state | queue: remainder, current_concurrency: current_concurrency + Enum.count(spawned_tasks)}} + else + Process.send_after(self(), :migrate, migration_timeout()) + {:noreply, state} + end + end + + @impl true + def handle_info({ref, _answer}, %{stream_ref: ref} = state) do + {:noreply, %{state | stream_is_over: true}} + end + + @impl true + def handle_info({ref, _answer}, %{current_concurrency: counter} = state) do + Process.demonitor(ref, [:flush]) + Process.send(self(), :migrate, []) + {:noreply, %{state | current_concurrency: counter - 1}} + end + + @impl true + def handle_info({:DOWN, ref, :process, _pid, _reason}, %{stream_ref: ref} = state) do + {:noreply, %{state | stream_is_over: true}} + end + + @impl true + def handle_info({:DOWN, _ref, :process, _pid, _reason}, %{current_concurrency: counter} = state) do + Process.send(self(), :migrate, []) + {:noreply, %{state | current_concurrency: counter - 1}} + end + + defp migrate_batch(batch) do + {token_transfers, token_balances} = + batch + |> Enum.map(fn log -> + with %{second_topic: second_topic, third_topic: nil, fourth_topic: nil, data: data} + when not is_nil(second_topic) <- + log, + [amount] <- Helper.decode_data(data, [{:uint, 256}]) do + {from_address_hash, to_address_hash, balance_address_hash} = + if log.first_topic == TokenTransfer.weth_deposit_signature() do + to_address_hash = Helper.truncate_address_hash(to_string(second_topic)) + {burn_address_hash_string(), to_address_hash, to_address_hash} + else + from_address_hash = Helper.truncate_address_hash(to_string(second_topic)) + {from_address_hash, burn_address_hash_string(), from_address_hash} + end + + token_transfer = %{ + amount: Decimal.new(amount || 0), + block_number: log.block_number, + block_hash: log.block_hash, + log_index: log.index, + from_address_hash: from_address_hash, + to_address_hash: to_address_hash, + token_contract_address_hash: log.address_hash, + transaction_hash: log.transaction_hash, + token_ids: nil, + token_type: "ERC-20" + } + + token_balance = %{ + address_hash: balance_address_hash, + token_contract_address_hash: log.address_hash, + block_number: log.block_number, + token_id: nil, + token_type: "ERC-20" + } + + {token_transfer, token_balance} + else + _ -> + Logger.error( + "Failed to decode log: (tx_hash, block_hash, index) = #{to_string(log.transaction_hash)}, #{to_string(log.block_hash)}, #{to_string(log.index)}" + ) + + nil + end + end) + |> Enum.reject(&is_nil/1) + |> Enum.unzip() + + current_token_balances = + token_balances + |> Enum.group_by(fn %{ + address_hash: address_hash, + token_contract_address_hash: token_contract_address_hash + } -> + {address_hash, token_contract_address_hash} + end) + |> Enum.map(fn {_, grouped_address_token_balances} -> + Enum.max_by(grouped_address_token_balances, fn %{block_number: block_number} -> block_number end) + end) + |> Enum.sort_by(&{&1.token_contract_address_hash, &1.address_hash}) + + if !Enum.empty?(token_transfers) do + Chain.import(%{ + token_transfers: %{params: token_transfers}, + address_token_balances: %{params: token_balances}, + address_current_token_balances: %{ + params: current_token_balances + } + }) + end + end + + defp run_task(batch) do + Task.Supervisor.async_nolink(Explorer.WETHMigratorSupervisor, fn -> + migrate_batch(batch) + end) + end + + defp check_token_types(token_address_hashes) do + token_address_hashes + |> Chain.get_token_types() + |> Enum.reduce(true, fn {token_hash, token_type}, acc -> + if token_type == "ERC-20" do + acc + else + Logger.error("Wrong token type of #{to_string(token_hash)}: #{token_type}") + false + end + end) + end + + def concurrency, do: Application.get_env(:explorer, __MODULE__)[:concurrency] + + def batch_size, do: Application.get_env(:explorer, __MODULE__)[:batch_size] + + def migration_timeout, do: Application.get_env(:explorer, __MODULE__)[:timeout] + + def max_queue_size, do: concurrency() * batch_size() * 2 +end diff --git a/apps/indexer/lib/indexer/transform/token_transfers.ex b/apps/indexer/lib/indexer/transform/token_transfers.ex index 761323d1a4b5..161e8dfdcb2e 100644 --- a/apps/indexer/lib/indexer/transform/token_transfers.ex +++ b/apps/indexer/lib/indexer/transform/token_transfers.ex @@ -6,6 +6,7 @@ defmodule Indexer.Transform.TokenTransfers do require Logger import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + import Explorer.Helper, only: [truncate_address_hash: 1] alias Explorer.{Helper, Repo} alias Explorer.Chain.{Hash, Token, TokenTransfer} @@ -483,12 +484,6 @@ defmodule Indexer.Transform.TokenTransfers do end end - defp truncate_address_hash(nil), do: burn_address_hash_string() - - defp truncate_address_hash("0x000000000000000000000000" <> truncated_hash) do - "0x#{truncated_hash}" - end - defp encode_address_hash(binary) do "0x" <> Base.encode16(binary, case: :lower) end diff --git a/config/runtime.exs b/config/runtime.exs index 671cfcd0a02d..10941697c35e 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -583,6 +583,11 @@ config :explorer, Explorer.Migrator.SanitizeIncorrectWETHTokenTransfers, batch_size: ConfigHelper.parse_integer_env_var("SANITIZE_INCORRECT_WETH_BATCH_SIZE", 100), concurrency: ConfigHelper.parse_integer_env_var("SANITIZE_INCORRECT_WETH_CONCURRENCY", 1) +config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, + concurrency: ConfigHelper.parse_integer_env_var("MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_CONCURRENCY", 5), + batch_size: ConfigHelper.parse_integer_env_var("MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_BATCH_SIZE", 50), + timeout: ConfigHelper.parse_time_env_var("MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_TIMEOUT", "250ms") + config :explorer, Explorer.Chain.BridgedToken, eth_omni_bridge_mediator: System.get_env("BRIDGED_TOKENS_ETH_OMNI_BRIDGE_MEDIATOR"), bsc_omni_bridge_mediator: System.get_env("BRIDGED_TOKENS_BSC_OMNI_BRIDGE_MEDIATOR"), diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 1d7d0f2509a0..489e32ebfed1 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -374,6 +374,9 @@ EIP_1559_ELASTICITY_MULTIPLIER=2 # TOKEN_TRANSFER_TOKEN_TYPE_MIGRATION_CONCURRENCY= # SANITIZE_INCORRECT_NFT_BATCH_SIZE= # SANITIZE_INCORRECT_NFT_CONCURRENCY= +# MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_CONCURRENCY= +# MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_BATCH_SIZE= +# MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_TIMEOUT= SOURCIFY_INTEGRATION_ENABLED=false SOURCIFY_SERVER_URL= SOURCIFY_REPO_URL= From 2984bcf5b6576e506e14b01ff46a63400861892a Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Mon, 29 Jul 2024 15:20:15 +0300 Subject: [PATCH 028/363] fix: Error on internal transactions CSV export (#10495) --- ...dress_internal_transaction_csv_exporter.ex | 3 +- ...internal_transaction_csv_exporter_test.exs | 118 ++++++++++++++++++ 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain/csv_export/address_internal_transaction_csv_exporter.ex b/apps/explorer/lib/explorer/chain/csv_export/address_internal_transaction_csv_exporter.ex index 855abf7de969..4265961e98ad 100644 --- a/apps/explorer/lib/explorer/chain/csv_export/address_internal_transaction_csv_exporter.ex +++ b/apps/explorer/lib/explorer/chain/csv_export/address_internal_transaction_csv_exporter.ex @@ -95,7 +95,8 @@ defmodule Explorer.Chain.CSVExport.AddressInternalTransactionCsvExporter do internal_transaction.input, internal_transaction.output, internal_transaction.error, - gas_price && gas_price |> Wei.mult(internal_transaction.gas_used) |> Wei.to(:wei) + gas_price && internal_transaction.gas_used && + gas_price |> Wei.mult(internal_transaction.gas_used) |> Wei.to(:wei) ] end) diff --git a/apps/explorer/test/explorer/chain/csv_export/address_internal_transaction_csv_exporter_test.exs b/apps/explorer/test/explorer/chain/csv_export/address_internal_transaction_csv_exporter_test.exs index ac644a081382..d9d1a1a0e496 100644 --- a/apps/explorer/test/explorer/chain/csv_export/address_internal_transaction_csv_exporter_test.exs +++ b/apps/explorer/test/explorer/chain/csv_export/address_internal_transaction_csv_exporter_test.exs @@ -196,5 +196,123 @@ defmodule Explorer.Chain.CSVExport.AddressInternalTransactionCsvExporterTest do assert Enum.count(result) == 600 end + + test "don't fall on is_nil(gas_used)" do + address = insert(:address) + + transaction = + :transaction + |> insert() + |> with_block() + + internal_transaction = + insert(:internal_transaction, + index: 1, + transaction: transaction, + from_address: address, + block_number: transaction.block_number, + block_hash: transaction.block_hash, + block_index: 1, + transaction_index: transaction.index, + error: "reverted", + gas_used: nil, + output: nil + ) + + from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime) + to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) + + res = + address.hash + |> AddressInternalTransactionCsvExporter.export(from_period, to_period) + |> Enum.to_list() + |> Enum.drop(1) + + [result] = + res + |> Enum.map(fn [ + [[], transaction_hash], + _, + [[], index], + _, + [[], block_number], + _, + [[], block_hash], + _, + [[], block_index], + _, + [[], transaction_index], + _, + [[], timestamp], + _, + [[], from_address_hash], + _, + [[], to_address_hash], + _, + [[], created_contract_address_hash], + _, + [[], type], + _, + [[], call_type], + _, + [[], gas], + _, + [[], gas_used], + _, + [[], value], + _, + [[], input], + _, + [[], output], + _, + [[], error], + _, + [[], fee], + _ + ] -> + %{ + transaction_hash: transaction_hash, + index: index, + block_number: block_number, + block_index: block_index, + block_hash: block_hash, + transaction_index: transaction_index, + timestamp: timestamp, + from_address_hash: from_address_hash, + to_address_hash: to_address_hash, + created_contract_address_hash: created_contract_address_hash, + type: type, + call_type: call_type, + gas: gas, + gas_used: gas_used, + value: value, + input: input, + output: output, + error: error, + fee: fee + } + end) + + assert result.transaction_hash == to_string(internal_transaction.transaction_hash) + assert result.index == to_string(internal_transaction.index) + assert result.block_number == to_string(internal_transaction.block_number) + assert result.block_index == to_string(internal_transaction.block_index) + assert result.block_hash == to_string(internal_transaction.block_hash) + assert result.transaction_index == to_string(internal_transaction.transaction_index) + assert result.timestamp + assert result.from_address_hash == Address.checksum(internal_transaction.from_address_hash) + assert result.to_address_hash == Address.checksum(internal_transaction.to_address_hash) + assert result.created_contract_address_hash == to_string(internal_transaction.created_contract_address_hash) + assert result.type == to_string(internal_transaction.type) + assert result.call_type == to_string(internal_transaction.call_type) + assert result.gas == to_string(internal_transaction.gas) + assert result.gas_used == "" + assert result.value == internal_transaction.value |> Wei.to(:wei) |> to_string() + assert result.input == to_string(internal_transaction.input) + assert result.output == to_string(internal_transaction.output) + assert result.error == to_string(internal_transaction.error) + + assert result.fee == "" + end end end From a6beceb1dce31e4c00ea961d5f252405d40724f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 22:34:27 +0300 Subject: [PATCH 029/363] chore(deps): bump jason from 1.4.3 to 1.4.4 (#10504) Bumps [jason](https://github.com/michalmuskala/jason) from 1.4.3 to 1.4.4. - [Release notes](https://github.com/michalmuskala/jason/releases) - [Changelog](https://github.com/michalmuskala/jason/blob/v1.4.4/CHANGELOG.md) - [Commits](https://github.com/michalmuskala/jason/compare/v1.4.3...v1.4.4) --- updated-dependencies: - dependency-name: jason dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index cea29a9fdf80..3a4ee6363179 100644 --- a/mix.lock +++ b/mix.lock @@ -72,7 +72,7 @@ "httpoison": {:hex, :httpoison, "2.2.1", "87b7ed6d95db0389f7df02779644171d7319d319178f6680438167d7b69b1f3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "51364e6d2f429d80e14fe4b5f8e39719cacd03eb3f9a9286e61e216feac2d2df"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"}, - "jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"}, + "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm", "fc3499fed7a726995aa659143a248534adc754ebd16ccd437cd93b649a95091f"}, "junit_formatter": {:hex, :junit_formatter, "3.4.0", "d0e8db6c34dab6d3c4154c3b46b21540db1109ae709d6cf99ba7e7a2ce4b1ac2", [:mix], [], "hexpm", "bb36e2ae83f1ced6ab931c4ce51dd3dbef1ef61bb4932412e173b0cfa259dacd"}, "logger_file_backend": {:hex, :logger_file_backend, "0.0.14", "774bb661f1c3fed51b624d2859180c01e386eb1273dc22de4f4a155ef749a602", [:mix], [], "hexpm", "071354a18196468f3904ef09413af20971d55164267427f6257b52cfba03f9e6"}, From 0b7db73150b078c43d5a3c5c6f7e562ec6b5fc46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 22:36:10 +0300 Subject: [PATCH 030/363] chore(deps): bump ex_cldr from 2.39.2 to 2.40.0 (#10503) Bumps [ex_cldr](https://github.com/elixir-cldr/cldr) from 2.39.2 to 2.40.0. - [Release notes](https://github.com/elixir-cldr/cldr/releases) - [Changelog](https://github.com/elixir-cldr/cldr/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-cldr/cldr/compare/v2.39.2...v2.40.0) --- updated-dependencies: - dependency-name: ex_cldr dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 3a4ee6363179..5364c7e932b9 100644 --- a/mix.lock +++ b/mix.lock @@ -43,7 +43,7 @@ "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_abi": {:hex, :ex_abi, "0.8.0", "bb08827bd8d71dbb311c69ac55a008669dfabe2ce5b58d65f97c08c0aba60ec6", [:mix], [{:ex_keccak, "~> 0.7.5", [hex: :ex_keccak, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "bbdae12c186aeeb4c53dd7c7c57f457923602db315aa1f66d7427467c8ad77af"}, - "ex_cldr": {:hex, :ex_cldr, "2.39.2", "4a3a77797da8f900369822ea9353adfa035a5bbbbfff09b2d3d1b6fa461768e3", [:mix], [{:cldr_utils, "~> 2.25", [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", "02fd8913ef28d1b2a4190fd8016c2dec1f2291c9ce56c17d7649848c0261a6eb"}, + "ex_cldr": {:hex, :ex_cldr, "2.40.0", "624717778dbf0a8cd307f1576eabbd44470c16190172abf293fed24150440a5a", [:mix], [{:cldr_utils, "~> 2.28", [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", "113394b6dd23aaf7912da583aab103d9cf082b9821bc4a6e287543a895af7cb4"}, "ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.16.1", "29317f533cb5ec046d04523256cca4090291e9157028f28731395149b06ff8b2", [:mix], [{:ex_cldr, "~> 2.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "095d5e973bf0ee066dd1153990d10cb6fa6d8ff0e028295bdce7a7821c70a0e4"}, "ex_cldr_lists": {:hex, :ex_cldr_lists, "2.11.0", "1d39e75f0e493ccc95adfc85c55b4ca34f0771626350ce326d9ab8813d91444e", [: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", "8132b30a5506ae8a09e5c9a21c23fd60c8837ce6c3a1de9966d813eb78951695"}, "ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.33.1", "49dc6e77e6d9ad22660aaa2480a7408ad3aedfbe517e4e83e5fe3a7bf5345770", [: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.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, "~> 2.16", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "c003bfaa3fdee6bab5195f128b94038c2ce1cf4879a759eef431dd075d9a5dac"}, From a2625803c831fb86e38ffe0e28d94bfd697914ce Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 30 Jul 2024 16:33:39 +0300 Subject: [PATCH 031/363] chore: Set Geth as default JSON RPC Variant (#10509) --- apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/variant.ex | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/variant.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/variant.ex index b1256b3ce5dc..d6fabf09ea43 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/variant.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/variant.ex @@ -113,16 +113,9 @@ defmodule EthereumJSONRPC.Variant do # credo:disable-for-next-line defp get_default_variant do case Application.get_env(:explorer, :chain_type) do - :optimism -> "geth" - :polygon_zkevm -> "geth" - :zetachain -> "geth" - :shibarium -> "geth" - :stability -> "geth" - :zksync -> "geth" - :arbitrum -> "geth" :rsk -> "rsk" :filecoin -> "filecoin" - _ -> "nethermind" + _ -> "geth" end end end From 51f9d846fa8cd95f8ed730976f1ce439815e9906 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Thu, 1 Aug 2024 19:09:46 +0400 Subject: [PATCH 032/363] fix: Fix address_to_logs consensus filtering (#10528) --- apps/explorer/lib/explorer/chain.ex | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 391899bb38b8..7612ca8adc97 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -377,26 +377,15 @@ defmodule Explorer.Chain do to_block = to_block(options) base = - if DenormalizationHelper.transactions_denormalization_finished?() do - from(log in Log, - order_by: [desc: log.block_number, desc: log.index], - where: log.address_hash == ^address_hash, - limit: ^paging_options.page_size, - select: log, - inner_join: transaction in assoc(log, :transaction), - where: transaction.block_consensus == true - ) - else - from(log in Log, - order_by: [desc: log.block_number, desc: log.index], - where: log.address_hash == ^address_hash, - limit: ^paging_options.page_size, - select: log, - inner_join: block in Block, - on: block.hash == log.block_hash, - where: block.consensus == true - ) - end + from(log in Log, + order_by: [desc: log.block_number, desc: log.index], + where: log.address_hash == ^address_hash, + limit: ^paging_options.page_size, + select: log, + inner_join: block in Block, + on: block.hash == log.block_hash, + where: block.consensus == true + ) preloaded_query = if csv_export? do From 51e0246cb6f9937063816852e3c28f70b222f454 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:26:55 +0300 Subject: [PATCH 033/363] chore(deps): bump photoswipe in /apps/block_scout_web/assets (#10540) Bumps [photoswipe](https://github.com/dimsemenov/Photoswipe) from 5.4.3 to 5.4.4. - [Release notes](https://github.com/dimsemenov/Photoswipe/releases) - [Commits](https://github.com/dimsemenov/Photoswipe/commits/v5.4.4) --- updated-dependencies: - dependency-name: photoswipe dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 90243dfa5ad9..4b16aa97f074 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -54,7 +54,7 @@ "path-parser": "^6.1.0", "phoenix": "file:../../../deps/phoenix", "phoenix_html": "file:../../../deps/phoenix_html", - "photoswipe": "^5.4.3", + "photoswipe": "^5.4.4", "pikaday": "^1.8.2", "popper.js": "^1.14.7", "reduce-reducers": "^1.0.4", @@ -13649,9 +13649,9 @@ "link": true }, "node_modules/photoswipe": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.4.3.tgz", - "integrity": "sha512-9UC6oJBK4oXFZ5HcdlcvGkfEHsVrmE4csUdCQhEjHYb3PvPLO3PG7UhnPuOgjxwmhq5s17Un5NUdum01LgBDng==", + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.4.4.tgz", + "integrity": "sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==", "engines": { "node": ">= 0.12.0" } @@ -28275,9 +28275,9 @@ "version": "file:../../../deps/phoenix_html" }, "photoswipe": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.4.3.tgz", - "integrity": "sha512-9UC6oJBK4oXFZ5HcdlcvGkfEHsVrmE4csUdCQhEjHYb3PvPLO3PG7UhnPuOgjxwmhq5s17Un5NUdum01LgBDng==" + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.4.4.tgz", + "integrity": "sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==" }, "picocolors": { "version": "1.0.1", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 6650a3dde3ac..cce029bd8641 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -66,7 +66,7 @@ "path-parser": "^6.1.0", "phoenix": "file:../../../deps/phoenix", "phoenix_html": "file:../../../deps/phoenix_html", - "photoswipe": "^5.4.3", + "photoswipe": "^5.4.4", "pikaday": "^1.8.2", "popper.js": "^1.14.7", "reduce-reducers": "^1.0.4", From 8c5e5a5979531628b8b4e76c2bfb46c0a8fa8a82 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:27:21 +0300 Subject: [PATCH 034/363] chore(deps): bump mixpanel-browser in /apps/block_scout_web/assets (#10538) Bumps [mixpanel-browser](https://github.com/mixpanel/mixpanel-js) from 2.53.0 to 2.54.1. - [Release notes](https://github.com/mixpanel/mixpanel-js/releases) - [Changelog](https://github.com/mixpanel/mixpanel-js/blob/master/CHANGELOG.md) - [Commits](https://github.com/mixpanel/mixpanel-js/compare/v2.53.0...v2.54.1) --- updated-dependencies: - dependency-name: mixpanel-browser dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 4b16aa97f074..34142f32a740 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -46,7 +46,7 @@ "lodash.reduce": "^4.6.0", "luxon": "^3.4.4", "malihu-custom-scrollbar-plugin": "3.1.5", - "mixpanel-browser": "^2.53.0", + "mixpanel-browser": "^2.54.1", "moment": "^2.30.1", "nanomorph": "^5.4.0", "numeral": "^2.0.6", @@ -12968,9 +12968,9 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" }, "node_modules/mixpanel-browser": { - "version": "2.53.0", - "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.53.0.tgz", - "integrity": "sha512-8U7zCTT82yCIH2vfdCvs0ZRWlCgyHMuU4jtC6yOAiNUR4HhnQYk7re/o2GnhfdvYtkPxdda60/3eH1igUlIXuw==", + "version": "2.54.1", + "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.54.1.tgz", + "integrity": "sha512-1FDs/E0UHPJLry/J0r3pBXrnEhJnz7H2CqzWWZgmdDAm3WiMva69X/qFIqcW3TOmHPaprJVfgwzqt6MJavMrHQ==", "dependencies": { "rrweb": "2.0.0-alpha.13" } @@ -27762,9 +27762,9 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" }, "mixpanel-browser": { - "version": "2.53.0", - "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.53.0.tgz", - "integrity": "sha512-8U7zCTT82yCIH2vfdCvs0ZRWlCgyHMuU4jtC6yOAiNUR4HhnQYk7re/o2GnhfdvYtkPxdda60/3eH1igUlIXuw==", + "version": "2.54.1", + "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.54.1.tgz", + "integrity": "sha512-1FDs/E0UHPJLry/J0r3pBXrnEhJnz7H2CqzWWZgmdDAm3WiMva69X/qFIqcW3TOmHPaprJVfgwzqt6MJavMrHQ==", "requires": { "rrweb": "2.0.0-alpha.13" } diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index cce029bd8641..faef26287b4a 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -58,7 +58,7 @@ "lodash.reduce": "^4.6.0", "luxon": "^3.4.4", "malihu-custom-scrollbar-plugin": "3.1.5", - "mixpanel-browser": "^2.53.0", + "mixpanel-browser": "^2.54.1", "moment": "^2.30.1", "nanomorph": "^5.4.0", "numeral": "^2.0.6", From 7dca8ef9a1c7f03a0bbd13e607ae7e912a4cece1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:27:43 +0300 Subject: [PATCH 035/363] chore(deps): bump @amplitude/analytics-browser (#10535) Bumps [@amplitude/analytics-browser](https://github.com/amplitude/Amplitude-TypeScript) from 2.8.1 to 2.9.3. - [Release notes](https://github.com/amplitude/Amplitude-TypeScript/releases) - [Commits](https://github.com/amplitude/Amplitude-TypeScript/compare/@amplitude/analytics-browser@2.8.1...@amplitude/analytics-browser@2.9.3) --- updated-dependencies: - dependency-name: "@amplitude/analytics-browser" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 138 +++++++++--------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 34142f32a740..5b14cf870413 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.8.1", + "@amplitude/analytics-browser": "^2.9.3", "@fortawesome/fontawesome-free": "^6.5.2", "@tarekraafat/autocomplete.js": "^10.2.7", "@walletconnect/web3-provider": "^1.8.0", @@ -116,14 +116,14 @@ } }, "node_modules/@amplitude/analytics-browser": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.8.1.tgz", - "integrity": "sha512-gvCvGQUbHjrmQQma5qBDQo25TD/p+TCoWX20XfGJC0rR51MCWacbYQZ6CQrECXte7c6XNzc11hC6Kf50bO0Sxw==", - "dependencies": { - "@amplitude/analytics-client-common": "^2.2.1", - "@amplitude/analytics-core": "^2.2.8", - "@amplitude/analytics-types": "^2.5.1", - "@amplitude/plugin-page-view-tracking-browser": "^2.2.13", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.9.3.tgz", + "integrity": "sha512-PnkJrJGRUfdE9nFgQZbWvUjNGnbcFojQROfj7KQeKGJTzQZXMt+xafmfXWDnhsf0JRgbk273Wh7Z6WNXxS1seA==", + "dependencies": { + "@amplitude/analytics-client-common": "^2.2.4", + "@amplitude/analytics-core": "^2.3.0", + "@amplitude/analytics-types": "^2.6.0", + "@amplitude/plugin-page-view-tracking-browser": "^2.2.17", "tslib": "^2.4.1" } }, @@ -133,20 +133,20 @@ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" }, "node_modules/@amplitude/analytics-client-common": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.2.1.tgz", - "integrity": "sha512-JXpLjKgP7LiBMZF5eMjtiPiSPW8FxvF4bHubDg26Ck2buBUQvrBq6D4VyOky0m1zG7WA4vOoLANu3sWfF02/7Q==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.2.4.tgz", + "integrity": "sha512-+zOW3/Yb4LzK1DfhFCnSOcb8vgeZgIQffLM6yrgzKGedtQOnwDQeKTDI3aaMzckXQPVcJs1M6bJcT/l5TmAkDw==", "dependencies": { "@amplitude/analytics-connector": "^1.4.8", - "@amplitude/analytics-core": "^2.2.8", - "@amplitude/analytics-types": "^2.5.1", + "@amplitude/analytics-core": "^2.3.0", + "@amplitude/analytics-types": "^2.6.0", "tslib": "^2.4.1" } }, "node_modules/@amplitude/analytics-client-common/node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" }, "node_modules/@amplitude/analytics-connector": { "version": "1.5.0", @@ -154,38 +154,38 @@ "integrity": "sha512-T8mOYzB9RRxckzhL0NTHwdge9xuFxXEOplC8B1Y3UX3NHa3BLh7DlBUZlCOwQgMc2nxDfnSweDL5S3bhC+W90g==" }, "node_modules/@amplitude/analytics-core": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.2.8.tgz", - "integrity": "sha512-T4ZFk1LUD+4z02XMSWK4qPvMREUTgNtEoYQc6BvNyfOx1Ih21qyJLFcdSw05gL26JuuuYZWFkpHxxKm8IoZh8Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.3.0.tgz", + "integrity": "sha512-Knafvocs29cPOHM3GHyBkP4nXUrh/oXnj2fULoSyh/03Bt63UPoyQCNS/EtQB/dOUhapTP35ZU9yZnrY+jvndQ==", "dependencies": { - "@amplitude/analytics-types": "^2.5.1", + "@amplitude/analytics-types": "^2.6.0", "tslib": "^2.4.1" } }, "node_modules/@amplitude/analytics-core/node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" }, "node_modules/@amplitude/analytics-types": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.5.1.tgz", - "integrity": "sha512-xGuLiHRLv5z3RSAHiICjzHK4TjUZJ7aHr/jM0XhSgD/V1YdmG/s8y1UAWGI2Stsxm2x0DWNOyEpjYkGohlnXtA==" + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.6.0.tgz", + "integrity": "sha512-7MSENvLCTGjec7K45JT+RcOuoPTCvq1MMq/HRLiQK/BMR4taX7f/uXldEc8b//o+ZZP45IBqFroR7Bl8LwJQrQ==" }, "node_modules/@amplitude/plugin-page-view-tracking-browser": { - "version": "2.2.13", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.2.13.tgz", - "integrity": "sha512-EYvG4RTyNqIGlq3J+UnrTRRj75xp3nOSETeInPQbnHhiPaCzxTjWrb9URkzy2IYSyBSn4KEQkZegmzhWXxlvvw==", + "version": "2.2.17", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.2.17.tgz", + "integrity": "sha512-s19LUuPMvOhvM/36XOUeVSMGOhtSOPA6bhhrR4x/lEsyylsS+rfi6/fILd2B5gojplXKMvVlJXEP+t1fjIr1HA==", "dependencies": { - "@amplitude/analytics-client-common": "^2.2.1", - "@amplitude/analytics-types": "^2.5.1", + "@amplitude/analytics-client-common": "^2.2.4", + "@amplitude/analytics-types": "^2.6.0", "tslib": "^2.4.1" } }, "node_modules/@amplitude/plugin-page-view-tracking-browser/node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" }, "node_modules/@ampproject/remapping": { "version": "2.2.0", @@ -17981,14 +17981,14 @@ "dev": true }, "@amplitude/analytics-browser": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.8.1.tgz", - "integrity": "sha512-gvCvGQUbHjrmQQma5qBDQo25TD/p+TCoWX20XfGJC0rR51MCWacbYQZ6CQrECXte7c6XNzc11hC6Kf50bO0Sxw==", - "requires": { - "@amplitude/analytics-client-common": "^2.2.1", - "@amplitude/analytics-core": "^2.2.8", - "@amplitude/analytics-types": "^2.5.1", - "@amplitude/plugin-page-view-tracking-browser": "^2.2.13", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.9.3.tgz", + "integrity": "sha512-PnkJrJGRUfdE9nFgQZbWvUjNGnbcFojQROfj7KQeKGJTzQZXMt+xafmfXWDnhsf0JRgbk273Wh7Z6WNXxS1seA==", + "requires": { + "@amplitude/analytics-client-common": "^2.2.4", + "@amplitude/analytics-core": "^2.3.0", + "@amplitude/analytics-types": "^2.6.0", + "@amplitude/plugin-page-view-tracking-browser": "^2.2.17", "tslib": "^2.4.1" }, "dependencies": { @@ -18000,20 +18000,20 @@ } }, "@amplitude/analytics-client-common": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.2.1.tgz", - "integrity": "sha512-JXpLjKgP7LiBMZF5eMjtiPiSPW8FxvF4bHubDg26Ck2buBUQvrBq6D4VyOky0m1zG7WA4vOoLANu3sWfF02/7Q==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.2.4.tgz", + "integrity": "sha512-+zOW3/Yb4LzK1DfhFCnSOcb8vgeZgIQffLM6yrgzKGedtQOnwDQeKTDI3aaMzckXQPVcJs1M6bJcT/l5TmAkDw==", "requires": { "@amplitude/analytics-connector": "^1.4.8", - "@amplitude/analytics-core": "^2.2.8", - "@amplitude/analytics-types": "^2.5.1", + "@amplitude/analytics-core": "^2.3.0", + "@amplitude/analytics-types": "^2.6.0", "tslib": "^2.4.1" }, "dependencies": { "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" } } }, @@ -18023,40 +18023,40 @@ "integrity": "sha512-T8mOYzB9RRxckzhL0NTHwdge9xuFxXEOplC8B1Y3UX3NHa3BLh7DlBUZlCOwQgMc2nxDfnSweDL5S3bhC+W90g==" }, "@amplitude/analytics-core": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.2.8.tgz", - "integrity": "sha512-T4ZFk1LUD+4z02XMSWK4qPvMREUTgNtEoYQc6BvNyfOx1Ih21qyJLFcdSw05gL26JuuuYZWFkpHxxKm8IoZh8Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.3.0.tgz", + "integrity": "sha512-Knafvocs29cPOHM3GHyBkP4nXUrh/oXnj2fULoSyh/03Bt63UPoyQCNS/EtQB/dOUhapTP35ZU9yZnrY+jvndQ==", "requires": { - "@amplitude/analytics-types": "^2.5.1", + "@amplitude/analytics-types": "^2.6.0", "tslib": "^2.4.1" }, "dependencies": { "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" } } }, "@amplitude/analytics-types": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.5.1.tgz", - "integrity": "sha512-xGuLiHRLv5z3RSAHiICjzHK4TjUZJ7aHr/jM0XhSgD/V1YdmG/s8y1UAWGI2Stsxm2x0DWNOyEpjYkGohlnXtA==" + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.6.0.tgz", + "integrity": "sha512-7MSENvLCTGjec7K45JT+RcOuoPTCvq1MMq/HRLiQK/BMR4taX7f/uXldEc8b//o+ZZP45IBqFroR7Bl8LwJQrQ==" }, "@amplitude/plugin-page-view-tracking-browser": { - "version": "2.2.13", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.2.13.tgz", - "integrity": "sha512-EYvG4RTyNqIGlq3J+UnrTRRj75xp3nOSETeInPQbnHhiPaCzxTjWrb9URkzy2IYSyBSn4KEQkZegmzhWXxlvvw==", + "version": "2.2.17", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.2.17.tgz", + "integrity": "sha512-s19LUuPMvOhvM/36XOUeVSMGOhtSOPA6bhhrR4x/lEsyylsS+rfi6/fILd2B5gojplXKMvVlJXEP+t1fjIr1HA==", "requires": { - "@amplitude/analytics-client-common": "^2.2.1", - "@amplitude/analytics-types": "^2.5.1", + "@amplitude/analytics-client-common": "^2.2.4", + "@amplitude/analytics-types": "^2.6.0", "tslib": "^2.4.1" }, "dependencies": { "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" } } }, diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index faef26287b4a..d967946166e8 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.5.2", - "@amplitude/analytics-browser": "^2.8.1", + "@amplitude/analytics-browser": "^2.9.3", "@tarekraafat/autocomplete.js": "^10.2.7", "@walletconnect/web3-provider": "^1.8.0", "assert": "^2.1.0", From dcb582299a6c1c2b168a1041fa0a8315be3903b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:28:02 +0300 Subject: [PATCH 036/363] chore(deps): bump highlight.js in /apps/block_scout_web/assets (#10533) Bumps [highlight.js](https://github.com/highlightjs/highlight.js) from 11.9.0 to 11.10.0. - [Release notes](https://github.com/highlightjs/highlight.js/releases) - [Changelog](https://github.com/highlightjs/highlight.js/blob/main/CHANGES.md) - [Commits](https://github.com/highlightjs/highlight.js/compare/11.9.0...11.10.0) --- updated-dependencies: - dependency-name: highlight.js dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 5b14cf870413..7b4904875f72 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -21,7 +21,7 @@ "crypto-browserify": "^3.12.0", "dropzone": "^5.9.3", "eth-net-props": "^1.0.41", - "highlight.js": "^11.9.0", + "highlight.js": "^11.10.0", "https-browserify": "^1.0.0", "humps": "^2.0.1", "jquery": "^3.7.1", @@ -9517,9 +9517,9 @@ } }, "node_modules/highlight.js": { - "version": "11.9.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz", - "integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==", + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.10.0.tgz", + "integrity": "sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==", "engines": { "node": ">=12.0.0" } @@ -25111,9 +25111,9 @@ } }, "highlight.js": { - "version": "11.9.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz", - "integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==" + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.10.0.tgz", + "integrity": "sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==" }, "hmac-drbg": { "version": "1.0.1", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index d967946166e8..e283c98f795d 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -33,7 +33,7 @@ "crypto-browserify": "^3.12.0", "dropzone": "^5.9.3", "eth-net-props": "^1.0.41", - "highlight.js": "^11.9.0", + "highlight.js": "^11.10.0", "https-browserify": "^1.0.0", "humps": "^2.0.1", "jquery": "^3.7.1", From c57043e7acfec2532c1a0822e6164808b285d3d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:28:27 +0300 Subject: [PATCH 037/363] chore(deps): bump sweetalert2 in /apps/block_scout_web/assets (#10532) Bumps [sweetalert2](https://github.com/sweetalert2/sweetalert2) from 11.12.1 to 11.12.4. - [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.12.1...v11.12.4) --- updated-dependencies: - dependency-name: sweetalert2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 7b4904875f72..5b7f635ae36b 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -61,7 +61,7 @@ "redux": "^5.0.1", "stream-browserify": "^3.0.0", "stream-http": "^3.1.1", - "sweetalert2": "^11.12.1", + "sweetalert2": "^11.12.4", "urijs": "^1.19.11", "url": "^0.11.3", "util": "^0.12.5", @@ -16201,9 +16201,9 @@ } }, "node_modules/sweetalert2": { - "version": "11.12.1", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.12.1.tgz", - "integrity": "sha512-xV3/YI7Ah6BeP+bXKcrHy1yn6duh8eqlX2TSI9I/rTIzGLYQvnnTa3mOIo5RHUobAjSmacC2IhPApxjvppZaEQ==", + "version": "11.12.4", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.12.4.tgz", + "integrity": "sha512-ZSpyaLbAmn4b7xjnV9x9BFD1UOrCAhIzm1D8dZ443kGxtVKqbTIA5SgXs4xeEtmFfEXUyC3RBgpSlu1AXmCiHA==", "funding": { "type": "individual", "url": "https://github.com/sponsors/limonte" @@ -30084,9 +30084,9 @@ } }, "sweetalert2": { - "version": "11.12.1", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.12.1.tgz", - "integrity": "sha512-xV3/YI7Ah6BeP+bXKcrHy1yn6duh8eqlX2TSI9I/rTIzGLYQvnnTa3mOIo5RHUobAjSmacC2IhPApxjvppZaEQ==" + "version": "11.12.4", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.12.4.tgz", + "integrity": "sha512-ZSpyaLbAmn4b7xjnV9x9BFD1UOrCAhIzm1D8dZ443kGxtVKqbTIA5SgXs4xeEtmFfEXUyC3RBgpSlu1AXmCiHA==" }, "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 e283c98f795d..addb17991356 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -73,7 +73,7 @@ "redux": "^5.0.1", "stream-browserify": "^3.0.0", "stream-http": "^3.1.1", - "sweetalert2": "^11.12.1", + "sweetalert2": "^11.12.4", "urijs": "^1.19.11", "url": "^0.11.3", "util": "^0.12.5", From 0acc2b478744a7afd2b89e25a41a5f9316d8c9e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:29:05 +0300 Subject: [PATCH 038/363] chore(deps-dev): bump eslint-plugin-promise (#10536) Bumps [eslint-plugin-promise](https://github.com/eslint-community/eslint-plugin-promise) from 6.4.0 to 6.6.0. - [Release notes](https://github.com/eslint-community/eslint-plugin-promise/releases) - [Changelog](https://github.com/eslint-community/eslint-plugin-promise/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint-community/eslint-plugin-promise/compare/v6.4.0...v6.6.0) --- updated-dependencies: - dependency-name: eslint-plugin-promise dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 5b7f635ae36b..45069e5eb696 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -82,7 +82,7 @@ "eslint-config-standard": "^17.1.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^6.4.0", + "eslint-plugin-promise": "^6.6.0", "file-loader": "^6.2.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -7680,9 +7680,9 @@ } }, "node_modules/eslint-plugin-promise": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.4.0.tgz", - "integrity": "sha512-/KWWRaD3fGkVCZsdR0RU53PSthFmoHVhZl+y9+6DqeDLSikLdlUVpVEAmI6iCRR5QyOjBYBqHZV/bdv4DJ4Gtw==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", + "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -23769,9 +23769,9 @@ } }, "eslint-plugin-promise": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.4.0.tgz", - "integrity": "sha512-/KWWRaD3fGkVCZsdR0RU53PSthFmoHVhZl+y9+6DqeDLSikLdlUVpVEAmI6iCRR5QyOjBYBqHZV/bdv4DJ4Gtw==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", + "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", "dev": true, "requires": {} }, diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index addb17991356..04396eb1d91f 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -94,7 +94,7 @@ "eslint-config-standard": "^17.1.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^6.4.0", + "eslint-plugin-promise": "^6.6.0", "file-loader": "^6.2.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", From 7a33b7a23b897493344910c0ef03e4949acf1781 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:30:09 +0300 Subject: [PATCH 039/363] chore(deps-dev): bump sass in /apps/block_scout_web/assets (#10539) Bumps [sass](https://github.com/sass/dart-sass) from 1.72.0 to 1.77.8. - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.72.0...1.77.8) --- updated-dependencies: - dependency-name: sass dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 45069e5eb696..2abf0d323558 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -89,7 +89,7 @@ "mini-css-extract-plugin": "^2.9.0", "postcss": "^8.4.39", "postcss-loader": "^8.1.1", - "sass": "^1.72.0", + "sass": "^1.77.8", "sass-loader": "^14.2.1", "style-loader": "^4.0.0", "webpack": "^5.92.1", @@ -15298,9 +15298,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sass": { - "version": "1.72.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.72.0.tgz", - "integrity": "sha512-Gpczt3WA56Ly0Mn8Sl21Vj94s1axi9hDIzDFn9Ph9x3C3p4nNyvsqJoQyVXKou6cBlfFWEgRW4rT8Tb4i3XnVA==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", + "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -29431,9 +29431,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { - "version": "1.72.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.72.0.tgz", - "integrity": "sha512-Gpczt3WA56Ly0Mn8Sl21Vj94s1axi9hDIzDFn9Ph9x3C3p4nNyvsqJoQyVXKou6cBlfFWEgRW4rT8Tb4i3XnVA==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", + "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 04396eb1d91f..42e0edd1a3d0 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -101,7 +101,7 @@ "mini-css-extract-plugin": "^2.9.0", "postcss": "^8.4.39", "postcss-loader": "^8.1.1", - "sass": "^1.72.0", + "sass": "^1.77.8", "sass-loader": "^14.2.1", "style-loader": "^4.0.0", "webpack": "^5.92.1", From 79e7bc7f9a32ec22636778af22ef779ec291f1dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 11:41:36 +0300 Subject: [PATCH 040/363] chore(deps-dev): bump webpack in /apps/block_scout_web/assets (#10534) Bumps [webpack](https://github.com/webpack/webpack) from 5.92.1 to 5.93.0. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.92.1...v5.93.0) --- updated-dependencies: - dependency-name: webpack dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 2abf0d323558..17aa96e35b9a 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -92,7 +92,7 @@ "sass": "^1.77.8", "sass-loader": "^14.2.1", "style-loader": "^4.0.0", - "webpack": "^5.92.1", + "webpack": "^5.93.0", "webpack-cli": "^5.1.4" }, "engines": { @@ -17443,9 +17443,9 @@ } }, "node_modules/webpack": { - "version": "5.92.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", - "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", + "version": "5.93.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", + "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -31072,9 +31072,9 @@ "dev": true }, "webpack": { - "version": "5.92.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", - "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", + "version": "5.93.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", + "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 42e0edd1a3d0..37bd523e3179 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -104,7 +104,7 @@ "sass": "^1.77.8", "sass-loader": "^14.2.1", "style-loader": "^4.0.0", - "webpack": "^5.92.1", + "webpack": "^5.93.0", "webpack-cli": "^5.1.4" }, "jest": { From 24b1c8db1b512fcf9ac2355ffc8879b3958ec71f Mon Sep 17 00:00:00 2001 From: jianghuyiyuan Date: Sat, 3 Aug 2024 05:04:19 +0900 Subject: [PATCH 041/363] chore: fix some comments (#10519) Signed-off-by: jianghuyiyuan --- apps/explorer/lib/explorer/chain/arbitrum/batch_block.ex | 2 +- apps/explorer/lib/explorer/chain/arbitrum/batch_transaction.ex | 2 +- .../lib/explorer/chain/arbitrum/da_multi_purpose_record.ex | 2 +- apps/explorer/lib/explorer/chain/arbitrum/l1_batch.ex | 2 +- apps/explorer/lib/explorer/chain/arbitrum/l1_execution.ex | 2 +- .../lib/explorer/chain/arbitrum/lifecycle_transaction.ex | 2 +- apps/explorer/lib/explorer/chain/arbitrum/message.ex | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/arbitrum/batch_block.ex b/apps/explorer/lib/explorer/chain/arbitrum/batch_block.ex index cd5151de68a0..2efc578b824c 100644 --- a/apps/explorer/lib/explorer/chain/arbitrum/batch_block.ex +++ b/apps/explorer/lib/explorer/chain/arbitrum/batch_block.ex @@ -18,7 +18,7 @@ defmodule Explorer.Chain.Arbitrum.BatchBlock do @required_attrs ~w(batch_number block_number)a @typedoc """ - Descriptor of the a rollup block included in an Arbitrum batch: + Descriptor of the rollup block included in an Arbitrum batch: * `batch_number` - The number of the Arbitrum batch. * `block_number` - The number of the rollup block. * `confirmation_id` - The ID of the confirmation L1 transaction from diff --git a/apps/explorer/lib/explorer/chain/arbitrum/batch_transaction.ex b/apps/explorer/lib/explorer/chain/arbitrum/batch_transaction.ex index c163a8942295..d1487ced2965 100644 --- a/apps/explorer/lib/explorer/chain/arbitrum/batch_transaction.ex +++ b/apps/explorer/lib/explorer/chain/arbitrum/batch_transaction.ex @@ -17,7 +17,7 @@ defmodule Explorer.Chain.Arbitrum.BatchTransaction do @required_attrs ~w(batch_number tx_hash)a @typedoc """ - Descriptor of the a rollup transaction included in an Arbitrum batch: + Descriptor of the rollup transaction included in an Arbitrum batch: * `batch_number` - The number of the Arbitrum batch. * `tx_hash` - The hash of the rollup transaction. """ diff --git a/apps/explorer/lib/explorer/chain/arbitrum/da_multi_purpose_record.ex b/apps/explorer/lib/explorer/chain/arbitrum/da_multi_purpose_record.ex index 5cbc89afdcf9..36409e66d4f9 100644 --- a/apps/explorer/lib/explorer/chain/arbitrum/da_multi_purpose_record.ex +++ b/apps/explorer/lib/explorer/chain/arbitrum/da_multi_purpose_record.ex @@ -22,7 +22,7 @@ defmodule Explorer.Chain.Arbitrum.DaMultiPurposeRecord do @allowed_attrs @optional_attrs ++ @required_attrs @typedoc """ - Descriptor of the a multi purpose record related to Data Availability for Arbitrum rollups: + Descriptor of the multi purpose record related to Data Availability for Arbitrum rollups: * `data_key` - The hash of the data key. * `data_type` - The type of the data. * `data` - The data diff --git a/apps/explorer/lib/explorer/chain/arbitrum/l1_batch.ex b/apps/explorer/lib/explorer/chain/arbitrum/l1_batch.ex index f99ce884143f..624d75832750 100644 --- a/apps/explorer/lib/explorer/chain/arbitrum/l1_batch.ex +++ b/apps/explorer/lib/explorer/chain/arbitrum/l1_batch.ex @@ -23,7 +23,7 @@ defmodule Explorer.Chain.Arbitrum.L1Batch do @allowed_attrs @optional_attrs ++ @required_attrs @typedoc """ - Descriptor of the a L1 batch for Arbitrum rollups: + Descriptor of the L1 batch for Arbitrum rollups: * `number` - The number of the Arbitrum batch. * `transactions_count` - The number of transactions in the batch. * `start_block` - The number of the first block in the batch. diff --git a/apps/explorer/lib/explorer/chain/arbitrum/l1_execution.ex b/apps/explorer/lib/explorer/chain/arbitrum/l1_execution.ex index 8537d26f15b9..623c36e231cd 100644 --- a/apps/explorer/lib/explorer/chain/arbitrum/l1_execution.ex +++ b/apps/explorer/lib/explorer/chain/arbitrum/l1_execution.ex @@ -16,7 +16,7 @@ defmodule Explorer.Chain.Arbitrum.L1Execution do @required_attrs ~w(message_id execution_id)a @typedoc """ - Descriptor of the a L1 execution transaction related to a L2 to L1 message on Arbitrum rollups: + Descriptor of the L1 execution transaction related to a L2 to L1 message on Arbitrum rollups: * `message_id` - The ID of the message from `Explorer.Chain.Arbitrum.Message`. There could be situations when an execution of a message is discovered, but the message itself is not indexed yet. diff --git a/apps/explorer/lib/explorer/chain/arbitrum/lifecycle_transaction.ex b/apps/explorer/lib/explorer/chain/arbitrum/lifecycle_transaction.ex index 91b30610acb2..a70f15e20c39 100644 --- a/apps/explorer/lib/explorer/chain/arbitrum/lifecycle_transaction.ex +++ b/apps/explorer/lib/explorer/chain/arbitrum/lifecycle_transaction.ex @@ -18,7 +18,7 @@ defmodule Explorer.Chain.Arbitrum.LifecycleTransaction do @required_attrs ~w(id hash block_number timestamp status)a @typedoc """ - Descriptor of the a L1 transaction changing state of transactions and blocks of Arbitrum rollups: + Descriptor of the L1 transaction changing state of transactions and blocks of Arbitrum rollups: * `id` - The ID of the transaction used for referencing. * `hash` - The hash of the L1 transaction. * `block_number` - The number of the L1 block where the transaction is included. diff --git a/apps/explorer/lib/explorer/chain/arbitrum/message.ex b/apps/explorer/lib/explorer/chain/arbitrum/message.ex index 051219ab982d..4387096e6c8e 100644 --- a/apps/explorer/lib/explorer/chain/arbitrum/message.ex +++ b/apps/explorer/lib/explorer/chain/arbitrum/message.ex @@ -20,7 +20,7 @@ defmodule Explorer.Chain.Arbitrum.Message do @allowed_attrs @optional_attrs ++ @required_attrs @typedoc """ - Descriptor of the a L1<->L2 message on Arbitrum rollups: + Descriptor of the L1<->L2 message on Arbitrum rollups: * `direction` - The direction of the message: `:to_l2` or `:from_l2`. * `message_id` - The ID of the message used for referencing. * `originator_address` - The address of the message originator. The fields From ee0a51b03cd354c4de1958225964e362812dad52 Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Mon, 5 Aug 2024 13:11:18 +0400 Subject: [PATCH 042/363] fix: non-consensus logs in JSON RPC and ETH RPC APIs (#10545) * fix: omit non-consensus logs in JSON RPC and ETH RPC APIs * fix: treat pending filter as latest --- .../api/rpc/eth_controller_test.exs | 2 +- apps/explorer/lib/explorer/eth_rpc.ex | 52 ++++++---------- apps/explorer/lib/explorer/etherscan/logs.ex | 62 ++++--------------- apps/explorer/lib/explorer/etherscan/rpc.ex | 40 ------------ 4 files changed, 31 insertions(+), 125 deletions(-) delete mode 100644 apps/explorer/lib/explorer/etherscan/rpc.ex diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs index 7e224aec353b..48646b342e37 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs @@ -456,7 +456,7 @@ defmodule BlockScoutWeb.API.RPC.EthControllerTest do |> post("/api/eth-rpc", params) |> json_response(200) - assert [%{"data" => "0x030303"}] = response["result"] + assert [%{"data" => "0x020202"}] = response["result"] end test "numerical fields are hexadecimals with 0x prefix", diff --git a/apps/explorer/lib/explorer/eth_rpc.ex b/apps/explorer/lib/explorer/eth_rpc.ex index 30c3022528ae..0bd3381d6d6b 100644 --- a/apps/explorer/lib/explorer/eth_rpc.ex +++ b/apps/explorer/lib/explorer/eth_rpc.ex @@ -19,7 +19,7 @@ defmodule Explorer.EthRPC do } alias Explorer.Chain.Cache.{BlockNumber, GasPriceOracle} - alias Explorer.Etherscan.{Blocks, Logs, RPC} + alias Explorer.Etherscan.{Blocks, Logs} @nil_gas_price_message "Gas price is not estimated yet" @@ -935,7 +935,6 @@ defmodule Explorer.EthRPC do address_or_topic_params |> Map.put(:from_block, from_block) |> Map.put(:to_block, to_block) - |> Map.put(:allow_non_consensus, true) logs = filter @@ -1092,20 +1091,16 @@ defmodule Explorer.EthRPC do from_block = Map.get(filters, "fromBlock", "latest") to_block = Map.get(filters, "toBlock", "latest") - max_block_number = - if from_block == "latest" || to_block == "latest" do - max_consensus_block_number() - end + if from_block == "latest" || to_block == "latest" || from_block == "pending" || to_block == "pending" do + max_block_number = max_consensus_block_number() - pending_block_number = - if from_block == "pending" || to_block == "pending" do - max_non_consensus_block_number(max_block_number) + if is_nil(max_block_number) do + {:error, :empty} + else + to_block_numbers(from_block, to_block, max_block_number) end - - if is_nil(pending_block_number) && from_block == "pending" && to_block == "pending" do - {:error, :empty} else - to_block_numbers(from_block, to_block, max_block_number, pending_block_number) + to_block_numbers(from_block, to_block, nil) end {:block, _} -> @@ -1134,37 +1129,33 @@ defmodule Explorer.EthRPC do defp paging_options(_), do: {:ok, nil} - defp to_block_numbers(from_block, to_block, max_block_number, pending_block_number) do - actual_pending_block_number = pending_block_number || max_block_number - - with {:ok, from} <- - to_block_number(from_block, max_block_number, actual_pending_block_number), - {:ok, to} <- to_block_number(to_block, max_block_number, actual_pending_block_number) do + defp to_block_numbers(from_block, to_block, max_block_number) do + with {:ok, from} <- to_block_number(from_block, max_block_number), + {:ok, to} <- to_block_number(to_block, max_block_number) do {:ok, from, to} end end - defp to_block_number(integer, _, _) when is_integer(integer), do: {:ok, integer} - defp to_block_number("latest", max_block_number, _), do: {:ok, max_block_number || 0} - defp to_block_number("earliest", _, _), do: {:ok, 0} - defp to_block_number("pending", max_block_number, nil), do: {:ok, max_block_number || 0} - defp to_block_number("pending", _, pending), do: {:ok, pending} + defp to_block_number(integer, _) when is_integer(integer), do: {:ok, integer} + defp to_block_number("latest", max_block_number), do: {:ok, max_block_number || 0} + defp to_block_number("pending", max_block_number), do: {:ok, max_block_number || 0} + defp to_block_number("earliest", _), do: {:ok, 0} - defp to_block_number("0x" <> number, _, _) do + defp to_block_number("0x" <> number, _) do case Integer.parse(number, 16) do {integer, ""} -> {:ok, integer} _ -> {:error, "invalid block number"} end end - defp to_block_number(number, _, _) when is_bitstring(number) do + defp to_block_number(number, _) when is_bitstring(number) do case Integer.parse(number, 16) do {integer, ""} -> {:ok, integer} _ -> {:error, "invalid block number"} end end - defp to_block_number(_, _, _), do: {:error, "invalid block number"} + defp to_block_number(_, _), do: {:error, "invalid block number"} defp to_number(number, error_message) when is_bitstring(number) do case Integer.parse(number, 16) do @@ -1175,13 +1166,6 @@ defmodule Explorer.EthRPC do defp to_number(_, error_message), do: {:error, error_message} - defp max_non_consensus_block_number(max) do - case RPC.max_non_consensus_block_number(max) do - {:ok, number} -> number - _ -> nil - end - end - defp max_consensus_block_number do case Chain.max_consensus_block_number() do {:ok, number} -> number diff --git a/apps/explorer/lib/explorer/etherscan/logs.ex b/apps/explorer/lib/explorer/etherscan/logs.ex index d0eca16ef60f..f463f8232c68 100644 --- a/apps/explorer/lib/explorer/etherscan/logs.ex +++ b/apps/explorer/lib/explorer/etherscan/logs.ex @@ -88,7 +88,8 @@ defmodule Explorer.Etherscan.Logs do all_transaction_logs_query = from(log in subquery(logs_query), join: transaction in Transaction, - on: log.transaction_hash == transaction.hash, + on: log.transaction_hash == transaction.hash and log.block_hash == transaction.block_hash, + where: transaction.block_consensus == true, select: map(log, ^@log_fields), select_merge: %{ gas_price: transaction.gas_price, @@ -101,24 +102,7 @@ defmodule Explorer.Etherscan.Logs do } ) - query_with_blocks = - from(log_transaction_data in subquery(all_transaction_logs_query), - where: log_transaction_data.address_hash == ^address_hash, - select_merge: %{ - block_consensus: log_transaction_data.block_consensus - } - ) - - query_with_consensus = - if Map.get(filter, :allow_non_consensus) do - query_with_blocks - else - from([transaction] in query_with_blocks, - where: transaction.block_consensus == true - ) - end - - query_with_consensus + all_transaction_logs_query |> order_by([log], asc: log.block_number, asc: log.index) |> Repo.replica().all() else @@ -176,6 +160,7 @@ defmodule Explorer.Etherscan.Logs do join: block in Block, on: block.number == log_transaction_data.block_number, where: log_transaction_data.address_hash == ^address_hash, + where: block.consensus == true, order_by: block.number, limit: 1000, select_merge: %{ @@ -187,16 +172,7 @@ defmodule Explorer.Etherscan.Logs do } ) - query_with_consensus = - if Map.get(filter, :allow_non_consensus) do - query_with_blocks - else - from([_, block] in query_with_blocks, - where: block.consensus == true - ) - end - - query_with_consensus + query_with_blocks |> order_by([log], asc: log.index) |> page_logs(paging_options) |> Repo.replica().all() @@ -217,6 +193,7 @@ defmodule Explorer.Etherscan.Logs do from(transaction in Transaction, where: transaction.block_number >= ^prepared_filter.from_block, where: transaction.block_number <= ^prepared_filter.to_block, + where: transaction.block_consensus == true, select: %{ transaction_hash: transaction.hash, gas_price: transaction.gas_price, @@ -229,19 +206,12 @@ defmodule Explorer.Etherscan.Logs do } ) - query_with_consensus = - if Map.get(filter, :allow_non_consensus) do - block_transaction_query - else - from([transaction] in block_transaction_query, - where: transaction.block_consensus == true - ) - end - query_with_block_transaction_data = from(log in logs_query, - join: block_transaction_data in subquery(query_with_consensus), - on: block_transaction_data.transaction_hash == log.transaction_hash, + join: block_transaction_data in subquery(block_transaction_query), + on: + block_transaction_data.transaction_hash == log.transaction_hash and + block_transaction_data.block_hash == log.block_hash, order_by: block_transaction_data.block_number, limit: 1000, select: block_transaction_data, @@ -258,6 +228,7 @@ defmodule Explorer.Etherscan.Logs do join: block in assoc(transaction, :block), where: block.number >= ^prepared_filter.from_block, where: block.number <= ^prepared_filter.to_block, + where: block.consensus == true, select: %{ transaction_hash: transaction.hash, gas_price: transaction.gas_price, @@ -270,18 +241,9 @@ defmodule Explorer.Etherscan.Logs do } ) - query_with_consensus = - if Map.get(filter, :allow_non_consensus) do - block_transaction_query - else - from([_, block] in block_transaction_query, - where: block.consensus == true - ) - end - query_with_block_transaction_data = from(log in logs_query, - join: block_transaction_data in subquery(query_with_consensus), + join: block_transaction_data in subquery(block_transaction_query), on: block_transaction_data.transaction_hash == log.transaction_hash, order_by: block_transaction_data.block_number, limit: 1000, diff --git a/apps/explorer/lib/explorer/etherscan/rpc.ex b/apps/explorer/lib/explorer/etherscan/rpc.ex deleted file mode 100644 index efb163d64351..000000000000 --- a/apps/explorer/lib/explorer/etherscan/rpc.ex +++ /dev/null @@ -1,40 +0,0 @@ -defmodule Explorer.Etherscan.RPC do - @moduledoc """ - This module contains functions for working with mimicking of ETH RPC. - - """ - - import Ecto.Query, - only: [ - from: 2 - ] - - alias Explorer.{Chain, Repo} - alias Explorer.Chain.Block - - @spec max_non_consensus_block_number(integer | nil) :: {:ok, Block.block_number()} | {:error, :not_found} - def max_non_consensus_block_number(max_consensus_block_number \\ nil) do - max = - if max_consensus_block_number do - {:ok, max_consensus_block_number} - else - Chain.max_consensus_block_number() - end - - case max do - {:ok, number} -> - query = - from(block in Block, - where: block.consensus == false, - where: block.number > ^number - ) - - query - |> Repo.replica().aggregate(:max, :number) - |> case do - nil -> {:error, :not_found} - number -> {:ok, number} - end - end - end -end From e86036a657f43f09661dc3075045f4590ca68d41 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 12:22:20 +0300 Subject: [PATCH 043/363] chore(deps): bump absinthe from 1.7.7 to 1.7.8 (#10502) Bumps [absinthe](https://github.com/absinthe-graphql/absinthe) from 1.7.7 to 1.7.8. - [Release notes](https://github.com/absinthe-graphql/absinthe/releases) - [Changelog](https://github.com/absinthe-graphql/absinthe/blob/main/CHANGELOG.md) - [Commits](https://github.com/absinthe-graphql/absinthe/compare/v1.7.7...v1.7.8) --- updated-dependencies: - dependency-name: absinthe dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 5364c7e932b9..de85f663498a 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,5 @@ %{ - "absinthe": {:hex, :absinthe, "1.7.7", "ecbf4e9b21372dda271c79bb43dded3583b4f080348c5e68d9b5445e790ff17e", [:mix], [{:dataloader, "~> 1.0.0 or ~> 2.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:opentelemetry_process_propagator, "~> 0.2.1 or ~> 0.3", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2145519828bcb7c8621b72d7af2bcff88b01cba2774583c40ebd867e1d336ff6"}, + "absinthe": {:hex, :absinthe, "1.7.8", "43443d12ad2b4fcce60e257ac71caf3081f3d5c4ddd5eac63a02628bcaf5b556", [:mix], [{:dataloader, "~> 1.0.0 or ~> 2.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:opentelemetry_process_propagator, "~> 0.2.1 or ~> 0.3", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c4085df201892a498384f997649aedb37a4ce8a726c170d5b5617ed3bf45d40b"}, "absinthe_phoenix": {:hex, :absinthe_phoenix, "2.0.3", "74e0862f280424b7bc290f6f69e133268bce0b4e7db0218c7e129c5c2b1d3fd4", [:mix], [{:absinthe, "~> 1.5", [hex: :absinthe, repo: "hexpm", optional: false]}, {:absinthe_plug, "~> 1.5", [hex: :absinthe_plug, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.5", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.13 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}], "hexpm", "caffaea03c17ea7419fe07e4bc04c2399c47f0d8736900623dbf4749a826fd2c"}, "absinthe_plug": {:git, "https://github.com/blockscout/absinthe_plug.git", "90a8188e94e2650f13259fb16462075a87f98e18", [tag: "1.5.8"]}, "absinthe_relay": {:hex, :absinthe_relay, "1.5.2", "cfb8aed70f4e4c7718d3f1c212332d2ea728f17c7fc0f68f1e461f0f5f0c4b9a", [:mix], [{:absinthe, "~> 1.5.0 or ~> 1.6.0 or ~> 1.7.0", [hex: :absinthe, repo: "hexpm", optional: false]}, {:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "0587ee913afa31512e1457a5064ee88427f8fe7bcfbeeecd41c71d9cff0b62b6"}, From a914db55e76e8a62211bfd01e2bd581f64444c83 Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Mon, 5 Aug 2024 04:39:51 -0600 Subject: [PATCH 044/363] Skip the batch 0 (#10553) --- .../fetcher/arbitrum/workers/new_batches.ex | 145 +++++++++++++----- 1 file changed, 105 insertions(+), 40 deletions(-) diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex index cf768c041622..8543d339eb15 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex @@ -861,6 +861,9 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # For the existing batches, the function prepares a map of commitment transactions # assuming that they must be updated if reorgs occur. # + # The function skips the batch with number 0, as this batch does not contain any + # rollup blocks and transactions. + # # ## Parameters # - `logs`: A list of event logs to be processed. # - `existing_batches`: A list of batch numbers already processed. @@ -893,49 +896,15 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do defp parse_logs_for_new_batches(logs, existing_batches) do {batches, txs_requests, blocks_requests, existing_commitment_txs} = logs - |> Enum.reduce({%{}, [], %{}, %{}}, fn event, {batches, txs_requests, blocks_requests, existing_commitment_txs} -> - {batch_num, before_acc, after_acc} = sequencer_batch_delivered_event_parse(event) - + |> Enum.reduce({%{}, [], %{}, %{}}, fn event, acc -> tx_hash_raw = event["transactionHash"] - tx_hash = Rpc.string_hash_to_bytes_hash(tx_hash_raw) blk_num = quantity_to_integer(event["blockNumber"]) - {updated_batches, updated_txs_requests, updated_existing_commitment_txs} = - if batch_num in existing_batches do - {batches, txs_requests, Map.put(existing_commitment_txs, tx_hash, blk_num)} - else - log_info("New batch #{batch_num} found in #{tx_hash_raw}") - - updated_batches = - Map.put( - batches, - batch_num, - %{ - number: batch_num, - before_acc: before_acc, - after_acc: after_acc, - tx_hash: tx_hash - } - ) - - updated_txs_requests = [ - Rpc.transaction_by_hash_request(%{id: 0, hash: tx_hash_raw}) - | txs_requests - ] - - {updated_batches, updated_txs_requests, existing_commitment_txs} - end - - # In order to have an ability to update commitment transaction for the existing batches - # in case of reorgs, we need to re-execute the block requests - updated_blocks_requests = - Map.put( - blocks_requests, - blk_num, - BlockByNumber.request(%{id: 0, number: blk_num}, false, true) - ) - - {updated_batches, updated_txs_requests, updated_blocks_requests, updated_existing_commitment_txs} + handle_new_batch_data( + {sequencer_batch_delivered_event_parse(event), tx_hash_raw, blk_num}, + existing_batches, + acc + ) end) {batches, txs_requests, Map.values(blocks_requests), existing_commitment_txs} @@ -949,6 +918,102 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do {quantity_to_integer(batch_sequence_number), before_acc, after_acc} end + # Handles the new batch data to assemble a map of new batch descriptions. + # + # This function processes the new batch data by assembling a map of new batch + # descriptions and preparing RPC `eth_getTransactionByHash` and `eth_getBlockByNumber` + # requests to fetch details not present in the received batch data. To minimize + # subsequent RPC calls, requests to get the transaction details are only made for + # batches not previously known. For existing batches, the function prepares a map + # of commitment transactions, assuming that they must be updated if reorgs occur. + # If the batch number is zero, the function does nothing. + # + # ## Parameters + # - `batch_data`: A tuple containing the batch number, before and after accumulators, + # transaction hash, and block number. + # - `existing_batches`: A list of batch numbers that are already processed. + # - `acc`: A tuple containing new batch descriptions, transaction requests, + # block requests, and existing commitment transactions maps. + # + # ## Returns + # - A tuple containing: + # - A map of new batch descriptions, which are not yet ready for database import. + # - A list of RPC `eth_getTransactionByHash` requests for fetching details of + # the L1 transactions associated with these batches. + # - A map of RPC requests to fetch details of the L1 blocks where these batches + # were included. The keys of the map are L1 block numbers. + # - A map of commitment transactions for the existing batches where the value is + # the block number of the transaction. + @spec handle_new_batch_data( + {{non_neg_integer(), binary(), binary()}, binary(), non_neg_integer()}, + [non_neg_integer()], + {map(), list(), map(), map()} + ) :: { + %{ + non_neg_integer() => %{ + :number => non_neg_integer(), + :before_acc => binary(), + :after_acc => binary(), + :tx_hash => binary() + } + }, + [EthereumJSONRPC.Transport.request()], + %{non_neg_integer() => EthereumJSONRPC.Transport.request()}, + %{binary() => non_neg_integer()} + } + defp handle_new_batch_data( + batch_data, + existing_batches, + acc + ) + + defp handle_new_batch_data({{batch_num, _, _}, _, _}, _, acc) when batch_num == 0, do: acc + + defp handle_new_batch_data( + {{batch_num, before_acc, after_acc}, tx_hash_raw, blk_num}, + existing_batches, + {batches, txs_requests, blocks_requests, existing_commitment_txs} + ) do + tx_hash = Rpc.string_hash_to_bytes_hash(tx_hash_raw) + + {updated_batches, updated_txs_requests, updated_existing_commitment_txs} = + if batch_num in existing_batches do + {batches, txs_requests, Map.put(existing_commitment_txs, tx_hash, blk_num)} + else + log_info("New batch #{batch_num} found in #{tx_hash_raw}") + + updated_batches = + Map.put( + batches, + batch_num, + %{ + number: batch_num, + before_acc: before_acc, + after_acc: after_acc, + tx_hash: tx_hash + } + ) + + updated_txs_requests = [ + Rpc.transaction_by_hash_request(%{id: 0, hash: tx_hash_raw}) + | txs_requests + ] + + {updated_batches, updated_txs_requests, existing_commitment_txs} + end + + # In order to have an ability to update commitment transaction for the existing batches + # in case of reorgs, we need to re-execute the block requests + updated_blocks_requests = + Map.put( + blocks_requests, + blk_num, + BlockByNumber.request(%{id: 0, number: blk_num}, false, true) + ) + + {updated_batches, updated_txs_requests, updated_blocks_requests, updated_existing_commitment_txs} + end + # Executes transaction requests and parses the calldata to extract batch data. # # This function processes a list of RPC `eth_getTransactionByHash` requests, extracts From b05e066ff7d8107630bc603c13f2202e7088311b Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Mon, 5 Aug 2024 14:56:55 +0400 Subject: [PATCH 045/363] fix: Use token_type from tt instead of token (#10555) --- apps/explorer/lib/explorer/etherscan.ex | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/explorer/lib/explorer/etherscan.ex b/apps/explorer/lib/explorer/etherscan.ex index 3cf141fdcb44..f7a44bd8bc2e 100644 --- a/apps/explorer/lib/explorer/etherscan.ex +++ b/apps/explorer/lib/explorer/etherscan.ex @@ -4,7 +4,7 @@ defmodule Explorer.Etherscan do """ import Ecto.Query, - only: [from: 2, where: 3, union: 2, subquery: 1, order_by: 3, limit: 2, offset: 2, preload: 3] + only: [from: 2, where: 3, union: 2, subquery: 1, order_by: 3, limit: 2, offset: 2, preload: 3, select_merge: 3] import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] @@ -555,13 +555,19 @@ defmodule Explorer.Etherscan do token_name: tkn.name, token_symbol: tkn.symbol, token_decimals: tkn.decimals, - token_type: tkn.type, token_log_index: tt.log_index }) ) + tt_query_with_token_type = + if DenormalizationHelper.tt_denormalization_finished?() do + select_merge(tt_query, [tt, _tkn], %{token_type: tt.token_type}) + else + select_merge(tt_query, [_tt, tkn], %{token_type: tkn.type}) + end + tt_specific_token_query = - tt_query + tt_query_with_token_type |> where_start_block_match_tt(options) |> where_end_block_match_tt(options) |> where_contract_address_match(contract_address_hash) From 307f6f166bea33139de1956b8fb3e7ef5198a7d0 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 5 Aug 2024 15:42:41 +0300 Subject: [PATCH 046/363] Chore: Add workflow to generate separate pre-release indexer/API images for Arbitrum --- .../publish-docker-image-for-arbitrum.yml | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-docker-image-for-arbitrum.yml b/.github/workflows/publish-docker-image-for-arbitrum.yml index c870634a1751..fe2621d727ba 100644 --- a/.github/workflows/publish-docker-image-for-arbitrum.yml +++ b/.github/workflows/publish-docker-image-for-arbitrum.yml @@ -24,7 +24,7 @@ jobs: docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - - name: Build and push Docker image + - name: Build and push Docker image (indexer + API) uses: docker/build-push-action@v5 with: context: . @@ -45,4 +45,48 @@ jobs: CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=arbitrum + + - name: Build and push Docker image (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=arbitrum + + - name: Build and push Docker image (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=arbitrum \ No newline at end of file From 44e6b6d489251984eb34a556b90b740d7c845ee6 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Mon, 5 Aug 2024 15:45:48 +0300 Subject: [PATCH 047/363] feat: Add CSV_EXPORT_LIMIT env (#10497) * feat: Add CSV_EXPORT_LIMIT env * Reuse existing function --- .../controllers/api/v2/config_controller.ex | 8 ++++++++ .../controllers/api/v2/csv_export_controller.ex | 7 ++++--- .../lib/block_scout_web/routers/api_router.ex | 1 + .../address_internal_transaction_csv_exporter.ex | 6 ++---- .../explorer/chain/csv_export/address_log_csv_exporter.ex | 6 ++---- .../csv_export/address_token_transfer_csv_exporter.ex | 6 +++--- .../chain/csv_export/address_transaction_csv_exporter.ex | 6 ++---- apps/explorer/lib/explorer/chain/csv_export/helper.ex | 7 +++++-- config/runtime.exs | 3 ++- docker-compose/envs/common-blockscout.env | 3 ++- 10 files changed, 31 insertions(+), 22 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/config_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/config_controller.ex index f9d2dfc0a463..dd695a20f37c 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/config_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/config_controller.ex @@ -8,4 +8,12 @@ defmodule BlockScoutWeb.API.V2.ConfigController do |> put_status(200) |> render(:backend_version, %{version: backend_version}) end + + def csv_export(conn, _params) do + limit = Application.get_env(:explorer, :csv_export_limit) + + conn + |> put_status(200) + |> json(%{limit: limit}) + end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/csv_export_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/csv_export_controller.ex index d8ef80f8f834..30e1fa79c215 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/csv_export_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/csv_export_controller.ex @@ -2,14 +2,13 @@ defmodule BlockScoutWeb.API.V2.CSVExportController do use BlockScoutWeb, :controller alias BlockScoutWeb.AccessHelper - alias Explorer.{Chain, PagingOptions} + alias Explorer.Chain alias Explorer.Chain.Address.CurrentTokenBalance alias Explorer.Chain.CSVExport.Helper, as: CSVHelper alias Plug.Conn action_fallback(BlockScoutWeb.API.V2.FallbackController) - @options [paging_options: %PagingOptions{page_size: CSVHelper.limit()}, api?: true] @api_true [api?: true] @doc """ @@ -25,7 +24,7 @@ defmodule BlockScoutWeb.API.V2.CSVExportController do Application.get_env(:block_scout_web, :recaptcha)[:is_disabled] || CSVHelper.captcha_helper().recaptcha_passed?(params["recaptcha_response"])}, {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)} do - token_holders = Chain.fetch_token_holders_from_token_hash_for_csv(address_hash, @options) + token_holders = Chain.fetch_token_holders_from_token_hash_for_csv(address_hash, options()) token_holders |> CurrentTokenBalance.to_csv_format(token) @@ -50,4 +49,6 @@ defmodule BlockScoutWeb.API.V2.CSVExportController do |> put_resp_cookie("csv-downloaded", "true", max_age: 86_400, http_only: false) |> send_chunked(200) end + + defp options, do: [paging_options: CSVHelper.paging_options(), api?: true] end diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index ff21af9fa6ed..3aee52a138a8 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -107,6 +107,7 @@ defmodule BlockScoutWeb.Routers.ApiRouter do scope "/config" do get("/backend-version", V2.ConfigController, :backend_version) + get("/csv-export", V2.ConfigController, :csv_export) end scope "/transactions" do diff --git a/apps/explorer/lib/explorer/chain/csv_export/address_internal_transaction_csv_exporter.ex b/apps/explorer/lib/explorer/chain/csv_export/address_internal_transaction_csv_exporter.ex index 4265961e98ad..73883fcab698 100644 --- a/apps/explorer/lib/explorer/chain/csv_export/address_internal_transaction_csv_exporter.ex +++ b/apps/explorer/lib/explorer/chain/csv_export/address_internal_transaction_csv_exporter.ex @@ -3,18 +3,16 @@ defmodule Explorer.Chain.CSVExport.AddressInternalTransactionCsvExporter do Exports internal transactions to a csv file. """ - alias Explorer.{Chain, PagingOptions} + alias Explorer.Chain alias Explorer.Chain.{Address, Hash, Transaction, Wei} alias Explorer.Chain.CSVExport.Helper - @paging_options %PagingOptions{page_size: Helper.limit()} - @spec export(Hash.Address.t(), String.t(), String.t(), String.t() | nil, String.t() | nil) :: Enumerable.t() def export(address_hash, from_period, to_period, filter_type \\ nil, filter_value \\ nil) do {from_block, to_block} = Helper.block_from_period(from_period, to_period) address_hash - |> fetch_all_internal_transactions(from_block, to_block, filter_type, filter_value, @paging_options) + |> fetch_all_internal_transactions(from_block, to_block, filter_type, filter_value, Helper.paging_options()) |> Enum.sort_by(&{&1.block_number, &1.index, &1.transaction_index}, :desc) |> to_csv_format() |> Helper.dump_to_stream() diff --git a/apps/explorer/lib/explorer/chain/csv_export/address_log_csv_exporter.ex b/apps/explorer/lib/explorer/chain/csv_export/address_log_csv_exporter.ex index 7432cb8a274a..b72408b2f545 100644 --- a/apps/explorer/lib/explorer/chain/csv_export/address_log_csv_exporter.ex +++ b/apps/explorer/lib/explorer/chain/csv_export/address_log_csv_exporter.ex @@ -3,18 +3,16 @@ defmodule Explorer.Chain.CSVExport.AddressLogCsvExporter do Exports internal transactions to a csv file. """ - alias Explorer.{Chain, PagingOptions} + alias Explorer.Chain alias Explorer.Chain.{Address, Hash} alias Explorer.Chain.CSVExport.Helper - @paging_options %PagingOptions{page_size: Helper.limit()} - @spec export(Hash.Address.t(), String.t(), String.t(), String.t() | nil, String.t() | nil) :: Enumerable.t() def export(address_hash, from_period, to_period, _filter_type \\ nil, filter_value \\ nil) do {from_block, to_block} = Helper.block_from_period(from_period, to_period) address_hash - |> fetch_all_logs(from_block, to_block, filter_value, @paging_options) + |> fetch_all_logs(from_block, to_block, filter_value, Helper.paging_options()) |> to_csv_format() |> Helper.dump_to_stream() end diff --git a/apps/explorer/lib/explorer/chain/csv_export/address_token_transfer_csv_exporter.ex b/apps/explorer/lib/explorer/chain/csv_export/address_token_transfer_csv_exporter.ex index b82bd6487953..8b83bcf75776 100644 --- a/apps/explorer/lib/explorer/chain/csv_export/address_token_transfer_csv_exporter.ex +++ b/apps/explorer/lib/explorer/chain/csv_export/address_token_transfer_csv_exporter.ex @@ -15,14 +15,14 @@ defmodule Explorer.Chain.CSVExport.AddressTokenTransferCsvExporter do alias Explorer.Chain.{Address, DenormalizationHelper, Hash, TokenTransfer, Transaction} alias Explorer.Chain.CSVExport.Helper - @paging_options %PagingOptions{page_size: Helper.limit(), asc_order: true} - @spec export(Hash.Address.t(), String.t(), String.t(), String.t() | nil, String.t() | nil) :: Enumerable.t() def export(address_hash, from_period, to_period, filter_type \\ nil, filter_value \\ nil) do {from_block, to_block} = Helper.block_from_period(from_period, to_period) + paging_options = %PagingOptions{Helper.paging_options() | asc_order: true} + address_hash - |> fetch_all_token_transfers(from_block, to_block, filter_type, filter_value, @paging_options) + |> fetch_all_token_transfers(from_block, to_block, filter_type, filter_value, paging_options) |> to_csv_format(address_hash) |> Helper.dump_to_stream() end diff --git a/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex b/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex index bfe1e92cfd0e..d332cd7dd149 100644 --- a/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex +++ b/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex @@ -3,20 +3,18 @@ defmodule Explorer.Chain.CSVExport.AddressTransactionCsvExporter do Exports transactions to a csv file. """ - alias Explorer.{Market, PagingOptions} + alias Explorer.Market alias Explorer.Market.MarketHistory alias Explorer.Chain.{Address, DenormalizationHelper, Hash, Transaction, Wei} alias Explorer.Chain.CSVExport.Helper - @paging_options %PagingOptions{page_size: Helper.limit()} - @spec export(Hash.Address.t(), String.t(), String.t(), String.t() | nil, String.t() | nil) :: Enumerable.t() def export(address_hash, from_period, to_period, filter_type \\ nil, filter_value \\ nil) do {from_block, to_block} = Helper.block_from_period(from_period, to_period) exchange_rate = Market.get_coin_exchange_rate() address_hash - |> fetch_transactions(from_block, to_block, filter_type, filter_value, @paging_options) + |> fetch_transactions(from_block, to_block, filter_type, filter_value, Helper.paging_options()) |> to_csv_format(address_hash, exchange_rate) |> Helper.dump_to_stream() end diff --git a/apps/explorer/lib/explorer/chain/csv_export/helper.ex b/apps/explorer/lib/explorer/chain/csv_export/helper.ex index 53a815b79dc3..973f9f60975f 100644 --- a/apps/explorer/lib/explorer/chain/csv_export/helper.ex +++ b/apps/explorer/lib/explorer/chain/csv_export/helper.ex @@ -12,7 +12,6 @@ defmodule Explorer.Chain.CSVExport.Helper do where: 3 ] - @limit 10_000 @page_size 150 @default_paging_options %PagingOptions{page_size: @page_size} @@ -25,7 +24,11 @@ defmodule Explorer.Chain.CSVExport.Helper do def default_paging_options, do: @default_paging_options - def limit, do: @limit + @spec limit() :: integer() + def limit, do: Application.get_env(:explorer, :csv_export_limit) + + @spec paging_options() :: Explorer.PagingOptions.t() + def paging_options, do: %PagingOptions{page_size: limit()} def block_from_period(from_period, to_period) do from_block = Chain.convert_date_to_min_block(from_period) diff --git a/config/runtime.exs b/config/runtime.exs index 10941697c35e..9f4bf0005a7d 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -233,7 +233,8 @@ config :explorer, restricted_list_key: System.get_env("RESTRICTED_LIST_KEY"), checksum_function: checksum_function && String.to_atom(checksum_function), elasticity_multiplier: ConfigHelper.parse_integer_env_var("EIP_1559_ELASTICITY_MULTIPLIER", 2), - base_fee_max_change_denominator: ConfigHelper.parse_integer_env_var("EIP_1559_BASE_FEE_MAX_CHANGE_DENOMINATOR", 8) + base_fee_max_change_denominator: ConfigHelper.parse_integer_env_var("EIP_1559_BASE_FEE_MAX_CHANGE_DENOMINATOR", 8), + csv_export_limit: ConfigHelper.parse_integer_env_var("CSV_EXPORT_LIMIT", 10_000) config :explorer, :proxy, caching_implementation_data_enabled: true, diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 489e32ebfed1..882f37241a73 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -404,4 +404,5 @@ TENDERLY_CHAIN_PATH= # SANITIZE_INCORRECT_WETH_BATCH_SIZE=100 # SANITIZE_INCORRECT_WETH_CONCURRENCY=1 # PUBLIC_METRICS_ENABLED= -# PUBLIC_METRICS_UPDATE_PERIOD_HOURS= \ No newline at end of file +# PUBLIC_METRICS_UPDATE_PERIOD_HOURS= +# CSV_EXPORT_LIMIT= \ No newline at end of file From 11e71b734d7870dbd238ab869db461461e2ea4c0 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 5 Aug 2024 15:47:04 +0300 Subject: [PATCH 048/363] fix: Sanitize contractURI response (#10479) --- apps/explorer/lib/explorer/token/metadata_retriever.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/token/metadata_retriever.ex b/apps/explorer/lib/explorer/token/metadata_retriever.ex index 8570293f41a3..959add1e0900 100644 --- a/apps/explorer/lib/explorer/token/metadata_retriever.ex +++ b/apps/explorer/lib/explorer/token/metadata_retriever.ex @@ -255,7 +255,8 @@ defmodule Explorer.Token.MetadataRetriever do case erc_1155_name_uri do %{:name => name} when is_binary(name) -> - uri = {:ok, [name]} + sanitized_name = String.trim(name) + uri = {:ok, [sanitized_name]} with {:ok, %{metadata: metadata}} <- fetch_json(uri, nil, nil, false), true <- Map.has_key?(metadata, "name"), From a8c0c1ba92084a9f27534c379b0f2e6b60dc5577 Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Mon, 5 Aug 2024 06:47:50 -0600 Subject: [PATCH 049/363] limited usage of infinite timeout (#10551) --- .../lib/explorer/chain/arbitrum/reader.ex | 101 ++++++++++++++---- .../lib/indexer/fetcher/arbitrum/utils/db.ex | 33 +----- 2 files changed, 83 insertions(+), 51 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/arbitrum/reader.ex b/apps/explorer/lib/explorer/chain/arbitrum/reader.ex index e944d588cabb..1d5916fcf4ee 100644 --- a/apps/explorer/lib/explorer/chain/arbitrum/reader.ex +++ b/apps/explorer/lib/explorer/chain/arbitrum/reader.ex @@ -40,7 +40,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do ) query - |> Repo.one() + |> Repo.one(timeout: :infinity) end @doc """ @@ -60,7 +60,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do ) query - |> Repo.one() + |> Repo.one(timeout: :infinity) end @doc """ @@ -86,7 +86,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do |> order_by(desc: :block_number) |> limit(1) |> select([log], log.block_number) - |> Repo.one() + |> Repo.one(timeout: :infinity) end @doc """ @@ -107,7 +107,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do |> order_by(desc: :block_number) |> limit(1) |> select([rollup_tx], rollup_tx.block_number) - |> Repo.one() + |> Repo.one(timeout: :infinity) end @doc """ @@ -132,7 +132,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do case query # :required is used since the situation when commit transaction is not found is not possible |> Chain.join_associations(%{:commitment_transaction => :required}) - |> Repo.one() do + |> Repo.one(timeout: :infinity) do nil -> nil batch -> batch.commitment_transaction.block_number end @@ -160,7 +160,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do case query # :required is used since the situation when commit transaction is not found is not possible |> Chain.join_associations(%{:commitment_transaction => :required}) - |> Repo.one() do + |> Repo.one(timeout: :infinity) do nil -> nil batch -> batch.commitment_transaction.block_number end @@ -205,7 +205,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do where: lt.hash in ^l1_tx_hashes ) - Repo.all(query, timeout: :infinity) + Repo.all(query) end @doc """ @@ -227,7 +227,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do where: lt.hash in ^l1_tx_hashes ) - Repo.all(query, timeout: :infinity) + Repo.all(query) end @doc """ @@ -253,7 +253,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do # :required is used since execution records in the table are created only when # the corresponding execution transaction is indexed |> Chain.join_associations(%{:execution_transaction => :required}) - |> Repo.all(timeout: :infinity) + |> Repo.all() end @doc """ @@ -305,7 +305,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do where: lt.block_number <= ^finalized_block and lt.status == :unfinalized ) - Repo.all(query, timeout: :infinity) + Repo.all(query) end @doc """ @@ -357,7 +357,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do ) query - |> Repo.all(timeout: :infinity) + |> Repo.all() end @doc """ @@ -426,7 +426,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do case query # :required is used since existence of the confirmation id is checked above |> Chain.join_associations(%{:confirmation_transaction => :required}) - |> Repo.one() do + |> Repo.one(timeout: :infinity) do nil -> nil @@ -480,7 +480,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do ) query - |> Repo.one() + |> Repo.one(timeout: :infinity) end @doc """ @@ -502,7 +502,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do ) query - |> Repo.one() + |> Repo.one(timeout: :infinity) end @doc """ @@ -532,7 +532,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do order_by: [asc: rb.block_number] ) - Repo.all(query, timeout: :infinity) + Repo.all(query) end @doc """ @@ -553,7 +553,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do where: rb.batch_number == ^batch_number and not is_nil(rb.confirmation_id) ) - Repo.aggregate(query, :count, timeout: :infinity) + Repo.aggregate(query, :count) end @doc """ @@ -588,7 +588,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do order_by: [desc: msg.message_id] ) - Repo.all(query, timeout: :infinity) + Repo.all(query) end def l2_to_l1_messages(status, nil) when status in [:initiated, :sent, :confirmed, :relayed] do @@ -598,7 +598,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do order_by: [desc: msg.message_id] ) - Repo.all(query, timeout: :infinity) + Repo.all(query) end @doc """ @@ -704,7 +704,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do defp do_messages_count(direction, options) do Message |> where([msg], msg.direction == ^direction) - |> select_repo(options).aggregate(:count, timeout: :infinity) + |> select_repo(options).aggregate(:count) end @doc """ @@ -1223,7 +1223,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do ) query - |> Repo.all(timeout: :infinity) + |> Repo.all() end @doc """ @@ -1248,7 +1248,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do ) query - |> Repo.all(timeout: :infinity) + |> Repo.all() |> Enum.reduce(%{}, fn {batch_number, l1_block_number}, acc -> Map.put(acc, batch_number, l1_block_number) end) @@ -1267,6 +1267,63 @@ defmodule Explorer.Chain.Arbitrum.Reader do select: {min(batch.number), max(batch.number)} ) - Repo.one(query) + Repo.one(query, timeout: :infinity) + end + + ##################################################################################### + ### Below are the functions that implement functionality not specific to Arbitrum ### + ##################################################################################### + + @doc """ + Checks if a block with the given block number exists. + + This function queries the database to determine if a block with the specified + block number exists and has been marked as having reached consensus. + + ## Parameters + - `block_number`: The number of the block to check. + + ## Returns + - `true` if the block exists and has reached consensus. + - `false` otherwise. + """ + @spec rollup_block_exists?(FullBlock.block_number()) :: boolean() + def rollup_block_exists?(block_number) do + query = + from( + block in FullBlock, + where: block.number == ^block_number and block.consensus == true + ) + + Repo.exists?(query, timeout: :infinity) + end + + @doc """ + Retrieves full details of rollup blocks, including associated transactions, for each + block number specified in the input list. + + ## Parameters + - `list_of_block_numbers`: A list of block numbers for which full block details are to be retrieved. + + ## Returns + - A list of `Explorer.Chain.Block` instances containing detailed information for each + block number in the input list. Returns an empty list if no blocks are found for the given numbers. + """ + @spec rollup_blocks([FullBlock.block_number()]) :: [FullBlock.t()] + def rollup_blocks(list_of_block_numbers) + + def rollup_blocks([]), do: [] + + def rollup_blocks(list_of_block_numbers) do + query = + from( + block in FullBlock, + where: block.number in ^list_of_block_numbers + ) + + query + # :optional is used since a block may not have any transactions + |> Chain.join_associations(%{:transactions => :optional}) + |> Repo.all() end end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex index 66fe5b7035b7..8c9c640e1265 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex @@ -3,11 +3,9 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do Common functions to simplify DB routines for Indexer.Fetcher.Arbitrum fetchers """ - import Ecto.Query, only: [from: 2] - import Indexer.Fetcher.Arbitrum.Utils.Logging, only: [log_warning: 1, log_info: 1] - alias Explorer.{Chain, Repo} + alias Explorer.Chain alias Explorer.Chain.Arbitrum alias Explorer.Chain.Arbitrum.Reader alias Explorer.Chain.Block, as: FullBlock @@ -388,8 +386,7 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do end @doc """ - Retrieves full details of rollup blocks, including associated transactions, for each - block number specified in the input list. + Retrieves full details of rollup blocks, including associated transactions, for each block number specified in the input list. ## Parameters - `list_of_block_numbers`: A list of block numbers for which full block details are to be retrieved. @@ -399,23 +396,7 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do block number in the input list. Returns an empty list if no blocks are found for the given numbers. """ @spec rollup_blocks([FullBlock.block_number()]) :: [FullBlock.t()] - def rollup_blocks(list_of_block_numbers) - - def rollup_blocks([]), do: [] - - def rollup_blocks(list_of_block_numbers) - when is_list(list_of_block_numbers) do - query = - from( - block in FullBlock, - where: block.number in ^list_of_block_numbers - ) - - query - # :optional is used since a block may not have any transactions - |> Chain.join_associations(%{:transactions => :optional}) - |> Repo.all(timeout: :infinity) - end + def rollup_blocks(list_of_block_numbers), do: Reader.rollup_blocks(list_of_block_numbers) @doc """ Retrieves unfinalized L1 transactions that are involved in changing the statuses @@ -942,13 +923,7 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do # the block just after it rollup_tail = Application.get_all_env(:indexer)[:first_block] + 1 - query = - from( - block in FullBlock, - where: block.number == ^rollup_tail and block.consensus == true - ) - - if(is_nil(query |> Repo.one()), do: false, else: true) + Reader.rollup_block_exists?(rollup_tail) end @spec lifecycle_transaction_to_map(Arbitrum.LifecycleTransaction.t()) :: Arbitrum.LifecycleTransaction.to_import() From 208b0a20b23fa8176aba93d2ed9cebded527dd63 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 5 Aug 2024 15:52:28 +0300 Subject: [PATCH 050/363] feat: Public IPFS gateway URL (#10511) --- .../lib/block_scout_web/views/nft_helper.ex | 8 +++++++- config/runtime.exs | 3 ++- docker-compose/envs/common-blockscout.env | 4 ++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/nft_helper.ex b/apps/block_scout_web/lib/block_scout_web/views/nft_helper.ex index 1ccdf145f9ff..7515dc32bd78 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/nft_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/nft_helper.ex @@ -84,6 +84,12 @@ defmodule BlockScoutWeb.NFTHelper do defp ipfs_link(image_url, prefix) do ipfs_uid = String.slice(image_url, String.length(prefix)..-1//1) - "https://ipfs.io/ipfs/" <> ipfs_uid + public_ipfs_gateway_url = + :indexer + |> Application.get_env(:ipfs) + |> Keyword.get(:public_gateway_url) + |> String.trim_trailing("/") + + public_ipfs_gateway_url <> "/" <> ipfs_uid end end diff --git a/config/runtime.exs b/config/runtime.exs index 9f4bf0005a7d..19c2bd4e8c95 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -646,7 +646,8 @@ config :indexer, :ipfs, gateway_url_param_key: System.get_env("IPFS_GATEWAY_URL_PARAM_KEY"), gateway_url_param_value: System.get_env("IPFS_GATEWAY_URL_PARAM_VALUE"), gateway_url_param_location: - ConfigHelper.parse_catalog_value("IPFS_GATEWAY_URL_PARAM_LOCATION", ["query", "header"], true) + ConfigHelper.parse_catalog_value("IPFS_GATEWAY_URL_PARAM_LOCATION", ["query", "header"], true), + public_gateway_url: System.get_env("IPFS_PUBLIC_GATEWAY_URL", "https://ipfs.io/ipfs") 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 882f37241a73..313ac7b8f646 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -367,6 +367,10 @@ EIP_1559_ELASTICITY_MULTIPLIER=2 # AMPLITUDE_API_KEY= # AMPLITUDE_URL= # IPFS_GATEWAY_URL= +# IPFS_GATEWAY_URL_PARAM_KEY= +# IPFS_GATEWAY_URL_PARAM_VALUE= +# IPFS_GATEWAY_URL_PARAM_LOCATION= +# IPFS_PUBLIC_GATEWAY_URL= # ADDRESSES_TABS_COUNTERS_TTL=10m # DENORMALIZATION_MIGRATION_BATCH_SIZE= # DENORMALIZATION_MIGRATION_CONCURRENCY= From 273ede9ca035bd341a8d9e4ec9e1c0390aa65831 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 22:14:59 +0300 Subject: [PATCH 051/363] chore(deps): bump ex_cldr_units from 3.17.0 to 3.17.1 (#10561) Bumps [ex_cldr_units](https://github.com/elixir-cldr/cldr_units) from 3.17.0 to 3.17.1. - [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) --- updated-dependencies: - dependency-name: ex_cldr_units dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mix.lock b/mix.lock index de85f663498a..1f4454421b70 100644 --- a/mix.lock +++ b/mix.lock @@ -37,17 +37,17 @@ "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, "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.40", "f3534689f6b58f48aa3a9ac850d4f05832654fe257bf0549c08cc290035f70d5", [:mix], [], "hexpm", "cdb34f35892a45325bad21735fadb88033bcb7c4c296a999bde769783f53e46a"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 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", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, "ecto_sql": {:hex, :ecto_sql, "3.11.3", "4eb7348ff8101fbc4e6bbc5a4404a24fecbe73a3372d16569526b0cf34ebc195", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16 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", "e5f36e3d736b99c7fee3e631333b8394ade4bafe9d96d35669fca2d81c2be928"}, "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_abi": {:hex, :ex_abi, "0.8.0", "bb08827bd8d71dbb311c69ac55a008669dfabe2ce5b58d65f97c08c0aba60ec6", [:mix], [{:ex_keccak, "~> 0.7.5", [hex: :ex_keccak, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "bbdae12c186aeeb4c53dd7c7c57f457923602db315aa1f66d7427467c8ad77af"}, "ex_cldr": {:hex, :ex_cldr, "2.40.0", "624717778dbf0a8cd307f1576eabbd44470c16190172abf293fed24150440a5a", [:mix], [{:cldr_utils, "~> 2.28", [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", "113394b6dd23aaf7912da583aab103d9cf082b9821bc4a6e287543a895af7cb4"}, - "ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.16.1", "29317f533cb5ec046d04523256cca4090291e9157028f28731395149b06ff8b2", [:mix], [{:ex_cldr, "~> 2.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "095d5e973bf0ee066dd1153990d10cb6fa6d8ff0e028295bdce7a7821c70a0e4"}, + "ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.16.2", "670d96cc4fb18cfebd82488ed687742683be2d0725d66ec051578d4b13539aa8", [:mix], [{:ex_cldr, "~> 2.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "2ccfac2838f4df8c8e5424dbc68eb2f3ac9eeb45e10365050901f7ac7a914ce1"}, "ex_cldr_lists": {:hex, :ex_cldr_lists, "2.11.0", "1d39e75f0e493ccc95adfc85c55b4ca34f0771626350ce326d9ab8813d91444e", [: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", "8132b30a5506ae8a09e5c9a21c23fd60c8837ce6c3a1de9966d813eb78951695"}, - "ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.33.1", "49dc6e77e6d9ad22660aaa2480a7408ad3aedfbe517e4e83e5fe3a7bf5345770", [: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.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, "~> 2.16", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "c003bfaa3fdee6bab5195f128b94038c2ce1cf4879a759eef431dd075d9a5dac"}, - "ex_cldr_units": {:hex, :ex_cldr_units, "3.17.0", "f26dcde31a8fbb7808afa106ce2c7cbf38e0e0e0678ac523e795cdfdc67ab502", [:mix], [{:cldr_utils, "~> 2.25", [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.33.0", [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", "b9f09c420f5e3b86ed41f135751086bc59bf2bb8e633516e8d3e9f24d6d9e777"}, + "ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.33.2", "c5587a8d84214d9cc42e7827e4c3bed2aa9e52505a55b10540020725954ded2c", [: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.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, "~> 2.16", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "49f1dbaddc1ad6e3f496a97fa425d25b3ae89e8178ce0416d9909deaf2e5ad80"}, + "ex_cldr_units": {:hex, :ex_cldr_units, "3.17.1", "f03c7a138113511af903d0d2205b5cc01df1e599c28839ca2e1e78b7ca0bf2a2", [:mix], [{:cldr_utils, "~> 2.25", [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.33.0", [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", "7de1bf7ff7599cf4da9dd0f1a7b6e19ca8db83b883301f5dcd0f565aa5eaec8c"}, "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, "ex_json_schema": {:hex, :ex_json_schema, "0.10.2", "7c4b8c1481fdeb1741e2ce66223976edfb9bccebc8014f6aec35d4efe964fb71", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "37f43be60f8407659d4d0155a7e45e7f406dab1f827051d3d35858a709baf6a6"}, "ex_keccak": {:hex, :ex_keccak, "0.7.5", "f3b733173510d48ae9a1ea1de415e694b2651f35c787e63f33b5ed0013fbfd35", [:mix], [{:rustler, ">= 0.0.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.7", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "8a5e1cb7f96fff5e480ff6a121477b90c4fd8c150984086dffd98819f5d83763"}, @@ -79,7 +79,7 @@ "logger_json": {:hex, :logger_json, "5.1.4", "9e30a4f2e31a8b9e402bdc20bd37cf9b67d3a31f19d0b33082a19a06b4c50f6d", [:mix], [{:ecto, "~> 2.1 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.5.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "3f20eea58e406a33d3eb7814c7dff5accb503bab2ee8601e84da02976fa3934c"}, "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, - "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, "math": {:hex, :math, "0.7.0", "12af548c3892abf939a2e242216c3e7cbfb65b9b2fe0d872d05c6fb609f8127b", [:mix], [], "hexpm", "7987af97a0c6b58ad9db43eb5252a49fc1dfe1f6d98f17da9282e297f594ebc2"}, "meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"}, "memento": {:hex, :memento, "0.3.2", "38cfc8ff9bcb1adff7cbd0f3b78a762636b86dff764729d1c82d0464c539bdd0", [:mix], [], "hexpm", "25cf691a98a0cb70262f4a7543c04bab24648cb2041d937eb64154a8d6f8012b"}, From 059c698fa78c6a1b1b8db326e9ea39a12f8f49a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 09:27:12 +0300 Subject: [PATCH 052/363] chore(deps): bump postgrex from 0.18.0 to 0.19.0 (#10562) Bumps [postgrex](https://github.com/elixir-ecto/postgrex) from 0.18.0 to 0.19.0. - [Release notes](https://github.com/elixir-ecto/postgrex/releases) - [Changelog](https://github.com/elixir-ecto/postgrex/blob/master/CHANGELOG.md) - [Commits](https://github.com/elixir-ecto/postgrex/commits) --- updated-dependencies: - dependency-name: postgrex dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 1f4454421b70..cb43b587ebfb 100644 --- a/mix.lock +++ b/mix.lock @@ -110,7 +110,7 @@ "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, - "postgrex": {:hex, :postgrex, "0.18.0", "f34664101eaca11ff24481ed4c378492fed2ff416cd9b06c399e90f321867d7e", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "a042989ba1bc1cca7383ebb9e461398e3f89f868c92ce6671feb7ef132a252d1"}, + "postgrex": {:hex, :postgrex, "0.19.0", "f7d50e50cb42e0a185f5b9a6095125a9ab7e4abccfbe2ab820ab9aa92b71dbab", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "dba2d2a0a8637defbf2307e8629cb2526388ba7348f67d04ec77a5d6a72ecfae"}, "prometheus": {:hex, :prometheus, "4.11.0", "b95f8de8530f541bd95951e18e355a840003672e5eda4788c5fa6183406ba29a", [:mix, :rebar3], [{:quantile_estimator, "~> 0.2.1", [hex: :quantile_estimator, repo: "hexpm", optional: false]}], "hexpm", "719862351aabf4df7079b05dc085d2bbcbe3ac0ac3009e956671b1d5ab88247d"}, "prometheus_ecto": {:hex, :prometheus_ecto, "1.4.3", "3dd4da1812b8e0dbee81ea58bb3b62ed7588f2eae0c9e97e434c46807ff82311", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm", "8d66289f77f913b37eda81fd287340c17e61a447549deb28efc254532b2bed82"}, "prometheus_ex": {:git, "https://github.com/lanodan/prometheus.ex", "31f7fbe4b71b79ba27efc2a5085746c4011ceb8f", [branch: "fix/elixir-1.14"]}, From 05d8d198d44a5735a13d2f0a584c5cc6c0e218ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:07:39 +0300 Subject: [PATCH 053/363] chore(deps): bump gettext from 0.24.0 to 0.25.0 (#10560) Bumps [gettext](https://github.com/elixir-gettext/gettext) from 0.24.0 to 0.25.0. - [Changelog](https://github.com/elixir-gettext/gettext/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-gettext/gettext/compare/v0.24.0...v0.25.0) --- updated-dependencies: - dependency-name: gettext dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/mix.exs | 2 +- mix.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs index 7d458bf16b58..4662f1aaf478 100644 --- a/apps/block_scout_web/mix.exs +++ b/apps/block_scout_web/mix.exs @@ -106,7 +106,7 @@ defmodule BlockScoutWeb.Mixfile do # HTML CSS selectors for Phoenix controller tests {:floki, "~> 0.31"}, {:flow, "~> 1.2"}, - {:gettext, "~> 0.24.0"}, + {:gettext, "~> 0.25.0"}, {:hammer, "~> 6.0"}, {:httpoison, "~> 2.0"}, {:indexer, in_umbrella: true, runtime: false}, diff --git a/mix.lock b/mix.lock index cb43b587ebfb..1985ba88f158 100644 --- a/mix.lock +++ b/mix.lock @@ -57,14 +57,14 @@ "ex_utils": {:hex, :ex_utils, "0.1.7", "2c133e0bcdc49a858cf8dacf893308ebc05bc5fba501dc3d2935e65365ec0bf3", [:mix], [], "hexpm", "66d4fe75285948f2d1e69c2a5ddd651c398c813574f8d36a9eef11dc20356ef6"}, "exactor": {:hex, :exactor, "2.2.4", "5efb4ddeb2c48d9a1d7c9b465a6fffdd82300eb9618ece5d34c3334d5d7245b1", [:mix], [], "hexpm", "1222419f706e01bfa1095aec9acf6421367dcfab798a6f67c54cf784733cd6b5"}, "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm", "32e95820a97cffea67830e91514a2ad53b888850442d6d395f53a1ac60c82e07"}, - "expo": {:hex, :expo, "0.5.2", "beba786aab8e3c5431813d7a44b828e7b922bfa431d6bfbada0904535342efe2", [:mix], [], "hexpm", "8c9bfa06ca017c9cb4020fabe980bc7fdb1aaec059fd004c2ab3bff03b1c599c"}, + "expo": {:hex, :expo, "1.0.0", "647639267e088717232f4d4451526e7a9de31a3402af7fcbda09b27e9a10395a", [:mix], [], "hexpm", "18d2093d344d97678e8a331ca0391e85d29816f9664a25653fd7e6166827827c"}, "exvcr": {:hex, :exvcr, "0.15.1", "772db4d065f5136c6a984c302799a79e4ade3e52701c95425fa2229dd6426886", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:finch, "~> 0.16", [hex: :finch, repo: "hexpm", optional: true]}, {:httpoison, "~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "de4fc18b1d672d9b72bc7468735e19779aa50ea963a1f859ef82cd9e294b13e3"}, "file_info": {:hex, :file_info, "0.0.4", "2e0e77f211e833f38ead22cb29ce53761d457d80b3ffe0ffe0eb93880b0963b2", [:mix], [{:mimetype_parser, "~> 0.1.2", [hex: :mimetype_parser, repo: "hexpm", optional: false]}], "hexpm", "50e7ad01c2c8b9339010675fe4dc4a113b8d6ca7eddce24d1d74fd0e762781a5"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"}, "flow": {:hex, :flow, "1.2.4", "1dd58918287eb286656008777cb32714b5123d3855956f29aa141ebae456922d", [:mix], [{:gen_stage, "~> 1.0", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm", "874adde96368e71870f3510b91e35bc31652291858c86c0e75359cbdd35eb211"}, "gen_stage": {:hex, :gen_stage, "1.2.1", "19d8b5e9a5996d813b8245338a28246307fd8b9c99d1237de199d21efc4c76a1", [:mix], [], "hexpm", "83e8be657fa05b992ffa6ac1e3af6d57aa50aace8f691fcf696ff02f8335b001"}, - "gettext": {:hex, :gettext, "0.24.0", "6f4d90ac5f3111673cbefc4ebee96fe5f37a114861ab8c7b7d5b30a1108ce6d8", [:mix], [{:expo, "~> 0.5.1", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "bdf75cdfcbe9e4622dd18e034b227d77dd17f0f133853a1c73b97b3d6c770e8b"}, + "gettext": {:hex, :gettext, "0.25.0", "98a95a862a94e2d55d24520dd79256a15c87ea75b49673a2e2f206e6ebc42e5d", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "38e5d754e66af37980a94fb93bb20dcde1d2361f664b0a19f01e87296634051f"}, "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~>2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, "hammer": {:hex, :hammer, "6.2.1", "5ae9c33e3dceaeb42de0db46bf505bd9c35f259c8defb03390cd7556fea67ee2", [:mix], [{:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm", "b9476d0c13883d2dc0cc72e786bac6ac28911fba7cc2e04b70ce6a6d9c4b2bdc"}, "hammer_backend_redis": {:hex, :hammer_backend_redis, "6.1.2", "eb296bb4924928e24135308b2afc189201fd09411c870c6bbadea444a49b2f2c", [:mix], [{:hammer, "~> 6.0", [hex: :hammer, repo: "hexpm", optional: false]}, {:redix, "~> 1.1", [hex: :redix, repo: "hexpm", optional: false]}], "hexpm", "217ea066278910543a5e9b577d5bf2425419446b94fe76bdd9f255f39feec9fa"}, From 0555eaa6565c58430877ca4a1897290fc4927110 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 14:10:16 +0300 Subject: [PATCH 054/363] chore(deps): bump tesla from 1.11.2 to 1.12.1 (#10559) Bumps [tesla](https://github.com/elixir-tesla/tesla) from 1.11.2 to 1.12.1. - [Release notes](https://github.com/elixir-tesla/tesla/releases) - [Commits](https://github.com/elixir-tesla/tesla/compare/v1.11.2...v1.12.1) --- updated-dependencies: - dependency-name: tesla dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/explorer/mix.exs | 2 +- mix.exs | 2 +- mix.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index 534148a77a37..c7920b8fb9a4 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -114,7 +114,7 @@ defmodule Explorer.Mixfile do # `Timex.Duration` for `Explorer.Counters.AverageBlockTime.average_block_time/0` {:timex, "~> 3.7.1"}, {:con_cache, "~> 1.0"}, - {:tesla, "~> 1.11.1"}, + {:tesla, "~> 1.12.1"}, {:cbor, "~> 1.0"}, {:cloak_ecto, "~> 1.3.0"}, {:redix, "~> 1.1"}, diff --git a/mix.exs b/mix.exs index f3a29e2a0e40..b07340331ee5 100644 --- a/mix.exs +++ b/mix.exs @@ -94,7 +94,7 @@ defmodule BlockScout.Mixfile do [ {:prometheus_ex, git: "https://github.com/lanodan/prometheus.ex", branch: "fix/elixir-1.14", override: true}, {:absinthe_plug, git: "https://github.com/blockscout/absinthe_plug.git", tag: "1.5.8", override: true}, - {:tesla, "~> 1.11.1"}, + {:tesla, "~> 1.12.1"}, # Documentation {:ex_doc, "~> 0.34.1", only: :dev, runtime: false}, {:number, "~> 1.0.3"} diff --git a/mix.lock b/mix.lock index 1985ba88f158..70549d26d1a3 100644 --- a/mix.lock +++ b/mix.lock @@ -134,7 +134,7 @@ "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, - "tesla": {:hex, :tesla, "1.11.2", "24707ac48b52f72f88fc05d242b1c59a85d1ee6f16f19c312d7d3419665c9cd5", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "c549cd03aec6a7196a641689dd378b799e635eb393f689b4bd756f750c7a4014"}, + "tesla": {:hex, :tesla, "1.12.1", "fe2bf4250868ee72e5d8b8dfa408d13a00747c41b7237b6aa3b9a24057346681", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2391efc6243d37ead43afd0327b520314c7b38232091d4a440c1212626fdd6e7"}, "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, "toml": {:hex, :toml, "0.6.2", "38f445df384a17e5d382befe30e3489112a48d3ba4c459e543f748c2f25dd4d1", [:mix], [], "hexpm", "d013e45126d74c0c26a38d31f5e8e9b83ea19fc752470feb9a86071ca5a672fa"}, "typed_ecto_schema": {:hex, :typed_ecto_schema, "0.4.1", "a373ca6f693f4de84cde474a67467a9cb9051a8a7f3f615f1e23dc74b75237fa", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm", "85c6962f79d35bf543dd5659c6adc340fd2480cacc6f25d2cc2933ea6e8fcb3b"}, From e876e037638777d1915200db7cbba24cc3f1d808 Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Tue, 6 Aug 2024 05:11:55 -0600 Subject: [PATCH 055/363] fix: proper handling confirmations for Arbitrum rollup block in the middle of a batch (#10482) * Proper handling confirmation for block in middle of a batch * proper check for gaps, proper output for range of blocks * adjusted approach to revisit missing confirmations * module documentation updated * function comment extended --- .../arbitrum/tracking_batches_statuses.ex | 36 +- .../indexer/fetcher/arbitrum/utils/helper.ex | 124 ++- .../lib/indexer/fetcher/arbitrum/utils/rpc.ex | 51 ++ .../fetcher/arbitrum/workers/new_batches.ex | 137 ++-- .../arbitrum/workers/new_confirmations.ex | 760 ++++++++++++++---- config/runtime.exs | 1 + docker-compose/envs/common-blockscout.env | 1 + 7 files changed, 827 insertions(+), 283 deletions(-) diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/tracking_batches_statuses.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/tracking_batches_statuses.ex index 2b90b6d523f3..af44220f0d02 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/tracking_batches_statuses.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/tracking_batches_statuses.ex @@ -168,18 +168,11 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do l1_start_block = Rpc.get_l1_start_block(state.config.l1_start_block, json_l1_rpc_named_arguments) - # TODO: it is necessary to develop a way to discover missed batches to cover the case - # when the batch #1, #2 and #4 are in DB, but #3 is not - # One of the approaches is to look deeper than the latest committed batch and - # check whether batches were already handled or not. new_batches_start_block = Db.l1_block_to_discover_latest_committed_batch(l1_start_block) historical_batches_end_block = Db.l1_block_to_discover_earliest_committed_batch(l1_start_block - 1) new_confirmations_start_block = Db.l1_block_of_latest_confirmed_block(l1_start_block) - # TODO: it is necessary to develop a way to discover missed executions. - # One of the approaches is to look deeper than the latest execution and - # check whether executions were already handled or not. new_executions_start_block = Db.l1_block_to_discover_latest_execution(l1_start_block) historical_executions_end_block = Db.l1_block_to_discover_earliest_execution(l1_start_block - 1) @@ -268,22 +261,11 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do # block for the next iteration of new confirmation discovery. @impl GenServer def handle_info(:check_new_confirmations, state) do - {handle_duration, {retcode, end_block}} = :timer.tc(&NewConfirmations.discover_new_rollup_confirmation/1, [state]) + {handle_duration, {_, new_state}} = :timer.tc(&NewConfirmations.discover_new_rollup_confirmation/1, [state]) Process.send(self(), :check_new_executions, []) - updated_fields = - case retcode do - :ok -> %{} - _ -> %{historical_confirmations_end_block: nil, historical_confirmations_start_block: nil} - end - |> Map.merge(%{ - # credo:disable-for-previous-line Credo.Check.Refactor.PipeChainStart - duration: increase_duration(state.data, handle_duration), - new_confirmations_start_block: end_block + 1 - }) - - new_data = Map.merge(state.data, updated_fields) + new_data = Map.put(new_state.data, :duration, increase_duration(state.data, handle_duration)) {:noreply, %{state | data: new_data}} end @@ -417,22 +399,12 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do # end blocks for the next iteration of historical confirmations discovery. @impl GenServer def handle_info(:check_historical_confirmations, state) do - {handle_duration, {retcode, {start_block, end_block}}} = + {handle_duration, {_, new_state}} = :timer.tc(&NewConfirmations.discover_historical_rollup_confirmation/1, [state]) Process.send(self(), :check_historical_executions, []) - updated_fields = - case retcode do - :ok -> %{historical_confirmations_end_block: start_block - 1, historical_confirmations_start_block: end_block} - _ -> %{historical_confirmations_end_block: nil, historical_confirmations_start_block: nil} - end - |> Map.merge(%{ - # credo:disable-for-previous-line Credo.Check.Refactor.PipeChainStart - duration: increase_duration(state.data, handle_duration) - }) - - new_data = Map.merge(state.data, updated_fields) + new_data = Map.put(new_state.data, :duration, increase_duration(state.data, handle_duration)) {:noreply, %{state | data: new_data}} end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/helper.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/helper.ex index cd114749175c..d7abb6aa0080 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/helper.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/helper.ex @@ -1,4 +1,8 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Helper do + alias Explorer.Chain.Arbitrum.LifecycleTransaction + + import Indexer.Fetcher.Arbitrum.Utils.Logging, only: [log_info: 1] + @moduledoc """ Provides utility functions to support the handling of Arbitrum-specific data fetching and processing in the indexer. """ @@ -55,14 +59,7 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Helper do %{binary() => %{:block => non_neg_integer(), optional(any()) => any()}}, %{non_neg_integer() => DateTime.t()}, boolean() - ) :: %{ - binary() => %{ - :block => non_neg_integer(), - :timestamp => DateTime.t(), - :status => :unfinalized | :finalized, - optional(any()) => any() - } - } + ) :: %{binary() => LifecycleTransaction.to_import()} def extend_lifecycle_txs_with_ts_and_status(lifecycle_txs, blocks_to_ts, track_finalization?) when is_map(lifecycle_txs) and is_map(blocks_to_ts) and is_boolean(track_finalization?) do lifecycle_txs @@ -84,6 +81,44 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Helper do end) end + @doc """ + Compares a lifecycle transaction with new block number and timestamp, and updates if necessary. + + This function checks if the given lifecycle transaction has the same block number + and timestamp as the provided values. If they are the same, it returns `{:same, nil}`. + If they differ, it updates the transaction with the new block number and timestamp, + logs the update, and returns `{:updated, updated_tx}`. + + ## Parameters + - `tx`: The lifecycle transaction to compare and potentially update. + - `{new_block_num, new_ts}`: A tuple containing the new block number and timestamp. + - `tx_type_str`: A string describing the type of the transaction for logging purposes. + + ## Returns + - `{:same, nil}` if the transaction block number and timestamp are the same as the provided values. + - `{:updated, updated_tx}` if the transaction was updated with the new block number and timestamp. + """ + @spec compare_lifecycle_tx_and_update( + LifecycleTransaction.to_import(), + {non_neg_integer(), DateTime.t()}, + String.t() + ) :: {:same, nil} | {:updated, LifecycleTransaction.to_import()} + def compare_lifecycle_tx_and_update(tx, {new_block_num, new_ts}, tx_type_str) do + if tx.block_number == new_block_num and DateTime.compare(tx.timestamp, new_ts) == :eq do + {:same, nil} + else + log_info( + "The #{tx_type_str} transaction 0x#{tx.hash |> Base.encode16(case: :lower)} will be updated with the new block number and timestamp" + ) + + {:updated, + Map.merge(tx, %{ + block_number: new_block_num, + timestamp: new_ts + })} + end + end + @doc """ Converts a binary data to a hexadecimal string. @@ -97,4 +132,77 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Helper do def bytes_to_hex_str(data) do "0x" <> Base.encode16(data, case: :lower) end + + @doc """ + Executes a function over a specified block range in chunks. + + This function divides a block range into smaller chunks and executes the provided + function for each chunk. It collects the results of each function execution and + returns them as a list of tuples. Each tuple contains the start and end block numbers + of the chunk and the result of the function execution for that chunk. + + If `halt_on_error` is set to `true` and the function returns anything other than + `:ok` or `{:ok, ...}`, the execution halts. However, the result of the last function + execution is still included in the resulting list. + + ## Parameters + - `start_block`: The starting block number of the range. + - `end_block`: The ending block number of the range. + - `chunk_size`: The size of each chunk in terms of block numbers. + - `func`: The function to execute for each chunk. The function should accept two + arguments: the start and end block numbers of the chunk. + - `halt_on_error` (optional): A boolean flag indicating whether to halt execution + if an error occurs. Defaults to `false`. + + ## Returns + - A list of tuples. Each tuple contains: + - A tuple with the start and end block numbers of the chunk. + - The result of the function execution for that chunk. + + ## Examples + + iex> execute_for_block_range_in_chunks(5, 25, 7, fn (start_block, end_block) -> + ...> {:ok, start_block, end_block} + ...> end) + [ + {{5, 11}, {:ok, 5, 11}}, + {{12, 18}, {:ok, 12, 18}}, + {{19, 25}, {:ok, 19, 25}} + ] + """ + @spec execute_for_block_range_in_chunks( + non_neg_integer(), + non_neg_integer(), + non_neg_integer(), + fun() + ) :: [ + {{non_neg_integer(), non_neg_integer()}, any()} + ] + @spec execute_for_block_range_in_chunks( + non_neg_integer(), + non_neg_integer(), + non_neg_integer(), + fun(), + boolean() + ) :: [ + {{non_neg_integer(), non_neg_integer()}, any()} + ] + def execute_for_block_range_in_chunks(start_block, end_block, chunk_size, func, halt_on_error \\ false) do + 0..div(end_block - start_block, chunk_size) + |> Enum.reduce_while([], fn i, res -> + chunk_start = start_block + i * chunk_size + chunk_end = min(chunk_start + chunk_size - 1, end_block) + + func_res = func.(chunk_start, chunk_end) + acc_res = [{{chunk_start, chunk_end}, func_res} | res] + + case {halt_on_error, func_res} do + {false, _} -> {:cont, acc_res} + {true, :ok} -> {:cont, acc_res} + {true, {:ok, _}} -> {:cont, acc_res} + {true, _} -> {:halt, acc_res} + end + end) + |> Enum.reverse() + end end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex index 8fc1f2afd9c5..6f4ef02ba316 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex @@ -422,6 +422,57 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Rpc do end end + @doc """ + Retrieves the safe and latest L1 block numbers. + + This function fetches the latest block number from the chain and tries to determine + the safe block number. If the RPC node does not support the safe block feature or + if the safe block is too far behind the latest block, the safe block is determined + based on the finalization threshold. In both cases, it steps back from the latest + block to mark some blocks as unfinalized. + + ## Parameters + - `json_rpc_named_arguments`: The named arguments for the JSON RPC call. + - `hard_limit`: The maximum number of blocks to step back when determining the safe block. + + ## Returns + - A tuple containing the safe block number and the latest block number. + """ + @spec get_safe_and_latest_l1_blocks(EthereumJSONRPC.json_rpc_named_arguments(), non_neg_integer()) :: + {EthereumJSONRPC.block_number(), EthereumJSONRPC.block_number()} + def get_safe_and_latest_l1_blocks(json_rpc_named_arguments, hard_limit) do + finalization_threshold = Application.get_all_env(:indexer)[Indexer.Fetcher.Arbitrum][:l1_finalization_threshold] + + {safe_chain_block, is_latest?} = IndexerHelper.get_safe_block(json_rpc_named_arguments) + + latest_chain_block = + case is_latest? do + true -> + safe_chain_block + + false -> + {:ok, latest_block} = + IndexerHelper.get_block_number_by_tag("latest", json_rpc_named_arguments, get_resend_attempts()) + + latest_block + end + + safe_block = + if safe_chain_block < latest_chain_block + 1 - finalization_threshold or is_latest? do + # The first condition handles the case when the safe block is too far behind + # the latest block (L3 case). + # The second condition handles the case when the L1 RPC node does not support + # the safe block feature (non standard Arbitrum deployments). + # In both cases, it is necessary to step back a bit from the latest block to + # suspect these blocks as unfinalized. + latest_chain_block + 1 - min(finalization_threshold, hard_limit) + else + safe_chain_block + end + + {safe_block, latest_chain_block} + end + @doc """ Identifies the block range for a batch by using the block number located on one end of the range. diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex index 8543d339eb15..6a5e8c389e5e 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex @@ -33,6 +33,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do alias Indexer.Fetcher.Arbitrum.DA.Common, as: DataAvailabilityInfo alias Indexer.Fetcher.Arbitrum.DA.{Anytrust, Celestia} alias Indexer.Fetcher.Arbitrum.Utils.{Db, Logging, Rpc} + alias Indexer.Fetcher.Arbitrum.Utils.Helper, as: ArbitrumHelper alias Indexer.Helper, as: IndexerHelper alias Explorer.Chain @@ -44,8 +45,6 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # keccak256("SequencerBatchDelivered(uint256,bytes32,bytes32,bytes32,uint256,(uint64,uint64,uint64,uint64),uint8)") @event_sequencer_batch_delivered "0x7394f4a19a13c7b92b5bb71033245305946ef78452f7b4986ac1390b5df4ebd7" - @max_depth_for_safe_block 1000 - @doc """ Discovers and imports new batches of rollup transactions within the current L1 block range. @@ -115,31 +114,15 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do ) do # Requesting the "latest" block instead of "safe" allows to catch new batches # without latency. - {:ok, latest_block} = - IndexerHelper.get_block_number_by_tag( - "latest", - l1_rpc_config.json_rpc_named_arguments, - Rpc.get_resend_attempts() - ) - - {safe_chain_block, _} = IndexerHelper.get_safe_block(l1_rpc_config.json_rpc_named_arguments) - - # max() cannot be used here since l1_rpc_config.logs_block_range must not - # be taken into account to identify if it is L3 or not - safe_block = - if safe_chain_block < latest_block + 1 - @max_depth_for_safe_block do - # The case of L3, the safe block is too far behind the latest block, - # therefore it is assumed that there is no so deep re-orgs there. - latest_block + 1 - min(@max_depth_for_safe_block, l1_rpc_config.logs_block_range) - else - safe_chain_block - end # It is necessary to re-visit some amount of the previous blocks to ensure that # no batches are missed due to reorgs. The amount of blocks to re-visit depends - # either on the current safe block but must not exceed @max_depth_for_safe_block - # (or L1 RPC max block range for getting logs) since on L3 chains the safe block - # could be too far behind the latest block. + # on the current safe block or the block which is considered as safest in case + # of L3 (where the safe block could be too far behind the latest block) or if + # RPC does not support "safe" block. + {safe_block, latest_block} = + Rpc.get_safe_and_latest_l1_blocks(l1_rpc_config.json_rpc_named_arguments, l1_rpc_config.logs_block_range) + # At the same time it does not make sense to re-visit blocks that will be # re-visited by the historical batches discovery process. # If the new batches discovery process does not reach the chain head previously @@ -154,30 +137,23 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # Since with taking the safe block into account, the range safe_start_block..end_block # could be larger than L1 RPC max block range for getting logs, it is necessary to # divide the range into the chunks - safe_start_block - |> Stream.unfold(fn - current when current > end_block -> - nil - - current -> - next = min(current + l1_rpc_config.logs_block_range - 1, end_block) - {current, next + 1} - end) - |> Stream.each(fn chunk_start -> - chunk_end = min(chunk_start + l1_rpc_config.logs_block_range - 1, end_block) - - discover( - sequencer_inbox_address, - chunk_start, - chunk_end, - new_batches_limit, - messages_to_blocks_shift, - l1_rpc_config, - node_interface_address, - rollup_rpc_config - ) - end) - |> Stream.run() + ArbitrumHelper.execute_for_block_range_in_chunks( + safe_start_block, + end_block, + l1_rpc_config.logs_block_range, + fn chunk_start, chunk_end -> + discover( + sequencer_inbox_address, + chunk_start, + chunk_end, + new_batches_limit, + messages_to_blocks_shift, + l1_rpc_config, + node_interface_address, + rollup_rpc_config + ) + end + ) {:ok, end_block} else @@ -532,25 +508,25 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do rollup_rpc_config ) do Enum.each(l1_block_ranges, fn {start_block, end_block} -> - Enum.each(0..div(end_block - start_block, l1_rpc_config.logs_block_range), fn i -> - start_block = start_block + i * l1_rpc_config.logs_block_range - end_block = min(start_block + l1_rpc_config.logs_block_range - 1, end_block) - - log_info("Block range for missing batches discovery: #{start_block}..#{end_block}") - - # `do_discover` is not used here to demonstrate the need to fetch batches - # which are already historical - discover_historical( - sequencer_inbox_address, - start_block, - end_block, - new_batches_limit, - messages_to_blocks_shift, - l1_rpc_config, - node_interface_address, - rollup_rpc_config - ) - end) + ArbitrumHelper.execute_for_block_range_in_chunks( + start_block, + end_block, + l1_rpc_config.logs_block_range, + fn chunk_start, chunk_end -> + # `do_discover` is not used here to demonstrate the need to fetch batches + # which are already historical + discover_historical( + sequencer_inbox_address, + chunk_start, + chunk_end, + new_batches_limit, + messages_to_blocks_shift, + l1_rpc_config, + node_interface_address, + rollup_rpc_config + ) + end + ) end) end @@ -699,9 +675,11 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # This function analyzes SequencerBatchDelivered event logs to identify new batches # and retrieves their details, avoiding the reprocessing of batches already known # in the database. It enriches the details of new batches with data from corresponding - # L1 transactions and blocks, including timestamps and block ranges. The function - # then prepares batches, associated rollup blocks and transactions, lifecycle - # transactions and Data Availability related records for database import. + # L1 transactions and blocks, including timestamps and block ranges. The lifecycle + # transactions for already known batches are updated with actual block numbers and + # timestamps. The function then prepares batches, associated rollup blocks and + # transactions, lifecycle transactions and Data Availability related records for + # database import. # Additionally, L2-to-L1 messages initiated in the rollup blocks associated with the # discovered batches are retrieved from the database, marked as `:sent`, and prepared # for database import. @@ -1341,21 +1319,12 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do block_num = existing_commitment_txs[tx.hash] ts = block_to_ts[block_num] - if tx.block_number == block_num and DateTime.compare(tx.timestamp, ts) == :eq do - txs - else - log_info( - "The commitment transaction 0x#{tx.hash |> Base.encode16(case: :lower)} will be updated with the new block number and timestamp" - ) + case ArbitrumHelper.compare_lifecycle_tx_and_update(tx, {block_num, ts}, "commitment") do + {:updated, updated_tx} -> + Map.put(txs, tx.hash, updated_tx) - Map.put( - txs, - tx.hash, - Map.merge(tx, %{ - block_number: block_num, - timestamp: ts - }) - ) + _ -> + txs end end) end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_confirmations.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_confirmations.ex index ceb0d1695df7..9631ccf78816 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_confirmations.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_confirmations.ex @@ -24,10 +24,34 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do confirmation (where X is the rollup block mentioned in an even earlier confirmation). - Currently, the process of discovering confirmed rollup blocks works with any - position of the top confirmed block in a batch. Later, if it is confirmed that - the top block in a confirmation always aligns with the last block in a batch, - this approach to rollup block discovery can be revisited for simplification. + Since the confirmations discovery process is asynchronous with respect to the + block fetching process and the batches discovery process, there could be + situations when the information about rollup blocks or their linkage with a + batch is not available yet. Here is a list of possible scenarios and expected + behavior: + 1. A rollup block required to proceed with the new confirmation discovery is + not indexed yet, or the batch where this block is included is not indexed + yet. + - The new confirmation discovery process will proceed with discovering new + confirmations and the L1 blocks range where the confirmation handling is + aborted will be passed to the historical confirmations discovery process. + 2. A rollup block required to proceed with the historical confirmation discovery + is not indexed yet, or the batch where this block is included is not indexed + yet. + - The historical confirmation discovery process will proceed with the same + L1 blocks range where the confirmation handling is aborted until this + confirmation is handled properly. + + As it is clear from the above, the historical confirmation discovery process + could be interrupted by the new confirmation discovery process. As soon as the + historical confirmation discovery process reaches the lower end of the L1 block + range where the new confirmation discovery process is aborted, the historical + confirmation discovery process will request the database to provide the next L1 + block range of missing confirmations. Such a range could be closed when there + are end and start L1 blocks in which a missing confirmation is expected, or + open-ended where the start block is not defined and the end block is the block + preceding the L1 block where a confirmation was already handled by the discovery + process. """ import EthereumJSONRPC, only: [quantity_to_integer: 1] @@ -62,12 +86,25 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do Discovers and processes new confirmations of rollup blocks within a calculated block range. This function identifies the appropriate L1 block range for discovering new - rollup confirmations. It fetches logs representing `SendRootUpdated` events - within this range to identify the new tops of rollup block confirmations. The + rollup confirmations. In order to make sure that no confirmation is missed due + to re-orgs, it adjusts the range to re-inspect some L1 blocks in the past. + Therefore the lower bound of the L1 blocks range is identified based on the + safe block or the block which is considered as safest if RPC does not support + "safe" block retrieval. + + Then the function fetches logs representing `SendRootUpdated` events within + the found range to identify the new tops of rollup block confirmations. The discovered confirmations are processed to update the status of rollup blocks - and L2-to-L1 messages accordingly. Eventually, updated rollup blocks, - cross-chain messages, and newly constructed lifecycle transactions are imported - into the database. + and L2-to-L1 messages accordingly. Eventually, updated rollup blocks, cross-chain + messages, and newly constructed lifecycle transactions are imported into the + database. + + After processing the confirmations, the function updates the state to prepare + for the next iteration. It adjusts the `new_confirmations_start_block` to the + block number after the last processed block. If a confirmation is missed, the + range for the next iteration of the historical confirmations discovery process + is adjusted to re-inspect the range where the confirmation was not handled + properly. ## Parameters - A map containing: @@ -77,13 +114,9 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do confirmation discovery. ## Returns - - `{retcode, end_block}` where `retcode` is either `:ok` or - `:confirmation_missed` indicating the success or failure of the discovery - process, and `end_block` is used to determine the start block number for the - next iteration of new confirmations discovery. - - `{:ok, start_block - 1}` if there are no new blocks to be processed, - indicating that the current start block should be reconsidered in the next - iteration. + - `{:ok, new_state}`: If the discovery process completes successfully. + - `{:confirmation_missed, new_state}`: If a confirmation is missed and further + action is needed. """ @spec discover_new_rollup_confirmation(%{ :config => %{ @@ -96,57 +129,165 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do }, optional(any()) => any() }, - :data => %{:new_confirmations_start_block => non_neg_integer(), optional(any()) => any()}, + :data => %{ + :new_confirmations_start_block => non_neg_integer(), + :historical_confirmations_end_block => non_neg_integer(), + optional(any()) => any() + }, optional(any()) => any() - }) :: {:confirmation_missed, non_neg_integer()} | {:ok, non_neg_integer()} + }) :: + {:ok | :confirmation_missed, + %{ + :data => %{ + :new_confirmations_start_block => non_neg_integer(), + :historical_confirmations_end_block => nil | non_neg_integer(), + :historical_confirmations_start_block => nil | non_neg_integer(), + optional(any()) => any() + }, + optional(any()) => any() + }} def discover_new_rollup_confirmation( %{ config: %{ l1_rpc: l1_rpc_config, l1_outbox_address: outbox_address }, - data: %{new_confirmations_start_block: start_block} - } = _state + data: %{ + new_confirmations_start_block: start_block, + historical_confirmations_end_block: historical_confirmations_end_block + } + } = state ) do - # It makes sense to use "safe" here. Blocks are confirmed with delay in one week - # (applicable for ArbitrumOne and Nova), so 10 mins delay is not significant - {:ok, latest_block} = - IndexerHelper.get_block_number_by_tag( - if(l1_rpc_config.finalized_confirmations, do: "safe", else: "latest"), - l1_rpc_config.json_rpc_named_arguments, - Rpc.get_resend_attempts() - ) + {safe_start_block, latest_block} = + if l1_rpc_config.finalized_confirmations do + # It makes sense to use "safe" here. Blocks are confirmed with delay in one week + # (applicable for ArbitrumOne and Nova), so 10 mins delay is not significant. + # By using "safe" we can avoid re-visiting the same blocks in case of reorgs. + {safe_chain_block, _} = IndexerHelper.get_safe_block(l1_rpc_config.json_rpc_named_arguments) + + {start_block, safe_chain_block} + else + # There are situations when it could be necessary to react on L1 confirmation + # transactions earlier than the safe block. For example, for testnets. + # Another situation when the rollup uses L1 RPC which does not support "safe" + # block retrieval. + # In both cases it is desired to re-visit some amount head blocks to ensure + # that no confirmation is missed due to reorgs. + + # The amount of blocks to re-visit depends on the current safe block or the + # block which is considered as safest if RPC does not support "safe" block. + {safe_block, latest_block} = + Rpc.get_safe_and_latest_l1_blocks(l1_rpc_config.json_rpc_named_arguments, l1_rpc_config.logs_block_range) + + # If the new confirmations discovery process does not reach the chain head + # previously no need to re-visit the blocks. + {min(start_block, safe_block), latest_block} + end + + # If ranges for the new confirmations discovery and the historical confirmations + # discovery are overlapped - it could be after confirmations gap identification, + # it is necessary to adjust the start block for the new confirmations discovery. + actual_start_block = + if is_nil(historical_confirmations_end_block) do + safe_start_block + else + max(safe_start_block, historical_confirmations_end_block + 1) + end end_block = min(start_block + l1_rpc_config.logs_block_range - 1, latest_block) - if start_block <= end_block do - log_info("Block range for new rollup confirmations discovery: #{start_block}..#{end_block}") + if actual_start_block <= end_block do + log_info("Block range for new rollup confirmations discovery: #{actual_start_block}..#{end_block}") - retcode = - discover( - outbox_address, - start_block, + # Since for the case l1_rpc_config.finalized_confirmations = false the range + # actual_start_block..end_block could be larger than L1 RPC max block range for + # getting logs, it is necessary to divide the range into the chunks. + results = + ArbitrumHelper.execute_for_block_range_in_chunks( + actual_start_block, end_block, - l1_rpc_config + l1_rpc_config.logs_block_range, + fn chunk_start, chunk_end -> + discover( + outbox_address, + chunk_start, + chunk_end, + l1_rpc_config + ) + end, + true ) - {retcode, end_block} + # Since halt_on_error was set to true, it is OK to consider the last result + # only. + {{start_block, end_block}, retcode} = List.last(results) + + case retcode do + :ok -> + {retcode, state_for_next_iteration_new(state, end_block + 1)} + + :confirmation_missed -> + {retcode, state_for_next_iteration_new(state, end_block + 1, {start_block, end_block})} + end else - {:ok, start_block - 1} + {:ok, state_for_next_iteration_new(state, start_block)} end end + # Updates the state for the next iteration of new confirmations discovery. + @spec state_for_next_iteration_new( + %{ + :data => map(), + optional(any()) => any() + }, + non_neg_integer(), + nil | {non_neg_integer(), non_neg_integer()} + ) :: + %{ + :data => %{ + :new_confirmations_start_block => non_neg_integer(), + :historical_confirmations_end_block => non_neg_integer(), + :historical_confirmations_start_block => non_neg_integer(), + optional(any()) => any() + }, + optional(any()) => any() + } + defp state_for_next_iteration_new(prev_state, start_block, historical_blocks \\ nil) do + data_for_new_confirmations = + %{new_confirmations_start_block: start_block} + + data_to_update = + case historical_blocks do + nil -> + data_for_new_confirmations + + {start_block, end_block} -> + Map.merge(data_for_new_confirmations, %{ + historical_confirmations_start_block: start_block, + historical_confirmations_end_block: end_block + }) + end + + %{ + prev_state + | data: Map.merge(prev_state.data, data_to_update) + } + end + @doc """ Discovers and processes historical confirmations of rollup blocks within a calculated block range. This function determines the appropriate L1 block range for discovering historical rollup confirmations based on the provided end block or from the - analysis of confirmations missed in the database. It then fetches logs - representing `SendRootUpdated` events within this range to identify the - historical tops of rollup block confirmations. The discovered confirmations - are processed to update the status of rollup blocks and L2-to-L1 messages - accordingly. Eventually, updated rollup blocks, cross-chain messages, and newly - constructed lifecycle transactions are imported into the database. + analysis of confirmations missed in the database. It fetches logs representing + `SendRootUpdated` events within this range to identify the historical tops of + rollup block confirmations. The discovered confirmations are processed to update + the status of rollup blocks and L2-to-L1 messages accordingly. Eventually, + updated rollup blocks, cross-chain messages, and newly constructed lifecycle + transactions are imported into the database. + + After processing the confirmations, the function updates the state with the + blocks range for the next iteration. ## Parameters - A map containing: @@ -157,10 +298,9 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do for historical confirmation discovery. ## Returns - - `{retcode, {start_block, interim_start_block}}` where - - `retcode` is either `:ok` or `:confirmation_missed` - - `start_block` is the starting block for the next iteration of discovery - - `interim_start_block` is the end block for the next iteration of discovery + - `{:ok, new_state}`: If the discovery process completes successfully. + - `{:confirmation_missed, new_state}`: If a confirmation is missed and further + action is needed. """ @spec discover_historical_rollup_confirmation(%{ :config => %{ @@ -182,8 +322,15 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do }, optional(any()) => any() }) :: - {:confirmation_missed, {nil | non_neg_integer(), nil | non_neg_integer()}} - | {:ok, {nil | non_neg_integer(), nil | non_neg_integer()}} + {:ok | :confirmation_missed, + %{ + :data => %{ + :historical_confirmations_end_block => nil | non_neg_integer(), + :historical_confirmations_start_block => nil | non_neg_integer(), + optional(any()) => any() + }, + optional(any()) => any() + }} def discover_historical_rollup_confirmation( %{ config: %{ @@ -196,11 +343,17 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do historical_confirmations_end_block: expected_confirmation_end_block, historical_confirmations_start_block: expected_confirmation_start_block } - } = _state + } = state ) do {interim_start_block, end_block} = case expected_confirmation_end_block do nil -> + # Three options are possible: + # {nil, nil} - there are no confirmations + # {nil, value} - there are no confirmations between L1 block corresponding + # to the rollup genesis and the L1 block _value_. + # {lower, higher} - there are no confirmations between L1 block _lower_ + # and the L1 block _higher_. Db.l1_blocks_to_expect_rollup_blocks_confirmation(nil) _ -> @@ -215,6 +368,9 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do max(l1_rollup_init_block, end_block - l1_rpc_config.logs_block_range + 1) value -> + # The interim start block is not nil when a gap between two confirmations + # identified. Therefore there is no need to go deeper than the interim + # start block. Enum.max([l1_rollup_init_block, value, end_block - l1_rpc_config.logs_block_range + 1]) end @@ -228,20 +384,70 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do l1_rpc_config ) - {retcode, {start_block, interim_start_block}} + case {retcode, start_block == interim_start_block} do + {:ok, true} -> + # The situation when the interim start block is equal to the start block + # means that gap between confirmation has been inspected. It is necessary + # to identify the next gap. + {retcode, state_for_next_iteration_historical(state, nil, nil)} + + {:ok, false} -> + # The situation when the interim start block is not equal to the start block + # means that the confirmations gap has not been inspected fully yet. It is + # necessary to continue the confirmations discovery from the interim start + # block to the the block predecessor of the current start block. + {retcode, state_for_next_iteration_historical(state, start_block - 1, interim_start_block)} + + {:confirmation_missed, _} -> + # The situation when the confirmation has been missed. It is necessary to + # re-do the confirmations discovery for the same block range. + {retcode, state_for_next_iteration_historical(state, end_block, interim_start_block)} + end else # TODO: Investigate on a live system what will happen when all blocks are confirmed # the situation when end block is `nil` is possible when there is no confirmed # block in the database and the historical confirmations discovery must start # from the L1 block specified as L1 start block (configured, or the latest block number) - {:end_block_defined, false} -> {:ok, {l1_start_block, nil}} + {:end_block_defined, false} -> {:ok, state_for_next_iteration_historical(state, l1_start_block - 1, nil)} # If the genesis of the rollup has been reached during historical confirmations # discovery, no further actions are needed. - {:genesis_not_reached, false} -> {:ok, {l1_rollup_init_block, nil}} + {:genesis_not_reached, false} -> {:ok, state_for_next_iteration_historical(state, l1_rollup_init_block - 1, nil)} end end + # Updates the state for the next iteration of historical confirmations discovery. + @spec state_for_next_iteration_historical( + %{ + :data => %{ + :historical_confirmations_end_block => non_neg_integer() | nil, + :historical_confirmations_start_block => non_neg_integer() | nil, + optional(any()) => any() + }, + optional(any()) => any() + }, + non_neg_integer() | nil, + non_neg_integer() | nil + ) :: + %{ + :data => %{ + :historical_confirmations_end_block => non_neg_integer() | nil, + :historical_confirmations_start_block => non_neg_integer() | nil, + optional(any()) => any() + }, + optional(any()) => any() + } + defp state_for_next_iteration_historical(prev_state, end_block, lowest_block_in_gap) when end_block >= 0 do + %{ + prev_state + | data: %{ + prev_state.data + | historical_confirmations_end_block: end_block, + historical_confirmations_start_block: lowest_block_in_gap + } + } + end + # Discovers and processes new confirmations of rollup blocks within the given block range. # # This function fetches logs within the specified L1 block range to find new @@ -260,6 +466,18 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # ## Returns # - The retcode indicating the result of the discovery and processing operation, # either `:ok` or `:confirmation_missed`. + @spec discover( + binary(), + non_neg_integer(), + non_neg_integer(), + %{ + :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), + :logs_block_range => non_neg_integer(), + :chunk_size => non_neg_integer(), + :finalized_confirmations => boolean(), + optional(any()) => any() + } + ) :: :ok | :confirmation_missed defp discover( outbox_address, start_block, @@ -295,12 +513,15 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # Processes logs to handle confirmations for rollup blocks. # # This function analyzes logs containing `SendRootUpdated` events with information - # about the confirmations up to a specific point in time. It identifies the ranges - # of rollup blocks covered by the confirmations and constructs lifecycle - # transactions linked to these confirmed blocks. Considering the highest confirmed - # rollup block number, it discovers L2-to-L1 messages that have been committed and - # updates their status to confirmed. Lists of confirmed rollup blocks, lifecycle - # transactions, and confirmed messages are prepared for database import. + # about the confirmations up to a specific point in time, avoiding the reprocessing + # of confirmations already known in the database. It identifies the ranges of + # rollup blocks covered by the confirmations and constructs lifecycle transactions + # linked to these confirmed blocks. Considering the highest confirmed rollup block + # number, it discovers L2-to-L1 messages that have been committed and updates their + # status to confirmed. The confirmations already processed are also updated to + # ensure the correct L1 block number and timestamp, which may have changed due to + # re-orgs. Lists of confirmed rollup blocks, lifecycle transactions, and confirmed + # messages are prepared for database import. # # ## Parameters # - `logs`: Log entries representing `SendRootUpdated` events. @@ -323,7 +544,8 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), :logs_block_range => non_neg_integer(), :chunk_size => non_neg_integer(), - :finalized_confirmations => boolean() + :finalized_confirmations => boolean(), + optional(any()) => any() }, binary() ) :: @@ -341,38 +563,72 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do l1_rpc_config, outbox_address ) do - {rollup_blocks_to_l1_txs, lifecycle_txs_basic, blocks_requests} = parse_logs_for_new_confirmations(logs) - + # On this step there could be lifecycle transactions for the rollup blocks which are + # already confirmed. It is only possible in the scenario when the confirmation + # discovery process does not wait for the safe L1 block. In this case: + # - rollup_blocks_to_l1_txs will not contain the corresponding block hash associated + # with the L1 transaction hash + # - lifecycle_txs_basic will contain all discovered lifecycle transactions + # - blocks_requests will contain all requests to fetch block data for the lifecycle + # transactions + # - existing_lifecycle_txs will contain lifecycle transactions which was found in the + # logs and already imported into the database. + {rollup_blocks_to_l1_txs, lifecycle_txs_basic, blocks_requests, existing_lifecycle_txs} = + parse_logs_for_new_confirmations(logs) + + # This step must be run only if there are hashes of the confirmed rollup blocks + # in rollup_blocks_to_l1_txs - when there are newly discovered confirmations. rollup_blocks = - discover_rollup_blocks( - rollup_blocks_to_l1_txs, - %{ - json_rpc_named_arguments: l1_rpc_config.json_rpc_named_arguments, - logs_block_range: l1_rpc_config.logs_block_range, - outbox_address: outbox_address - } - ) + if Enum.empty?(rollup_blocks_to_l1_txs) do + [] + else + discover_rollup_blocks( + rollup_blocks_to_l1_txs, + %{ + json_rpc_named_arguments: l1_rpc_config.json_rpc_named_arguments, + logs_block_range: l1_rpc_config.logs_block_range, + outbox_address: outbox_address + } + ) + end + # Will return %{} if there are no new confirmations applicable_lifecycle_txs = take_lifecycle_txs_for_confirmed_blocks(rollup_blocks, lifecycle_txs_basic) + # Will contain :ok if no new confirmations are found retcode = - if Enum.count(lifecycle_txs_basic) != Enum.count(applicable_lifecycle_txs) do + if Enum.count(lifecycle_txs_basic) != Enum.count(applicable_lifecycle_txs) + length(existing_lifecycle_txs) do :confirmation_missed else :ok end - if Enum.empty?(applicable_lifecycle_txs) do + if Enum.empty?(applicable_lifecycle_txs) and existing_lifecycle_txs == [] do + # Only if both new confirmations and already existing confirmations are empty {retcode, {[], [], []}} else - {lifecycle_txs, rollup_blocks, highest_confirmed_block_number} = + l1_blocks_to_ts = + Rpc.execute_blocks_requests_and_get_ts( + blocks_requests, + l1_rpc_config.json_rpc_named_arguments, + l1_rpc_config.chunk_size + ) + + # The lifecycle transactions for the new confirmations are finalized here. + {lifecycle_txs_for_new_confirmations, rollup_blocks, highest_confirmed_block_number} = finalize_lifecycle_txs_and_confirmed_blocks( applicable_lifecycle_txs, rollup_blocks, - blocks_requests, - l1_rpc_config + l1_blocks_to_ts, + l1_rpc_config.track_finalization ) + # The lifecycle transactions for the already existing confirmations are updated here + # to ensure correct L1 block number and timestamp that could appear due to re-orgs. + lifecycle_txs = + lifecycle_txs_for_new_confirmations ++ + update_lifecycle_txs_for_new_blocks(existing_lifecycle_txs, lifecycle_txs_basic, l1_blocks_to_ts) + # Drawback of marking messages as confirmed during a new confirmation handling # is that the status change could become stuck if confirmations are not handled. # For example, due to DB inconsistency: some blocks/batches are missed. @@ -384,12 +640,13 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # Parses logs to extract new confirmations for rollup blocks and prepares related data. # - # This function processes `SendRootUpdated` event logs. For each event, it maps - # the hash of the top confirmed rollup block provided in the event to - # the confirmation description, containing the L1 transaction hash and - # block number. It also prepares a set of lifecycle transactions in basic form - # and block requests to later fetch timestamps for the corresponding lifecycle - # transactions. + # This function processes `SendRootUpdated` event logs. For each event which + # was not processed before, it maps the hash of the top confirmed rollup block + # provided in the event to the confirmation description, containing the L1 + # transaction hash and block number. It also prepares a set of lifecycle + # transactions in basic form, the set of lifecycle transaction already + # existing in the database and block requests to later fetch timestamps for + # the corresponding lifecycle transactions. # # ## Parameters # - `logs`: A list of log entries representing `SendRootUpdated` events. @@ -399,22 +656,53 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # - A map associating rollup block hashes with their confirmation descriptions. # - A map of basic-form lifecycle transactions keyed by L1 transaction hash. # - A list of RPC requests to fetch block data for these lifecycle transactions. + # - A list of discovered lifecycle transactions which are already in the + # database. Each transaction is compatible with the database import operation. + @spec parse_logs_for_new_confirmations([%{String.t() => any()}]) :: + { + %{binary() => %{l1_tx_hash: binary(), l1_block_num: non_neg_integer()}}, + %{binary() => %{hash: binary(), block_number: non_neg_integer()}}, + [EthereumJSONRPC.Transport.request()], + [Arbitrum.LifecycleTransaction.to_import()] + } defp parse_logs_for_new_confirmations(logs) do + transaction_hashes = + logs + |> Enum.reduce(%{}, fn event, acc -> + l1_tx_hash_raw = event["transactionHash"] + Map.put_new(acc, l1_tx_hash_raw, Rpc.string_hash_to_bytes_hash(l1_tx_hash_raw)) + end) + + existing_lifecycle_txs = + transaction_hashes + |> Map.values() + |> Db.lifecycle_transactions() + |> Enum.reduce(%{}, fn tx, acc -> + Map.put(acc, tx.hash, tx) + end) + {rollup_block_to_l1_txs, lifecycle_txs, blocks_requests} = logs |> Enum.reduce({%{}, %{}, %{}}, fn event, {block_to_txs, lifecycle_txs, blocks_requests} -> rollup_block_hash = send_root_updated_event_parse(event) l1_tx_hash_raw = event["transactionHash"] - l1_tx_hash = Rpc.string_hash_to_bytes_hash(l1_tx_hash_raw) + l1_tx_hash = transaction_hashes[l1_tx_hash_raw] l1_blk_num = quantity_to_integer(event["blockNumber"]) + # There is no need to include the found block hash for the consequent confirmed + # blocks discovery step since it is assumed that already existing lifecycle + # transactions are already linked with the corresponding rollup blocks. updated_block_to_txs = - Map.put( - block_to_txs, - rollup_block_hash, - %{l1_tx_hash: l1_tx_hash, l1_block_num: l1_blk_num} - ) + if Map.has_key?(existing_lifecycle_txs, l1_tx_hash) do + block_to_txs + else + Map.put( + block_to_txs, + rollup_block_hash, + %{l1_tx_hash: l1_tx_hash, l1_block_num: l1_blk_num} + ) + end updated_lifecycle_txs = Map.put( @@ -435,7 +723,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do {updated_block_to_txs, updated_lifecycle_txs, updated_blocks_requests} end) - {rollup_block_to_l1_txs, lifecycle_txs, Map.values(blocks_requests)} + {rollup_block_to_l1_txs, lifecycle_txs, Map.values(blocks_requests), Map.values(existing_lifecycle_txs)} end # Transforms rollup block hashes to numbers and associates them with their confirmation descriptions. @@ -455,6 +743,15 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # ## Returns # - A list of rollup blocks each associated with the transaction's hash that # confirms the block. + @spec discover_rollup_blocks( + %{binary() => %{l1_tx_hash: binary(), l1_block_num: non_neg_integer()}}, + %{ + :logs_block_range => non_neg_integer(), + :outbox_address => binary(), + :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), + optional(any()) => any() + } + ) :: [Arbitrum.BatchBlock.to_import()] defp discover_rollup_blocks(rollup_blocks_to_l1_txs, outbox_config) do block_to_l1_txs = rollup_blocks_to_l1_txs @@ -510,11 +807,16 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # This function follows these steps to identify unconfirmed rollup blocks related # to a single confirmation event: # 1. Retrieve the batch associated with the specified rollup block number. - # 2. Obtain a list of unconfirmed blocks within that batch. - # 3. Determine the first unconfirmed block in the batch, considering potential - # gaps or already confirmed blocks. - # 4. Verify the continuity of the unconfirmed blocks range to ensure there are no - # database inconsistencies or unindexed blocks. + # 2. Obtain a list of unconfirmed blocks within that batch. For the historical + # confirmations discovery, the list will include both unconfirmed blocks that + # are covered by the current confirmation and those that a going to be covered + # by the predecessor confirmation. + # 3. Determine the first unconfirmed block in the batch. It could be the first + # block in the batch or a block the next after the last confirmed block in the + # predecessor confirmation. + # 4. Verify the continuity of the unconfirmed blocks to be covered by the current + # confirmation to ensure there are no database inconsistencies or unindexed + # blocks. # 5. If the first unconfirmed block is at the start of the batch, check if the # confirmation also covers blocks from previous batches. If so, include their # unconfirmed blocks in the range. @@ -552,6 +854,17 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # the current confirmation but not yet marked as confirmed in the database. # - `{:error, []}`: If a discrepancy or inconsistency is found during the # discovery process. + @spec discover_rollup_blocks_belonging_to_one_confirmation( + non_neg_integer(), + %{:l1_block_num => non_neg_integer(), optional(any()) => any()}, + %{ + :logs_block_range => non_neg_integer(), + :outbox_address => binary(), + :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), + optional(any()) => any() + }, + __MODULE__.cached_logs() + ) :: {:ok, [Arbitrum.BatchBlock.to_import()]} | {:error, []} defp discover_rollup_blocks_belonging_to_one_confirmation( rollup_block_num, confirmation_desc, @@ -561,7 +874,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # The following batch fields are required in the further processing: # number, start_block, end_block, commitment_transaction.block_number with {:ok, batch} <- discover_rollup_blocks__get_batch(rollup_block_num), - {:ok, unconfirmed_rollup_blocks} when unconfirmed_rollup_blocks != [] <- + {:ok, raw_unconfirmed_rollup_blocks} when raw_unconfirmed_rollup_blocks != [] <- discover_rollup_blocks__get_unconfirmed_rollup_blocks(batch, rollup_block_num), # It is not the issue to request logs for the first call of # discover_rollup_blocks_belonging_to_one_confirmation since we need @@ -572,14 +885,19 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do {:ok, {first_unconfirmed_block, new_cache}} <- discover_rollup_blocks__check_confirmed_blocks_in_batch( rollup_block_num, - length(unconfirmed_rollup_blocks), batch, confirmation_desc, outbox_config, cache ), - true <- discover_rollup_blocks__check_consecutive_rollup_blocks(unconfirmed_rollup_blocks, batch.number) do - if List.first(unconfirmed_rollup_blocks).block_number == batch.start_block do + {:ok, unconfirmed_rollup_blocks} <- + discover_rollup_blocks__check_consecutive_rollup_blocks( + raw_unconfirmed_rollup_blocks, + first_unconfirmed_block, + rollup_block_num, + batch.number + ) do + if first_unconfirmed_block == batch.start_block do log_info("End of the batch #{batch.number} discovered, moving to the previous batch") {status, updated_rollup_blocks} = @@ -598,13 +916,19 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do :ok -> {:ok, unconfirmed_rollup_blocks ++ updated_rollup_blocks} end else - log_info("All unconfirmed blocks in the batch ##{batch.number} found") + # During the process of new confirmations discovery it will show "N of N", + # for the process of historical confirmations discovery it will show "N of M". + log_info( + "#{length(unconfirmed_rollup_blocks)} of #{length(raw_unconfirmed_rollup_blocks)} blocks in the batch ##{batch.number} corresponds to current confirmation" + ) + {:ok, unconfirmed_rollup_blocks} end end end # Retrieves the batch containing the specified rollup block and logs the attempt. + @spec discover_rollup_blocks__get_batch(non_neg_integer()) :: {:ok, Arbitrum.L1Batch.t()} | {:error, []} defp discover_rollup_blocks__get_batch(rollup_block_num) do # Generally if batch is nil it means either # - a batch to a rollup block association is not found, not recoverable @@ -631,6 +955,10 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # Identifies unconfirmed rollup blocks within a batch up to specified block # number, checking for potential synchronization issues. + @spec discover_rollup_blocks__get_unconfirmed_rollup_blocks( + Arbitrum.L1Batch.t(), + non_neg_integer() + ) :: {:ok, [Arbitrum.BatchBlock.to_import()]} | {:error, []} defp discover_rollup_blocks__get_unconfirmed_rollup_blocks(batch, rollup_block_num) do unconfirmed_rollup_blocks = Db.unconfirmed_rollup_blocks(batch.start_block, rollup_block_num) @@ -657,14 +985,14 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # mentions any block of the batch as the top of the confirmed blocks. Depending # on the lookup result, it either considers the found block or the very # first block of the batch as the start of the range of unconfirmed blocks ending - # with `rollup_block_num`. It also checks for a gap in the identified rollup - # blocks range, indicating potential database inconsistency or an unprocessed batch. + # with `rollup_block_num`. # To optimize `eth_getLogs` calls required for the `SendRootUpdated` event lookup, # it uses a cache. + # Since this function only discovers the number of the unconfirmed block, it does + # not check continuity of the unconfirmed blocks range in the batch. # # ## Parameters # - `rollup_block_num`: The rollup block number to check for confirmation. - # - `unconfirmed_rollup_blocks_length`: The number of unconfirmed blocks in the batch. # - `batch`: The batch containing the rollup blocks. # - `confirmation_desc`: Details of the latest confirmation. # - `outbox_config`: Configuration for the Arbitrum outbox contract. @@ -676,20 +1004,30 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # found. # - `{:ok, {first_unconfirmed_block_in_batch, new_cache}}` with the number of the # first unconfirmed block in the batch and updated cache. + @spec discover_rollup_blocks__check_confirmed_blocks_in_batch( + non_neg_integer(), + Arbitrum.L1Batch.t(), + %{:l1_block_num => non_neg_integer(), optional(any()) => any()}, + %{ + :logs_block_range => non_neg_integer(), + :outbox_address => binary(), + :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), + optional(any()) => any() + }, + __MODULE__.cached_logs() + ) :: {:ok, {non_neg_integer(), __MODULE__.cached_logs()}} | {:ok, []} | {:error, []} defp discover_rollup_blocks__check_confirmed_blocks_in_batch( rollup_block_num, - unconfirmed_rollup_blocks_length, batch, confirmation_desc, outbox_config, cache ) do - # This function might be over-engineered, as confirmations are likely always - # aligned with the end of a batch. If, after analyzing the databases of fully - # synchronized BS instances across several Arbitrum-based chains, it is confirmed - # that this alignment is consistent, then this functionality can be optimized. + # This function might look like over-engineered, but confirmations are not always + # aligned with the boundaries of a batch unfortunately. - {status, block?, new_cache} = check_if_batch_confirmed(batch, confirmation_desc, outbox_config, cache) + {status, block?, new_cache} = + check_if_batch_confirmed(batch, confirmation_desc, outbox_config, rollup_block_num, cache) case {status, block? == rollup_block_num} do {:error, _} -> @@ -712,20 +1050,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do value + 1 end - if unconfirmed_rollup_blocks_length == rollup_block_num - first_unconfirmed_block_in_batch + 1 do - {:ok, {first_unconfirmed_block_in_batch, new_cache}} - else - # The case when there is a gap in the blocks range is possible when there is - # a DB inconsistency. From another side, the case when the confirmation is for blocks - # in two batches -- one batch has been already indexed, another one has not been yet. - # Both cases should be handled in the same way - this confirmation must be postponed - # until the case resolution. - log_warning( - "Only #{unconfirmed_rollup_blocks_length} of #{rollup_block_num - first_unconfirmed_block_in_batch + 1} blocks found. Skipping the blocks from the batch #{batch.number}" - ) - - {:error, []} - end + {:ok, {first_unconfirmed_block_in_batch, new_cache}} end end @@ -741,6 +1066,8 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # - `confirmation_desc`: Description of the latest confirmation details. # - `l1_outbox_config`: Configuration for the L1 outbox contract, including block # range for logs retrieval. + # - `highest_unconfirmed_block`: The batch's highest rollup block number which is + # considered as unconfirmed. # - `cache`: A cache for the logs to reduce the number of `eth_getLogs` calls. # # ## Returns @@ -754,9 +1081,21 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # such as when a rollup block corresponding to a given hash is not found in the # database. # - `new_cache` contains the updated logs cache despite the error. - defp check_if_batch_confirmed(batch, confirmation_desc, l1_outbox_config, cache) do + @spec check_if_batch_confirmed( + Arbitrum.L1Batch.t(), + %{:l1_block_num => non_neg_integer(), optional(any()) => any()}, + %{ + :logs_block_range => non_neg_integer(), + :outbox_address => binary(), + :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), + optional(any()) => any() + }, + non_neg_integer(), + __MODULE__.cached_logs() + ) :: {:ok, nil | non_neg_integer(), __MODULE__.cached_logs()} | {:error, nil, __MODULE__.cached_logs()} + defp check_if_batch_confirmed(batch, confirmation_desc, l1_outbox_config, highest_unconfirmed_block, cache) do log_info( - "Use L1 blocks #{batch.commitment_transaction.block_number}..#{confirmation_desc.l1_block_num - 1} to look for a rollup block confirmation within #{batch.start_block}..#{batch.end_block} of ##{batch.number}" + "Use L1 blocks #{batch.commitment_transaction.block_number}..#{confirmation_desc.l1_block_num - 1} to look for a rollup block confirmation within #{batch.start_block}..#{highest_unconfirmed_block} of ##{batch.number}" ) block_pairs = @@ -851,7 +1190,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do {non_neg_integer(), non_neg_integer()}, {non_neg_integer(), non_neg_integer()}, %{ - :outbox_address => String.t(), + :outbox_address => binary(), :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), optional(any()) => any() }, @@ -979,50 +1318,101 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do end # Extracts the rollup block hash from a `SendRootUpdated` event log. + @spec send_root_updated_event_parse(%{String.t() => any()}) :: binary() defp send_root_updated_event_parse(event) do [_, _, l2_block_hash] = event["topics"] l2_block_hash end - # Validates if the list of rollup blocks are consecutive without gaps in their numbering. - defp discover_rollup_blocks__check_consecutive_rollup_blocks(unconfirmed_rollup_blocks, batch_number) do - if consecutive_rollup_blocks?(unconfirmed_rollup_blocks) do - true - else - # The case when there is a gap in the blocks range is possible when there is - # a DB inconsistency. From another side, the case when the confirmation is for blocks - # in two batches -- one batch has been already indexed, another one has not been yet. - # Both cases should be handled in the same way - this confirmation must be postponed - # until the case resolution. - log_warning("Skipping the blocks from the batch #{batch_number}") - {:error, []} + # Returns consecutive rollup blocks within the range of lowest_confirmed_block..highest_confirmed_block + # assuming that the list of unconfirmed rollup blocks finishes on highest_confirmed_block and + # is sorted by block number + @spec discover_rollup_blocks__check_consecutive_rollup_blocks( + [Arbitrum.BatchBlock.to_import()], + non_neg_integer(), + non_neg_integer(), + non_neg_integer() + ) :: {:ok, [Arbitrum.BatchBlock.to_import()]} | {:error, []} + defp discover_rollup_blocks__check_consecutive_rollup_blocks( + all_unconfirmed_rollup_blocks, + lowest_confirmed_block, + highest_confirmed_block, + batch_number + ) do + {status, unconfirmed_rollup_blocks} = + check_consecutive_rollup_blocks_and_cut(all_unconfirmed_rollup_blocks, lowest_confirmed_block) + + unconfirmed_rollup_blocks_length = length(unconfirmed_rollup_blocks) + expected_blocks_range_length = highest_confirmed_block - lowest_confirmed_block + 1 + + case {status, unconfirmed_rollup_blocks_length == expected_blocks_range_length} do + {true, true} -> + {:ok, unconfirmed_rollup_blocks} + + {true, false} -> + log_warning( + "Only #{unconfirmed_rollup_blocks_length} of #{expected_blocks_range_length} blocks found. Skipping the blocks from the batch #{batch_number}" + ) + + {:error, []} + + _ -> + # The case when there is a gap in the blocks range is possible when there is + # a DB inconsistency. From another side, the case when the confirmation is for blocks + # in two batches -- one batch has been already indexed, another one has not been yet. + # Both cases should be handled in the same way - this confirmation must be postponed + # until the case resolution. + log_warning("Skipping the blocks from the batch #{batch_number}") + {:error, []} end end - # Checks if the list of rollup blocks are consecutive without gaps in their numbering. - defp consecutive_rollup_blocks?(blocks) do - {_, status} = - Enum.reduce_while(blocks, {nil, false}, fn block, {prev, _} -> - case prev do - nil -> - {:cont, {block.block_number, true}} - - value -> + # Checks for consecutive rollup blocks starting from the lowest confirmed block + # and returns the status and the list of consecutive blocks. + # + # This function processes a list of rollup blocks to verify if they are consecutive, + # starting from the `lowest_confirmed_block`. If a gap is detected between the + # blocks, the process halts and returns false along with an empty list. If all + # blocks are consecutive, it returns true along with the list of consecutive + # blocks. + # + # ## Parameters + # - `blocks`: A list of rollup blocks to check. + # - `lowest_confirmed_block`: The lowest confirmed block number to start the check. + # + # ## Returns + # - A tuple where the first element is a boolean indicating if all blocks are + # consecutive, and the second element is the list of consecutive blocks if the + # first element is true, otherwise an empty list. + @spec check_consecutive_rollup_blocks_and_cut([Arbitrum.BatchBlock.to_import()], non_neg_integer()) :: + {boolean(), [Arbitrum.BatchBlock.to_import()]} + defp check_consecutive_rollup_blocks_and_cut(blocks, lowest_confirmed_block) do + {_, status, cut_blocks} = + Enum.reduce_while(blocks, {nil, false, []}, fn block, {prev, _, cut_blocks} -> + case {prev, block.block_number >= lowest_confirmed_block} do + {nil, true} -> + {:cont, {block.block_number, true, [block | cut_blocks]}} + + {value, true} -> # credo:disable-for-next-line Credo.Check.Refactor.Nesting if block.block_number - 1 == value do - {:cont, {block.block_number, true}} + {:cont, {block.block_number, true, [block | cut_blocks]}} else log_warning("A gap between blocks ##{value} and ##{block.block_number} found") - {:halt, {block.block_number, false}} + {:halt, {block.block_number, false, []}} end + + {_, false} -> + {:cont, {nil, false, []}} end end) - status + {status, cut_blocks} end # Adds the confirmation transaction hash to each rollup block description in the list. + @spec add_confirmation_transaction([Arbitrum.BatchBlock.to_import()], binary()) :: [Arbitrum.BatchBlock.to_import()] defp add_confirmation_transaction(block_descriptions_list, confirm_tx_hash) do block_descriptions_list |> Enum.reduce([], fn block_descr, updated -> @@ -1035,6 +1425,10 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do end # Selects lifecycle transaction descriptions used for confirming a given list of rollup blocks. + @spec take_lifecycle_txs_for_confirmed_blocks( + [Arbitrum.BatchBlock.to_import()], + %{binary() => %{hash: binary(), block_number: non_neg_integer()}} + ) :: %{binary() => %{hash: binary(), block_number: non_neg_integer()}} defp take_lifecycle_txs_for_confirmed_blocks(confirmed_rollup_blocks, lifecycle_txs) do confirmed_rollup_blocks |> Enum.reduce(%{}, fn block_descr, updated_txs -> @@ -1064,25 +1458,40 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # # ## Returns # - A tuple containing: - # - The list of lifecycle transactions, ready for import. + # - The map of lifecycle transactions where each transaction is ready for import. # - The list of confirmed rollup blocks, ready for import. # - The highest confirmed block number processed during this run. + @spec finalize_lifecycle_txs_and_confirmed_blocks( + %{binary() => %{hash: binary(), block_number: non_neg_integer()}}, + [Arbitrum.BatchBlock.to_import()], + %{required(EthereumJSONRPC.block_number()) => DateTime.t()}, + boolean() + ) :: { + [Arbitrum.LifecycleTransaction.to_import()], + [Arbitrum.BatchBlock.to_import()], + integer() + } defp finalize_lifecycle_txs_and_confirmed_blocks( basic_lifecycle_txs, confirmed_rollup_blocks, - l1_blocks_requests, - %{ - json_rpc_named_arguments: l1_json_rpc_named_arguments, - chunk_size: l1_chunk_size, - track_finalization: track_finalization? - } = _l1_rpc_config - ) do - blocks_to_ts = - Rpc.execute_blocks_requests_and_get_ts(l1_blocks_requests, l1_json_rpc_named_arguments, l1_chunk_size) + l1_blocks_to_ts, + track_finalization? + ) + + defp finalize_lifecycle_txs_and_confirmed_blocks(basic_lifecycle_txs, _, _, _) + when map_size(basic_lifecycle_txs) == 0 do + {[], [], -1} + end + defp finalize_lifecycle_txs_and_confirmed_blocks( + basic_lifecycle_txs, + confirmed_rollup_blocks, + l1_blocks_to_ts, + track_finalization? + ) do lifecycle_txs = basic_lifecycle_txs - |> ArbitrumHelper.extend_lifecycle_txs_with_ts_and_status(blocks_to_ts, track_finalization?) + |> ArbitrumHelper.extend_lifecycle_txs_with_ts_and_status(l1_blocks_to_ts, track_finalization?) |> Db.get_indices_for_l1_transactions() {updated_rollup_blocks, highest_confirmed_block_number} = @@ -1101,7 +1510,40 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do {Map.values(lifecycle_txs), updated_rollup_blocks, highest_confirmed_block_number} end + # Updates lifecycle transactions with new L1 block numbers and timestamps which could appear due to re-orgs. + # + # ## Parameters + # - `existing_commitment_txs`: A list of existing confirmation transactions to be checked and updated. + # - `tx_to_l1_block`: A map from transaction hashes to their corresponding new L1 block numbers. + # - `l1_block_to_ts`: A map from L1 block numbers to their corresponding new timestamps. + # + # ## Returns + # - A list of updated confirmation transactions with new block numbers and timestamps. + @spec update_lifecycle_txs_for_new_blocks( + [Arbitrum.LifecycleTransaction.to_import()], + %{binary() => non_neg_integer()}, + %{non_neg_integer() => DateTime.t()} + ) :: [Arbitrum.LifecycleTransaction.to_import()] + defp update_lifecycle_txs_for_new_blocks(existing_commitment_txs, tx_to_l1_block, l1_block_to_ts) do + existing_commitment_txs + |> Enum.reduce([], fn tx, updated_txs -> + new_block_num = tx_to_l1_block[tx.hash].block_number + new_ts = l1_block_to_ts[new_block_num] + + case ArbitrumHelper.compare_lifecycle_tx_and_update(tx, {new_block_num, new_ts}, "confirmation") do + {:updated, updated_tx} -> + [updated_tx | updated_txs] + + _ -> + updated_txs + end + end) + end + # Retrieves committed L2-to-L1 messages up to specified block number and marks them as 'confirmed'. + @spec get_confirmed_l2_to_l1_messages(integer()) :: [Arbitrum.Message.to_import()] + defp get_confirmed_l2_to_l1_messages(block_number) + defp get_confirmed_l2_to_l1_messages(-1) do [] end diff --git a/config/runtime.exs b/config/runtime.exs index 19c2bd4e8c95..db25f492158e 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -895,6 +895,7 @@ config :indexer, Indexer.Fetcher.Arbitrum, l1_rollup_address: System.get_env("INDEXER_ARBITRUM_L1_ROLLUP_CONTRACT"), l1_rollup_init_block: ConfigHelper.parse_integer_env_var("INDEXER_ARBITRUM_L1_ROLLUP_INIT_BLOCK", 1), l1_start_block: ConfigHelper.parse_integer_env_var("INDEXER_ARBITRUM_L1_COMMON_START_BLOCK", 0), + l1_finalization_threshold: ConfigHelper.parse_integer_env_var("INDEXER_ARBITRUM_L1_FINALIZATION_THRESHOLD", 1000), rollup_chunk_size: ConfigHelper.parse_integer_env_var("INDEXER_ARBITRUM_ROLLUP_CHUNK_SIZE", 20) config :indexer, Indexer.Fetcher.Arbitrum.TrackingMessagesOnL1, diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 313ac7b8f646..2b23e63402d7 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -236,6 +236,7 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_ARBITRUM_L1_ROLLUP_CONTRACT= # INDEXER_ARBITRUM_L1_ROLLUP_INIT_BLOCK= # INDEXER_ARBITRUM_L1_COMMON_START_BLOCK= +# INDEXER_ARBITRUM_L1_FINALIZATION_THRESHOLD= # INDEXER_ARBITRUM_ROLLUP_CHUNK_SIZE= # INDEXER_ARBITRUM_BATCHES_TRACKING_ENABLED= # INDEXER_ARBITRUM_BATCHES_TRACKING_RECHECK_INTERVAL= From 6c072464466f3ed51dbb437ef62fb0ccea594c11 Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Tue, 6 Aug 2024 19:40:49 +0300 Subject: [PATCH 056/363] feat: support for `:celo` chain type (#10564) * feat: indexing of Celo transactions (#10275) * feat(json-rpc): support celo-specific fields in transactions * chore: add new chain type to matrix builder * feat: add celo-specific fields to transactions schema * chore: add "celo" to `cspell.json` * chore: fix formatting * refactor: improve naming for celo fields and ignore `ethCompatible` field * chore: add block fetcher test for celo fields * feat: add celo core contract abis * feat: add a cache for core celo contracts * feat: treat celo native token transfers as token transfers * fix: update `cspell.json` with celo words * fix(dialyzer): pattern can never match the type * fix: update `cspell.json` with celo words * feat: move `CELO_NETWORK` env to config * feat: parse native Celo token transfers from internal transactions * feat: add token balances fetching * refactor: remove `dbg` and organize code better * fix: start TokenBalance fetcher * fix: start token balance fetcher * fix: typo in log message * fix: missing fields in celo transaction token transfer * refactor: remove `AbiHandler` in favour of hardcoded pieces of abi * refactor: native token transfers from internal transactions fether * feat: implement parsing of token transfers for traceable rpc node * feat: fetch address coin balances after erc20 celo token transfers * feat: add `gas_token` to transaction response in api v2 * feat: index tokens specified as `feeCurrency` * chore: fix credo warnings * chore: decrease log level to debug * chore: fix cspell warnings * refactor: apply suggestions * fix: handle case when `gas_token` is not loaded * dbg * chore: expect contract calls in celo fetcher test * fix: compile errors for celo view on default chain type * chore: remove unused import * refactor: move `celo_gas_tokens` to chain type imports * refactor: apply suggestions * fix: swap arguments in `put_if_present` * fix: indexer tests * refactor: always include `gas_token` to the response * feat: add `gas_token` to all transaction-related endpoints * feat: celo core contracts with historical data * fix: `uncles` field is expected but not present in RPC node response * fix: credo & format * fix: define `CELO_CORE_CONTRACTS` in tests * fix: put `gas_token` under `celo` section * refactor: move token transfers filtering from `TokenTotalSupplyUpdater` to `Transform.TokenTransfers` * feat: add workflow to publish docker image * refactor: apply suggestions by @nikitosing * chore: add docs and specs * fix: malformed import * fix: publish image for celo * fix: ci build for celo * feat: index celo epoch transactions (#9944) * feat(json-rpc): support celo-specific fields in transactions * chore: add new chain type to matrix builder * feat: add celo-specific fields to transactions schema * chore: add "celo" to `cspell.json` * chore: fix formatting * refactor: improve naming for celo fields and ignore `ethCompatible` field * chore: add block fetcher test for celo fields * feat: add celo core contract abis * feat: add a cache for core celo contracts * feat: treat celo native token transfers as token transfers * fix: update `cspell.json` with celo words * fix(dialyzer): pattern can never match the type * fix: update `cspell.json` with celo words * feat: move `CELO_NETWORK` env to config * feat: parse native Celo token transfers from internal transactions * feat: add token balances fetching * refactor: remove `dbg` and organize code better * fix: start TokenBalance fetcher * fix: start token balance fetcher * fix: typo in log message * fix: missing fields in celo transaction token transfer * refactor: remove `AbiHandler` in favour of hardcoded pieces of abi * refactor: native token transfers from internal transactions fether * feat: implement parsing of token transfers for traceable rpc node * feat: fetch address coin balances after erc20 celo token transfers * feat: add `gas_token` to transaction response in api v2 * feat: index tokens specified as `feeCurrency` * chore: fix credo warnings * chore: decrease log level to debug * chore: fix cspell warnings * refactor: apply suggestions * fix: handle case when `gas_token` is not loaded * dbg * chore: expect contract calls in celo fetcher test * fix: compile errors for celo view on default chain type * chore: remove unused import * refactor: move `celo_gas_tokens` to chain type imports * refactor: apply suggestions * fix: swap arguments in `put_if_present` * fix: indexer tests * refactor: always include `gas_token` to the response * feat: add `gas_token` to all transaction-related endpoints * feat: celo core contracts with historical data * fix: `uncles` field is expected but not present in RPC node response * fix: credo & format * fix: define `CELO_CORE_CONTRACTS` in tests * fix: put `gas_token` under `celo` section * refactor: move token transfers filtering from `TokenTotalSupplyUpdater` to `Transform.TokenTransfers` * feat: add workflow to publish docker image * refactor: apply suggestions by @nikitosing * chore: add docs and specs * fix: malformed import * feat: add pending epoch operations table * refactor: fix format * feat: add transformers for epoch events * feat: add query to stream pending epoch block operations * fix: call to renamed function * fix: add factory method for pending epoch block operation * feat: add utils for logs parsing * feat: add schemas for epoch rewards and election rewards * fix: rename transformer according to event name * feat: implement epoch rewards fetcher * feat: fetch and import epoch logs * feat: improve epoch rewards fetcher 1. Do not fetch logs -- use the ones stored in DB 2. Import epochs to the database 3. Configure fetcher in runtime.exs * feat: add epoch helper functions * activated_validator_group_vote.ex * feat: add task to fetch core contracts * feat: fetch core contract events in the task * fix: merge artifacts * refactor: logs requests and reduce scope of epoch logs request * chore: fix formatting, credo warning, etc. * feat: fetch epoch rewards in one sql query * feat: fetch validator group votes (historical data and on demand) * refactor: rename fields and replace all entries on conflict * feat: validator group votes fetcher * fix: put each topic in a separate request * feat: fetch voter rewards * refactor: split epoch fetcher to multiple modules * feat: send epoch blocks for async fetching from block fetcher * chore: fix credo and formatting issues * fix: dialyzer warnings * fix: add on demand fetch of group votes * fix: failed explorer and indexer tests * fix: block fetcher tests * fix: match error in epoch logs * feat: add env to manage logs batch size when fetching validator group votes * fix: Add `ssl_opts` for Celo repo * fix: add `disabled?` predicate to supervisor config * fix: return empty list when `getPaymentDelegation` is not available * fix: validate the case when there is no voter rewards for an epoch * fix: formatting * fix: import `put_if_present/3` * fix: do not treat genesis block as an epoch block * fix: some of the fetcher tests * dbg * chore: remove commented code * chore: canonical disable flag env name * fix: run test only for celo chain type * fix: add explicit `wait_ for_tasks` in token instance realtime test * fix: set missing `CELO_CORE_CONTRACTS` env * fix: rollback token instance realtime test * chore: remove debug artifact * chore: add docs for new tables * perf: remove unused fields from `celo_validator_group_votes` table * fix: add on_exit clause * refactor: use `remove/3` function for migration rollback possibility * refactor: extract celo core contracts environment variable setup into a separate function * chore: add new vars to `common-blockscout.env` file * chore: add specs and docs for new modules and functions * refactor: eliminate unused import warnings * fix: credo warning * feat: API for celo epoch rewards and fees breakdown (#10308) * feat(json-rpc): support celo-specific fields in transactions * chore: add new chain type to matrix builder * feat: add celo-specific fields to transactions schema * chore: add "celo" to `cspell.json` * chore: fix formatting * refactor: improve naming for celo fields and ignore `ethCompatible` field * chore: add block fetcher test for celo fields * feat: add celo core contract abis * feat: add a cache for core celo contracts * feat: treat celo native token transfers as token transfers * fix: update `cspell.json` with celo words * fix(dialyzer): pattern can never match the type * fix: update `cspell.json` with celo words * feat: move `CELO_NETWORK` env to config * feat: parse native Celo token transfers from internal transactions * feat: add token balances fetching * refactor: remove `dbg` and organize code better * fix: start TokenBalance fetcher * fix: start token balance fetcher * fix: typo in log message * fix: missing fields in celo transaction token transfer * refactor: remove `AbiHandler` in favour of hardcoded pieces of abi * refactor: native token transfers from internal transactions fether * feat: implement parsing of token transfers for traceable rpc node * feat: fetch address coin balances after erc20 celo token transfers * feat: add `gas_token` to transaction response in api v2 * feat: index tokens specified as `feeCurrency` * chore: fix credo warnings * chore: decrease log level to debug * chore: fix cspell warnings * refactor: apply suggestions * fix: handle case when `gas_token` is not loaded * dbg * chore: expect contract calls in celo fetcher test * fix: compile errors for celo view on default chain type * chore: remove unused import * refactor: move `celo_gas_tokens` to chain type imports * refactor: apply suggestions * fix: swap arguments in `put_if_present` * fix: indexer tests * feat: add pending epoch operations table * feat: add several celo core contracts * refactor: fix format * feat: add new core contract default addresses * feat: add transformers for epoch events * feat: add query to stream pending epoch block operations * fix: call to renamed function * fix: add factory method for pending epoch block operation * feat: add utils for logs parsing * feat: add schemas for epoch rewards and election rewards * fix: rename transformer according to event name * feat: implement epoch rewards fetcher * feat: fetch and import epoch logs * feat: improve epoch rewards fetcher 1. Do not fetch logs -- use the ones stored in DB 2. Import epochs to the database 3. Configure fetcher in runtime.exs * fix: remove duplicated module attribute * feat: add epoch helper functions * activated_validator_group_vote.ex * feat: tmp * refactor: always include `gas_token` to the response * feat: add `gas_token` to all transaction-related endpoints * feat: celo core contracts with historical data * fix: `uncles` field is expected but not present in RPC node response * fix: credo & format * fix: define `CELO_CORE_CONTRACTS` in tests * fix: put `gas_token` under `celo` section * refactor: move token transfers filtering from `TokenTotalSupplyUpdater` to `Transform.TokenTransfers` * feat: add workflow to publish docker image * refactor: apply suggestions by @nikitosing * chore: add docs and specs * fix: malformed import * feat: add pending epoch operations table * refactor: fix format * feat: add transformers for epoch events * feat: add query to stream pending epoch block operations * fix: call to renamed function * fix: add factory method for pending epoch block operation * feat: add utils for logs parsing * feat: add schemas for epoch rewards and election rewards * fix: rename transformer according to event name * feat: implement epoch rewards fetcher * feat: fetch and import epoch logs * feat: improve epoch rewards fetcher 1. Do not fetch logs -- use the ones stored in DB 2. Import epochs to the database 3. Configure fetcher in runtime.exs * feat: add epoch helper functions * activated_validator_group_vote.ex * feat: add task to fetch core contracts * feat: fetch core contract events in the task * fix: merge artifacts * refactor: logs requests and reduce scope of epoch logs request * chore: fix formatting, credo warning, etc. * feat: fetch epoch rewards in one sql query * feat: fetch validator group votes (historical data and on demand) * refactor: rename fields and replace all entries on conflict * feat: validator group votes fetcher * fix: put each topic in a separate request * feat: fetch voter rewards * fix: merge artifacts * feat: add epoch rewards in block api response * fix: merge artifacts * feat: add base fee breakdown in block api response * feat: add aggregated election rewards to block api response * perf: use replica when querying epoch rewards * feat: endpoint for paginated election rewards * feat: endpoint for paginated election rewards for address * chore: rename `rewards` to `distributions` * fix: add missing reward type to API error message * refactor: split epoch fetcher to multiple modules * feat: send epoch blocks for async fetching from block fetcher * chore: fix credo and formatting issues * fix: dialyzer warnings * fix: add on demand fetch of group votes * fix: failed explorer and indexer tests * fix: block fetcher tests * chore: remove unused module * fix: sort election rewards by block number * fix: match error in epoch logs * feat: add env to manage logs batch size when fetching validator group votes * fix: credo and formatting warnings * fix: cspell errors * fix: dialyzer errors * fix: Add `ssl_opts` for Celo repo * fix: add missing preloads and make more explicit api response for block * fix: add `disabled?` predicate to supervisor config * fix: return empty list when `getPaymentDelegation` is not available * fix: validate the case when there is no voter rewards for an epoch * fix: formatting * fix: add missing preload * refactor: more robust fees breakdown logic for the case of fee handler * fix: formatting * refactor: move epoch information to the separate endpoint * fix: import `put_if_present/3` * fix: formatting * fix: do not treat genesis block as an epoch block * fix: some of the fetcher tests * dbg * chore: remove commented code * chore: canonical disable flag env name * fix: run test only for celo chain type * fix: add explicit `wait_ for_tasks` in token instance realtime test * fix: set missing `CELO_CORE_CONTRACTS` env * fix: rollback token instance realtime test * chore: remove debug artifact * chore: add `@docs` and `@specs` * fix: missing core contracts var in tests * feat: extend `tabs-counters` endpoint with election rewards count * refactor: move dead address to `Explorer.Chain.SmartContract` * perf: clause with simplified queries in the case of `amount == 0` and/or `block_number == 0` * fix: it comes that `NotLoaded` clause is not redundant actually... * fix: add missing preload in `BlockScoutWeb.AddressChannel` * chore: unset `CELO_CORE_CONTRACTS` in tests * chore: add missing specs * fix: `paginate` clause when `amount == 0` * refactor: move `paging_options/2` to `Explorer.Chain.Celo.ElectionReward` * refactor: avoid using virtual field for block number. * fix: remove redundant condition in the query * chore: clarify `@spec` * chore: fix credo warning * chore: remove `fi-celo` from branches that trigger ci * fix: merge artifacts * fix: remove todo comment --- .github/workflows/config.yml | 2 +- .../publish-docker-image-for-celo.yml | 48 ++ .../lib/block_scout_web/chain.ex | 17 +- .../channels/address_channel.ex | 25 +- .../controllers/api/v2/address_controller.ex | 116 +++- .../api/v2/advanced_filter_controller.ex | 3 +- .../controllers/api/v2/block_controller.ex | 136 +++- .../controllers/api/v2/fallback_controller.ex | 24 +- .../api/v2/main_page_controller.ex | 24 +- .../controllers/api/v2/mud_controller.ex | 4 +- .../controllers/api/v2/token_controller.ex | 4 +- .../api/v2/transaction_controller.ex | 5 + .../models/transaction_state_helper.ex | 2 +- .../lib/block_scout_web/routers/api_router.ex | 9 + .../views/api/v2/block_view.ex | 6 + .../block_scout_web/views/api/v2/celo_view.ex | 391 +++++++++++ .../views/api/v2/transaction_view.ex | 10 + .../api/v2/block_controller_test.exs | 22 + .../api/v2/transaction_controller_test.exs | 179 +++++ apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex | 12 + .../lib/ethereum_jsonrpc/block.ex | 43 +- .../lib/ethereum_jsonrpc/geth/call.ex | 3 +- .../lib/ethereum_jsonrpc/logs.ex | 106 ++- .../lib/ethereum_jsonrpc/nethermind/trace.ex | 2 +- .../lib/ethereum_jsonrpc/transaction.ex | 53 +- apps/explorer/config/dev.exs | 2 + apps/explorer/config/prod.exs | 5 + apps/explorer/config/test.exs | 1 + .../account/notifier/forbidden_address.ex | 8 +- apps/explorer/lib/explorer/application.ex | 1 + apps/explorer/lib/explorer/chain.ex | 4 +- .../lib/explorer/chain/address/counters.ex | 23 +- apps/explorer/lib/explorer/chain/block.ex | 24 +- .../chain/cache/celo_core_contracts.ex | 235 +++++++ .../explorer/chain/celo/election_reward.ex | 455 +++++++++++++ .../lib/explorer/chain/celo/epoch_reward.ex | 117 ++++ .../lib/explorer/chain/celo/helper.ex | 94 +++ .../celo/pending_epoch_block_operation.ex | 74 ++ .../lib/explorer/chain/celo/reader.ex | 193 ++++++ .../chain/celo/validator_group_vote.ex | 76 +++ .../explorer/chain/import/runner/blocks.ex | 44 ++ .../import/runner/celo/election_rewards.ex | 93 +++ .../chain/import/runner/celo/epoch_rewards.ex | 139 ++++ .../runner/celo/validator_group_votes.ex | 87 +++ .../lib/explorer/chain/import/runner/logs.ex | 99 ++- .../chain/import/runner/token_transfers.ex | 106 ++- .../chain/import/runner/transactions.ex | 78 +++ .../chain/import/stage/block_referencing.ex | 139 ++-- apps/explorer/lib/explorer/chain/log.ex | 128 +++- .../lib/explorer/chain/smart_contract.ex | 11 +- .../explorer/chain/smart_contract/proxy.ex | 2 +- .../lib/explorer/chain/token_transfer.ex | 188 +++-- .../lib/explorer/chain/transaction.ex | 25 + apps/explorer/lib/explorer/helper.ex | 36 +- apps/explorer/lib/explorer/paging_options.ex | 10 + apps/explorer/lib/explorer/repo.ex | 10 + .../explorer/lib/fetch_celo_core_contracts.ex | 236 +++++++ .../20240323152023_add_custom_fields.exs | 11 + ...856_add_pending_epoch_block_operations.exs | 14 + ...nsaction_hash_from_primary_key_in_logs.exs | 26 + ...sh_from_primary_key_in_token_transfers.exs | 23 + .../20240607185817_add_epoch_rewards.exs | 62 ++ ...240612135216_add_validator_group_votes.exs | 36 + .../20240614125614_add_election_rewards.exs | 38 ++ ...used_fields_from_validator_group_votes.exs | 10 + .../chain/cache/celo_core_contracts_test.exs | 84 +++ .../chain/import/runner/blocks_test.exs | 57 ++ apps/explorer/test/support/factory.ex | 5 + .../lib/indexer/block/catchup/fetcher.ex | 4 +- apps/indexer/lib/indexer/block/fetcher.ex | 42 +- .../lib/indexer/block/realtime/fetcher.ex | 8 +- .../fetcher/celo/epoch_block_operations.ex | 146 ++++ .../core_contract_version.ex | 92 +++ .../delegated_payments.ex | 166 +++++ .../epoch_block_operations/distributions.ex | 101 +++ .../validator_and_group_payments.ex | 67 ++ .../epoch_block_operations/voter_payments.ex | 201 ++++++ .../lib/indexer/fetcher/celo/epoch_logs.ex | 121 ++++ .../lib/indexer/fetcher/celo/helper.ex | 29 + .../fetcher/celo/validator_group_votes.ex | 217 ++++++ .../indexer/fetcher/internal_transaction.ex | 59 +- apps/indexer/lib/indexer/helper.ex | 109 ++- apps/indexer/lib/indexer/supervisor.ex | 7 + .../transform/address_coin_balances.ex | 40 +- .../lib/indexer/transform/addresses.ex | 36 +- .../transform/celo/transaction_gas_tokens.ex | 42 ++ .../celo/transaction_token_transfers.ex | 138 ++++ .../validator_epoch_payment_distributions.ex | 72 ++ .../lib/indexer/transform/token_transfers.ex | 19 +- .../bound_interval_supervisor_test.exs | 29 + .../indexer/block/catchup/fetcher_test.exs | 29 +- .../test/indexer/block/fetcher_test.exs | 643 ++++++++++++++---- .../indexer/block/realtime/fetcher_test.exs | 144 +++- .../celo/epoch_block_operations_test.exs | 46 ++ .../fetcher/internal_transaction_test.exs | 19 +- .../celo_epoch_rewards_supervisor_case.ex | 17 + config/config_helper.exs | 6 +- config/runtime.exs | 16 + config/runtime/dev.exs | 9 + config/runtime/prod.exs | 8 + cspell.json | 10 +- docker-compose/envs/common-blockscout.env | 5 +- 102 files changed, 6495 insertions(+), 557 deletions(-) create mode 100644 .github/workflows/publish-docker-image-for-celo.yml create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex create mode 100644 apps/explorer/lib/explorer/chain/celo/election_reward.ex create mode 100644 apps/explorer/lib/explorer/chain/celo/epoch_reward.ex create mode 100644 apps/explorer/lib/explorer/chain/celo/helper.ex create mode 100644 apps/explorer/lib/explorer/chain/celo/pending_epoch_block_operation.ex create mode 100644 apps/explorer/lib/explorer/chain/celo/reader.ex create mode 100644 apps/explorer/lib/explorer/chain/celo/validator_group_vote.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/celo/election_rewards.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/celo/epoch_rewards.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/celo/validator_group_votes.ex create mode 100644 apps/explorer/lib/fetch_celo_core_contracts.ex create mode 100644 apps/explorer/priv/celo/migrations/20240323152023_add_custom_fields.exs create mode 100644 apps/explorer/priv/celo/migrations/20240424121856_add_pending_epoch_block_operations.exs create mode 100644 apps/explorer/priv/celo/migrations/20240512143204_remove_transaction_hash_from_primary_key_in_logs.exs create mode 100644 apps/explorer/priv/celo/migrations/20240513091316_remove_transaction_hash_from_primary_key_in_token_transfers.exs create mode 100644 apps/explorer/priv/celo/migrations/20240607185817_add_epoch_rewards.exs create mode 100644 apps/explorer/priv/celo/migrations/20240612135216_add_validator_group_votes.exs create mode 100644 apps/explorer/priv/celo/migrations/20240614125614_add_election_rewards.exs create mode 100644 apps/explorer/priv/celo/migrations/20240715110334_remove_unused_fields_from_validator_group_votes.exs create mode 100644 apps/explorer/test/explorer/chain/cache/celo_core_contracts_test.exs create mode 100644 apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations.ex create mode 100644 apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/core_contract_version.ex create mode 100644 apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/delegated_payments.ex create mode 100644 apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/distributions.ex create mode 100644 apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/validator_and_group_payments.ex create mode 100644 apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/voter_payments.ex create mode 100644 apps/indexer/lib/indexer/fetcher/celo/epoch_logs.ex create mode 100644 apps/indexer/lib/indexer/fetcher/celo/helper.ex create mode 100644 apps/indexer/lib/indexer/fetcher/celo/validator_group_votes.ex create mode 100644 apps/indexer/lib/indexer/transform/celo/transaction_gas_tokens.ex create mode 100644 apps/indexer/lib/indexer/transform/celo/transaction_token_transfers.ex create mode 100644 apps/indexer/lib/indexer/transform/celo/validator_epoch_payment_distributions.ex create mode 100644 apps/indexer/test/indexer/fetcher/celo/epoch_block_operations_test.exs create mode 100644 apps/indexer/test/support/indexer/fetcher/celo_epoch_rewards_supervisor_case.ex diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml index 7a852ee6d03d..edefb0f392b7 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/config.yml @@ -49,7 +49,7 @@ jobs: // Add/remove CI matrix chain types here const defaultChainTypes = ["default"]; - const chainTypes = ["ethereum", "polygon_zkevm", "rsk", "stability", "filecoin", "optimism", "arbitrum"]; + const chainTypes = ["ethereum", "polygon_zkevm", "rsk", "stability", "filecoin", "optimism", "arbitrum", "celo"]; const extraChainTypes = ["suave", "polygon_edge"]; // Chain type matrix we use in master branch diff --git a/.github/workflows/publish-docker-image-for-celo.yml b/.github/workflows/publish-docker-image-for-celo.yml new file mode 100644 index 000000000000..ace16f10edf4 --- /dev/null +++ b/.github/workflows/publish-docker-image-for-celo.yml @@ -0,0 +1,48 @@ +name: Celo Publish Docker image + +on: + workflow_dispatch: + push: + branches: + - production-celo +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + DOCKER_CHAIN_NAME: celo + steps: + - uses: actions/checkout@v4 + - name: Setup repo + uses: ./.github/actions/setup-repo + id: setup + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + docker-remote-multi-platform: true + docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} + docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} + + - name: Build and push Docker image for Celo (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }} + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} diff --git a/apps/block_scout_web/lib/block_scout_web/chain.ex b/apps/block_scout_web/lib/block_scout_web/chain.ex index 5e652ce4a5a0..b6b4ddd1c36d 100644 --- a/apps/block_scout_web/lib/block_scout_web/chain.ex +++ b/apps/block_scout_web/lib/block_scout_web/chain.ex @@ -14,14 +14,18 @@ defmodule BlockScoutWeb.Chain do string_to_transaction_hash: 1 ] + import Explorer.PagingOptions, + only: [ + default_paging_options: 0, + page_size: 0 + ] + import Explorer.Helper, only: [parse_integer: 1] alias BlockScoutWeb.PagingHelper alias Ecto.Association.NotLoaded - alias Explorer.Chain.UserOperation alias Explorer.Account.{TagAddress, TagTransaction, WatchlistAddress} alias Explorer.Chain.Beacon.Reader, as: BeaconReader - alias Explorer.Chain.Block.Reward alias Explorer.Chain.{ Address, @@ -29,6 +33,7 @@ defmodule BlockScoutWeb.Chain do Address.CurrentTokenBalance, Beacon.Blob, Block, + Block.Reward, Hash, InternalTransaction, Log, @@ -59,15 +64,11 @@ defmodule BlockScoutWeb.Chain do end end - @page_size 50 - @default_paging_options %PagingOptions{page_size: @page_size + 1} + @page_size page_size() + @default_paging_options default_paging_options() @address_hash_len 40 @full_hash_len 64 - def default_paging_options do - @default_paging_options - end - def current_filter(%{paging_options: paging_options} = params) do params |> Map.get("filter") diff --git a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex index 4ea5943e0c4f..7fd6932a41df 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex @@ -38,6 +38,23 @@ defmodule BlockScoutWeb.AddressChannel do @burn_address_hash burn_address_hash @current_token_balances_limit 50 + case Application.compile_env(:explorer, :chain_type) do + :celo -> + @chain_type_transaction_associations [ + :gas_token + ] + + _ -> + @chain_type_transaction_associations [] + end + + @transaction_associations [ + from_address: [:names, :smart_contract, :proxy_implementations], + to_address: [:names, :smart_contract, :proxy_implementations], + created_contract_address: [:names, :smart_contract, :proxy_implementations] + ] ++ + @chain_type_transaction_associations + def join("addresses:" <> address_hash, _params, socket) do {:ok, %{}, assign(socket, :address_hash, address_hash)} end @@ -335,13 +352,7 @@ defmodule BlockScoutWeb.AddressChannel do TransactionViewAPI.render("transactions.json", %{ transactions: transactions - |> Repo.preload([ - [ - from_address: [:names, :smart_contract, :proxy_implementations], - to_address: [:names, :smart_contract, :proxy_implementations], - created_contract_address: [:names, :smart_contract, :proxy_implementations] - ] - ]), + |> Repo.preload(@transaction_associations), conn: nil }) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index dd6a721d6bf2..f5c2ef44f44e 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -30,17 +30,33 @@ defmodule BlockScoutWeb.API.V2.AddressController do alias Explorer.Chain.Address.Counters alias Explorer.Chain.Token.Instance + alias BlockScoutWeb.API.V2.CeloView + alias Explorer.Chain.Celo.ElectionReward, as: CeloElectionReward + alias Explorer.Chain.Celo.Reader, as: CeloReader + alias Indexer.Fetcher.OnDemand.CoinBalance, as: CoinBalanceOnDemand alias Indexer.Fetcher.OnDemand.ContractCode, as: ContractCodeOnDemand alias Indexer.Fetcher.OnDemand.TokenBalance, as: TokenBalanceOnDemand + case Application.compile_env(:explorer, :chain_type) do + :celo -> + @chain_type_transaction_necessity_by_association %{ + :gas_token => :optional + } + + _ -> + @chain_type_transaction_necessity_by_association %{} + end + @transaction_necessity_by_association [ - necessity_by_association: %{ - [created_contract_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - :block => :optional - }, + necessity_by_association: + %{ + [created_contract_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + :block => :optional + } + |> Map.merge(@chain_type_transaction_necessity_by_association), api?: true ] @@ -79,6 +95,26 @@ defmodule BlockScoutWeb.API.V2.AddressController do @api_true [api?: true] + @celo_election_rewards_options [ + necessity_by_association: %{ + [ + account_address: [ + :names, + :smart_contract, + :proxy_implementations + ] + ] => :optional, + [ + associated_account_address: [ + :names, + :smart_contract, + :proxy_implementations + ] + ] => :optional + }, + api?: true + ] + action_fallback(BlockScoutWeb.API.V2.FallbackController) def address(conn, %{"address_hash_param" => address_hash_string} = params) do @@ -447,20 +483,35 @@ defmodule BlockScoutWeb.API.V2.AddressController do def tabs_counters(conn, %{"address_hash_param" => address_hash_string} = params) do with {:ok, address_hash, _address} <- validate_address(address_hash_string, params) do - {validations, transactions, token_transfers, token_balances, logs, withdrawals, internal_txs} = - Counters.address_limited_counters(address_hash, @api_true) + counter_name_to_json_field_name = %{ + validations: :validations_count, + txs: :transactions_count, + token_transfers: :token_transfers_count, + token_balances: :token_balances_count, + logs: :logs_count, + withdrawals: :withdrawals_count, + internal_txs: :internal_txs_count, + celo_election_rewards: :celo_election_rewards_count + } + + counters_json = + address_hash + |> Counters.address_limited_counters(@api_true) + |> Enum.reduce(%{}, fn {counter_name, counter_value}, acc -> + counter_name_to_json_field_name + |> Map.fetch(counter_name) + |> case do + {:ok, json_field_name} -> + Map.put(acc, json_field_name, counter_value) + + :error -> + acc + end + end) conn |> put_status(200) - |> json(%{ - validations_count: validations, - transactions_count: transactions, - token_transfers_count: token_transfers, - token_balances_count: token_balances, - logs_count: logs, - withdrawals_count: withdrawals, - internal_txs_count: internal_txs - }) + |> json(counters_json) end end @@ -520,6 +571,37 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end + @doc """ + Function to handle GET requests to `/api/v2/addresses/:address_hash_param/election-rewards` endpoint. + """ + def celo_election_rewards(conn, %{"address_hash_param" => address_hash_string} = params) do + with {:ok, address_hash, _address} <- validate_address(address_hash_string, params) do + full_options = + @celo_election_rewards_options + |> Keyword.merge(CeloElectionReward.address_paging_options(params)) + + results_plus_one = CeloReader.address_hash_to_election_rewards(address_hash, full_options) + + {rewards, next_page} = split_list_by_page(results_plus_one) + + next_page_params = + next_page_params( + next_page, + rewards, + delete_parameters_from_next_page_params(params), + &CeloElectionReward.to_address_paging_params/1 + ) + + conn + |> put_status(200) + |> put_view(CeloView) + |> render(:celo_election_rewards, %{ + rewards: rewards, + next_page_params: next_page_params + }) + end + end + @doc """ Checks if this valid address hash string, and this address is not prohibited address. Returns the `{:ok, address_hash, address}` if address hash passed all the checks. diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex index a3c2f332a26f..3178aaae5d7d 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex @@ -1,7 +1,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do use BlockScoutWeb, :controller - import BlockScoutWeb.Chain, only: [default_paging_options: 0, split_list_by_page: 1, next_page_params: 4] + import BlockScoutWeb.Chain, only: [split_list_by_page: 1, next_page_params: 4] + import Explorer.PagingOptions, only: [default_paging_options: 0] alias BlockScoutWeb.API.V2.{AdvancedFilterView, CSVExportController, TransactionView} alias Explorer.{Chain, PagingOptions} diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex index e4495740cdc7..4db96f9c11e6 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex @@ -17,9 +17,23 @@ defmodule BlockScoutWeb.API.V2.BlockController do import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1] import Explorer.MicroserviceInterfaces.Metadata, only: [maybe_preload_metadata: 1] - alias BlockScoutWeb.API.V2.{TransactionView, WithdrawalView} + import Explorer.Chain.Celo.Helper, + only: [ + validate_epoch_block_number: 1, + block_number_to_epoch_number: 1 + ] + + alias BlockScoutWeb.API.V2.{ + CeloView, + TransactionView, + WithdrawalView + } + alias Explorer.Chain alias Explorer.Chain.Arbitrum.Reader, as: ArbitrumReader + alias Explorer.Chain.Celo.ElectionReward, as: CeloElectionReward + alias Explorer.Chain.Celo.EpochReward, as: CeloEpochReward + alias Explorer.Chain.Celo.Reader, as: CeloReader alias Explorer.Chain.InternalTransaction case Application.compile_env(:explorer, :chain_type) do @@ -46,6 +60,12 @@ defmodule BlockScoutWeb.API.V2.BlockController do :zksync_execute_transaction => :optional } + :celo -> + @chain_type_transaction_necessity_by_association %{ + :gas_token => :optional + } + @chain_type_block_necessity_by_association %{} + :arbitrum -> @chain_type_transaction_necessity_by_association %{} @chain_type_block_necessity_by_association %{ @@ -278,9 +298,123 @@ defmodule BlockScoutWeb.API.V2.BlockController do end end + @doc """ + Function to handle GET requests to `/api/v2/blocks/:block_hash_or_number/epoch` endpoint. + """ + @spec celo_epoch(Plug.Conn.t(), map()) :: + {:error, :not_found | {:invalid, :hash | :number | :celo_election_reward_type}} + | {:lost_consensus, {:error, :not_found} | {:ok, Explorer.Chain.Block.t()}} + | Plug.Conn.t() + def celo_epoch(conn, %{"block_hash_or_number" => block_hash_or_number}) do + params = [ + necessity_by_association: %{ + :celo_epoch_reward => :optional + }, + api?: true + ] + + with {:ok, block} <- block_param_to_block(block_hash_or_number, params), + :ok <- validate_epoch_block_number(block.number) do + epoch_number = block_number_to_epoch_number(block.number) + + epoch_distribution = + block + |> Map.get(:celo_epoch_reward) + |> case do + %CeloEpochReward{} = epoch_reward -> + CeloEpochReward.load_token_transfers(epoch_reward, api?: true) + + _ -> + nil + end + + aggregated_election_rewards = + CeloReader.block_hash_to_aggregated_election_rewards_by_type( + block.hash, + api?: true + ) + + conn + |> put_status(200) + |> put_view(CeloView) + |> render(:celo_epoch, %{ + epoch_number: epoch_number, + epoch_distribution: epoch_distribution, + aggregated_election_rewards: aggregated_election_rewards + }) + end + end + + @doc """ + Function to handle GET requests to `/api/v2/blocks/:block_hash_or_number/election-rewards/:reward_type` endpoint. + """ + @spec celo_election_rewards(Plug.Conn.t(), map()) :: + {:error, :not_found | {:invalid, :hash | :number | :celo_election_reward_type}} + | {:lost_consensus, {:error, :not_found} | {:ok, Explorer.Chain.Block.t()}} + | Plug.Conn.t() + def celo_election_rewards( + conn, + %{"block_hash_or_number" => block_hash_or_number, "reward_type" => reward_type} = params + ) do + with {:ok, reward_type_atom} <- celo_reward_type_to_atom(reward_type), + {:ok, block} <- + block_param_to_block(block_hash_or_number) do + address_associations = [:names, :smart_contract, :proxy_implementations] + + full_options = + [ + necessity_by_association: %{ + [account_address: address_associations] => :optional, + [associated_account_address: address_associations] => :optional + } + ] + |> Keyword.merge(CeloElectionReward.block_paging_options(params)) + |> Keyword.merge(@api_true) + + rewards_plus_one = + CeloReader.block_hash_to_election_rewards_by_type( + block.hash, + reward_type_atom, + full_options + ) + + {rewards, next_page} = split_list_by_page(rewards_plus_one) + + filtered_params = + params + |> delete_parameters_from_next_page_params() + |> Map.delete("reward_type") + + next_page_params = + next_page_params( + next_page, + rewards, + filtered_params, + &CeloElectionReward.to_block_paging_params/1 + ) + + conn + |> put_status(200) + |> put_view(CeloView) + |> render(:celo_election_rewards, %{ + rewards: rewards, + next_page_params: next_page_params + }) + end + end + defp block_param_to_block(block_hash_or_number, options \\ @api_true) do with {:ok, type, value} <- parse_block_hash_or_number_param(block_hash_or_number) do fetch_block(type, value, options) end end + + defp celo_reward_type_to_atom(reward_type_string) do + reward_type_string + |> CeloElectionReward.type_from_string() + |> case do + {:ok, type} -> {:ok, type} + :error -> {:error, {:invalid, :celo_election_reward_type}} + end + end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex index 19448ad5fa79..7f2544b453ec 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex @@ -12,6 +12,7 @@ defmodule BlockScoutWeb.API.V2.FallbackController do @invalid_hash "Invalid hash" @invalid_number "Invalid number" @invalid_url "Invalid URL" + @invalid_celo_election_reward_type "Invalid Celo reward type, allowed types are: validator, group, voter, delegated-payment" @not_found "Not found" @contract_interaction_disabled "Contract interaction disabled" @restricted_access "Restricted access" @@ -97,26 +98,23 @@ defmodule BlockScoutWeb.API.V2.FallbackController do |> render(:message, %{message: @contract_interaction_disabled}) end - def call(conn, {:error, {:invalid, :hash}}) do - Logger.error(fn -> - ["#{@invalid_hash}"] - end) - - conn - |> put_status(:unprocessable_entity) - |> put_view(ApiView) - |> render(:message, %{message: @invalid_hash}) - end + def call(conn, {:error, {:invalid, entity}}) + when entity in ~w(hash number celo_election_reward_type)a do + message = + case entity do + :hash -> @invalid_hash + :number -> @invalid_number + :celo_election_reward_type -> @invalid_celo_election_reward_type + end - def call(conn, {:error, {:invalid, :number}}) do Logger.error(fn -> - ["#{@invalid_number}"] + ["#{message}"] end) conn |> put_status(:unprocessable_entity) |> put_view(ApiView) - |> render(:message, %{message: @invalid_number}) + |> render(:message, %{message: message}) end def call(conn, {:error, :not_found}) do diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex index ecfaebd16f5c..79a89f8edda7 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex @@ -10,13 +10,25 @@ defmodule BlockScoutWeb.API.V2.MainPageController do import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1] import Explorer.MicroserviceInterfaces.Metadata, only: [maybe_preload_metadata: 1] + case Application.compile_env(:explorer, :chain_type) do + :celo -> + @chain_type_transaction_necessity_by_association %{ + :gas_token => :optional + } + + _ -> + @chain_type_transaction_necessity_by_association %{} + end + @transactions_options [ - necessity_by_association: %{ - :block => :required, - [created_contract_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional - }, + necessity_by_association: + %{ + :block => :required, + [created_contract_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional + } + |> Map.merge(@chain_type_transaction_necessity_by_association), paging_options: %PagingOptions{page_size: 6}, api?: true ] diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex index 6cbba16d32dc..bf735edbaaa6 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex @@ -4,11 +4,11 @@ defmodule BlockScoutWeb.API.V2.MudController do import BlockScoutWeb.Chain, only: [ next_page_params: 4, - split_list_by_page: 1, - default_paging_options: 0 + split_list_by_page: 1 ] import BlockScoutWeb.PagingHelper, only: [mud_records_sorting: 1] + import Explorer.PagingOptions, only: [default_paging_options: 0] import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1] import Explorer.MicroserviceInterfaces.Metadata, only: [maybe_preload_metadata: 1] diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex index 86f9093d5fbc..934b4dbaf7bb 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex @@ -17,8 +17,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do next_page_params: 3, token_transfers_next_page_params: 3, unique_tokens_paging_options: 1, - unique_tokens_next_page: 3, - default_paging_options: 0 + unique_tokens_next_page: 3 ] import BlockScoutWeb.PagingHelper, @@ -31,6 +30,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1] import Explorer.MicroserviceInterfaces.Metadata, only: [maybe_preload_metadata: 1] + import Explorer.PagingOptions, only: [default_paging_options: 0] action_fallback(BlockScoutWeb.API.V2.FallbackController) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex index 485411b11593..0ebb3a4c6b29 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex @@ -54,6 +54,11 @@ defmodule BlockScoutWeb.API.V2.TransactionController do :beacon_blob_transaction => :optional } + :celo -> + @chain_type_transaction_necessity_by_association %{ + :gas_token => :optional + } + _ -> @chain_type_transaction_necessity_by_association %{} end diff --git a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex index 8d492ed1841e..3d96d69ff19b 100644 --- a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex @@ -3,7 +3,7 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do Transaction state changes related functions """ - import BlockScoutWeb.Chain, only: [default_paging_options: 0] + import Explorer.PagingOptions, only: [default_paging_options: 0] import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] alias Explorer.Chain.Transaction.StateChange diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index 3aee52a138a8..b976c38720ff 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -158,6 +158,11 @@ defmodule BlockScoutWeb.Routers.ApiRouter do if Application.compile_env(:explorer, :chain_type) == :arbitrum do get("/arbitrum-batch/:batch_number", V2.BlockController, :arbitrum_batch) end + + if Application.compile_env(:explorer, :chain_type) == :celo do + get("/:block_hash_or_number/epoch", V2.BlockController, :celo_epoch) + get("/:block_hash_or_number/election-rewards/:reward_type", V2.BlockController, :celo_election_rewards) + end end scope "/addresses" do @@ -177,6 +182,10 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/:address_hash_param/withdrawals", V2.AddressController, :withdrawals) get("/:address_hash_param/nft", V2.AddressController, :nft_list) get("/:address_hash_param/nft/collections", V2.AddressController, :nft_collections) + + if Application.compile_env(:explorer, :chain_type) == :celo do + get("/:address_hash_param/election-rewards", V2.AddressController, :celo_election_rewards) + end end scope "/main-page" do diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex index 0df80c938421..75a22551b884 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex @@ -146,6 +146,12 @@ defmodule BlockScoutWeb.API.V2.BlockView do BlockScoutWeb.API.V2.EthereumView.extend_block_json_response(result, block, single_block?) end + :celo -> + defp chain_type_fields(result, block, single_block?) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + BlockScoutWeb.API.V2.CeloView.extend_block_json_response(result, block, single_block?) + end + _ -> defp chain_type_fields(result, _block, _single_block?) do result diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex new file mode 100644 index 000000000000..eab95a7559ca --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex @@ -0,0 +1,391 @@ +defmodule BlockScoutWeb.API.V2.CeloView do + @moduledoc """ + View functions for rendering Celo-related data in JSON format. + """ + + require Logger + + import Explorer.Chain.SmartContract, only: [dead_address_hash_string: 0] + + alias BlockScoutWeb.API.V2.{Helper, TokenView, TransactionView} + alias Ecto.Association.NotLoaded + alias Explorer.Chain + alias Explorer.Chain.Cache.CeloCoreContracts + alias Explorer.Chain.Celo.Helper, as: CeloHelper + alias Explorer.Chain.Celo.{ElectionReward, EpochReward} + alias Explorer.Chain.Hash + alias Explorer.Chain.{Block, Transaction} + + @address_params [ + necessity_by_association: %{ + :names => :optional, + :smart_contract => :optional, + :proxy_implementations => :optional + }, + api?: true + ] + + def render("celo_epoch_distribution.json", %EpochReward{ + reserve_bolster_transfer: reserve_bolster_transfer, + community_transfer: community_transfer, + carbon_offsetting_transfer: carbon_offsetting_transfer + }) do + Map.new( + [ + reserve_bolster_transfer: reserve_bolster_transfer, + community_transfer: community_transfer, + carbon_offsetting_transfer: carbon_offsetting_transfer + ], + fn {field, token_transfer} -> + token_transfer_json = + token_transfer && + TransactionView.render( + "token_transfer.json", + %{token_transfer: token_transfer, conn: nil} + ) + + {field, token_transfer_json} + end + ) + end + + def render("celo_epoch_distribution.json", _distribution), + do: nil + + def render("celo_aggregated_election_rewards.json", aggregated_election_rewards) do + Map.new( + aggregated_election_rewards, + fn {type, %{total: total, count: count, token: token}} -> + {type, + %{ + total: total, + count: count, + token: + TokenView.render("token.json", %{ + token: token, + contract_address_hash: token.contract_address_hash + }) + }} + end + ) + end + + def render("celo_epoch.json", %{ + epoch_number: epoch_number, + epoch_distribution: epoch_distribution, + aggregated_election_rewards: aggregated_election_rewards + }) do + distribution_json = render("celo_epoch_distribution.json", epoch_distribution) + + # Workaround: we assume that if epoch rewards are not fetched for a block, + # we should not display aggregated election rewards for it. + # + # todo: consider checking pending block epoch operations to determine if + # epoch is fetched or not + aggregated_election_rewards_json = + if distribution_json do + render("celo_aggregated_election_rewards.json", aggregated_election_rewards) + else + nil + end + + %{ + number: epoch_number, + distribution: distribution_json, + aggregated_election_rewards: aggregated_election_rewards_json + } + end + + def render("celo_base_fee.json", %Block{} = block) do + # For the blocks, where both FeeHandler and Governance contracts aren't + # deployed, the base fee is not burnt, but refunded to transaction sender, + # so we return nil in this case. + + base_fee = Block.burnt_fees(block.transactions, block.base_fee_per_gas) + + fee_handler_base_fee_breakdown( + base_fee, + block.number + ) || + governance_base_fee_breakdown( + base_fee, + block.number + ) + end + + def render("celo_election_rewards.json", %{ + rewards: rewards, + next_page_params: next_page_params + }) do + %{ + "items" => Enum.map(rewards, &prepare_election_reward/1), + "next_page_params" => next_page_params + } + end + + @doc """ + Extends the JSON output with a sub-map containing information related to Celo, + such as the epoch number, whether the block is an epoch block, and the routing + of the base fee. + + ## Parameters + - `out_json`: A map defining the output JSON which will be extended. + - `block`: The block structure containing Celo-related data. + - `single_block?`: A boolean indicating if it is a single block. + + ## Returns + - A map extended with data related to Celo. + """ + def extend_block_json_response(out_json, %Block{} = block, single_block?) do + celo_json = + %{ + "is_epoch_block" => CeloHelper.epoch_block_number?(block.number), + "epoch_number" => CeloHelper.block_number_to_epoch_number(block.number) + } + |> maybe_add_base_fee_info(block, single_block?) + + Map.put(out_json, "celo", celo_json) + end + + @doc """ + Extends the JSON output with a sub-map containing information about the gas + token used to pay for the transaction fees. + + ## Parameters + - `out_json`: A map defining the output JSON which will be extended. + - `transaction`: The transaction structure containing Celo-related data. + + ## Returns + - A map extended with data related to the gas token. + """ + def extend_transaction_json_response(out_json, %Transaction{} = transaction) do + token_json = + case { + Map.get(transaction, :gas_token_contract_address), + Map.get(transaction, :gas_token) + } do + # {_, %NotLoaded{}} -> + # nil + + {nil, _} -> + nil + + {gas_token_contract_address, gas_token} -> + if is_nil(gas_token) do + Logger.error(fn -> + [ + "Transaction #{transaction.hash} has a ", + "gas token contract address #{gas_token_contract_address} ", + "but no associated token found in the database" + ] + end) + end + + TokenView.render("token.json", %{ + token: gas_token, + contract_address_hash: gas_token_contract_address + }) + end + + Map.put(out_json, "celo", %{"gas_token" => token_json}) + end + + @spec prepare_election_reward(Explorer.Chain.Celo.ElectionReward.t()) :: %{ + :account => nil | %{optional(String.t()) => any()}, + :amount => Decimal.t(), + :associated_account => nil | %{optional(String.t()) => any()}, + optional(:block_hash) => Hash.Full.t(), + optional(:block_number) => Block.block_number(), + optional(:epoch_number) => non_neg_integer(), + optional(:type) => ElectionReward.type() + } + defp prepare_election_reward(%ElectionReward{block: %NotLoaded{}} = reward) do + %{ + amount: reward.amount, + account: + Helper.address_with_info( + reward.account_address, + reward.account_address_hash + ), + associated_account: + Helper.address_with_info( + reward.associated_account_address, + reward.associated_account_address_hash + ) + } + end + + defp prepare_election_reward(%ElectionReward{} = reward) do + %{ + amount: reward.amount, + block_number: reward.block.number, + block_hash: reward.block_hash, + epoch_number: reward.block.number |> CeloHelper.block_number_to_epoch_number(), + account: + Helper.address_with_info( + reward.account_address, + reward.account_address_hash + ), + associated_account: + Helper.address_with_info( + reward.associated_account_address, + reward.associated_account_address_hash + ), + type: reward.type + } + end + + # Convert the burn fraction from FixidityLib value to decimal. + @spec burn_fraction_decimal(integer()) :: Decimal.t() + defp burn_fraction_decimal(burn_fraction_fixidity_lib) + when is_integer(burn_fraction_fixidity_lib) do + base = Decimal.new(1, 10, 24) + fraction = Decimal.new(1, burn_fraction_fixidity_lib, 0) + Decimal.div(fraction, base) + end + + # Get the breakdown of the base fee for the case when FeeHandler is a contract + # that receives the base fee. + @spec fee_handler_base_fee_breakdown(Decimal.t(), Block.block_number()) :: + %{ + :recipient => %{optional(String.t()) => any()}, + :amount => float(), + :breakdown => [ + %{ + :address => %{optional(String.t()) => any()}, + :amount => float(), + :percentage => float() + } + ] + } + | nil + defp fee_handler_base_fee_breakdown(base_fee, block_number) do + with {:ok, fee_handler_contract_address_hash} <- + CeloCoreContracts.get_address(:fee_handler, block_number), + {:ok, %{"address" => fee_beneficiary_address_hash}} <- + CeloCoreContracts.get_event(:fee_handler, :fee_beneficiary_set, block_number), + {:ok, %{"value" => burn_fraction_fixidity_lib}} <- + CeloCoreContracts.get_event(:fee_handler, :burn_fraction_set, block_number) do + burn_fraction = burn_fraction_decimal(burn_fraction_fixidity_lib) + + burnt_amount = Decimal.mult(base_fee, burn_fraction) + burnt_percentage = Decimal.mult(burn_fraction, 100) + + carbon_offsetting_amount = Decimal.sub(base_fee, burnt_amount) + carbon_offsetting_percentage = Decimal.sub(100, burnt_percentage) + + celo_burn_address_hash_string = dead_address_hash_string() + + address_hashes_to_fetch_from_db = [ + fee_handler_contract_address_hash, + fee_beneficiary_address_hash, + celo_burn_address_hash_string + ] + + address_hash_string_to_address = + address_hashes_to_fetch_from_db + |> Enum.map(&(&1 |> Chain.string_to_address_hash() |> elem(1))) + # todo: Querying database in the view is not a good practice. Consider + # refactoring. + |> Chain.hashes_to_addresses(@address_params) + |> Map.new(fn address -> + { + to_string(address.hash), + address + } + end) + + %{ + ^fee_handler_contract_address_hash => fee_handler_contract_address_info, + ^fee_beneficiary_address_hash => fee_beneficiary_address_info, + ^celo_burn_address_hash_string => burn_address_info + } = + Map.new( + address_hashes_to_fetch_from_db, + &{ + &1, + Helper.address_with_info( + Map.get(address_hash_string_to_address, &1), + &1 + ) + } + ) + + %{ + recipient: fee_handler_contract_address_info, + amount: base_fee, + breakdown: [ + %{ + address: burn_address_info, + amount: Decimal.to_float(burnt_amount), + percentage: Decimal.to_float(burnt_percentage) + }, + %{ + address: fee_beneficiary_address_info, + amount: Decimal.to_float(carbon_offsetting_amount), + percentage: Decimal.to_float(carbon_offsetting_percentage) + } + ] + } + else + _ -> nil + end + end + + # Get the breakdown of the base fee for the case when Governance is a contract + # that receives the base fee. + # + # Note that the base fee is not burnt in this case, but simply kept on the + # contract balance. + @spec governance_base_fee_breakdown(Decimal.t(), Block.block_number()) :: + %{ + :recipient => %{optional(String.t()) => any()}, + :amount => float(), + :breakdown => [ + %{ + :address => %{optional(String.t()) => any()}, + :amount => float(), + :percentage => float() + } + ] + } + | nil + defp governance_base_fee_breakdown(base_fee, block_number) do + with {:ok, address_hash_string} when not is_nil(address_hash_string) <- + CeloCoreContracts.get_address(:governance, block_number), + {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string) do + address = + address_hash + # todo: Querying database in the view is not a good practice. Consider + # refactoring. + |> Chain.hash_to_address(@address_params) + |> case do + {:ok, address} -> address + {:error, :not_found} -> nil + end + + address_with_info = + Helper.address_with_info( + address, + address_hash + ) + + %{ + recipient: address_with_info, + amount: base_fee, + breakdown: [] + } + else + _ -> + nil + end + end + + defp maybe_add_base_fee_info(celo_json, block_or_transaction, true) do + base_fee_breakdown_json = render("celo_base_fee.json", block_or_transaction) + Map.put(celo_json, "base_fee", base_fee_breakdown_json) + end + + defp maybe_add_base_fee_info(celo_json, _block_or_transaction, false), + do: celo_json +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex index 819857317659..0b43013a4a20 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex @@ -909,6 +909,16 @@ defmodule BlockScoutWeb.API.V2.TransactionView do BlockScoutWeb.API.V2.EthereumView.extend_transaction_json_response(result, transaction) end + :celo -> + defp chain_type_transformations(transactions) do + transactions + end + + defp chain_type_fields(result, transaction, _single_tx?, _conn, _watchlist_names) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + BlockScoutWeb.API.V2.CeloView.extend_transaction_json_response(result, transaction) + end + _ -> defp chain_type_transformations(transactions) do transactions diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/block_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/block_controller_test.exs index 9ba0952cb111..bfb690c04d60 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/block_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/block_controller_test.exs @@ -9,6 +9,28 @@ defmodule BlockScoutWeb.API.V2.BlockControllerTest do Supervisor.terminate_child(Explorer.Supervisor, Explorer.Chain.Cache.Uncles.child_id()) Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.Uncles.child_id()) + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, + contracts: %{ + "addresses" => %{ + "Accounts" => [], + "Election" => [], + "EpochRewards" => [], + "FeeHandler" => [], + "GasPriceMinimum" => [], + "GoldToken" => [], + "Governance" => [], + "LockedGold" => [], + "Reserve" => [], + "StableToken" => [], + "Validators" => [] + } + } + ) + + on_exit(fn -> + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, contracts: %{}) + end) + :ok end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs index f28b729e2983..148d17226543 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs @@ -1103,6 +1103,185 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end end + if Application.compile_env(:explorer, :chain_type) == :celo do + describe "celo gas token" do + test "when gas is paid with token and token is present in db", %{conn: conn} do + token = insert(:token) + + tx = + :transaction + |> insert(gas_token_contract_address: token.contract_address) + |> with_block() + + request = get(conn, "/api/v2/transactions") + + token_address_hash = Address.checksum(token.contract_address_hash) + token_type = token.type + token_name = token.name + token_symbol = token.symbol + + assert %{ + "items" => [ + %{ + "celo" => %{ + "gas_token" => %{ + "address" => ^token_address_hash, + "name" => ^token_name, + "symbol" => ^token_symbol, + "type" => ^token_type + } + } + } + ] + } = json_response(request, 200) + + request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}") + + assert %{ + "celo" => %{ + "gas_token" => %{ + "address" => ^token_address_hash, + "name" => ^token_name, + "symbol" => ^token_symbol, + "type" => ^token_type + } + } + } = json_response(request, 200) + + request = get(conn, "/api/v2/addresses/#{to_string(tx.from_address_hash)}/transactions") + + assert %{ + "items" => [ + %{ + "celo" => %{ + "gas_token" => %{ + "address" => ^token_address_hash, + "name" => ^token_name, + "symbol" => ^token_symbol, + "type" => ^token_type + } + } + } + ] + } = json_response(request, 200) + + request = get(conn, "/api/v2/main-page/transactions") + + assert [ + %{ + "celo" => %{ + "gas_token" => %{ + "address" => ^token_address_hash, + "name" => ^token_name, + "symbol" => ^token_symbol, + "type" => ^token_type + } + } + } + ] = json_response(request, 200) + end + + test "when gas is paid with token and token is not present in db", %{conn: conn} do + unknown_token_address = insert(:address) + + tx = + :transaction + |> insert(gas_token_contract_address: unknown_token_address) + |> with_block() + + unknown_token_address_hash = Address.checksum(unknown_token_address.hash) + + request = get(conn, "/api/v2/transactions") + + assert %{ + "items" => [ + %{ + "celo" => %{ + "gas_token" => %{ + "address" => ^unknown_token_address_hash + } + } + } + ] + } = json_response(request, 200) + + request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}") + + assert %{ + "celo" => %{ + "gas_token" => %{ + "address" => ^unknown_token_address_hash + } + } + } = json_response(request, 200) + + request = get(conn, "/api/v2/addresses/#{to_string(tx.from_address_hash)}/transactions") + + assert %{ + "items" => [ + %{ + "celo" => %{ + "gas_token" => %{ + "address" => ^unknown_token_address_hash + } + } + } + ] + } = json_response(request, 200) + + request = get(conn, "/api/v2/main-page/transactions") + + assert [ + %{ + "celo" => %{ + "gas_token" => %{ + "address" => ^unknown_token_address_hash + } + } + } + ] = json_response(request, 200) + end + + test "when gas is paid in native coin", %{conn: conn} do + tx = :transaction |> insert() |> with_block() + + request = get(conn, "/api/v2/transactions") + + assert %{ + "items" => [ + %{ + "celo" => %{"gas_token" => nil} + } + ] + } = json_response(request, 200) + + request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}") + + assert %{ + "celo" => %{"gas_token" => nil} + } = json_response(request, 200) + + request = get(conn, "/api/v2/addresses/#{to_string(tx.from_address_hash)}/transactions") + + assert %{ + "items" => [ + %{ + "celo" => %{"gas_token" => nil} + } + ] + } = json_response(request, 200) + + request = get(conn, "/api/v2/main-page/transactions") + + assert [ + %{ + "celo" => %{"gas_token" => nil} + } + ] = json_response(request, 200) + end + end + end + if Application.compile_env(:explorer, :chain_type) == :stability do @first_topic_hex_string_1 "0x99e7b0ba56da2819c37c047f0511fd2bf6c9b4e27b4a979a19d6da0f74be8155" diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex index 7cd5c6484279..e960e08cf86a 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex @@ -579,4 +579,16 @@ defmodule EthereumJSONRPC do defp chunk_requests(requests, nil), do: requests defp chunk_requests(requests, chunk_size), do: Enum.chunk_every(requests, chunk_size) + + def put_if_present(result, map, keys) do + Enum.reduce(keys, result, fn {from_key, to_key}, acc -> + value = map[from_key] + + if value do + Map.put(acc, to_key, value) + else + acc + end + end) + end end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex index 1c6b870b2864..9b3869256008 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex @@ -8,6 +8,11 @@ defmodule EthereumJSONRPC.Block do alias EthereumJSONRPC.{Transactions, Uncles, Withdrawals} + # Because proof of stake does not naturally produce uncles like proof of work, + # the list of these in each block is empty, and the hash of this list + # (sha3Uncles) is the RLP-encoded hash of an empty list. + @sha3_uncles_empty_list "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + case Application.compile_env(:explorer, :chain_type) do :rsk -> @chain_type_fields quote( @@ -320,13 +325,11 @@ defmodule EthereumJSONRPC.Block do "number" => number, "parentHash" => parent_hash, "receiptsRoot" => receipts_root, - "sha3Uncles" => sha3_uncles, "size" => size, "stateRoot" => state_root, "timestamp" => timestamp, "totalDifficulty" => total_difficulty, "transactionsRoot" => transactions_root, - "uncles" => uncles, "baseFeePerGas" => base_fee_per_gas } = elixir ) do @@ -343,13 +346,15 @@ defmodule EthereumJSONRPC.Block do number: number, parent_hash: parent_hash, receipts_root: receipts_root, - sha3_uncles: sha3_uncles, + # In case of CELO, `sha3_uncles` may not be returned by eth_getBlockByHash + sha3_uncles: Map.get(elixir, "sha3Uncles", @sha3_uncles_empty_list), size: size, state_root: state_root, timestamp: timestamp, total_difficulty: total_difficulty, transactions_root: transactions_root, - uncles: uncles, + # In case of CELO, `uncles` may not be returned by eth_getBlockByHash + uncles: Map.get(elixir, "uncles", []), base_fee_per_gas: base_fee_per_gas } end @@ -366,12 +371,10 @@ defmodule EthereumJSONRPC.Block do "number" => number, "parentHash" => parent_hash, "receiptsRoot" => receipts_root, - "sha3Uncles" => sha3_uncles, "size" => size, "stateRoot" => state_root, "timestamp" => timestamp, "transactionsRoot" => transactions_root, - "uncles" => uncles, "baseFeePerGas" => base_fee_per_gas } = elixir ) do @@ -388,12 +391,14 @@ defmodule EthereumJSONRPC.Block do number: number, parent_hash: parent_hash, receipts_root: receipts_root, - sha3_uncles: sha3_uncles, + # In case of CELO, `sha3_uncles` may not be returned by eth_getBlockByHash + sha3_uncles: Map.get(elixir, "sha3Uncles", @sha3_uncles_empty_list), size: size, state_root: state_root, timestamp: timestamp, transactions_root: transactions_root, - uncles: uncles, + # In case of CELO, `uncles` may not be returned by eth_getBlockByHash + uncles: Map.get(elixir, "uncles", []), base_fee_per_gas: base_fee_per_gas } end @@ -410,13 +415,11 @@ defmodule EthereumJSONRPC.Block do "number" => number, "parentHash" => parent_hash, "receiptsRoot" => receipts_root, - "sha3Uncles" => sha3_uncles, "size" => size, "stateRoot" => state_root, "timestamp" => timestamp, "totalDifficulty" => total_difficulty, - "transactionsRoot" => transactions_root, - "uncles" => uncles + "transactionsRoot" => transactions_root } = elixir ) do %{ @@ -432,13 +435,15 @@ defmodule EthereumJSONRPC.Block do number: number, parent_hash: parent_hash, receipts_root: receipts_root, - sha3_uncles: sha3_uncles, + # In case of CELO, `sha3_uncles` may not be returned by eth_getBlockByHash + sha3_uncles: Map.get(elixir, "sha3Uncles", @sha3_uncles_empty_list), size: size, state_root: state_root, timestamp: timestamp, total_difficulty: total_difficulty, transactions_root: transactions_root, - uncles: uncles + # In case of CELO, `uncles` may not be returned by eth_getBlockByHash + uncles: Map.get(elixir, "uncles", []) } end @@ -455,12 +460,10 @@ defmodule EthereumJSONRPC.Block do "number" => number, "parentHash" => parent_hash, "receiptsRoot" => receipts_root, - "sha3Uncles" => sha3_uncles, "size" => size, "stateRoot" => state_root, "timestamp" => timestamp, - "transactionsRoot" => transactions_root, - "uncles" => uncles + "transactionsRoot" => transactions_root } = elixir ) do %{ @@ -476,12 +479,14 @@ defmodule EthereumJSONRPC.Block do number: number, parent_hash: parent_hash, receipts_root: receipts_root, - sha3_uncles: sha3_uncles, + # In case of CELO, `sha3_uncles` may not be returned by eth_getBlockByHash + sha3_uncles: Map.get(elixir, "sha3Uncles", @sha3_uncles_empty_list), size: size, state_root: state_root, timestamp: timestamp, transactions_root: transactions_root, - uncles: uncles + # In case of CELO, `uncles` may not be returned by eth_getBlockByHash + uncles: Map.get(elixir, "uncles", []) } end @@ -659,6 +664,8 @@ defmodule EthereumJSONRPC.Block do |> Enum.map(fn {uncle_hash, index} -> %{"hash" => uncle_hash, "nephewHash" => nephew_hash, "index" => index} end) end + def elixir_to_uncles(_), do: [] + @doc """ Get `t:EthereumJSONRPC.Withdrawals.elixir/0` from `t:elixir/0`. diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth/call.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth/call.ex index d019e0210ea6..2d8e2723b5ab 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth/call.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth/call.ex @@ -3,8 +3,7 @@ defmodule EthereumJSONRPC.Geth.Call do A single call returned from [debug_traceTransaction](https://github.com/ethereum/go-ethereum/wiki/Management-APIs#debug_tracetransaction) using a custom tracer (`priv/js/ethereum_jsonrpc/geth/debug_traceTransaction/tracer.js`). """ - import EthereumJSONRPC, only: [quantity_to_integer: 1] - import EthereumJSONRPC.Transaction, only: [put_if_present: 3] + import EthereumJSONRPC, only: [quantity_to_integer: 1, put_if_present: 3] @doc """ A call can call another another contract: diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/logs.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/logs.ex index e62765d859c6..de6b81dbf367 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/logs.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/logs.ex @@ -4,7 +4,13 @@ defmodule EthereumJSONRPC.Logs do [`eth_getTransactionReceipt`](https://github.com/ethereum/wiki/wiki/JSON-RPC/e8e0771b9f3677693649d945956bc60e886ceb2b#eth_gettransactionreceipt). """ - alias EthereumJSONRPC.Log + import EthereumJSONRPC, + only: [ + integer_to_quantity: 1, + put_if_present: 3 + ] + + alias EthereumJSONRPC.{Log, Transport} @type elixir :: [Log.elixir()] @type t :: [Log.t()] @@ -18,4 +24,102 @@ defmodule EthereumJSONRPC.Logs do def to_elixir(logs) when is_list(logs) do Enum.map(logs, &Log.to_elixir/1) end + + @spec request( + id :: integer(), + params :: + %{ + :from_block => EthereumJSONRPC.tag() | EthereumJSONRPC.block_number(), + :to_block => EthereumJSONRPC.tag() | EthereumJSONRPC.block_number(), + optional(:topics) => list(EthereumJSONRPC.hash()), + optional(:address) => EthereumJSONRPC.address() + } + | %{ + :block_hash => EthereumJSONRPC.hash(), + optional(:topics) => list(EthereumJSONRPC.hash()), + optional(:address) => EthereumJSONRPC.address() + } + ) :: Transport.request() + def request(id, params) when is_integer(id) do + EthereumJSONRPC.request(%{ + id: id, + method: "eth_getLogs", + params: [to_request_params(params)] + }) + end + + defp to_request_params( + %{ + from_block: from_block, + to_block: to_block + } = params + ) do + %{ + fromBlock: block_number_to_quantity_or_tag(from_block), + toBlock: block_number_to_quantity_or_tag(to_block) + } + |> maybe_add_topics_and_address(params) + end + + defp to_request_params(%{block_hash: block_hash} = params) + when is_binary(block_hash) do + %{ + blockHash: block_hash + } + |> maybe_add_topics_and_address(params) + end + + defp maybe_add_topics_and_address(request_params, params) do + put_if_present(request_params, params, [ + {:topics, :topics}, + {:address, :address} + ]) + end + + defp block_number_to_quantity_or_tag(block_number) when is_integer(block_number) do + integer_to_quantity(block_number) + end + + defp block_number_to_quantity_or_tag(tag) when tag in ~w(earliest latest pending safe) do + tag + end + + def from_responses(responses) when is_list(responses) do + responses + |> reduce_responses() + |> case do + {:ok, logs} -> + { + :ok, + logs + |> to_elixir() + |> elixir_to_params() + } + + {:error, reasons} -> + {:error, reasons} + end + end + + defp reduce_responses(responses) do + responses + |> Enum.reduce( + {:ok, []}, + fn + %{result: result}, {:ok, logs} + when is_list(result) -> + {:ok, result ++ logs} + + %{result: _}, {:error, _} = error -> + error + + %{error: reason}, {:ok, _} -> + {:error, [reason]} + + %{error: reason}, {:error, reasons} + when is_list(reasons) -> + {:error, [reason | reasons]} + end + ) + end end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/nethermind/trace.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/nethermind/trace.ex index 6e8cf42af866..17b781224ccd 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/nethermind/trace.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/nethermind/trace.ex @@ -4,7 +4,7 @@ defmodule EthereumJSONRPC.Nethermind.Trace do [`trace_replayTransaction`](https://openethereum.github.io/JSONRPC-trace-module#trace_replaytransaction). """ - import EthereumJSONRPC.Transaction, only: [put_if_present: 3] + import EthereumJSONRPC, only: [put_if_present: 3] alias EthereumJSONRPC.Nethermind.Trace.{Action, Result} @doc """ diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex index 8fb447894e69..f835c4fb6fbf 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex @@ -7,7 +7,13 @@ defmodule EthereumJSONRPC.Transaction do [`eth_getTransactionByBlockHashAndIndex`](https://github.com/ethereum/wiki/wiki/JSON-RPC/e8e0771b9f3677693649d945956bc60e886ceb2b#eth_gettransactionbyblockhashandindex), and [`eth_getTransactionByBlockNumberAndIndex`](https://github.com/ethereum/wiki/wiki/JSON-RPC/e8e0771b9f3677693649d945956bc60e886ceb2b#eth_gettransactionbyblocknumberandindex) """ - import EthereumJSONRPC, only: [quantity_to_integer: 1, integer_to_quantity: 1, request: 1] + import EthereumJSONRPC, + only: [ + quantity_to_integer: 1, + integer_to_quantity: 1, + request: 1, + put_if_present: 3 + ] alias EthereumJSONRPC @@ -48,6 +54,15 @@ defmodule EthereumJSONRPC.Transaction do ] ) + :celo -> + @chain_type_fields quote( + do: [ + gas_token_contract_address_hash: EthereumJSONRPC.address(), + gas_fee_recipient_address_hash: EthereumJSONRPC.address(), + gateway_fee: non_neg_integer() + ] + ) + :arbitrum -> @chain_type_fields quote( do: [ @@ -103,6 +118,11 @@ defmodule EthereumJSONRPC.Transaction do * `"executionNode"` - `t:EthereumJSONRPC.address/0` of execution node (used by Suave). * `"requestRecord"` - map of wrapped transaction data (used by Suave). """ + :celo -> """ + * `"feeCurrency"` - `t:EthereumJSONRPC.address/0` of the currency used to pay for gas. + * `"gatewayFee"` - `t:EthereumJSONRPC.quantity/0` of the gateway fee. + * `"gatewayFeeRecipient"` - `t:EthereumJSONRPC.address/0` of the gateway fee recipient. + """ _ -> "" end} """ @@ -134,7 +154,7 @@ defmodule EthereumJSONRPC.Transaction do } @doc """ - Geth `elixir` can be converted to `params`. Geth does not supply `"publicKey"` or `"standardV"`, unlike Nethermind. + Geth `elixir` can be converted to `params`. Geth does not supply `"publicKey"` or `"standardV"`, unlike Nethermind. iex> EthereumJSONRPC.Transaction.elixir_to_params( ...> %{ @@ -516,6 +536,13 @@ defmodule EthereumJSONRPC.Transaction do }) end + :celo -> + put_if_present(params, elixir, [ + {"feeCurrency", :gas_token_contract_address_hash}, + {"gatewayFee", :gateway_fee}, + {"gatewayFeeRecipient", :gas_fee_recipient_address_hash} + ]) + :arbitrum -> put_if_present(params, elixir, [ {"requestId", :request_id} @@ -673,19 +700,17 @@ defmodule EthereumJSONRPC.Transaction do end end - defp entry_to_elixir(_) do - {:ignore, :ignore} - end + # Celo-specific fields + if Application.compile_env(:explorer, :chain_type) == :celo do + defp entry_to_elixir({key, value}) + when key in ~w(feeCurrency gatewayFeeRecipient), + do: {key, value} - def put_if_present(result, transaction, keys) do - Enum.reduce(keys, result, fn {from_key, to_key}, acc -> - value = transaction[from_key] + defp entry_to_elixir({"gatewayFee" = key, quantity_or_nil}), + do: {key, quantity_or_nil && quantity_to_integer(quantity_or_nil)} + end - if value do - Map.put(acc, to_key, value) - else - acc - end - end) + defp entry_to_elixir(_) do + {:ignore, :ignore} end end diff --git a/apps/explorer/config/dev.exs b/apps/explorer/config/dev.exs index 4fbc76dbe887..47e5689bffab 100644 --- a/apps/explorer/config/dev.exs +++ b/apps/explorer/config/dev.exs @@ -23,6 +23,8 @@ config :explorer, Explorer.Repo.PolygonZkevm, timeout: :timer.seconds(80) # Configure ZkSync database config :explorer, Explorer.Repo.ZkSync, timeout: :timer.seconds(80) +config :explorer, Explorer.Repo.Celo, timeout: :timer.seconds(80) + config :explorer, Explorer.Repo.RSK, timeout: :timer.seconds(80) config :explorer, Explorer.Repo.Shibarium, timeout: :timer.seconds(80) diff --git a/apps/explorer/config/prod.exs b/apps/explorer/config/prod.exs index a7166e19aace..7af6ee216926 100644 --- a/apps/explorer/config/prod.exs +++ b/apps/explorer/config/prod.exs @@ -39,6 +39,11 @@ config :explorer, Explorer.Repo.ZkSync, timeout: :timer.seconds(60), ssl_opts: [verify: :verify_none] +config :explorer, Explorer.Repo.Celo, + prepare: :unnamed, + timeout: :timer.seconds(60), + ssl_opts: [verify: :verify_none] + config :explorer, Explorer.Repo.RSK, prepare: :unnamed, timeout: :timer.seconds(60), diff --git a/apps/explorer/config/test.exs b/apps/explorer/config/test.exs index b006aef935c4..6687e8df4df4 100644 --- a/apps/explorer/config/test.exs +++ b/apps/explorer/config/test.exs @@ -59,6 +59,7 @@ for repo <- [ Explorer.Repo.PolygonEdge, Explorer.Repo.PolygonZkevm, Explorer.Repo.ZkSync, + Explorer.Repo.Celo, Explorer.Repo.RSK, Explorer.Repo.Shibarium, Explorer.Repo.Suave, diff --git a/apps/explorer/lib/explorer/account/notifier/forbidden_address.ex b/apps/explorer/lib/explorer/account/notifier/forbidden_address.ex index f977b3e4dcb2..16ab861c4627 100644 --- a/apps/explorer/lib/explorer/account/notifier/forbidden_address.ex +++ b/apps/explorer/lib/explorer/account/notifier/forbidden_address.ex @@ -3,13 +3,17 @@ defmodule Explorer.Account.Notifier.ForbiddenAddress do Check if address is forbidden to notify """ - import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + import Explorer.Chain.SmartContract, + only: [ + burn_address_hash_string: 0, + dead_address_hash_string: 0 + ] alias Explorer.Chain.Address @blacklist [ burn_address_hash_string(), - "0x000000000000000000000000000000000000dEaD" + dead_address_hash_string() ] alias Explorer.AccessHelper diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index a92c10a8c131..59951fae2ce7 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -157,6 +157,7 @@ defmodule Explorer.Application do Explorer.Repo.PolygonEdge, Explorer.Repo.PolygonZkevm, Explorer.Repo.ZkSync, + Explorer.Repo.Celo, Explorer.Repo.RSK, Explorer.Repo.Shibarium, Explorer.Repo.Suave, diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 7612ca8adc97..6ce7ec66df06 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -93,7 +93,8 @@ defmodule Explorer.Chain do alias Dataloader.Ecto, as: DataloaderEcto - @default_paging_options %PagingOptions{page_size: 50} + @default_page_size 50 + @default_paging_options %PagingOptions{page_size: @default_page_size} @token_transfers_per_transaction_preview 10 @token_transfers_necessity_by_association %{ @@ -121,7 +122,6 @@ defmodule Explorer.Chain do @revert_msg_prefix_6_empty "execution reverted" @limit_showing_transactions 10_000 - @default_page_size 50 @typedoc """ The name of an association on the `t:Ecto.Schema.t/0` diff --git a/apps/explorer/lib/explorer/chain/address/counters.ex b/apps/explorer/lib/explorer/chain/address/counters.ex index d6d1da965cff..d58a55ed73bd 100644 --- a/apps/explorer/lib/explorer/chain/address/counters.ex +++ b/apps/explorer/lib/explorer/chain/address/counters.ex @@ -31,6 +31,7 @@ defmodule Explorer.Chain.Address.Counters do alias Explorer.Chain.Cache.AddressesTabsCounters alias Explorer.Chain.Cache.Helper, as: CacheHelper + alias Explorer.Chain.Celo.ElectionReward, as: CeloElectionReward require Logger @@ -327,8 +328,7 @@ defmodule Explorer.Chain.Address.Counters do AddressTransactionsGasUsageCounter.fetch(address) end - @spec address_limited_counters(Hash.t(), Keyword.t()) :: - {counter(), counter(), counter(), counter(), counter(), counter(), counter()} + @spec address_limited_counters(Hash.t(), Keyword.t()) :: %{atom() => counter} def address_limited_counters(address_hash, options) do cached_counters = Enum.reduce(@types, %{}, fn type, acc -> @@ -464,6 +464,19 @@ defmodule Explorer.Chain.Address.Counters do options ) + celo_election_rewards_count_task = + if Application.get_env(:explorer, :chain_type) == :celo do + configure_task( + :celo_election_rewards, + cached_counters, + CeloElectionReward.address_hash_to_rewards_query(address_hash), + address_hash, + options + ) + else + nil + end + map = [ validations_count_task, @@ -474,7 +487,8 @@ defmodule Explorer.Chain.Address.Counters do token_balances_count_task, logs_count_task, withdrawals_count_task, - internal_txs_count_task + internal_txs_count_task, + celo_election_rewards_count_task ] |> Enum.reject(&is_nil/1) |> Task.yield_many(:timer.seconds(1)) @@ -512,8 +526,7 @@ defmodule Explorer.Chain.Address.Counters do end) |> process_txs_counter() - {map[:validations], map[:txs], map[:token_transfers], map[:token_balances], map[:logs], map[:withdrawals], - map[:internal_txs]} + map end defp run_or_ignore({ok, _counter}, _type, _address_hash, _fun) when ok in [:up_to_date, :limit_value], do: nil diff --git a/apps/explorer/lib/explorer/chain/block.ex b/apps/explorer/lib/explorer/chain/block.ex index 785b5634daa2..60f60251cdb6 100644 --- a/apps/explorer/lib/explorer/chain/block.ex +++ b/apps/explorer/lib/explorer/chain/block.ex @@ -5,10 +5,19 @@ defmodule Explorer.Chain.Block.Schema do Changes in the schema should be reflected in the bulk import module: - Explorer.Chain.Import.Runner.Blocks """ + alias Explorer.Chain.{ + Address, + Block, + Hash, + PendingBlockOperation, + Transaction, + Wei, + Withdrawal + } - alias Explorer.Chain.{Address, Block, Hash, PendingBlockOperation, Transaction, Wei, Withdrawal} alias Explorer.Chain.Arbitrum.BatchBlock, as: ArbitrumBatchBlock alias Explorer.Chain.Block.{Reward, SecondDegreeRelation} + alias Explorer.Chain.Celo.EpochReward, as: CeloEpochReward alias Explorer.Chain.Optimism.TxnBatch, as: OptimismTxnBatch alias Explorer.Chain.ZkSync.BatchBlock, as: ZkSyncBatchBlock @@ -59,6 +68,19 @@ defmodule Explorer.Chain.Block.Schema do 2 ) + :celo -> + elem( + quote do + has_one(:celo_epoch_reward, CeloEpochReward, foreign_key: :block_hash, references: :hash) + + has_many(:celo_epoch_election_rewards, CeloEpochReward, + foreign_key: :block_hash, + references: :hash + ) + end, + 2 + ) + :arbitrum -> elem( quote do diff --git a/apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex b/apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex new file mode 100644 index 000000000000..42e86fc2035d --- /dev/null +++ b/apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex @@ -0,0 +1,235 @@ +defmodule Explorer.Chain.Cache.CeloCoreContracts do + @moduledoc """ + Cache for Celo core contract addresses. + + This module operates with a `CELO_CORE_CONTRACTS` environment variable, which + contains the JSON map of core contract addresses on the Celo network. The + module provides functions to fetch the addresses of core contracts at a given + block. Additionally, it provides a function to obtain the state of a specific + contract by fetching the latest event for the contract at a given block. + + For details on the structure of the `CELO_CORE_CONTRACTS` environment + variable, see `app/explorer/lib/fetch_celo_core_contracts.ex`. + """ + @dialyzer :no_match + + require Logger + + alias EthereumJSONRPC + alias Explorer.Chain.Block + + @type contract_name :: String.t() + + @atom_to_contract_name %{ + accounts: "Accounts", + celo_token: "GoldToken", + election: "Election", + epoch_rewards: "EpochRewards", + locked_gold: "LockedGold", + reserve: "Reserve", + usd_token: "StableToken", + validators: "Validators", + governance: "Governance", + fee_handler: "FeeHandler", + gas_price_minimum: "GasPriceMinimum" + } + + @atom_to_contract_event_names %{ + fee_handler: %{ + fee_beneficiary_set: "FeeBeneficiarySet", + burn_fraction_set: "BurnFractionSet" + }, + epoch_rewards: %{ + carbon_offsetting_fund_set: "CarbonOffsettingFundSet" + } + } + + @doc """ + A map where keys are atoms representing contract types, and values are strings + representing the names of the contracts. + """ + @spec atom_to_contract_name() :: %{atom() => contract_name} + def atom_to_contract_name, do: @atom_to_contract_name + + @doc """ + A nested map where keys are atoms representing contract types, and values are + maps of event atoms to event names. + """ + @spec atom_to_contract_event_names() :: %{atom() => %{atom() => contract_name}} + def atom_to_contract_event_names, do: @atom_to_contract_event_names + + defp core_contracts, do: Application.get_env(:explorer, __MODULE__)[:contracts] + + @doc """ + Gets the specified event for a core contract at a given block number. + + ## Parameters + - `contract_atom`: The atom representing the contract. + - `event_atom`: The atom representing the event. + - `block_number`: The block number at which to fetch the event. + + ## Returns (one of the following) + - `{:ok, map() | nil}`: The event data if found, or `nil` if no event is found. + - `{:error, reason}`: An error tuple with the reason for the failure. + """ + @spec get_event(atom(), atom(), Block.block_number()) :: + {:ok, map() | nil} + | {:error, + :contract_atom_not_found + | :event_atom_not_found + | :contract_name_not_found + | :event_name_not_found + | :contract_address_not_found + | :event_does_not_exist} + def get_event(contract_atom, event_atom, block_number) do + with {:ok, address} <- get_address(contract_atom, block_number), + {:contract_atom, {:ok, contract_name}} <- + {:contract_atom, Map.fetch(@atom_to_contract_name, contract_atom)}, + {:event_atom, {:ok, event_name}} <- + { + :event_atom, + @atom_to_contract_event_names + |> Map.get(contract_atom, %{}) + |> Map.fetch(event_atom) + }, + {:events, {:ok, contract_name_to_addresses}} <- + {:events, Map.fetch(core_contracts(), "events")}, + {:contract_name, {:ok, contract_addresses}} <- + {:contract_name, Map.fetch(contract_name_to_addresses, contract_name)}, + {:contract_address, {:ok, contract_events}} <- + {:contract_address, Map.fetch(contract_addresses, address)}, + {:event_name, {:ok, event_updates}} <- + {:event_name, Map.fetch(contract_events, event_name)}, + current_event when not is_nil(current_event) <- + event_updates + |> Enum.take_while(&(&1["updated_at_block_number"] <= block_number)) + |> List.last() do + {:ok, current_event} + else + nil -> + {:ok, nil} + + {:contract_atom, :error} -> + Logger.error("Unknown contract atom: #{inspect(contract_atom)}") + {:error, :contract_atom_not_found} + + {:event_atom, :error} -> + Logger.error("Unknown event atom: #{inspect(event_atom)}") + {:error, :event_atom_not_found} + + {:events, :error} -> + raise "Missing `events` key in CELO core contracts JSON" + + {:contract_name, :error} -> + Logger.error(fn -> + [ + "Unknown name for contract atom: #{contract_atom}, ", + "ensure `CELO_CORE_CONTRACTS` env var is set ", + "and the provided JSON contains required key" + ] + end) + + {:error, :contract_name_not_found} + + {:event_name, :error} -> + Logger.error(fn -> + [ + "Unknown name for event atom: #{event_atom}, ", + "ensure `CELO_CORE_CONTRACTS` env var is set ", + "and the provided JSON contains required key" + ] + end) + + {:error, :event_name_not_found} + + nil -> + {:error, :event_does_not_exist} + + {:contract_address, :error} -> + Logger.error(fn -> + [ + "Unknown address for contract atom: #{contract_atom}, ", + "ensure `CELO_CORE_CONTRACTS` env var is set ", + "and the provided JSON contains required key" + ] + end) + + {:error, :contract_address_not_found} + + error -> + error + end + end + + defp get_address_updates(contract_atom) do + with {:atom, {:ok, contract_name}} <- + {:atom, Map.fetch(@atom_to_contract_name, contract_atom)}, + {:addresses, {:ok, contract_name_to_addresses}} <- + {:addresses, Map.fetch(core_contracts(), "addresses")}, + {:name, {:ok, address_updates}} <- + {:name, Map.fetch(contract_name_to_addresses, contract_name)} do + {:ok, address_updates} + else + {:atom, :error} -> + Logger.error("Unknown contract atom: #{inspect(contract_atom)}") + {:error, :contract_atom_not_found} + + {:addresses, :error} -> + raise "Missing `addresses` key in CELO core contracts JSON" + + {:name, :error} -> + Logger.error(fn -> + [ + "Unknown name for contract atom: #{contract_atom}, ", + "ensure `CELO_CORE_CONTRACTS` env var is set ", + "and the provided JSON contains required key" + ] + end) + + {:error, :contract_name_not_found} + end + end + + @doc """ + Gets the address of a core contract at a given block number. + + ## Parameters + - `contract_atom`: The atom representing the contract. + - `block_number`: The block number at which to fetch the address. + + ## Returns (one of the following) + - `{:ok, EthereumJSONRPC.address() | nil}`: The address of the contract, or `nil` if not found. + - `{:error, reason}`: An error tuple with the reason for the failure. + """ + @spec get_address(atom(), Block.block_number()) :: + {:ok, EthereumJSONRPC.address() | nil} + | {:error, + :contract_atom_not_found + | :contract_name_not_found + | :address_does_not_exist} + def get_address(contract_atom, block_number) do + with {:ok, address_updates} <- get_address_updates(contract_atom), + %{"address" => current_address} <- + address_updates + |> Enum.take_while(&(&1["updated_at_block_number"] <= block_number)) + |> List.last() do + {:ok, current_address} + else + nil -> + {:error, :address_does_not_exist} + + error -> + error + end + end + + def get_first_update_block_number(contract_atom) do + with {:ok, address_updates} <- get_address_updates(contract_atom), + %{"updated_at_block_number" => updated_at_block_number} <- + address_updates + |> Enum.sort_by(& &1["updated_at_block_number"]) + |> List.first() do + {:ok, updated_at_block_number} + end + end +end diff --git a/apps/explorer/lib/explorer/chain/celo/election_reward.ex b/apps/explorer/lib/explorer/chain/celo/election_reward.ex new file mode 100644 index 000000000000..f31898bf9d1e --- /dev/null +++ b/apps/explorer/lib/explorer/chain/celo/election_reward.ex @@ -0,0 +1,455 @@ +defmodule Explorer.Chain.Celo.ElectionReward do + @moduledoc """ + Represents the rewards distributed in an epoch election. Each reward has a + type, and each type of reward is paid in a specific token. The rewards are + paid to an account address and are also associated with another account + address. + + ## Reward Types and Addresses + + Here is the breakdown of what each address means for each type of reward: + + - `voter`: + - Account address: The voter address. + - Associated account address: The group address. + - `validator`: + - Account address: The validator address. + - Associated account address: The validator group address. + - `group`: + - Account address: The validator group address. + - Associated account address: The validator address that the reward was paid + on behalf of. + - `delegated_payment`: + - Account address: The beneficiary receiving the part of the reward on + behalf of the validator. + - Associated account address: The validator that set the delegation of a + part of their reward to some external address. + """ + + use Explorer.Schema + + import Explorer.PagingOptions, only: [default_paging_options: 0] + import Ecto.Query, only: [from: 2, where: 3] + import Explorer.Helper, only: [safe_parse_non_negative_integer: 1] + + alias Explorer.{Chain, PagingOptions} + alias Explorer.Chain.{Address, Block, Hash, Wei} + + @type type :: :voter | :validator | :group | :delegated_payment + @types_enum ~w(voter validator group delegated_payment)a + + @reward_type_string_to_atom %{ + "voter" => :voter, + "validator" => :validator, + "group" => :group, + "delegated-payment" => :delegated_payment + } + + @reward_type_atom_to_token_atom %{ + :voter => :celo_token, + :validator => :usd_token, + :group => :usd_token, + :delegated_payment => :usd_token + } + + @required_attrs ~w(amount type block_hash account_address_hash associated_account_address_hash)a + + @primary_key false + typed_schema "celo_election_rewards" do + field(:amount, Wei, null: false) + + field( + :type, + Ecto.Enum, + values: @types_enum, + null: false, + primary_key: true + ) + + belongs_to( + :block, + Block, + primary_key: true, + foreign_key: :block_hash, + references: :hash, + type: Hash.Full, + null: false + ) + + belongs_to( + :account_address, + Address, + primary_key: true, + foreign_key: :account_address_hash, + references: :hash, + type: Hash.Address, + null: false + ) + + belongs_to( + :associated_account_address, + Address, + primary_key: true, + foreign_key: :associated_account_address_hash, + references: :hash, + type: Hash.Address, + null: false + ) + + timestamps() + end + + @spec changeset( + Explorer.Chain.Celo.ElectionReward.t(), + map() + ) :: Ecto.Changeset.t() + def changeset(%__MODULE__{} = rewards, attrs) do + rewards + |> cast(attrs, @required_attrs) + |> validate_required(@required_attrs) + |> foreign_key_constraint(:block_hash) + |> foreign_key_constraint(:account_address_hash) + |> foreign_key_constraint(:associated_account_address_hash) + + # todo: do I need to set this unique constraint here? or it is redundant? + # |> unique_constraint( + # [:block_hash, :type, :account_address_hash, :associated_account_address_hash], + # name: :celo_election_rewards_pkey + # ) + end + + @doc """ + Returns the list of election reward types. + """ + @spec types() :: [type] + def types, do: @types_enum + + @doc """ + Converts a reward type string to its corresponding atom. + + ## Parameters + - `type_string` (`String.t()`): The string representation of the reward type. + + ## Returns + - `{:ok, type}` if the string is valid, `:error` otherwise. + + ## Examples + + iex> ElectionReward.type_from_string("voter") + {:ok, :voter} + + iex> ElectionReward.type_from_string("invalid") + :error + """ + @spec type_from_string(String.t()) :: {:ok, type} | :error + def type_from_string(type_string) do + Map.fetch(@reward_type_string_to_atom, type_string) + end + + @doc """ + Returns a map of reward type atoms to their corresponding token atoms. + + ## Returns + - A map where the keys are reward type atoms and the values are token atoms. + + ## Examples + + iex> ElectionReward.reward_type_atom_to_token_atom() + %{voter: :celo_token, validator: :usd_token, group: :usd_token, delegated_payment: :usd_token} + """ + @spec reward_type_atom_to_token_atom() :: %{type => atom()} + def reward_type_atom_to_token_atom, do: @reward_type_atom_to_token_atom + + @doc """ + Builds a query to aggregate rewards by type for a given block hash. + + ## Parameters + - `block_hash` (`Hash.Full.t()`): The block hash to filter rewards. + + ## Returns + - An Ecto query. + """ + @spec block_hash_to_aggregated_rewards_by_type_query(Hash.Full.t()) :: Ecto.Query.t() + def block_hash_to_aggregated_rewards_by_type_query(block_hash) do + from( + r in __MODULE__, + where: r.block_hash == ^block_hash, + select: {r.type, sum(r.amount), count(r)}, + group_by: r.type + ) + end + + @doc """ + Builds a query to get rewards by type for a given block hash. + + ## Parameters + - `block_hash` (`Hash.Full.t()`): The block hash to filter rewards. + - `reward_type` (`type`): The type of reward to filter. + + ## Returns + - An Ecto query. + """ + @spec block_hash_to_rewards_by_type_query(Hash.Full.t(), type) :: Ecto.Query.t() + def block_hash_to_rewards_by_type_query(block_hash, reward_type) do + from( + r in __MODULE__, + where: r.block_hash == ^block_hash and r.type == ^reward_type, + select: r, + order_by: [ + desc: :amount, + asc: :account_address_hash, + asc: :associated_account_address_hash + ] + ) + end + + @doc """ + Builds a query to get rewards by account address hash. + """ + @spec address_hash_to_rewards_query(Hash.Address.t()) :: Ecto.Query.t() + def address_hash_to_rewards_query(address_hash) do + from( + r in __MODULE__, + where: r.account_address_hash == ^address_hash, + select: r + ) + end + + @doc """ + Builds a query to get ordered rewards by account address hash. + + ## Parameters + - `address_hash` (`Hash.Address.t()`): The account address hash to filter + rewards. + + ## Returns + - An Ecto query. + """ + @spec address_hash_to_ordered_rewards_query(Hash.Address.t()) :: Ecto.Query.t() + def address_hash_to_ordered_rewards_query(address_hash) do + from( + r in __MODULE__, + join: b in assoc(r, :block), + as: :block, + preload: [block: b], + where: r.account_address_hash == ^address_hash, + select: r, + order_by: [ + desc: b.number, + desc: r.amount, + asc: r.associated_account_address_hash, + asc: r.type + ] + ) + end + + @doc """ + Makes Explorer.PagingOptions map for election rewards. + """ + @spec address_paging_options(map()) :: [Chain.paging_options()] + def block_paging_options(params) do + with %{ + "amount" => amount_string, + "account_address_hash" => account_address_hash_string, + "associated_account_address_hash" => associated_account_address_hash_string + } + when is_binary(amount_string) and + is_binary(account_address_hash_string) and + is_binary(associated_account_address_hash_string) <- params, + {amount, ""} <- Decimal.parse(amount_string), + {:ok, account_address_hash} <- Hash.Address.cast(account_address_hash_string), + {:ok, associated_account_address_hash} <- + Hash.Address.cast(associated_account_address_hash_string) do + [ + paging_options: %{ + default_paging_options() + | key: {amount, account_address_hash, associated_account_address_hash} + } + ] + else + _ -> + [paging_options: default_paging_options()] + end + end + + @doc """ + Makes Explorer.PagingOptions map for election rewards. + """ + @spec address_paging_options(map()) :: [Chain.paging_options()] + def address_paging_options(params) do + with %{ + "block_number" => block_number_string, + "amount" => amount_string, + "associated_account_address_hash" => associated_account_address_hash_string, + "type" => type_string + } + when is_binary(block_number_string) and + is_binary(amount_string) and + is_binary(associated_account_address_hash_string) and + is_binary(type_string) <- params, + {:ok, block_number} <- safe_parse_non_negative_integer(block_number_string), + {amount, ""} <- Decimal.parse(amount_string), + {:ok, associated_account_address_hash} <- + Hash.Address.cast(associated_account_address_hash_string), + {:ok, type} <- type_from_string(type_string) do + [ + paging_options: %{ + default_paging_options() + | key: {block_number, amount, associated_account_address_hash, type} + } + ] + else + _ -> + [paging_options: default_paging_options()] + end + end + + @doc """ + Paginates the given query based on the provided `PagingOptions`. + + ## Parameters + - `query` (`Ecto.Query.t()`): The query to paginate. + - `paging_options` (`PagingOptions.t()`): The pagination options. + + ## Returns + - An Ecto query with pagination applied. + """ + def paginate(query, %PagingOptions{key: nil}), do: query + + def paginate(query, %PagingOptions{key: {0 = _amount, account_address_hash, associated_account_address_hash}}) do + where( + query, + [reward], + reward.amount == 0 and + (reward.account_address_hash > ^account_address_hash or + (reward.account_address_hash == ^account_address_hash and + reward.associated_account_address_hash > ^associated_account_address_hash)) + ) + end + + def paginate(query, %PagingOptions{key: {amount, account_address_hash, associated_account_address_hash}}) do + where( + query, + [reward], + reward.amount < ^amount or + (reward.amount == ^amount and + reward.account_address_hash > ^account_address_hash) or + (reward.amount == ^amount and + reward.account_address_hash == ^account_address_hash and + reward.associated_account_address_hash > ^associated_account_address_hash) + ) + end + + def paginate(query, %PagingOptions{key: {0 = _block_number, 0 = _amount, associated_account_address_hash, type}}) do + where( + query, + [reward, block], + block.number == 0 and reward.amount == 0 and + (reward.associated_account_address_hash > ^associated_account_address_hash or + (reward.associated_account_address_hash == ^associated_account_address_hash and + reward.type > ^type)) + ) + end + + def paginate(query, %PagingOptions{key: {0 = _block_number, amount, associated_account_address_hash, type}}) do + where( + query, + [reward, block], + block.number == 0 and + (reward.amount < ^amount or + (reward.amount == ^amount and + reward.associated_account_address_hash > ^associated_account_address_hash) or + (reward.amount == ^amount and + reward.associated_account_address_hash == ^associated_account_address_hash and + reward.type > ^type)) + ) + end + + # credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity + def paginate(query, %PagingOptions{key: {block_number, 0 = _amount, associated_account_address_hash, type}}) do + where( + query, + [reward, block], + block.number < ^block_number or + (block.number == ^block_number and + reward.amount == 0 and + reward.associated_account_address_hash > ^associated_account_address_hash) or + (block.number == ^block_number and + reward.amount == 0 and + reward.associated_account_address_hash == ^associated_account_address_hash and + reward.type > ^type) + ) + end + + # credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity + def paginate(query, %PagingOptions{key: {block_number, amount, associated_account_address_hash, type}}) do + where( + query, + [reward, block], + block.number < ^block_number or + (block.number == ^block_number and + reward.amount < ^amount) or + (block.number == ^block_number and + reward.amount == ^amount and + reward.associated_account_address_hash > ^associated_account_address_hash) or + (block.number == ^block_number and + reward.amount == ^amount and + reward.associated_account_address_hash == ^associated_account_address_hash and + reward.type > ^type) + ) + end + + @doc """ + Converts an `ElectionReward` struct to paging parameters on the block view. + + ## Parameters + - `reward` (`%__MODULE__{}`): The election reward struct. + + ## Returns + - A map representing the block paging parameters. + + ## Examples + + iex> ElectionReward.to_block_paging_params(%ElectionReward{amount: 1000, account_address_hash: "0x123", associated_account_address_hash: "0x456"}) + %{"amount" => 1000, "account_address_hash" => "0x123", "associated_account_address_hash" => "0x456"} + """ + def to_block_paging_params(%__MODULE__{ + amount: amount, + account_address_hash: account_address_hash, + associated_account_address_hash: associated_account_address_hash + }) do + %{ + "amount" => amount, + "account_address_hash" => account_address_hash, + "associated_account_address_hash" => associated_account_address_hash + } + end + + @doc """ + Converts an `ElectionReward` struct to paging parameters on the address view. + + ## Parameters + - `reward` (`%__MODULE__{}`): The election reward struct. + + ## Returns + - A map representing the address paging parameters. + + ## Examples + + iex> ElectionReward.to_address_paging_params(%ElectionReward{block_number: 1, amount: 1000, associated_account_address_hash: "0x456", type: :voter}) + %{"block_number" => 1, "amount" => 1000, "associated_account_address_hash" => "0x456", "type" => :voter} + """ + def to_address_paging_params(%__MODULE__{ + block: %Block{number: block_number}, + amount: amount, + associated_account_address_hash: associated_account_address_hash, + type: type + }) do + %{ + "block_number" => block_number, + "amount" => amount, + "associated_account_address_hash" => associated_account_address_hash, + "type" => type + } + end +end diff --git a/apps/explorer/lib/explorer/chain/celo/epoch_reward.ex b/apps/explorer/lib/explorer/chain/celo/epoch_reward.ex new file mode 100644 index 000000000000..8356456f25a9 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/celo/epoch_reward.ex @@ -0,0 +1,117 @@ +defmodule Explorer.Chain.Celo.EpochReward do + @moduledoc """ + Represents the distributions in the Celo epoch. Each log index points to a + token transfer event in the `TokenTransfer` relation. These include the + reserve bolster, community, and carbon offsetting transfers. + """ + use Explorer.Schema + + import Ecto.Query, only: [from: 2] + import Explorer.Chain, only: [select_repo: 1] + + alias Explorer.Chain.Celo.EpochReward + alias Explorer.Chain.{Block, Hash, TokenTransfer} + + @required_attrs ~w(block_hash)a + @optional_attrs ~w(reserve_bolster_transfer_log_index community_transfer_log_index carbon_offsetting_transfer_log_index)a + @allowed_attrs @required_attrs ++ @optional_attrs + + @primary_key false + typed_schema "celo_epoch_rewards" do + field(:reserve_bolster_transfer_log_index, :integer) + field(:community_transfer_log_index, :integer) + field(:carbon_offsetting_transfer_log_index, :integer) + field(:reserve_bolster_transfer, :any, virtual: true) :: TokenTransfer.t() | nil + field(:community_transfer, :any, virtual: true) :: TokenTransfer.t() | nil + field(:carbon_offsetting_transfer, :any, virtual: true) :: TokenTransfer.t() | nil + + belongs_to( + :block, + Block, + primary_key: true, + foreign_key: :block_hash, + references: :hash, + type: Hash.Full, + null: false + ) + + timestamps() + end + + @spec changeset(%__MODULE__{}, map()) :: Ecto.Changeset.t() + def changeset(%__MODULE__{} = rewards, attrs) do + rewards + |> cast(attrs, @allowed_attrs) + |> validate_required(@required_attrs) + |> foreign_key_constraint(:block_hash) + |> unique_constraint(:block_hash) + end + + @doc """ + Loads the token transfers for the given epoch reward. + + This function retrieves token transfers related to the specified epoch reward + by knowing the index of the log of the token transfer and populates the + virtual fields in the `EpochReward` struct. We manually preload token + transfers since Ecto does not support automatically preloading objects by + composite key (i.e., `log_index` and `block_hash`). + + ## Parameters + - `epoch_reward` (`EpochReward.t()`): The epoch reward struct. + - `options` (`Keyword.t()`): Optional parameters for selecting the repository. + + ## Returns + - `EpochReward.t()`: The epoch reward struct with the token transfers loaded. + + ## Example + + iex> epoch_reward = %Explorer.Chain.Celo.EpochReward{block_hash: "some_hash", reserve_bolster_transfer_log_index: 1} + iex> Explorer.Chain.Celo.EpochReward.load_token_transfers(epoch_reward) + %Explorer.Chain.Celo.EpochReward{ + block_hash: "some_hash", + reserve_bolster_transfer_log_index: 1, + reserve_bolster_transfer: %Explorer.Chain.TokenTransfer{log_index: 1, ...} + } + """ + + @spec load_token_transfers(EpochReward.t()) :: EpochReward.t() + def load_token_transfers( + %EpochReward{ + reserve_bolster_transfer_log_index: reserve_bolster_transfer_log_index, + community_transfer_log_index: community_transfer_log_index, + carbon_offsetting_transfer_log_index: carbon_offsetting_transfer_log_index + } = epoch_reward, + options \\ [] + ) do + virtual_field_to_log_index = [ + reserve_bolster_transfer: reserve_bolster_transfer_log_index, + community_transfer: community_transfer_log_index, + carbon_offsetting_transfer: carbon_offsetting_transfer_log_index + ] + + log_indexes = + virtual_field_to_log_index + |> Enum.map(&elem(&1, 1)) + |> Enum.reject(&is_nil/1) + + query = + from( + tt in TokenTransfer.only_consensus_transfers_query(), + where: tt.log_index in ^log_indexes and tt.block_hash == ^epoch_reward.block_hash, + select: {tt.log_index, tt}, + preload: [ + :token, + [from_address: [:names, :smart_contract, :proxy_implementations]], + [to_address: [:names, :smart_contract, :proxy_implementations]] + ] + ) + + log_index_to_token_transfer = query |> select_repo(options).all() |> Map.new() + + Enum.reduce(virtual_field_to_log_index, epoch_reward, fn + {field, log_index}, acc -> + token_transfer = Map.get(log_index_to_token_transfer, log_index) + Map.put(acc, field, token_transfer) + end) + end +end diff --git a/apps/explorer/lib/explorer/chain/celo/helper.ex b/apps/explorer/lib/explorer/chain/celo/helper.ex new file mode 100644 index 000000000000..0ede3c91fa09 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/celo/helper.ex @@ -0,0 +1,94 @@ +defmodule Explorer.Chain.Celo.Helper do + @moduledoc """ + Common helper functions for Celo. + """ + + import Explorer.Chain.Cache.CeloCoreContracts, only: [atom_to_contract_name: 0] + + alias Explorer.Chain.Block + + @blocks_per_epoch 17_280 + @core_contract_atoms atom_to_contract_name() |> Map.keys() + + @doc """ + Returns the number of blocks per epoch in the Celo network. + """ + @spec blocks_per_epoch() :: non_neg_integer() + def blocks_per_epoch, do: @blocks_per_epoch + + defguard is_epoch_block_number(block_number) + when is_integer(block_number) and + block_number > 0 and + rem(block_number, @blocks_per_epoch) == 0 + + defguard is_core_contract_atom(atom) + when atom in @core_contract_atoms + + @doc """ + Validates if a block number is an epoch block number. + + ## Parameters + - `block_number` (`Block.block_number()`): The block number to validate. + + ## Returns + - `:ok` if the block number is an epoch block number. + - `{:error, :not_found}` if the block number is not an epoch block number. + + ## Examples + + iex> Explorer.Chain.Celo.Helper.validate_epoch_block_number(17280) + :ok + + iex> Explorer.Chain.Celo.Helper.validate_epoch_block_number(17281) + {:error, :not_found} + """ + @spec validate_epoch_block_number(Block.block_number()) :: :ok | {:error, :not_found} + def validate_epoch_block_number(block_number) when is_epoch_block_number(block_number), + do: :ok + + def validate_epoch_block_number(_block_number), do: {:error, :not_found} + + @doc """ + Checks if a block number belongs to a block that finalized an epoch. + + ## Parameters + - `block_number` (`Block.block_number()`): The block number to check. + + ## Returns + - `boolean()`: `true` if the block number is an epoch block number, `false` + otherwise. + + ## Examples + + iex> Explorer.Chain.Celo.Helper.epoch_block_number?(17280) + true + + iex> Explorer.Chain.Celo.Helper.epoch_block_number?(17281) + false + """ + @spec epoch_block_number?(block_number :: Block.block_number()) :: boolean() + def epoch_block_number?(block_number) when is_epoch_block_number(block_number), do: true + def epoch_block_number?(_), do: false + + @doc """ + Converts a block number to an epoch number. + + ## Parameters + - `block_number` (`Block.block_number()`): The block number to convert. + + ## Returns + - `non_neg_integer()`: The corresponding epoch number. + + ## Examples + + iex> Explorer.Chain.Celo.Helper.block_number_to_epoch_number(17280) + 1 + + iex> Explorer.Chain.Celo.Helper.block_number_to_epoch_number(17281) + 2 + """ + @spec block_number_to_epoch_number(block_number :: Block.block_number()) :: non_neg_integer() + def block_number_to_epoch_number(block_number) when is_integer(block_number) do + (block_number / @blocks_per_epoch) |> Float.ceil() |> trunc() + end +end diff --git a/apps/explorer/lib/explorer/chain/celo/pending_epoch_block_operation.ex b/apps/explorer/lib/explorer/chain/celo/pending_epoch_block_operation.ex new file mode 100644 index 000000000000..7a0152bcd1f9 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/celo/pending_epoch_block_operation.ex @@ -0,0 +1,74 @@ +defmodule Explorer.Chain.Celo.PendingEpochBlockOperation do + @moduledoc """ + Tracks an epoch block that has pending operation. + """ + + use Explorer.Schema + + import Explorer.Chain, only: [add_fetcher_limit: 2] + alias Explorer.Chain.{Block, Hash} + alias Explorer.Repo + + @required_attrs ~w(block_hash)a + + @typedoc """ + * `block_hash` - the hash of the block that has pending epoch operations. + """ + @primary_key false + typed_schema "celo_pending_epoch_block_operations" do + belongs_to(:block, Block, + foreign_key: :block_hash, + primary_key: true, + references: :hash, + type: Hash.Full, + null: false + ) + + timestamps() + end + + def changeset(%__MODULE__{} = pending_ops, attrs) do + pending_ops + |> cast(attrs, @required_attrs) + |> validate_required(@required_attrs) + |> foreign_key_constraint(:block_hash) + |> unique_constraint(:block_hash, name: :pending_epoch_block_operations_pkey) + end + + @doc """ + Returns a stream of all blocks with unfetched epochs, using + the `celo_pending_epoch_block_operations` table. + + iex> unfetched = insert(:block, number: 1 * blocks_per_epoch()) + iex> insert(:celo_pending_epoch_block_operation, block: unfetched) + iex> {:ok, blocks} = PendingEpochBlockOperation.stream_epoch_blocks_with_unfetched_rewards( + ...> [], + ...> fn block, acc -> + ...> [block | acc] + ...> end + ...> ) + iex> [{unfetched.number, unfetched.hash}] == blocks + true + """ + @spec stream_epoch_blocks_with_unfetched_rewards( + initial :: accumulator, + reducer :: (entry :: term(), accumulator -> accumulator), + limited? :: boolean() + ) :: {:ok, accumulator} + when accumulator: term() + def stream_epoch_blocks_with_unfetched_rewards(initial, reducer, limited? \\ false) + when is_function(reducer, 2) do + query = + from( + op in __MODULE__, + join: block in assoc(op, :block), + select: %{block_number: block.number, block_hash: block.hash}, + where: block.consensus == true, + order_by: [desc: block.number] + ) + + query + |> add_fetcher_limit(limited?) + |> Repo.stream_reduce(initial, reducer) + end +end diff --git a/apps/explorer/lib/explorer/chain/celo/reader.ex b/apps/explorer/lib/explorer/chain/celo/reader.ex new file mode 100644 index 000000000000..d1c2dfff923b --- /dev/null +++ b/apps/explorer/lib/explorer/chain/celo/reader.ex @@ -0,0 +1,193 @@ +defmodule Explorer.Chain.Celo.Reader do + @moduledoc """ + Read functions for Celo modules. + """ + + import Ecto.Query, only: [limit: 2] + + import Explorer.Chain, + only: [ + select_repo: 1, + join_associations: 2, + default_paging_options: 0 + ] + + alias Explorer.Chain.Cache.CeloCoreContracts + alias Explorer.Chain.Celo.ElectionReward + alias Explorer.Chain.{Hash, Token, Wei} + + @election_reward_types ElectionReward.types() + @default_paging_options default_paging_options() + + @doc """ + Retrieves election rewards associated with a given address hash. + + ## Parameters + - `address_hash` (`Hash.Address.t()`): The address hash to search for election + rewards. + - `options` (`Keyword.t()`): Optional parameters for fetching data. + + ## Returns + - `[ElectionReward.t()]`: A list of election rewards associated with the + address hash. + + ## Examples + + iex> address_hash = %Hash.Address{ + ...> byte_count: 20, + ...> bytes: <<0x1d1f7f0e1441c37e28b89e0b5e1edbbd34d77649 :: size(160)>> + ...> } + iex> Explorer.Chain.Celo.Reader.address_hash_to_election_rewards(address_hash) + [%ElectionReward{}, ...] + """ + @spec address_hash_to_election_rewards( + Hash.Address.t(), + Keyword.t() + ) :: [ElectionReward.t()] + def address_hash_to_election_rewards(address_hash, options \\ []) do + necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) + paging_options = Keyword.get(options, :paging_options, @default_paging_options) + + address_hash + |> ElectionReward.address_hash_to_ordered_rewards_query() + |> ElectionReward.paginate(paging_options) + |> limit(^paging_options.page_size) + |> join_associations(necessity_by_association) + |> select_repo(options).all() + end + + @doc """ + Retrieves election rewards by block hash and reward type. + + ## Parameters + - `block_hash` (`Hash.t()`): The block hash to search for election rewards. + - `reward_type` (`ElectionReward.type()`): The type of reward to filter. + - `options` (`Keyword.t()`): Optional parameters for fetching data. + + ## Returns + - `[ElectionReward.t()]`: A list of election rewards filtered by block hash + and reward type. + + ## Examples + + iex> block_hash = %Hash.Full{ + ...> byte_count: 32, + ...> bytes: <<0x9fc76417374aa880d4449a1f7f31ec597f00b1f6f3dd2d66f4c9c6c445836d8b :: big-integer-size(32)-unit(8)>> + ...> } + iex> Explorer.Chain.Celo.Reader.block_hash_to_election_rewards_by_type(block_hash, :voter_reward) + [%ElectionReward{}, ...] + """ + @spec block_hash_to_election_rewards_by_type( + Hash.t(), + ElectionReward.type(), + Keyword.t() + ) :: [ElectionReward.t()] + def block_hash_to_election_rewards_by_type(block_hash, reward_type, options \\ []) + when reward_type in @election_reward_types do + necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) + paging_options = Keyword.get(options, :paging_options, @default_paging_options) + + block_hash + |> ElectionReward.block_hash_to_rewards_by_type_query(reward_type) + |> ElectionReward.paginate(paging_options) + |> limit(^paging_options.page_size) + |> join_associations(necessity_by_association) + |> select_repo(options).all() + end + + @doc """ + Retrieves aggregated election rewards by block hash. + + ## Parameters + - `block_hash` (`Hash.Full.t()`): The block hash to aggregate election + rewards. + - `options` (`Keyword.t()`): Optional parameters for fetching data. + + ## Returns + - `%{atom() => Wei.t() | nil}`: A map of aggregated election rewards by type. + + ## Examples + + iex> block_hash = %Hash.Full{ + ...> byte_count: 32, + ...> bytes: <<0x9fc76417374aa880d4449a1f7f31ec597f00b1f6f3dd2d66f4c9c6c445836d8b :: big-integer-size(32)-unit(8)>> + ...> } + iex> Explorer.Chain.Celo.Reader.block_hash_to_aggregated_election_rewards_by_type(block_hash) + %{voter_reward: %{total: %Decimal{}, count: 2}, ...} + """ + @spec block_hash_to_aggregated_election_rewards_by_type( + Hash.Full.t(), + Keyword.t() + ) :: %{atom() => Wei.t() | nil} + def block_hash_to_aggregated_election_rewards_by_type(block_hash, options \\ []) do + reward_type_to_token = + block_hash_to_election_reward_token_addresses_by_type( + block_hash, + options + ) + + reward_type_to_aggregated_rewards = + block_hash + |> ElectionReward.block_hash_to_aggregated_rewards_by_type_query() + |> select_repo(options).all() + |> Map.new(fn {type, total, count} -> + {type, %{total: total, count: count}} + end) + + ElectionReward.types() + |> Map.new(&{&1, %{total: Decimal.new(0), count: 0}}) + |> Map.merge(reward_type_to_aggregated_rewards) + |> Map.new(fn {type, aggregated_reward} -> + token = Map.get(reward_type_to_token, type) + aggregated_reward_with_token = Map.put(aggregated_reward, :token, token) + {type, aggregated_reward_with_token} + end) + end + + # Retrieves the token for each type of election reward on the given block. + # + # ## Parameters + # - `block_hash` (`Hash.Full.t()`): The block hash to search for token + # addresses. + # - `options` (`Keyword.t()`): Optional parameters for fetching data. + # + # ## Returns + # - `%{atom() => Token.t() | nil}`: A map of reward types to token. + # + # ## Examples + # + # iex> block_hash = %Hash.Full{ + # ...> byte_count: 32, + # ...> bytes: <<0x9fc76417374aa880d4449a1f7f31ec597f00b1f6f3dd2d66f4c9c6c445836d8b :: big-integer-size(32)-unit(8)>> + # ...> } + # iex> Explorer.Chain.Celo.Reader.block_hash_to_election_reward_token_addresses_by_type(block_hash) + # %{voter_reward: %Token{}, ...} + @spec block_hash_to_election_reward_token_addresses_by_type( + Hash.Full.t(), + Keyword.t() + ) :: %{atom() => Token.t() | nil} + defp block_hash_to_election_reward_token_addresses_by_type(block_hash, options) do + contract_address_hash_to_atom = + ElectionReward.reward_type_atom_to_token_atom() + |> Map.values() + |> Map.new(fn token_atom -> + {:ok, contract_address_hash} = CeloCoreContracts.get_address(token_atom, block_hash) + {contract_address_hash, token_atom} + end) + + token_atom_to_token = + contract_address_hash_to_atom + |> Map.keys() + |> Token.get_by_contract_address_hashes(options) + |> Map.new(fn token -> + hash = to_string(token.contract_address_hash) + atom = contract_address_hash_to_atom[hash] + {atom, token} + end) + + ElectionReward.reward_type_atom_to_token_atom() + |> Map.new(fn {reward_type_atom, token_atom} -> + {reward_type_atom, Map.get(token_atom_to_token, token_atom)} + end) + end +end diff --git a/apps/explorer/lib/explorer/chain/celo/validator_group_vote.ex b/apps/explorer/lib/explorer/chain/celo/validator_group_vote.ex new file mode 100644 index 000000000000..dcb639e49f56 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/celo/validator_group_vote.ex @@ -0,0 +1,76 @@ +defmodule Explorer.Chain.Celo.ValidatorGroupVote do + @moduledoc """ + Represents the information about a vote for a validator group made by an + account. + """ + + use Explorer.Schema + + alias Explorer.Chain.{Address, Block, Hash, Transaction} + + @types_enum ~w(activated revoked)a + @required_attrs ~w(account_address_hash group_address_hash type transaction_hash block_hash block_number)a + + @typedoc """ + * `account_address_hash` - the address of the account that made the vote. + * `group_address_hash` - the address of the validator group that + was voted for. + * `type` - whether this vote is `activated` or `revoked`. + * `block_number` - the block number of the vote. + * `block_hash` - the hash of the block that contains the vote. + * `transaction_hash` - the hash of the transaction that made the vote. + """ + @primary_key false + typed_schema "celo_validator_group_votes" do + belongs_to(:account_address, Address, + foreign_key: :account_address_hash, + references: :hash, + type: Hash.Address + ) + + belongs_to(:group_address, Address, + foreign_key: :group_address_hash, + references: :hash, + type: Hash.Address + ) + + field(:type, Ecto.Enum, + values: @types_enum, + null: false + ) + + field(:block_number, :integer, null: false) + + belongs_to(:block, Block, + foreign_key: :block_hash, + primary_key: true, + references: :hash, + type: Hash.Full, + null: false + ) + + belongs_to(:transaction, Transaction, + foreign_key: :transaction_hash, + primary_key: true, + references: :hash, + type: Hash.Full, + null: false + ) + + timestamps() + end + + @spec changeset( + __MODULE__.t(), + :invalid | %{optional(:__struct__) => none, optional(atom | binary) => any} + ) :: Ecto.Changeset.t() + def changeset(%__MODULE__{} = vote, attrs \\ %{}) do + vote + |> cast(attrs, @required_attrs) + |> validate_required(@required_attrs) + |> foreign_key_constraint(:account_address_hash) + |> foreign_key_constraint(:group_address_hash) + |> foreign_key_constraint(:block_hash) + |> foreign_key_constraint(:transaction_hash) + end +end diff --git a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex index 8ef328d55b09..0af2896b2c6f 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex @@ -24,6 +24,9 @@ defmodule Explorer.Chain.Import.Runner.Blocks do Transaction } + alias Explorer.Chain.Celo.Helper, as: CeloHelper + alias Explorer.Chain.Celo.PendingEpochBlockOperation, as: CeloPendingEpochBlockOperation + alias Explorer.Chain.Block.Reward alias Explorer.Chain.Import.Runner alias Explorer.Chain.Import.Runner.Address.CurrentTokenBalances @@ -207,11 +210,32 @@ defmodule Explorer.Chain.Import.Runner.Blocks do :blocks_update_token_holder_counts ) end) + |> chain_type_dependent_import( + :celo, + &Multi.run(&1, :celo_pending_epoch_block_operations, fn repo, %{blocks: blocks} -> + Instrumenter.block_import_stage_runner( + fn -> + celo_pending_epoch_block_operations(repo, blocks, insert_options) + end, + :address_referencing, + :blocks, + :celo_pending_epoch_block_operations + ) + end) + ) end @impl Runner def timeout, do: @timeout + def chain_type_dependent_import(multi, chain_type, multi_run) do + if Application.get_env(:explorer, :chain_type) == chain_type do + multi_run.(multi) + else + multi + end + end + defp fork_transactions(%{ repo: repo, timeout: timeout, @@ -916,4 +940,24 @@ defmodule Explorer.Chain.Import.Runner.Blocks do blocks end end + + defp celo_pending_epoch_block_operations(repo, inserted_blocks, %{timeout: timeout, timestamps: timestamps}) do + ordered_epoch_blocks = + inserted_blocks + |> Enum.filter(fn block -> CeloHelper.epoch_block_number?(block.number) && block.consensus end) + |> Enum.map(&%{block_hash: &1.hash}) + |> Enum.sort_by(& &1.block_hash) + |> Enum.dedup_by(& &1.block_hash) + + Import.insert_changes_list( + repo, + ordered_epoch_blocks, + conflict_target: :block_hash, + on_conflict: :nothing, + for: CeloPendingEpochBlockOperation, + returning: true, + timeout: timeout, + timestamps: timestamps + ) + end end diff --git a/apps/explorer/lib/explorer/chain/import/runner/celo/election_rewards.ex b/apps/explorer/lib/explorer/chain/import/runner/celo/election_rewards.ex new file mode 100644 index 000000000000..b7ff06f78157 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/celo/election_rewards.ex @@ -0,0 +1,93 @@ +defmodule Explorer.Chain.Import.Runner.Celo.ElectionRewards do + @moduledoc """ + Bulk imports `t:Explorer.Chain.Celo.ElectionReward.t/0`. + """ + + require Ecto.Query + + alias Ecto.{Changeset, Multi, Repo} + alias Explorer.Chain.Celo.ElectionReward + alias Explorer.Chain.Import + alias Explorer.Prometheus.Instrumenter + + @behaviour Import.Runner + + # milliseconds + @timeout 60_000 + + @type imported :: [ElectionReward.t()] + + @impl Import.Runner + def ecto_schema_module, do: ElectionReward + + @impl Import.Runner + def option_key, do: :celo_election_rewards + + @impl Import.Runner + @spec imported_table_row() :: %{:value_description => binary(), :value_type => binary()} + 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 + @spec run(Multi.t(), list(), map()) :: Multi.t() + 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, :insert_celo_election_rewards, fn repo, _ -> + Instrumenter.block_import_stage_runner( + fn -> insert(repo, changes_list, insert_options) end, + :block_referencing, + :celo_election_rewards, + :celo_election_rewards + ) + end) + end + + @impl Import.Runner + def timeout, do: @timeout + + @spec insert(Repo.t(), [map()], %{required(:timeout) => timeout(), required(:timestamps) => Import.timestamps()}) :: + {:ok, [ElectionReward.t()]} + | {:error, [Changeset.t()]} + def insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = _options) when is_list(changes_list) do + # Enforce Celo.Epoch.ElectionReward ShareLocks order (see docs: sharelock.md) + ordered_changes_list = + Enum.sort_by( + changes_list, + &{ + &1.block_hash, + &1.type, + &1.account_address_hash, + &1.associated_account_address_hash + } + ) + + {:ok, inserted} = + Import.insert_changes_list( + repo, + ordered_changes_list, + for: ElectionReward, + returning: true, + timeout: timeout, + timestamps: timestamps, + conflict_target: [ + :block_hash, + :type, + :account_address_hash, + :associated_account_address_hash + ], + on_conflict: :replace_all + ) + + {:ok, inserted} + end +end diff --git a/apps/explorer/lib/explorer/chain/import/runner/celo/epoch_rewards.ex b/apps/explorer/lib/explorer/chain/import/runner/celo/epoch_rewards.ex new file mode 100644 index 000000000000..7c7cd5752e68 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/celo/epoch_rewards.ex @@ -0,0 +1,139 @@ +defmodule Explorer.Chain.Import.Runner.Celo.EpochRewards do + @moduledoc """ + Bulk imports `t:Explorer.Chain.Celo.EpochReward.t/0`. + """ + + require Ecto.Query + + alias Ecto.{Changeset, Multi, Repo} + alias Explorer.Chain.Celo.{EpochReward, PendingEpochBlockOperation} + alias Explorer.Chain.Import + alias Explorer.Prometheus.Instrumenter + + import Ecto.Query + + @behaviour Import.Runner + + # milliseconds + @timeout 60_000 + + @type imported :: [EpochReward.t()] + + @impl Import.Runner + def ecto_schema_module, do: EpochReward + + @impl Import.Runner + def option_key, do: :celo_epoch_rewards + + @impl Import.Runner + @spec imported_table_row() :: %{:value_description => binary(), :value_type => binary()} + 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 + @spec run(Multi.t(), list(), map()) :: Multi.t() + 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 + |> Multi.run(:acquire_pending_epoch_block_operations, fn repo, _ -> + Instrumenter.block_import_stage_runner( + fn -> acquire_pending_epoch_block_operations(repo, changes_list) end, + :block_pending, + :celo_epoch_rewards, + :acquire_pending_epoch_block_operations + ) + end) + |> Multi.run(:insert_celo_epoch_rewards, fn repo, _ -> + Instrumenter.block_import_stage_runner( + fn -> insert(repo, changes_list, insert_options) end, + :block_referencing, + :celo_epoch_rewards, + :celo_epoch_rewards + ) + end) + |> Multi.run( + :delete_pending_epoch_block_operations, + fn repo, + %{ + acquire_pending_epoch_block_operations: pending_block_hashes + } -> + Instrumenter.block_import_stage_runner( + fn -> delete_pending_epoch_block_operations(repo, pending_block_hashes) end, + :block_pending, + :celo_epoch_rewards, + :delete_pending_epoch_block_operations + ) + end + ) + end + + @impl Import.Runner + def timeout, do: @timeout + + @spec insert(Repo.t(), [map()], %{required(:timeout) => timeout(), required(:timestamps) => Import.timestamps()}) :: + {:ok, [EpochReward.t()]} + | {:error, [Changeset.t()]} + def insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = _options) when is_list(changes_list) do + # Enforce Celo.EpochReward ShareLocks order (see docs: sharelock.md) + ordered_changes_list = Enum.sort_by(changes_list, & &1.block_hash) + + {:ok, inserted} = + Import.insert_changes_list( + repo, + ordered_changes_list, + for: EpochReward, + returning: true, + timeout: timeout, + timestamps: timestamps, + conflict_target: :block_hash, + on_conflict: :replace_all + ) + + {:ok, inserted} + end + + def acquire_pending_epoch_block_operations(repo, changes_list) do + block_hashes = Enum.map(changes_list, & &1.block_hash) + + query = + from( + pending_ops in PendingEpochBlockOperation, + where: pending_ops.block_hash in ^block_hashes, + select: pending_ops.block_hash, + # Enforce PendingBlockOperation ShareLocks order (see docs: sharelocks.md) + order_by: [asc: pending_ops.block_hash], + lock: "FOR UPDATE" + ) + + {:ok, repo.all(query)} + end + + def delete_pending_epoch_block_operations(repo, block_hashes) do + delete_query = + from( + pending_ops in PendingEpochBlockOperation, + where: pending_ops.block_hash in ^block_hashes + ) + + try do + # ShareLocks order already enforced by + # `acquire_pending_epoch_block_operations` (see docs: sharelocks.md) + {_count, deleted} = repo.delete_all(delete_query, []) + + {:ok, deleted} + rescue + postgrex_error in Postgrex.Error -> + {:error, %{exception: postgrex_error, pending_hashes: block_hashes}} + end + end +end diff --git a/apps/explorer/lib/explorer/chain/import/runner/celo/validator_group_votes.ex b/apps/explorer/lib/explorer/chain/import/runner/celo/validator_group_votes.ex new file mode 100644 index 000000000000..618b0bae52e6 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/celo/validator_group_votes.ex @@ -0,0 +1,87 @@ +defmodule Explorer.Chain.Import.Runner.Celo.ValidatorGroupVotes do + @moduledoc """ + Bulk imports `t:Explorer.Chain.Celo.ValidatorGroupVote.t/0`. + """ + + require Ecto.Query + + alias Ecto.{Changeset, Multi, Repo} + alias Explorer.Chain.Celo.ValidatorGroupVote + alias Explorer.Chain.Import + alias Explorer.Prometheus.Instrumenter + + @behaviour Import.Runner + + # milliseconds + @timeout 60_000 + + @type imported :: [ValidatorGroupVote.t()] + + @impl Import.Runner + def ecto_schema_module, do: ValidatorGroupVote + + @impl Import.Runner + def option_key, do: :celo_validator_group_votes + + @impl Import.Runner + @spec imported_table_row() :: %{:value_description => binary(), :value_type => binary()} + 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 + @spec run(Multi.t(), list(), map()) :: Multi.t() + 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, :insert_celo_validator_group_votes, fn repo, _ -> + Instrumenter.block_import_stage_runner( + fn -> insert(repo, changes_list, insert_options) end, + :block_referencing, + :celo_validator_group_votes, + :celo_validator_group_votes + ) + end) + end + + @impl Import.Runner + def timeout, do: @timeout + + @spec insert(Repo.t(), [map()], %{required(:timeout) => timeout(), required(:timestamps) => Import.timestamps()}) :: + {:ok, [ValidatorGroupVote.t()]} + | {:error, [Changeset.t()]} + def insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = _options) when is_list(changes_list) do + # Enforce Celo.Epoch.ValidatorGroupVote ShareLocks order (see docs: sharelock.md) + ordered_changes_list = + Enum.sort_by( + changes_list, + &{ + &1.transaction_hash, + &1.account_address_hash, + &1.group_address_hash + } + ) + + {:ok, inserted} = + Import.insert_changes_list( + repo, + ordered_changes_list, + for: ValidatorGroupVote, + returning: true, + timeout: timeout, + timestamps: timestamps, + conflict_target: :transaction_hash, + on_conflict: :replace_all + ) + + {:ok, inserted} + end +end diff --git a/apps/explorer/lib/explorer/chain/import/runner/logs.ex b/apps/explorer/lib/explorer/chain/import/runner/logs.ex index 7c0e591a0f92..cb8466f2b2ac 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/logs.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/logs.ex @@ -65,13 +65,23 @@ defmodule Explorer.Chain.Import.Runner.Logs do on_conflict = Map.get_lazy(options, :on_conflict, &default_on_conflict/0) # Enforce Log ShareLocks order (see docs: sharelocks.md) - ordered_changes_list = Enum.sort_by(changes_list, &{&1.transaction_hash, &1.block_hash, &1.index}) + ordered_changes_list = + case Application.get_env(:explorer, :chain_type) do + :celo -> Enum.sort_by(changes_list, &{&1.block_hash, &1.index}) + _ -> Enum.sort_by(changes_list, &{&1.transaction_hash, &1.block_hash, &1.index}) + end + + conflict_target = + case Application.get_env(:explorer, :chain_type) do + :celo -> [:index, :block_hash] + _ -> [:transaction_hash, :index, :block_hash] + end {:ok, _} = Import.insert_changes_list( repo, ordered_changes_list, - conflict_target: [:transaction_hash, :index, :block_hash], + conflict_target: conflict_target, on_conflict: on_conflict, for: Log, returning: true, @@ -81,32 +91,65 @@ defmodule Explorer.Chain.Import.Runner.Logs do end defp default_on_conflict do - from( - log in Log, - update: [ - set: [ - address_hash: fragment("EXCLUDED.address_hash"), - data: fragment("EXCLUDED.data"), - first_topic: fragment("EXCLUDED.first_topic"), - second_topic: fragment("EXCLUDED.second_topic"), - third_topic: fragment("EXCLUDED.third_topic"), - fourth_topic: fragment("EXCLUDED.fourth_topic"), - # Don't update `index` as it is part of the composite primary key and used for the conflict target - # Don't update `transaction_hash` as it is part of the composite primary key and used for the conflict target - inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", log.inserted_at), - updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", log.updated_at) - ] - ], - where: - fragment( - "(EXCLUDED.address_hash, EXCLUDED.data, EXCLUDED.first_topic, EXCLUDED.second_topic, EXCLUDED.third_topic, EXCLUDED.fourth_topic) IS DISTINCT FROM (?, ?, ?, ?, ?, ?)", - log.address_hash, - log.data, - log.first_topic, - log.second_topic, - log.third_topic, - log.fourth_topic + case Application.get_env(:explorer, :chain_type) do + :celo -> + from( + log in Log, + update: [ + set: [ + address_hash: fragment("EXCLUDED.address_hash"), + data: fragment("EXCLUDED.data"), + first_topic: fragment("EXCLUDED.first_topic"), + second_topic: fragment("EXCLUDED.second_topic"), + third_topic: fragment("EXCLUDED.third_topic"), + fourth_topic: fragment("EXCLUDED.fourth_topic"), + # Don't update `index` as it is part of the composite primary key and used for the conflict target + transaction_hash: fragment("EXCLUDED.transaction_hash"), + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", log.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", log.updated_at) + ] + ], + where: + fragment( + "(EXCLUDED.address_hash, EXCLUDED.data, EXCLUDED.first_topic, EXCLUDED.second_topic, EXCLUDED.third_topic, EXCLUDED.fourth_topic, EXCLUDED.transaction_hash) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?)", + log.address_hash, + log.data, + log.first_topic, + log.second_topic, + log.third_topic, + log.fourth_topic, + log.transaction_hash + ) ) - ) + + _ -> + from( + log in Log, + update: [ + set: [ + address_hash: fragment("EXCLUDED.address_hash"), + data: fragment("EXCLUDED.data"), + first_topic: fragment("EXCLUDED.first_topic"), + second_topic: fragment("EXCLUDED.second_topic"), + third_topic: fragment("EXCLUDED.third_topic"), + fourth_topic: fragment("EXCLUDED.fourth_topic"), + # Don't update `index` as it is part of the composite primary key and used for the conflict target + # Don't update `transaction_hash` as it is part of the composite primary key and used for the conflict target + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", log.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", log.updated_at) + ] + ], + where: + fragment( + "(EXCLUDED.address_hash, EXCLUDED.data, EXCLUDED.first_topic, EXCLUDED.second_topic, EXCLUDED.third_topic, EXCLUDED.fourth_topic) IS DISTINCT FROM (?, ?, ?, ?, ?, ?)", + log.address_hash, + log.data, + log.first_topic, + log.second_topic, + log.third_topic, + log.fourth_topic + ) + ) + end end end diff --git a/apps/explorer/lib/explorer/chain/import/runner/token_transfers.ex b/apps/explorer/lib/explorer/chain/import/runner/token_transfers.ex index 2dac07465ebc..acbbf3159207 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/token_transfers.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/token_transfers.ex @@ -61,13 +61,23 @@ defmodule Explorer.Chain.Import.Runner.TokenTransfers do on_conflict = Map.get_lazy(options, :on_conflict, &default_on_conflict/0) # Enforce TokenTransfer ShareLocks order (see docs: sharelocks.md) - ordered_changes_list = Enum.sort_by(changes_list, &{&1.transaction_hash, &1.block_hash, &1.log_index}) + ordered_changes_list = + case Application.get_env(:explorer, :chain_type) do + :celo -> Enum.sort_by(changes_list, &{&1.block_hash, &1.log_index}) + _ -> Enum.sort_by(changes_list, &{&1.transaction_hash, &1.block_hash, &1.log_index}) + end + + conflict_target = + case Application.get_env(:explorer, :chain_type) do + :celo -> [:log_index, :block_hash] + _ -> [:transaction_hash, :log_index, :block_hash] + end {:ok, inserted} = Import.insert_changes_list( repo, ordered_changes_list, - conflict_target: [:transaction_hash, :log_index, :block_hash], + conflict_target: conflict_target, on_conflict: on_conflict, for: TokenTransfer, returning: true, @@ -79,34 +89,70 @@ defmodule Explorer.Chain.Import.Runner.TokenTransfers do end defp default_on_conflict do - from( - token_transfer in TokenTransfer, - update: [ - set: [ - # Don't update `transaction_hash` as it is part of the composite primary key and used for the conflict target - # Don't update `log_index` as it is part of the composite primary key and used for the conflict target - amount: fragment("EXCLUDED.amount"), - from_address_hash: fragment("EXCLUDED.from_address_hash"), - to_address_hash: fragment("EXCLUDED.to_address_hash"), - token_contract_address_hash: fragment("EXCLUDED.token_contract_address_hash"), - token_ids: fragment("EXCLUDED.token_ids"), - token_type: fragment("EXCLUDED.token_type"), - block_consensus: fragment("EXCLUDED.block_consensus"), - inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", token_transfer.inserted_at), - updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", token_transfer.updated_at) - ] - ], - where: - fragment( - "(EXCLUDED.amount, EXCLUDED.from_address_hash, EXCLUDED.to_address_hash, EXCLUDED.token_contract_address_hash, EXCLUDED.token_ids, EXCLUDED.token_type, EXCLUDED.block_consensus) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?)", - token_transfer.amount, - token_transfer.from_address_hash, - token_transfer.to_address_hash, - token_transfer.token_contract_address_hash, - token_transfer.token_ids, - token_transfer.token_type, - token_transfer.block_consensus + case Application.get_env(:explorer, :chain_type) do + :celo -> + from( + token_transfer in TokenTransfer, + update: [ + set: [ + # Don't update `log_index` as it is part of the composite primary + # key and used for the conflict target + transaction_hash: fragment("EXCLUDED.transaction_hash"), + amount: fragment("EXCLUDED.amount"), + from_address_hash: fragment("EXCLUDED.from_address_hash"), + to_address_hash: fragment("EXCLUDED.to_address_hash"), + token_contract_address_hash: fragment("EXCLUDED.token_contract_address_hash"), + token_ids: fragment("EXCLUDED.token_ids"), + token_type: fragment("EXCLUDED.token_type"), + block_consensus: fragment("EXCLUDED.block_consensus"), + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", token_transfer.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", token_transfer.updated_at) + ] + ], + where: + fragment( + "(EXCLUDED.amount, EXCLUDED.from_address_hash, EXCLUDED.to_address_hash, EXCLUDED.token_contract_address_hash, EXCLUDED.token_ids, EXCLUDED.token_type, EXCLUDED.block_consensus, EXCLUDED.transaction_hash) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?, ?)", + token_transfer.amount, + token_transfer.from_address_hash, + token_transfer.to_address_hash, + token_transfer.token_contract_address_hash, + token_transfer.token_ids, + token_transfer.token_type, + token_transfer.block_consensus, + token_transfer.transaction_hash + ) ) - ) + + _ -> + from( + token_transfer in TokenTransfer, + update: [ + set: [ + # Don't update `transaction_hash` as it is part of the composite primary key and used for the conflict target + # Don't update `log_index` as it is part of the composite primary key and used for the conflict target + amount: fragment("EXCLUDED.amount"), + from_address_hash: fragment("EXCLUDED.from_address_hash"), + to_address_hash: fragment("EXCLUDED.to_address_hash"), + token_contract_address_hash: fragment("EXCLUDED.token_contract_address_hash"), + token_ids: fragment("EXCLUDED.token_ids"), + token_type: fragment("EXCLUDED.token_type"), + block_consensus: fragment("EXCLUDED.block_consensus"), + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", token_transfer.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", token_transfer.updated_at) + ] + ], + where: + fragment( + "(EXCLUDED.amount, EXCLUDED.from_address_hash, EXCLUDED.to_address_hash, EXCLUDED.token_contract_address_hash, EXCLUDED.token_ids, EXCLUDED.token_type, EXCLUDED.block_consensus) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?)", + token_transfer.amount, + token_transfer.from_address_hash, + token_transfer.to_address_hash, + token_transfer.token_contract_address_hash, + token_transfer.token_ids, + token_transfer.token_type, + token_transfer.block_consensus + ) + ) + end end end diff --git a/apps/explorer/lib/explorer/chain/import/runner/transactions.ex b/apps/explorer/lib/explorer/chain/import/runner/transactions.ex index 1b1772afd9eb..aee467977f3e 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/transactions.ex @@ -107,6 +107,7 @@ defmodule Explorer.Chain.Import.Runner.Transactions do ) end + # todo: avoid code duplication case Application.compile_env(:explorer, :chain_type) do :suave -> defp default_on_conflict do @@ -360,6 +361,83 @@ defmodule Explorer.Chain.Import.Runner.Transactions do ) end + :celo -> + defp default_on_conflict do + from( + transaction in Transaction, + update: [ + set: [ + block_hash: fragment("EXCLUDED.block_hash"), + old_block_hash: transaction.block_hash, + block_number: fragment("EXCLUDED.block_number"), + block_consensus: fragment("EXCLUDED.block_consensus"), + block_timestamp: fragment("EXCLUDED.block_timestamp"), + created_contract_address_hash: fragment("EXCLUDED.created_contract_address_hash"), + created_contract_code_indexed_at: fragment("EXCLUDED.created_contract_code_indexed_at"), + cumulative_gas_used: fragment("EXCLUDED.cumulative_gas_used"), + error: fragment("EXCLUDED.error"), + from_address_hash: fragment("EXCLUDED.from_address_hash"), + gas: fragment("EXCLUDED.gas"), + gas_price: fragment("EXCLUDED.gas_price"), + gas_used: fragment("EXCLUDED.gas_used"), + index: fragment("EXCLUDED.index"), + input: fragment("EXCLUDED.input"), + nonce: fragment("EXCLUDED.nonce"), + r: fragment("EXCLUDED.r"), + s: fragment("EXCLUDED.s"), + status: fragment("EXCLUDED.status"), + to_address_hash: fragment("EXCLUDED.to_address_hash"), + v: fragment("EXCLUDED.v"), + value: fragment("EXCLUDED.value"), + earliest_processing_start: fragment("EXCLUDED.earliest_processing_start"), + revert_reason: fragment("EXCLUDED.revert_reason"), + max_priority_fee_per_gas: fragment("EXCLUDED.max_priority_fee_per_gas"), + max_fee_per_gas: fragment("EXCLUDED.max_fee_per_gas"), + type: fragment("EXCLUDED.type"), + # Don't update `hash` as it is part of the primary key and used for the conflict target + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", transaction.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", transaction.updated_at), + # Celo custom fields + gas_token_contract_address_hash: fragment("EXCLUDED.gas_token_contract_address_hash"), + gas_fee_recipient_address_hash: fragment("EXCLUDED.gas_fee_recipient_address_hash"), + gateway_fee: fragment("EXCLUDED.gateway_fee") + ] + ], + where: + fragment( + "(EXCLUDED.block_hash, EXCLUDED.block_number, EXCLUDED.block_consensus, EXCLUDED.block_timestamp, EXCLUDED.created_contract_address_hash, EXCLUDED.created_contract_code_indexed_at, EXCLUDED.cumulative_gas_used, EXCLUDED.from_address_hash, EXCLUDED.gas, EXCLUDED.gas_price, EXCLUDED.gas_used, EXCLUDED.index, EXCLUDED.input, EXCLUDED.nonce, EXCLUDED.r, EXCLUDED.s, EXCLUDED.status, EXCLUDED.to_address_hash, EXCLUDED.v, EXCLUDED.value, EXCLUDED.earliest_processing_start, EXCLUDED.revert_reason, EXCLUDED.max_priority_fee_per_gas, EXCLUDED.max_fee_per_gas, EXCLUDED.type, EXCLUDED.gas_token_contract_address_hash, EXCLUDED.gas_fee_recipient_address_hash, EXCLUDED.gateway_fee) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + transaction.block_hash, + transaction.block_number, + transaction.block_consensus, + transaction.block_timestamp, + transaction.created_contract_address_hash, + transaction.created_contract_code_indexed_at, + transaction.cumulative_gas_used, + transaction.from_address_hash, + transaction.gas, + transaction.gas_price, + transaction.gas_used, + transaction.index, + transaction.input, + transaction.nonce, + transaction.r, + transaction.s, + transaction.status, + transaction.to_address_hash, + transaction.v, + transaction.value, + transaction.earliest_processing_start, + transaction.revert_reason, + transaction.max_priority_fee_per_gas, + transaction.max_fee_per_gas, + transaction.type, + transaction.gas_token_contract_address_hash, + transaction.gas_fee_recipient_address_hash, + transaction.gateway_fee + ) + ) + end + _ -> defp default_on_conflict do from( diff --git a/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex b/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex index 0d830810edf8..0d339a78d331 100644 --- a/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex +++ b/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex @@ -7,6 +7,7 @@ defmodule Explorer.Chain.Import.Stage.BlockReferencing do alias Explorer.Chain.Import.{Runner, Stage} @behaviour Stage + @default_runners [ Runner.Transaction.Forks, Runner.Logs, @@ -17,92 +18,74 @@ defmodule Explorer.Chain.Import.Stage.BlockReferencing do Runner.Withdrawals ] - @optimism_runners [ - Runner.Optimism.FrameSequences, - Runner.Optimism.FrameSequenceBlobs, - Runner.Optimism.TxnBatches, - Runner.Optimism.OutputRoots, - Runner.Optimism.DisputeGames, - Runner.Optimism.Deposits, - Runner.Optimism.Withdrawals, - Runner.Optimism.WithdrawalEvents - ] - - @polygon_edge_runners [ - Runner.PolygonEdge.Deposits, - Runner.PolygonEdge.DepositExecutes, - Runner.PolygonEdge.Withdrawals, - Runner.PolygonEdge.WithdrawalExits - ] - - @polygon_zkevm_runners [ - Runner.PolygonZkevm.LifecycleTransactions, - Runner.PolygonZkevm.TransactionBatches, - Runner.PolygonZkevm.BatchTransactions, - Runner.PolygonZkevm.BridgeL1Tokens, - Runner.PolygonZkevm.BridgeOperations - ] - - @zksync_runners [ - Runner.ZkSync.LifecycleTransactions, - Runner.ZkSync.TransactionBatches, - Runner.ZkSync.BatchTransactions, - Runner.ZkSync.BatchBlocks - ] - - @shibarium_runners [ - Runner.Shibarium.BridgeOperations - ] - - @ethereum_runners [ - Runner.Beacon.BlobTransactions - ] - - @arbitrum_runners [ - Runner.Arbitrum.Messages, - Runner.Arbitrum.LifecycleTransactions, - Runner.Arbitrum.L1Executions, - Runner.Arbitrum.L1Batches, - Runner.Arbitrum.BatchBlocks, - Runner.Arbitrum.BatchTransactions, - Runner.Arbitrum.DaMultiPurposeRecords - ] + @extra_runners_by_chain_type %{ + optimism: [ + Runner.Optimism.FrameSequences, + Runner.Optimism.FrameSequenceBlobs, + Runner.Optimism.TxnBatches, + Runner.Optimism.OutputRoots, + Runner.Optimism.DisputeGames, + Runner.Optimism.Deposits, + Runner.Optimism.Withdrawals, + Runner.Optimism.WithdrawalEvents + ], + polygon_edge: [ + Runner.PolygonEdge.Deposits, + Runner.PolygonEdge.DepositExecutes, + Runner.PolygonEdge.Withdrawals, + Runner.PolygonEdge.WithdrawalExits + ], + polygon_zkevm: [ + Runner.PolygonZkevm.LifecycleTransactions, + Runner.PolygonZkevm.TransactionBatches, + Runner.PolygonZkevm.BatchTransactions, + Runner.PolygonZkevm.BridgeL1Tokens, + Runner.PolygonZkevm.BridgeOperations + ], + zksync: [ + Runner.ZkSync.LifecycleTransactions, + Runner.ZkSync.TransactionBatches, + Runner.ZkSync.BatchTransactions, + Runner.ZkSync.BatchBlocks + ], + shibarium: [ + Runner.Shibarium.BridgeOperations + ], + ethereum: [ + Runner.Beacon.BlobTransactions + ], + arbitrum: [ + Runner.Arbitrum.Messages, + Runner.Arbitrum.LifecycleTransactions, + Runner.Arbitrum.L1Executions, + Runner.Arbitrum.L1Batches, + Runner.Arbitrum.BatchBlocks, + Runner.Arbitrum.BatchTransactions, + Runner.Arbitrum.DaMultiPurposeRecords + ], + celo: [ + Runner.Celo.ValidatorGroupVotes, + Runner.Celo.ElectionRewards, + Runner.Celo.EpochRewards + ] + } @impl Stage def runners do - case Application.get_env(:explorer, :chain_type) do - :optimism -> - @default_runners ++ @optimism_runners - - :polygon_edge -> - @default_runners ++ @polygon_edge_runners - - :polygon_zkevm -> - @default_runners ++ @polygon_zkevm_runners + chain_type = Application.get_env(:explorer, :chain_type) + chain_type_runners = Map.get(@extra_runners_by_chain_type, chain_type, []) - :shibarium -> - @default_runners ++ @shibarium_runners - - :ethereum -> - @default_runners ++ @ethereum_runners - - :zksync -> - @default_runners ++ @zksync_runners - - :arbitrum -> - @default_runners ++ @arbitrum_runners - - _ -> - @default_runners - end + @default_runners ++ chain_type_runners end @impl Stage def all_runners do - @default_runners ++ - @ethereum_runners ++ - @optimism_runners ++ - @polygon_edge_runners ++ @polygon_zkevm_runners ++ @shibarium_runners ++ @zksync_runners ++ @arbitrum_runners + all_extra_runners = + @extra_runners_by_chain_type + |> Map.values() + |> Enum.concat() + + @default_runners ++ all_extra_runners end @impl Stage diff --git a/apps/explorer/lib/explorer/chain/log.ex b/apps/explorer/lib/explorer/chain/log.ex index 7147f2098693..7ef35888444b 100644 --- a/apps/explorer/lib/explorer/chain/log.ex +++ b/apps/explorer/lib/explorer/chain/log.ex @@ -1,18 +1,111 @@ +defmodule Explorer.Chain.Log.Schema do + @moduledoc false + + alias Explorer.Chain.{ + Address, + Block, + Data, + Hash, + Transaction + } + + # In certain situations, like on Polygon, multiple logs may share the same + # index within a single block due to a RPC node bug. To prevent system crashes + # due to not unique primary keys, we've included `transaction_hash` in the + # primary key. + # + # However, on Celo, logs may exist where `transaction_hash` equals block_hash. + # In these instances, we set `transaction_hash` to `nil`. This action, though, + # violates the primary key constraint. To resolve this issue, we've excluded + # `transaction_hash` from the composite primary key when dealing with `:celo` + # chain type. + @transaction_field (case Application.compile_env(:explorer, :chain_type) do + :celo -> + quote do + [ + belongs_to(:transaction, Transaction, + foreign_key: :transaction_hash, + references: :hash, + type: Hash.Full + ) + ] + end + + _ -> + quote do + [ + belongs_to(:transaction, Transaction, + foreign_key: :transaction_hash, + primary_key: true, + references: :hash, + type: Hash.Full, + null: false + ) + ] + end + end) + + defmacro generate do + quote do + @primary_key false + typed_schema "logs" do + field(:data, Data, null: false) + field(:first_topic, Hash.Full) + field(:second_topic, Hash.Full) + field(:third_topic, Hash.Full) + field(:fourth_topic, Hash.Full) + field(:index, :integer, primary_key: true, null: false) + field(:block_number, :integer) + + timestamps() + + belongs_to(:address, Address, foreign_key: :address_hash, references: :hash, type: Hash.Address, null: false) + + belongs_to(:block, Block, + foreign_key: :block_hash, + primary_key: true, + references: :hash, + type: Hash.Full, + null: false + ) + + unquote_splicing(@transaction_field) + end + end + end +end + defmodule Explorer.Chain.Log do @moduledoc "Captures a Web3 log entry generated by a transaction" use Explorer.Schema + require Explorer.Chain.Log.Schema require Logger alias ABI.{Event, FunctionSelector} alias Explorer.{Chain, Repo} - alias Explorer.Chain.{Address, Block, ContractMethod, Data, Hash, Log, TokenTransfer, Transaction} + alias Explorer.Chain.{ContractMethod, Hash, Log, TokenTransfer, Transaction} alias Explorer.Chain.SmartContract.Proxy alias Explorer.SmartContract.SigProviderInterface - @required_attrs ~w(address_hash data block_hash index transaction_hash)a + @required_attrs ~w(address_hash data block_hash index)a + |> (&(case Application.compile_env(:explorer, :chain_type) do + :celo -> + &1 + + _ -> + [:transaction_hash | &1] + end)).() + @optional_attrs ~w(first_topic second_topic third_topic fourth_topic block_number)a + |> (&(case Application.compile_env(:explorer, :chain_type) do + :celo -> + [:transaction_hash | &1] + + _ -> + &1 + end)).() @typedoc """ * `address` - address of contract that generate the event @@ -28,36 +121,7 @@ defmodule Explorer.Chain.Log do * `transaction_hash` - foreign key for `transaction`. * `index` - index of the log entry within the block """ - @primary_key false - typed_schema "logs" do - field(:data, Data, null: false) - field(:first_topic, Hash.Full) - field(:second_topic, Hash.Full) - field(:third_topic, Hash.Full) - field(:fourth_topic, Hash.Full) - field(:index, :integer, primary_key: true, null: false) - field(:block_number, :integer) - - timestamps() - - belongs_to(:address, Address, foreign_key: :address_hash, references: :hash, type: Hash.Address, null: false) - - belongs_to(:transaction, Transaction, - foreign_key: :transaction_hash, - primary_key: true, - references: :hash, - type: Hash.Full, - null: false - ) - - belongs_to(:block, Block, - foreign_key: :block_hash, - primary_key: true, - references: :hash, - type: Hash.Full, - null: false - ) - end + Explorer.Chain.Log.Schema.generate() @doc """ `address_hash` and `transaction_hash` are converted to `t:Explorer.Chain.Hash.t/0`. diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index 71a2a0064fde..121b3c686377 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -37,15 +37,24 @@ defmodule Explorer.Chain.SmartContract do @typep api? :: {:api?, true | false} @burn_address_hash_string "0x0000000000000000000000000000000000000000" + @dead_address_hash_string "0x000000000000000000000000000000000000dEaD" @doc """ Returns burn address hash """ - @spec burn_address_hash_string() :: String.t() + @spec burn_address_hash_string() :: EthereumJSONRPC.address() def burn_address_hash_string do @burn_address_hash_string end + @doc """ + Returns dead address hash + """ + @spec dead_address_hash_string() :: EthereumJSONRPC.address() + def dead_address_hash_string do + @dead_address_hash_string + end + @default_sorting [desc: :id] @typedoc """ diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex index 4edd81c6be23..35c874c6ee51 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex @@ -42,7 +42,7 @@ defmodule Explorer.Chain.SmartContract.Proxy do @get_implementation_signature "aaf10f42" # bb82aa5e = keccak256(comptrollerImplementation()) Compound protocol proxy pattern @comptroller_implementation_signature "bb82aa5e" - # aaf10f42 = keccak256(getAddress(bytes32)) + # 21f8a721 = keccak256(getAddress(bytes32)) @get_address_signature "21f8a721" @typep options :: [{:api?, true | false}, {:proxy_without_abi?, true | false}] diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index 2201b6230538..bafd52e39cbc 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -1,3 +1,112 @@ +defmodule Explorer.Chain.TokenTransfer.Schema do + @moduledoc """ + Models token transfers. + + Changes in the schema should be reflected in the bulk import module: + - Explorer.Chain.Import.Runner.TokenTransfers + """ + + alias Explorer.Chain.{ + Address, + Block, + Hash, + Transaction + } + + alias Explorer.Chain.Token.Instance + + # Remove `transaction_hash` from primary key for `:celo` chain type. See + # `Explorer.Chain.Log.Schema` for more details. + @transaction_field (case Application.compile_env(:explorer, :chain_type) do + :celo -> + quote do + [ + belongs_to(:transaction, Transaction, + foreign_key: :transaction_hash, + references: :hash, + type: Hash.Full + ) + ] + end + + _ -> + quote do + [ + belongs_to(:transaction, Transaction, + foreign_key: :transaction_hash, + primary_key: true, + references: :hash, + type: Hash.Full, + null: false + ) + ] + end + end) + + defmacro generate do + quote do + @primary_key false + typed_schema "token_transfers" do + field(:amount, :decimal) + field(:block_number, :integer) :: Block.block_number() + field(:log_index, :integer, primary_key: true, null: false) + field(:amounts, {:array, :decimal}) + field(:token_ids, {:array, :decimal}) + field(:token_id, :decimal, virtual: true) + field(:index_in_batch, :integer, virtual: true) + field(:reverse_index_in_batch, :integer, virtual: true) + field(:token_decimals, :decimal, virtual: true) + field(:token_type, :string) + field(:block_consensus, :boolean) + + belongs_to(:from_address, Address, + foreign_key: :from_address_hash, + references: :hash, + type: Hash.Address, + null: false + ) + + belongs_to(:to_address, Address, + foreign_key: :to_address_hash, + references: :hash, + type: Hash.Address, + null: false + ) + + belongs_to( + :token_contract_address, + Address, + foreign_key: :token_contract_address_hash, + references: :hash, + type: Hash.Address, + null: false + ) + + belongs_to(:block, Block, + foreign_key: :block_hash, + primary_key: true, + references: :hash, + type: Hash.Full, + null: false + ) + + has_many( + :instances, + Instance, + foreign_key: :token_contract_address_hash, + references: :token_contract_address_hash + ) + + has_one(:token, through: [:token_contract_address, :token]) + + timestamps() + + unquote_splicing(@transaction_field) + end + end + end +end + defmodule Explorer.Chain.TokenTransfer do @moduledoc """ Represents a token transfer between addresses for a given token. @@ -24,11 +133,12 @@ defmodule Explorer.Chain.TokenTransfer do use Explorer.Schema + require Explorer.Chain.TokenTransfer.Schema + import Ecto.Changeset alias Explorer.Chain - alias Explorer.Chain.{Address, Block, DenormalizationHelper, Hash, Log, TokenTransfer, Transaction} - alias Explorer.Chain.Token.Instance + alias Explorer.Chain.{DenormalizationHelper, Hash, Log, TokenTransfer} alias Explorer.{PagingOptions, Repo} @default_paging_options %PagingOptions{page_size: 50} @@ -66,68 +176,24 @@ defmodule Explorer.Chain.TokenTransfer do * `:reverse_index_in_batch` - Reverse index of the token transfer in the ERC-1155 batch transfer, last element index is 1 * `:block_consensus` - Consensus of the block that the transfer took place """ - @primary_key false - typed_schema "token_transfers" do - field(:amount, :decimal) - field(:block_number, :integer) :: Block.block_number() - field(:log_index, :integer, primary_key: true, null: false) - field(:amounts, {:array, :decimal}) - field(:token_ids, {:array, :decimal}) - field(:token_id, :decimal, virtual: true) - field(:index_in_batch, :integer, virtual: true) - field(:reverse_index_in_batch, :integer, virtual: true) - field(:token_decimals, :decimal, virtual: true) - field(:token_type, :string) - field(:block_consensus, :boolean) - - belongs_to(:from_address, Address, - foreign_key: :from_address_hash, - references: :hash, - type: Hash.Address, - null: false - ) - - belongs_to(:to_address, Address, foreign_key: :to_address_hash, references: :hash, type: Hash.Address, null: false) - - belongs_to( - :token_contract_address, - Address, - foreign_key: :token_contract_address_hash, - references: :hash, - type: Hash.Address, - null: false - ) + Explorer.Chain.TokenTransfer.Schema.generate() - belongs_to(:transaction, Transaction, - foreign_key: :transaction_hash, - primary_key: true, - references: :hash, - type: Hash.Full, - null: false - ) - - belongs_to(:block, Block, - foreign_key: :block_hash, - primary_key: true, - references: :hash, - type: Hash.Full, - null: false - ) + @required_attrs ~w(block_number log_index from_address_hash to_address_hash token_contract_address_hash block_hash token_type)a + |> (&(case Application.compile_env(:explorer, :chain_type) do + :celo -> + &1 - has_many( - :instances, - Instance, - foreign_key: :token_contract_address_hash, - references: :token_contract_address_hash - ) - - has_one(:token, through: [:token_contract_address, :token]) - - timestamps() - end - - @required_attrs ~w(block_number log_index from_address_hash to_address_hash token_contract_address_hash transaction_hash block_hash token_type)a + _ -> + [:transaction_hash | &1] + end)).() @optional_attrs ~w(amount amounts token_ids block_consensus)a + |> (&(case Application.compile_env(:explorer, :chain_type) do + :celo -> + [:transaction_hash | &1] + + _ -> + &1 + end)).() @doc false def changeset(%TokenTransfer{} = struct, params \\ %{}) do diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index 3b24bc235424..2954241ab741 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -122,6 +122,28 @@ defmodule Explorer.Chain.Transaction.Schema do 2 ) + :celo -> + elem( + quote do + field(:gateway_fee, Wei) + + belongs_to(:gas_fee_recipient, Address, + foreign_key: :gas_fee_recipient_address_hash, + references: :hash, + type: Hash.Address + ) + + belongs_to(:gas_token_contract_address, Address, + foreign_key: :gas_token_contract_address_hash, + references: :hash, + type: Hash.Address + ) + + has_one(:gas_token, through: [:gas_token_contract_address, :token]) + end, + 2 + ) + :arbitrum -> elem( quote do @@ -297,6 +319,9 @@ defmodule Explorer.Chain.Transaction do :arbitrum -> ~w(gas_used_for_l1)a + :celo -> + ~w(gateway_fee gas_fee_recipient_address_hash gas_token_contract_address_hash)a + _ -> ~w()a end) diff --git a/apps/explorer/lib/explorer/helper.ex b/apps/explorer/lib/explorer/helper.ex index 5f33572cfa89..e4d2bc6e4689 100644 --- a/apps/explorer/lib/explorer/helper.ex +++ b/apps/explorer/lib/explorer/helper.ex @@ -33,6 +33,36 @@ defmodule Explorer.Helper do |> TypeDecoder.decode_raw(types) end + @doc """ + Takes an Ethereum hash and converts it to a standard 20-byte address by + truncating the leading zeroes. If the input is `nil`, it returns the burn + address. + + ## Parameters + - `address_hash` (`EthereumJSONRPC.hash()` | `nil`): The full address hash to + be truncated, or `nil`. + + ## Returns + - `EthereumJSONRPC.address()`: The truncated address or the burn address if + the input is `nil`. + + ## Examples + + iex> truncate_address_hash("0x000000000000000000000000abcdef1234567890abcdef1234567890abcdef") + "0xabcdef1234567890abcdef1234567890abcdef" + + iex> truncate_address_hash(nil) + "0x0000000000000000000000000000000000000000" + """ + @spec truncate_address_hash(EthereumJSONRPC.hash() | nil) :: EthereumJSONRPC.address() + def truncate_address_hash(address_hash) + + def truncate_address_hash(nil), do: burn_address_hash_string() + + def truncate_address_hash("0x000000000000000000000000" <> truncated_hash) do + "0x#{truncated_hash}" + end + def parse_integer(integer_string) when is_binary(integer_string) do case Integer.parse(integer_string) do {integer, ""} -> integer @@ -182,10 +212,4 @@ defmodule Explorer.Helper do true -> :eq end end - - def truncate_address_hash(nil), do: burn_address_hash_string() - - def truncate_address_hash("0x000000000000000000000000" <> truncated_hash) do - "0x#{truncated_hash}" - end end diff --git a/apps/explorer/lib/explorer/paging_options.ex b/apps/explorer/lib/explorer/paging_options.ex index 303bd53c37f5..081d160c3d15 100644 --- a/apps/explorer/lib/explorer/paging_options.ex +++ b/apps/explorer/lib/explorer/paging_options.ex @@ -31,4 +31,14 @@ defmodule Explorer.PagingOptions do asc_order: false, batch_key: nil ] + + @page_size 50 + + def page_size do + @page_size + end + + def default_paging_options do + %__MODULE__{page_size: @page_size + 1} + end end diff --git a/apps/explorer/lib/explorer/repo.ex b/apps/explorer/lib/explorer/repo.ex index 8105224b4f92..a0ae857fa4bf 100644 --- a/apps/explorer/lib/explorer/repo.ex +++ b/apps/explorer/lib/explorer/repo.ex @@ -177,6 +177,16 @@ defmodule Explorer.Repo do end end + defmodule Celo do + use Ecto.Repo, + otp_app: :explorer, + adapter: Ecto.Adapters.Postgres + + def init(_, opts) do + ConfigHelper.init_repo_module(__MODULE__, opts) + end + end + defmodule RSK do use Ecto.Repo, otp_app: :explorer, diff --git a/apps/explorer/lib/fetch_celo_core_contracts.ex b/apps/explorer/lib/fetch_celo_core_contracts.ex new file mode 100644 index 000000000000..a1a12884ab15 --- /dev/null +++ b/apps/explorer/lib/fetch_celo_core_contracts.ex @@ -0,0 +1,236 @@ +defmodule Mix.Tasks.FetchCeloCoreContracts do + @moduledoc """ + Fetch the addresses of celo core contracts: `mix help celo-contracts` + """ + @shortdoc "Fetches the addresses of celo core contracts" + + use Mix.Task + + import Explorer.Helper, + only: [ + decode_data: 2, + truncate_address_hash: 1 + ] + + alias Mix.Task + + alias EthereumJSONRPC.Logs + alias Explorer.Chain.Cache.CeloCoreContracts + alias Indexer.Helper, as: IndexerHelper + + @registry_proxy_contract_address "0x000000000000000000000000000000000000ce10" + @registry_updated_event_signature "0x4166d073a7a5e704ce0db7113320f88da2457f872d46dc020c805c562c1582a0" + @carbon_offsetting_fund_set_event_signature "0xe296227209b47bb8f4a76768ebd564dcde1c44be325a5d262f27c1fd4fd4538b" + @fee_beneficiary_set_event_signature "0xf7015098f8d6fa48f0560725ffd5f81bf687ee5ac45153b590bdcb04648bbdd3" + @burn_fraction_set_event_signature "0x41c679f4bcdc2c95f79a3647e2237162d9763d86685ef6c667781230c8ba9157" + + @chunk_size 200_000 + @max_request_retries 3 + + def run(_) do + Task.run("app.start") + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + atom_to_contract_name = CeloCoreContracts.atom_to_contract_name() + atom_to_contract_event_names = CeloCoreContracts.atom_to_contract_event_names() + contract_names = atom_to_contract_name |> Map.values() + {:ok, latest_block_number} = EthereumJSONRPC.fetch_block_number_by_tag("latest", json_rpc_named_arguments) + + core_contract_addresses = + 0..latest_block_number + |> fetch_logs_by_chunks( + fn chunk_start, chunk_end -> + [ + Logs.request( + 0, + %{ + from_block: chunk_start, + to_block: chunk_end, + address: @registry_proxy_contract_address, + topics: [@registry_updated_event_signature] + } + ) + ] + end, + json_rpc_named_arguments + ) + |> Enum.reduce(%{}, fn log, acc -> + [contract_name] = decode_data(log.data, [:string]) + new_contract_address = truncate_address_hash(log.third_topic) + + entry = %{ + address: new_contract_address, + updated_at_block_number: log.block_number + } + + if contract_name in contract_names do + acc + |> Map.update( + contract_name, + [entry], + &[entry | &1] + ) + else + acc + end + end) + |> Map.new(fn {contract_name, entries} -> + {contract_name, Enum.reverse(entries)} + end) + + epoch_rewards_events = + [@carbon_offsetting_fund_set_event_signature] + |> fetch_events_for_contract( + :epoch_rewards, + core_contract_addresses, + latest_block_number, + json_rpc_named_arguments + ) + |> Map.new(fn {address, logs} -> + entries = + logs + |> Enum.map( + &%{ + address: truncate_address_hash(&1.second_topic), + updated_at_block_number: &1.block_number + } + ) + + event_name = atom_to_contract_event_names[:epoch_rewards][:carbon_offsetting_fund_set] + {address, %{event_name => entries}} + end) + + fee_handler_events = + [ + @fee_beneficiary_set_event_signature, + @burn_fraction_set_event_signature + ] + |> fetch_events_for_contract( + :fee_handler, + core_contract_addresses, + latest_block_number, + json_rpc_named_arguments + ) + |> Map.new(fn {address, logs} -> + topic_to_logs = logs |> Enum.group_by(& &1.first_topic) + + fee_beneficiary_set_event_name = atom_to_contract_event_names[:fee_handler][:fee_beneficiary_set] + burn_fraction_set_event_name = atom_to_contract_event_names[:fee_handler][:burn_fraction_set] + + { + address, + %{ + fee_beneficiary_set_event_name => + topic_to_logs + |> Map.get(@fee_beneficiary_set_event_signature, []) + |> Enum.map( + &%{ + address: truncate_address_hash(&1.data), + updated_at_block_number: &1.block_number + } + ), + burn_fraction_set_event_name => + topic_to_logs + |> Map.get(@burn_fraction_set_event_signature, []) + |> Enum.map(fn log -> + [fraction] = decode_data(log.data, [{:int, 256}]) + + %{ + value: fraction, + updated_at_block_number: log.block_number + } + end) + } + } + end) + + core_contracts_json = + %{ + "addresses" => core_contract_addresses, + "events" => %{ + atom_to_contract_name[:epoch_rewards] => epoch_rewards_events, + atom_to_contract_name[:fee_handler] => fee_handler_events + } + } + |> Jason.encode!() + + IO.puts("CELO_CORE_CONTRACTS=#{core_contracts_json}") + end + + defp fetch_logs_by_chunks(from_block..to_block, requests_func, json_rpc_named_arguments) do + from_block..to_block + |> IndexerHelper.range_chunk_every(@chunk_size) + |> Enum.reduce([], fn chunk_start..chunk_end, acc -> + IndexerHelper.log_blocks_chunk_handling(chunk_start, chunk_end, 0, to_block, nil, :L1) + + requests = requests_func.(chunk_start, chunk_end) + + {:ok, responses} = + IndexerHelper.repeated_batch_rpc_call( + requests, + json_rpc_named_arguments, + fn message -> "Could not fetch logs: #{message}" end, + @max_request_retries + ) + + {:ok, logs} = Logs.from_responses(responses) + + acc ++ logs + end) + end + + defp fetch_events_for_contract( + event_signatures, + contract_atom, + core_contract_addresses, + latest_block_number, + json_rpc_named_arguments + ) do + contract_name = + CeloCoreContracts.atom_to_contract_name() + |> Map.get(contract_atom) + + core_contract_addresses + |> Map.get(contract_name, []) + |> Enum.chunk_every(2, 1) + |> Enum.map(fn + [entry, %{updated_at_block_number: to_block}] -> + {entry, to_block} + + [entry] -> + {entry, latest_block_number} + end) + |> Enum.map(fn {%{address: address}, to_block} -> + logs = + fetch_events_for_address( + 0..to_block, + event_signatures, + address, + json_rpc_named_arguments + ) + + {address, logs} + end) + end + + defp fetch_events_for_address(chunk_range, event_signatures, address, json_rpc_named_arguments) do + fetch_logs_by_chunks( + chunk_range, + fn chunk_start, chunk_end -> + event_signatures + |> Enum.with_index() + |> Enum.map(fn {signature, index} -> + Logs.request( + index, + %{ + from_block: chunk_start, + to_block: chunk_end, + address: address, + topics: [signature] + } + ) + end) + end, + json_rpc_named_arguments + ) + end +end diff --git a/apps/explorer/priv/celo/migrations/20240323152023_add_custom_fields.exs b/apps/explorer/priv/celo/migrations/20240323152023_add_custom_fields.exs new file mode 100644 index 000000000000..8de00c05e774 --- /dev/null +++ b/apps/explorer/priv/celo/migrations/20240323152023_add_custom_fields.exs @@ -0,0 +1,11 @@ +defmodule Explorer.Repo.Celo.Migrations.AddCustomFields do + use Ecto.Migration + + def change do + alter table(:transactions) do + add(:gateway_fee, :numeric, precision: 100, null: true) + add(:gas_token_contract_address_hash, :bytea, null: true) + add(:gas_fee_recipient_address_hash, :bytea, null: true) + end + end +end diff --git a/apps/explorer/priv/celo/migrations/20240424121856_add_pending_epoch_block_operations.exs b/apps/explorer/priv/celo/migrations/20240424121856_add_pending_epoch_block_operations.exs new file mode 100644 index 000000000000..3b36446be154 --- /dev/null +++ b/apps/explorer/priv/celo/migrations/20240424121856_add_pending_epoch_block_operations.exs @@ -0,0 +1,14 @@ +defmodule Explorer.Repo.Celo.Migrations.AddPendingEpochBlockOperations do + use Ecto.Migration + + def change do + create table(:celo_pending_epoch_block_operations, primary_key: false) do + add(:block_hash, references(:blocks, column: :hash, type: :bytea, on_delete: :delete_all), + null: false, + primary_key: true + ) + + timestamps() + end + end +end diff --git a/apps/explorer/priv/celo/migrations/20240512143204_remove_transaction_hash_from_primary_key_in_logs.exs b/apps/explorer/priv/celo/migrations/20240512143204_remove_transaction_hash_from_primary_key_in_logs.exs new file mode 100644 index 000000000000..9429c33f0e91 --- /dev/null +++ b/apps/explorer/priv/celo/migrations/20240512143204_remove_transaction_hash_from_primary_key_in_logs.exs @@ -0,0 +1,26 @@ +defmodule Explorer.Repo.Celo.Migrations.RemoveTransactionHashFromPrimaryKeyInLogs do + use Ecto.Migration + + def change do + execute( + """ + ALTER TABLE logs + DROP CONSTRAINT logs_pkey, + ADD PRIMARY KEY (block_hash, index); + """, + """ + ALTER TABLE logs + DROP CONSTRAINT logs_pkey, + ADD PRIMARY KEY (transaction_hash, block_hash, index); + """ + ) + + execute( + "ALTER TABLE logs ALTER COLUMN transaction_hash DROP NOT NULL", + "ALTER TABLE logs ALTER COLUMN transaction_hash SET NOT NULL" + ) + + drop(unique_index(:logs, [:transaction_hash, :index])) + create_if_not_exists(index(:logs, [:transaction_hash, :index])) + end +end diff --git a/apps/explorer/priv/celo/migrations/20240513091316_remove_transaction_hash_from_primary_key_in_token_transfers.exs b/apps/explorer/priv/celo/migrations/20240513091316_remove_transaction_hash_from_primary_key_in_token_transfers.exs new file mode 100644 index 000000000000..31dd75fb6e85 --- /dev/null +++ b/apps/explorer/priv/celo/migrations/20240513091316_remove_transaction_hash_from_primary_key_in_token_transfers.exs @@ -0,0 +1,23 @@ +defmodule Explorer.Repo.Celo.Migrations.RemoveTransactionHashFromPrimaryKeyInTokenTransfers do + use Ecto.Migration + + def change do + execute( + """ + ALTER TABLE token_transfers + DROP CONSTRAINT token_transfers_pkey, + ADD PRIMARY KEY (block_hash, log_index); + """, + """ + ALTER TABLE token_transfers + DROP CONSTRAINT token_transfers_pkey, + ADD PRIMARY KEY (transaction_hash, block_hash, log_index); + """ + ) + + execute( + "ALTER TABLE token_transfers ALTER COLUMN transaction_hash DROP NOT NULL", + "ALTER TABLE token_transfers ALTER COLUMN transaction_hash SET NOT NULL" + ) + end +end diff --git a/apps/explorer/priv/celo/migrations/20240607185817_add_epoch_rewards.exs b/apps/explorer/priv/celo/migrations/20240607185817_add_epoch_rewards.exs new file mode 100644 index 000000000000..b44e2069a2bd --- /dev/null +++ b/apps/explorer/priv/celo/migrations/20240607185817_add_epoch_rewards.exs @@ -0,0 +1,62 @@ +defmodule Explorer.Repo.Celo.Migrations.AddEpochRewards do + use Ecto.Migration + + def change do + create table(:celo_epoch_rewards, primary_key: false) do + add(:reserve_bolster_transfer_log_index, :integer) + add(:community_transfer_log_index, :integer) + add(:carbon_offsetting_transfer_log_index, :integer) + + add( + :block_hash, + references(:blocks, column: :hash, type: :bytea, on_delete: :delete_all), + null: false, + primary_key: true + ) + + timestamps() + end + + execute( + """ + ALTER TABLE celo_epoch_rewards + ADD CONSTRAINT celo_epoch_rewards_reserve_bolster_transfer_log_index_fkey + FOREIGN KEY (reserve_bolster_transfer_log_index, block_hash) + REFERENCES token_transfers (log_index, block_hash) + ON DELETE CASCADE + """, + """ + ALTER TABLE celo_epoch_rewards + DROP CONSTRAINT celo_epoch_rewards_reserve_bolster_transfer_log_index_fkey + """ + ) + + execute( + """ + ALTER TABLE celo_epoch_rewards + ADD CONSTRAINT celo_epoch_rewards_community_transfer_log_index_fkey + FOREIGN KEY (community_transfer_log_index, block_hash) + REFERENCES token_transfers (log_index, block_hash) + ON DELETE CASCADE + """, + """ + ALTER TABLE celo_epoch_rewards + DROP CONSTRAINT celo_epoch_rewards_community_transfer_log_index_fkey + """ + ) + + execute( + """ + ALTER TABLE celo_epoch_rewards + ADD CONSTRAINT celo_epoch_rewards_carbon_offsetting_transfer_log_index_fkey + FOREIGN KEY (carbon_offsetting_transfer_log_index, block_hash) + REFERENCES token_transfers (log_index, block_hash) + ON DELETE CASCADE + """, + """ + ALTER TABLE celo_epoch_rewards + DROP CONSTRAINT celo_epoch_rewards_carbon_offsetting_transfer_log_index_fkey + """ + ) + end +end diff --git a/apps/explorer/priv/celo/migrations/20240612135216_add_validator_group_votes.exs b/apps/explorer/priv/celo/migrations/20240612135216_add_validator_group_votes.exs new file mode 100644 index 000000000000..3d6f1f4eeb18 --- /dev/null +++ b/apps/explorer/priv/celo/migrations/20240612135216_add_validator_group_votes.exs @@ -0,0 +1,36 @@ +defmodule Explorer.Repo.Celo.Migrations.AddValidatorGroupVotes do + use Ecto.Migration + + def change do + execute( + "CREATE TYPE celo_validator_group_vote_type AS ENUM ('activated', 'revoked')", + "DROP TYPE celo_validator_group_vote_type" + ) + + create table(:celo_validator_group_votes, primary_key: false) do + add( + :account_address_hash, + references(:addresses, column: :hash, on_delete: :delete_all, type: :bytea), + null: false + ) + + add( + :group_address_hash, + references(:addresses, column: :hash, on_delete: :delete_all, type: :bytea), + null: false + ) + + add(:value, :numeric, precision: 100, null: false) + add(:units, :numeric, precision: 100, null: false) + + add(:type, :celo_validator_group_vote_type, null: false) + + add(:transaction_hash, :bytea, null: false, primary_key: true) + + add(:block_number, :integer, null: false) + add(:block_hash, :bytea, null: false) + + timestamps() + end + end +end diff --git a/apps/explorer/priv/celo/migrations/20240614125614_add_election_rewards.exs b/apps/explorer/priv/celo/migrations/20240614125614_add_election_rewards.exs new file mode 100644 index 000000000000..30dc3a67de0a --- /dev/null +++ b/apps/explorer/priv/celo/migrations/20240614125614_add_election_rewards.exs @@ -0,0 +1,38 @@ +defmodule Explorer.Repo.Celo.Migrations.AddElectionRewards do + use Ecto.Migration + + def change do + execute( + "CREATE TYPE celo_election_reward_type AS ENUM ('voter', 'validator', 'group', 'delegated_payment')", + "DROP TYPE celo_election_reward_type" + ) + + create table(:celo_election_rewards, primary_key: false) do + add(:amount, :numeric, precision: 100, null: false) + add(:type, :celo_election_reward_type, null: false, primary_key: true) + + add( + :block_hash, + references(:blocks, column: :hash, type: :bytea, on_delete: :delete_all), + null: false, + primary_key: true + ) + + add( + :account_address_hash, + references(:addresses, column: :hash, on_delete: :delete_all, type: :bytea), + null: false, + primary_key: true + ) + + add( + :associated_account_address_hash, + references(:addresses, column: :hash, on_delete: :delete_all, type: :bytea), + null: false, + primary_key: true + ) + + timestamps() + end + end +end diff --git a/apps/explorer/priv/celo/migrations/20240715110334_remove_unused_fields_from_validator_group_votes.exs b/apps/explorer/priv/celo/migrations/20240715110334_remove_unused_fields_from_validator_group_votes.exs new file mode 100644 index 000000000000..8d678d5c8341 --- /dev/null +++ b/apps/explorer/priv/celo/migrations/20240715110334_remove_unused_fields_from_validator_group_votes.exs @@ -0,0 +1,10 @@ +defmodule Explorer.Repo.Celo.Migrations.RemoveUnusedFieldsFromValidatorGroupVotes do + use Ecto.Migration + + def change do + alter table(:celo_validator_group_votes) do + remove(:value, :numeric, precision: 100, null: false, default: 0) + remove(:units, :numeric, precision: 100, null: false, default: 0) + end + end +end diff --git a/apps/explorer/test/explorer/chain/cache/celo_core_contracts_test.exs b/apps/explorer/test/explorer/chain/cache/celo_core_contracts_test.exs new file mode 100644 index 000000000000..6a3284e46218 --- /dev/null +++ b/apps/explorer/test/explorer/chain/cache/celo_core_contracts_test.exs @@ -0,0 +1,84 @@ +defmodule Explorer.Chain.Cache.CeloCoreContractsTest do + use ExUnit.Case, async: true + + alias Explorer.Chain.Cache.CeloCoreContracts + + describe "get_address/2" do + test "returns address according to block number" do + first_address = "0xb10ee11244526b94879e1956745ba2e35ae2ba20" + + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, + contracts: %{ + "addresses" => %{ + "EpochRewards" => [ + %{ + "address" => first_address, + "updated_at_block_number" => 100 + } + ] + } + } + ) + + on_exit(fn -> + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, contracts: %{}) + end) + + assert {:error, :address_does_not_exist} = CeloCoreContracts.get_address(:epoch_rewards, 99) + assert {:ok, ^first_address} = CeloCoreContracts.get_address(:epoch_rewards, 100) + assert {:ok, ^first_address} = CeloCoreContracts.get_address(:epoch_rewards, 10_000) + end + end + + describe "get_event/3" do + test "returns event according to block number" do + first_address = "0x0000000000000000000000000000000000000000" + second_address = "0x22579ca45ee22e2e16ddf72d955d6cf4c767b0ef" + + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, + contracts: %{ + "addresses" => %{ + "EpochRewards" => [ + %{ + "address" => "0xb10ee11244526b94879e1956745ba2e35ae2ba20", + "updated_at_block_number" => 100 + } + ] + }, + "events" => %{ + "EpochRewards" => %{ + "0xb10ee11244526b94879e1956745ba2e35ae2ba20" => %{ + "CarbonOffsettingFundSet" => [ + %{ + "address" => first_address, + "updated_at_block_number" => 598 + }, + %{ + "address" => second_address, + "updated_at_block_number" => 15_049_265 + } + ] + } + } + } + } + ) + + on_exit(fn -> + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, contracts: %{}) + end) + + assert {:error, :address_does_not_exist} = + CeloCoreContracts.get_event(:epoch_rewards, :carbon_offsetting_fund_set, 99) + + assert {:ok, %{"address" => ^first_address, "updated_at_block_number" => 598}} = + CeloCoreContracts.get_event(:epoch_rewards, :carbon_offsetting_fund_set, 598) + + assert {:ok, %{"address" => ^first_address, "updated_at_block_number" => 598}} = + CeloCoreContracts.get_event(:epoch_rewards, :carbon_offsetting_fund_set, 599) + + assert {:ok, %{"address" => ^second_address, "updated_at_block_number" => 15_049_265}} = + CeloCoreContracts.get_event(:epoch_rewards, :carbon_offsetting_fund_set, 16_000_000) + end + 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 9c5107f98b91..2ba66c34640c 100644 --- a/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs +++ b/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs @@ -8,9 +8,12 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do alias Ecto.Multi alias Explorer.Chain.Import.Runner.{Blocks, Transactions} alias Explorer.Chain.{Address, Block, Transaction, PendingBlockOperation} + alias Explorer.Chain.Celo.PendingEpochBlockOperation alias Explorer.{Chain, Repo} alias Explorer.Utility.MissingBlockRange + alias Explorer.Chain.Celo.Helper, as: CeloHelper + describe "run/1" do setup do miner = insert(:address) @@ -411,6 +414,60 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do assert %{block_number: ^number, block_hash: ^hash} = Repo.one(PendingBlockOperation) end + if Application.compile_env(:explorer, :chain_type) == :celo do + test "inserts pending_epoch_block_operations only for epoch blocks", + %{consensus_block: %{miner_hash: miner_hash}, options: options} do + epoch_block_number = CeloHelper.blocks_per_epoch() + + %{hash: hash} = + epoch_block_params = + params_for( + :block, + miner_hash: miner_hash, + consensus: true, + number: epoch_block_number + ) + + non_epoch_block_params = + params_for( + :block, + miner_hash: miner_hash, + consensus: true, + number: epoch_block_number + 1 + ) + + insert_block(epoch_block_params, options) + insert_block(non_epoch_block_params, options) + + assert %{block_hash: ^hash} = Repo.one(PendingEpochBlockOperation) + end + + test "inserts pending_epoch_block_operations only for consensus epoch blocks", + %{consensus_block: %{miner_hash: miner_hash}, options: options} do + %{hash: hash} = + first_epoch_block_params = + params_for( + :block, + miner_hash: miner_hash, + consensus: true, + number: CeloHelper.blocks_per_epoch() + ) + + second_epoch_block_params = + params_for( + :block, + miner_hash: miner_hash, + consensus: false, + number: CeloHelper.blocks_per_epoch() * 2 + ) + + insert_block(first_epoch_block_params, options) + insert_block(second_epoch_block_params, options) + + assert %{block_hash: ^hash} = Repo.one(PendingEpochBlockOperation) + end + 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 diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index 659b40f2529a..4c090b69549a 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -24,6 +24,7 @@ defmodule Explorer.Factory do alias Explorer.Chain.Beacon.{Blob, BlobTransaction} alias Explorer.Chain.Block.{EmissionReward, Range, Reward} alias Explorer.Chain.Stability.Validator, as: ValidatorStability + alias Explorer.Chain.Celo.PendingEpochBlockOperation, as: CeloPendingEpochBlockOperation alias Explorer.Chain.{ Address, @@ -1254,4 +1255,8 @@ defmodule Explorer.Factory do state: Enum.random(0..2) } end + + def celo_pending_epoch_block_operation_factory do + %CeloPendingEpochBlockOperation{} + end end diff --git a/apps/indexer/lib/indexer/block/catchup/fetcher.ex b/apps/indexer/lib/indexer/block/catchup/fetcher.ex index 67ab266f98a4..180ddf37ec49 100644 --- a/apps/indexer/lib/indexer/block/catchup/fetcher.ex +++ b/apps/indexer/lib/indexer/block/catchup/fetcher.ex @@ -11,13 +11,14 @@ defmodule Indexer.Block.Catchup.Fetcher do only: [ async_import_blobs: 2, async_import_block_rewards: 2, + async_import_celo_epoch_block_operations: 2, async_import_coin_balances: 2, async_import_created_contract_codes: 2, async_import_internal_transactions: 2, async_import_replaced_transactions: 2, - async_import_tokens: 2, async_import_token_balances: 2, async_import_token_instances: 1, + async_import_tokens: 2, async_import_uncles: 2, fetch_and_import_range: 2 ] @@ -139,6 +140,7 @@ defmodule Indexer.Block.Catchup.Fetcher do async_import_replaced_transactions(imported, realtime?) async_import_token_instances(imported) async_import_blobs(imported, realtime?) + async_import_celo_epoch_block_operations(imported, realtime?) end defp stream_fetch_and_import(state, ranges) do diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index ff0a34288148..03082037a28f 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -16,11 +16,15 @@ defmodule Indexer.Block.Fetcher do alias Explorer.Chain.Cache.Blocks, as: BlocksCache alias Explorer.Chain.Cache.{Accounts, BlockNumber, Transactions, Uncles} alias Indexer.Block.Fetcher.Receipts + alias Indexer.Fetcher.Celo.EpochBlockOperations, as: CeloEpochBlockOperations + alias Indexer.Fetcher.Celo.EpochLogs, as: CeloEpochLogs alias Indexer.Fetcher.CoinBalance.Catchup, as: CoinBalanceCatchup alias Indexer.Fetcher.CoinBalance.Realtime, as: CoinBalanceRealtime alias Indexer.Fetcher.PolygonZkevm.BridgeL1Tokens, as: PolygonZkevmBridgeL1Tokens alias Indexer.Fetcher.TokenInstance.Realtime, as: TokenInstanceRealtime + alias Indexer.{Prometheus, TokenBalances, Tracer} + alias Indexer.Fetcher.{ Beacon.Blob, BlockReward, @@ -32,8 +36,6 @@ defmodule Indexer.Block.Fetcher do UncleBlock } - alias Indexer.{Prometheus, TokenBalances, Tracer} - alias Indexer.Transform.{ AddressCoinBalances, Addresses, @@ -54,6 +56,9 @@ defmodule Indexer.Block.Fetcher do alias Indexer.Transform.Blocks, as: TransformBlocks alias Indexer.Transform.PolygonZkevm.Bridge, as: PolygonZkevmBridge + alias Indexer.Transform.Celo.TransactionGasTokens, as: CeloTransactionGasTokens + alias Indexer.Transform.Celo.TransactionTokenTransfers, as: CeloTransactionTokenTransfers + @type address_hash_to_fetched_balance_block_number :: %{String.t() => Block.block_number()} @type t :: %__MODULE__{} @@ -148,9 +153,16 @@ defmodule Indexer.Block.Fetcher do }}} <- {:blocks, fetched_blocks}, blocks = TransformBlocks.transform_blocks(blocks_params), {:receipts, {:ok, receipt_params}} <- {:receipts, Receipts.fetch(state, transactions_params_without_receipts)}, - %{logs: logs, receipts: receipts} = receipt_params, + %{logs: receipt_logs, receipts: receipts} = receipt_params, transactions_with_receipts = Receipts.put(transactions_params_without_receipts, receipts), + celo_epoch_logs = CeloEpochLogs.fetch(blocks, json_rpc_named_arguments), + logs = receipt_logs ++ celo_epoch_logs, %{token_transfers: token_transfers, tokens: tokens} = TokenTransfers.parse(logs), + %{token_transfers: celo_native_token_transfers, tokens: celo_tokens} = + CeloTransactionTokenTransfers.parse_transactions(transactions_with_receipts), + celo_gas_tokens = CeloTransactionGasTokens.parse(transactions_with_receipts), + token_transfers = token_transfers ++ celo_native_token_transfers, + tokens = Enum.uniq(tokens ++ celo_tokens), %{transaction_actions: transaction_actions} = TransactionActions.parse(logs), %{mint_transfers: mint_transfers} = MintTransfers.parse(logs), optimism_withdrawals = @@ -229,6 +241,7 @@ defmodule Indexer.Block.Fetcher do polygon_edge_deposit_executes: polygon_edge_deposit_executes, polygon_zkevm_bridge_operations: polygon_zkevm_bridge_operations, shibarium_bridge_operations: shibarium_bridge_operations, + celo_gas_tokens: celo_gas_tokens, arbitrum_messages: arbitrum_xlevel_messages }, {:ok, inserted} <- @@ -264,6 +277,7 @@ defmodule Indexer.Block.Fetcher do polygon_edge_deposit_executes: polygon_edge_deposit_executes, polygon_zkevm_bridge_operations: polygon_zkevm_bridge_operations, shibarium_bridge_operations: shibarium_bridge_operations, + celo_gas_tokens: celo_gas_tokens, arbitrum_messages: arbitrum_xlevel_messages }) do case Application.get_env(:explorer, :chain_type) do @@ -290,6 +304,18 @@ defmodule Indexer.Block.Fetcher do basic_import_options |> Map.put_new(:shibarium_bridge_operations, %{params: shibarium_bridge_operations}) + :celo -> + tokens = + basic_import_options + |> Map.get(:tokens, %{}) + |> Map.get(:params, []) + + basic_import_options + |> Map.put( + :tokens, + %{params: (tokens ++ celo_gas_tokens) |> Enum.uniq()} + ) + :arbitrum -> basic_import_options |> Map.put_new(:arbitrum_messages, %{params: arbitrum_xlevel_messages}) @@ -477,6 +503,14 @@ defmodule Indexer.Block.Fetcher do def async_import_polygon_zkevm_bridge_l1_tokens(_), do: :ok + def async_import_celo_epoch_block_operations(%{blocks: operations}, realtime?) do + operations + |> Enum.map(&%{block_number: &1.number, block_hash: &1.hash}) + |> CeloEpochBlockOperations.async_fetch(realtime?) + end + + def async_import_celo_epoch_block_operations(_, _), do: :ok + defp block_reward_errors_to_block_numbers(block_reward_errors) when is_list(block_reward_errors) do Enum.map(block_reward_errors, &block_reward_error_to_block_number/1) end @@ -685,7 +719,7 @@ defmodule Indexer.Block.Fetcher do Map.delete(address_params, :fetched_coin_balance_block_number)} end - defp token_transfers_merge_token(token_transfers, tokens) do + def token_transfers_merge_token(token_transfers, tokens) do Enum.map(token_transfers, fn token_transfer -> token = Enum.find(tokens, fn token -> diff --git a/apps/indexer/lib/indexer/block/realtime/fetcher.ex b/apps/indexer/lib/indexer/block/realtime/fetcher.ex index dc51dce76a0f..10d6fbafc2b8 100644 --- a/apps/indexer/lib/indexer/block/realtime/fetcher.ex +++ b/apps/indexer/lib/indexer/block/realtime/fetcher.ex @@ -13,17 +13,18 @@ defmodule Indexer.Block.Realtime.Fetcher do import Indexer.Block.Fetcher, only: [ - async_import_realtime_coin_balances: 1, async_import_blobs: 2, async_import_block_rewards: 2, + async_import_celo_epoch_block_operations: 2, async_import_created_contract_codes: 2, async_import_internal_transactions: 2, + async_import_polygon_zkevm_bridge_l1_tokens: 1, + async_import_realtime_coin_balances: 1, async_import_replaced_transactions: 2, - async_import_tokens: 2, async_import_token_balances: 2, async_import_token_instances: 1, + async_import_tokens: 2, async_import_uncles: 2, - async_import_polygon_zkevm_bridge_l1_tokens: 1, fetch_and_import_range: 2 ] @@ -465,5 +466,6 @@ defmodule Indexer.Block.Realtime.Fetcher do async_import_replaced_transactions(imported, realtime?) async_import_blobs(imported, realtime?) async_import_polygon_zkevm_bridge_l1_tokens(imported) + async_import_celo_epoch_block_operations(imported, realtime?) end end diff --git a/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations.ex b/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations.ex new file mode 100644 index 000000000000..e023ac2bb9c4 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations.ex @@ -0,0 +1,146 @@ +defmodule Indexer.Fetcher.Celo.EpochBlockOperations do + @moduledoc """ + Tracks epoch blocks awaiting processing by the epoch fetcher. + """ + + import Explorer.Chain.Celo.Helper, only: [epoch_block_number?: 1] + + alias Explorer.Chain + alias Explorer.Chain.Block + alias Explorer.Chain.Celo.PendingEpochBlockOperation + alias Indexer.Fetcher.Celo.EpochBlockOperations.Supervisor, as: EpochBlockOperationsSupervisor + alias Indexer.Transform.Addresses + alias Indexer.{BufferedTask, Tracer} + + alias Indexer.Fetcher.Celo.EpochBlockOperations.{ + DelegatedPayments, + Distributions, + ValidatorAndGroupPayments, + VoterPayments + } + + require Logger + + use Indexer.Fetcher, restart: :permanent + use Spandex.Decorators + + @behaviour BufferedTask + + @default_max_batch_size 1 + @default_max_concurrency 1 + + @doc false + def child_spec([init_options, gen_server_options]) do + {state, mergeable_init_options} = Keyword.pop(init_options, :json_rpc_named_arguments) + + unless state do + raise ArgumentError, + ":json_rpc_named_arguments must be provided to `#{__MODULE__}.child_spec " <> + "to allow for json_rpc calls when running." + end + + merged_init_opts = + defaults() + |> Keyword.merge(mergeable_init_options) + |> Keyword.put(:state, state) + + Supervisor.child_spec({BufferedTask, [{__MODULE__, merged_init_opts}, gen_server_options]}, id: __MODULE__) + end + + def defaults do + [ + poll: false, + flush_interval: :timer.seconds(3), + 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, + task_supervisor: Indexer.Fetcher.Celo.EpochBlockOperations.TaskSupervisor, + metadata: [fetcher: :celo_epoch_rewards] + ] + end + + @spec async_fetch( + [%{block_number: Block.block_number(), block_hash: Hash.Full}], + boolean(), + integer() + ) :: :ok + def async_fetch(entries, realtime?, timeout \\ 5000) when is_list(entries) do + if EpochBlockOperationsSupervisor.disabled?() do + :ok + else + filtered_entries = Enum.filter(entries, &epoch_block_number?(&1.block_number)) + BufferedTask.buffer(__MODULE__, filtered_entries, realtime?, timeout) + end + end + + @impl BufferedTask + def init(initial, reducer, _json_rpc_named_arguments) do + {:ok, final} = + PendingEpochBlockOperation.stream_epoch_blocks_with_unfetched_rewards( + initial, + reducer, + true + ) + + final + end + + @impl BufferedTask + @decorate trace( + name: "fetch", + resource: "Indexer.Fetcher.Celo.EpochBlockOperations.run/2", + service: :indexer, + tracer: Tracer + ) + def run(pending_operations, json_rpc_named_arguments) do + Enum.each(pending_operations, fn pending_operation -> + fetch(pending_operation, json_rpc_named_arguments) + end) + + :ok + end + + defp fetch(pending_operation, json_rpc_named_arguments) do + {:ok, distributions} = Distributions.fetch(pending_operation) + {:ok, validator_and_group_payments} = ValidatorAndGroupPayments.fetch(pending_operation) + + {:ok, voter_payments} = + VoterPayments.fetch( + pending_operation, + json_rpc_named_arguments + ) + + {:ok, delegated_payments} = + validator_and_group_payments + |> Enum.filter(&(&1.type == :validator)) + |> Enum.map(& &1.account_address_hash) + |> DelegatedPayments.fetch( + pending_operation, + json_rpc_named_arguments + ) + + election_rewards = + [ + validator_and_group_payments, + voter_payments, + delegated_payments + ] + |> Enum.concat() + |> Enum.filter(&(&1.amount > 0)) + + addresses_params = + Addresses.extract_addresses(%{ + celo_election_rewards: election_rewards + }) + + {:ok, imported} = + Chain.import(%{ + addresses: %{params: addresses_params}, + celo_election_rewards: %{params: election_rewards}, + celo_epoch_rewards: %{params: [distributions]} + }) + + Logger.info("Fetched epoch rewards for block number: #{pending_operation.block_number}") + + {:ok, imported} + end +end diff --git a/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/core_contract_version.ex b/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/core_contract_version.ex new file mode 100644 index 000000000000..d6c011de9b12 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/core_contract_version.ex @@ -0,0 +1,92 @@ +defmodule Indexer.Fetcher.Celo.EpochBlockOperations.CoreContractVersion do + @moduledoc """ + Fetches the version of the celo core contract. + """ + import Indexer.Fetcher.Celo.Helper, only: [abi_to_method_id: 1] + import Indexer.Helper, only: [read_contracts_with_retries: 5] + + @repeated_request_max_retries 3 + + @get_version_number_abi [ + %{ + "name" => "getVersionNumber", + "type" => "function", + "payable" => false, + "constant" => true, + "stateMutability" => "pure", + "inputs" => [], + "outputs" => [ + %{"type" => "uint256"}, + %{"type" => "uint256"}, + %{"type" => "uint256"}, + %{"type" => "uint256"} + ] + } + ] + + @get_version_number_method_id @get_version_number_abi |> abi_to_method_id() + + @doc """ + Fetches the version number of a Celo core contract at a given block. + + ## Parameters + - `contract_address` (`EthereumJSONRPC.address()`): The address of the + contract. + - `block_number` (`EthereumJSONRPC.block_number()`): The block number at + which to fetch the version. + - `json_rpc_named_arguments` (`EthereumJSONRPC.json_rpc_named_arguments()`): + The JSON RPC named arguments. + + ## Returns + - `{:ok, {integer(), integer(), integer(), integer()}}`: A tuple containing + the version number components if successful. + - `{:ok, {1, 1, 0, 0}}`: A default version number if the `getVersionNumber` + function does not exist for the core contract at the requested block. + - `{:error, [{any(), any()}]}`: An error tuple with the list of errors. + """ + @spec fetch( + EthereumJSONRPC.address(), + EthereumJSONRPC.block_number(), + EthereumJSONRPC.json_rpc_named_arguments() + ) :: + { + :error, + [{any(), any()}] + } + | { + :ok, + {integer(), integer(), integer(), integer()} + } + def fetch(contract_address, block_number, json_rpc_named_arguments) do + request = %{ + contract_address: contract_address, + method_id: @get_version_number_method_id, + args: [], + block_number: block_number + } + + [request] + |> read_contracts_with_retries( + @get_version_number_abi, + json_rpc_named_arguments, + @repeated_request_max_retries, + false + ) + |> elem(0) + |> case do + [ok: [storage, major, minor, patch]] -> + {:ok, {storage, major, minor, patch}} + + # Celo Core Contracts deployed to a live network without the + # `getVersionNumber()` function, such as the original set of core + # contracts, are to be considered version 1.1.0.0. + # + # https://docs.celo.org/community/release-process/smart-contracts#core-contracts + [error: "(-32000) execution reverted"] -> + {:ok, {1, 1, 0, 0}} + + errors -> + {:error, errors} + end + end +end diff --git a/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/delegated_payments.ex b/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/delegated_payments.ex new file mode 100644 index 000000000000..8569019a3b42 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/delegated_payments.ex @@ -0,0 +1,166 @@ +defmodule Indexer.Fetcher.Celo.EpochBlockOperations.DelegatedPayments do + @moduledoc """ + Fetches delegated validator payments for the epoch block. + """ + import Ecto.Query, only: [from: 2] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + import Indexer.Fetcher.Celo.Helper, only: [abi_to_method_id: 1] + import Indexer.Helper, only: [read_contracts_with_retries: 4] + + alias Explorer.Chain.Cache.CeloCoreContracts + alias Explorer.Chain.{Hash, TokenTransfer} + alias Explorer.Repo + alias Indexer.Fetcher.Celo.EpochBlockOperations.CoreContractVersion + + require Logger + + @mint_address_hash_string burn_address_hash_string() + + @repeated_request_max_retries 3 + + # The method `getPaymentDelegation` was introduced in the following. Thus, we + # set version hardcoded in `getVersionNumber` method. + # + # https://github.com/celo-org/celo-monorepo/blob/d7c8936dc529f46d56799365f8b3383a23cc220b/packages/protocol/contracts/common/Accounts.sol#L128-L130 + @get_payment_delegation_available_since_version {1, 1, 3, 0} + @get_payment_delegation_abi [ + %{ + "name" => "getPaymentDelegation", + "type" => "function", + "payable" => false, + "constant" => true, + "stateMutability" => "view", + "inputs" => [ + %{"name" => "account", "type" => "address"} + ], + "outputs" => [ + %{"type" => "address"}, + %{"type" => "uint256"} + ] + } + ] + @get_payment_delegation_method_id @get_payment_delegation_abi |> abi_to_method_id() + + @spec fetch( + [EthereumJSONRPC.address()], + %{ + :block_hash => EthereumJSONRPC.hash(), + :block_number => EthereumJSONRPC.block_number() + }, + EthereumJSONRPC.json_rpc_named_arguments() + ) :: + {:ok, list()} + | {:error, any()} + def fetch( + validator_addresses, + %{block_number: block_number, block_hash: block_hash} = _pending_operation, + json_rpc_named_arguments + ) do + with {:ok, accounts_contract_address} <- + CeloCoreContracts.get_address(:accounts, block_number), + {:ok, accounts_contract_version} <- + CoreContractVersion.fetch( + accounts_contract_address, + block_number, + json_rpc_named_arguments + ), + true <- accounts_contract_version >= @get_payment_delegation_available_since_version, + {:ok, usd_token_contract_address} <- + CeloCoreContracts.get_address(:usd_token, block_number), + {responses, []} <- + read_payment_delegations( + validator_addresses, + accounts_contract_address, + block_number, + json_rpc_named_arguments + ) do + query = + from( + tt in TokenTransfer.only_consensus_transfers_query(), + where: + tt.block_hash == ^block_hash and + tt.token_contract_address_hash == ^usd_token_contract_address and + tt.from_address_hash == ^@mint_address_hash_string and + is_nil(tt.transaction_hash), + select: {tt.to_address_hash, tt.amount} + ) + + beneficiary_address_to_amount = + query + |> Repo.all() + |> Map.new(fn {address, amount} -> + {Hash.to_string(address), amount} + end) + + rewards = + validator_addresses + |> Enum.zip(responses) + |> Enum.filter(&match?({_, {:ok, [_, fraction]}} when fraction > 0, &1)) + |> Enum.map(fn + {validator_address, {:ok, [beneficiary_address, _]}} -> + amount = Map.get(beneficiary_address_to_amount, beneficiary_address, 0) + + %{ + block_hash: block_hash, + account_address_hash: beneficiary_address, + amount: amount, + associated_account_address_hash: validator_address, + type: :delegated_payment + } + end) + + {:ok, rewards} + else + false -> + Logger.info(fn -> + [ + "Do not fetch payment delegations since `getPaymentDelegation` ", + "method is not available on block #{block_number}" + ] + end) + + {:ok, []} + + {_, ["(-32000) execution reverted"]} -> + # todo: we should start fetching payment delegations only after the + # first `PaymentDelegationSet` event is emitted. Unfortunately, relying + # on contract version is not enough since the method could not be + # present. + Logger.info(fn -> + [ + "Could not fetch payment delegations since `getPaymentDelegation` constantly returns error. ", + "Most likely, the method is not available on block #{block_number}. " + ] + end) + + {:ok, []} + + error -> + Logger.error("Could not fetch payment delegations: #{inspect(error)}") + + error + end + end + + defp read_payment_delegations( + validator_addresses, + accounts_contract_address, + block_number, + json_rpc_named_arguments + ) do + validator_addresses + |> Enum.map( + &%{ + contract_address: accounts_contract_address, + method_id: @get_payment_delegation_method_id, + args: [&1], + block_number: block_number + } + ) + |> read_contracts_with_retries( + @get_payment_delegation_abi, + json_rpc_named_arguments, + @repeated_request_max_retries + ) + end +end diff --git a/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/distributions.ex b/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/distributions.ex new file mode 100644 index 000000000000..e21ce3f535b4 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/distributions.ex @@ -0,0 +1,101 @@ +defmodule Indexer.Fetcher.Celo.EpochBlockOperations.Distributions do + @moduledoc """ + Fetches Reserve bolster, Community, and Carbon offsetting distributions for + the epoch block. + """ + import Ecto.Query, only: [from: 2, subquery: 1] + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + + alias Explorer.Repo + + alias Explorer.Chain.{ + Cache.CeloCoreContracts, + Hash, + TokenTransfer + } + + @mint_address_hash_string burn_address_hash_string() + + @spec fetch(%{ + :block_hash => EthereumJSONRPC.hash(), + :block_number => EthereumJSONRPC.block_number() + }) :: + {:ok, map()} + | {:error, :multiple_transfers_to_same_address} + def fetch(%{block_number: block_number, block_hash: block_hash} = _pending_operation) do + {:ok, celo_token_contract_address_hash} = CeloCoreContracts.get_address(:celo_token, block_number) + {:ok, reserve_contract_address_hash} = CeloCoreContracts.get_address(:reserve, block_number) + {:ok, community_contract_address_hash} = CeloCoreContracts.get_address(:governance, block_number) + + {:ok, %{"address" => carbon_offsetting_contract_address_hash}} = + CeloCoreContracts.get_event(:epoch_rewards, :carbon_offsetting_fund_set, block_number) + + celo_mint_transfers_query = + from( + tt in TokenTransfer.only_consensus_transfers_query(), + where: + tt.block_hash == ^block_hash and + tt.token_contract_address_hash == ^celo_token_contract_address_hash and + tt.from_address_hash == ^@mint_address_hash_string and + is_nil(tt.transaction_hash) + ) + + # Every epoch has at least one CELO transfer from the zero address to the + # reserve. This is how cUSD is minted before it is distributed to + # validators. If there is only one CELO transfer, then there was no + # Reserve bolster distribution for that epoch. If there are multiple CELO + # transfers, then the last one is the Reserve bolster distribution. + reserve_bolster_transfer_log_index_query = + from( + tt in subquery( + from( + tt in subquery(celo_mint_transfers_query), + where: tt.to_address_hash == ^reserve_contract_address_hash, + order_by: tt.log_index, + offset: 1 + ) + ), + select: max(tt.log_index) + ) + + query = + from( + tt in subquery(celo_mint_transfers_query), + where: + tt.to_address_hash in ^[ + community_contract_address_hash, + carbon_offsetting_contract_address_hash + ] or + tt.log_index == subquery(reserve_bolster_transfer_log_index_query), + select: {tt.to_address_hash, tt.log_index} + ) + + transfers_with_log_index = query |> Repo.all() + + unique_addresses_count = + transfers_with_log_index + |> Enum.map(&elem(&1, 0)) + |> Enum.uniq() + |> Enum.count() + + address_to_key = %{ + reserve_contract_address_hash => :reserve_bolster_transfer_log_index, + community_contract_address_hash => :community_transfer_log_index, + carbon_offsetting_contract_address_hash => :carbon_offsetting_transfer_log_index + } + + if unique_addresses_count == Enum.count(transfers_with_log_index) do + distributions = + transfers_with_log_index + |> Enum.reduce(%{}, fn {address, log_index}, acc -> + key = Map.get(address_to_key, address |> Hash.to_string()) + Map.put(acc, key, log_index) + end) + |> Map.put(:block_hash, block_hash) + + {:ok, distributions} + else + {:error, :multiple_transfers_to_same_address} + end + end +end diff --git a/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/validator_and_group_payments.ex b/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/validator_and_group_payments.ex new file mode 100644 index 000000000000..f677d7439442 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/validator_and_group_payments.ex @@ -0,0 +1,67 @@ +defmodule Indexer.Fetcher.Celo.EpochBlockOperations.ValidatorAndGroupPayments do + @moduledoc """ + Fetches validator and group payments for the epoch block. + """ + import Ecto.Query, only: [from: 2] + + alias Explorer.Chain.Cache.CeloCoreContracts + alias Explorer.Chain.Log + alias Explorer.Repo + alias Indexer.Transform.Celo.ValidatorEpochPaymentDistributions + + @spec fetch(%{ + :block_hash => EthereumJSONRPC.hash(), + :block_number => EthereumJSONRPC.block_number() + }) :: {:ok, list()} + def fetch(%{block_number: block_number, block_hash: block_hash} = _pending_operation) do + epoch_payment_distributions_signature = ValidatorEpochPaymentDistributions.signature() + {:ok, validators_contract_address} = CeloCoreContracts.get_address(:validators, block_number) + + query = + from( + log in Log, + where: + log.block_hash == ^block_hash and + log.address_hash == ^validators_contract_address and + log.first_topic == ^epoch_payment_distributions_signature and + is_nil(log.transaction_hash), + select: log + ) + + payments = + query + |> Repo.all() + |> ValidatorEpochPaymentDistributions.parse() + |> process_distribution_events(block_hash) + + {:ok, payments} + end + + defp process_distribution_events(distribution_events, block_hash) do + distribution_events + |> Enum.map(fn %{ + validator_address: validator_address, + validator_payment: validator_payment, + group_address: group_address, + group_payment: group_payment + } -> + [ + %{ + block_hash: block_hash, + account_address_hash: validator_address, + amount: validator_payment, + associated_account_address_hash: group_address, + type: :validator + }, + %{ + block_hash: block_hash, + account_address_hash: group_address, + amount: group_payment, + associated_account_address_hash: validator_address, + type: :group + } + ] + end) + |> Enum.concat() + end +end diff --git a/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/voter_payments.ex b/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/voter_payments.ex new file mode 100644 index 000000000000..d526feb0c7e7 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations/voter_payments.ex @@ -0,0 +1,201 @@ +defmodule Indexer.Fetcher.Celo.EpochBlockOperations.VoterPayments do + @moduledoc """ + Fetches voter payments for the epoch block. + """ + import Ecto.Query, only: [from: 2] + + import Explorer.Helper, only: [decode_data: 2] + import Indexer.Fetcher.Celo.Helper, only: [abi_to_method_id: 1] + import Indexer.Helper, only: [read_contracts_with_retries: 4] + + alias Explorer.Repo + alias Indexer.Fetcher.Celo.ValidatorGroupVotes + + alias Explorer.Chain.{ + Cache.CeloCoreContracts, + Celo.ValidatorGroupVote, + Hash, + Log + } + + require Logger + + @repeated_request_max_retries 3 + + @epoch_rewards_distributed_to_voters_topic "0x91ba34d62474c14d6c623cd322f4256666c7a45b7fdaa3378e009d39dfcec2a7" + + @get_active_votes_for_group_by_account_abi [ + %{ + "name" => "getActiveVotesForGroupByAccount", + "type" => "function", + "payable" => false, + "constant" => true, + "stateMutability" => "view", + "inputs" => [ + %{"name" => "group", "type" => "address"}, + %{"name" => "account", "type" => "address"} + ], + "outputs" => [ + %{"type" => "uint256"} + ] + } + ] + + @get_active_votes_for_group_by_account_method_id @get_active_votes_for_group_by_account_abi + |> abi_to_method_id() + + @spec fetch( + %{ + :block_hash => EthereumJSONRPC.hash(), + :block_number => EthereumJSONRPC.block_number() + }, + EthereumJSONRPC.json_rpc_named_arguments() + ) :: {:error, list()} | {:ok, list()} + def fetch( + %{block_number: block_number, block_hash: block_hash} = pending_operation, + json_rpc_named_arguments + ) do + :ok = ValidatorGroupVotes.fetch(block_number) + + {:ok, election_contract_address} = CeloCoreContracts.get_address(:election, block_number) + + elected_groups_query = + from( + l in Log, + where: + l.block_hash == ^block_hash and + l.address_hash == ^election_contract_address and + l.first_topic == ^@epoch_rewards_distributed_to_voters_topic and + is_nil(l.transaction_hash), + select: fragment("SUBSTRING(? from 13)", l.second_topic) + ) + + query = + from( + v in ValidatorGroupVote, + where: + v.group_address_hash in subquery(elected_groups_query) and + v.block_number <= ^block_number, + distinct: true, + select: {v.account_address_hash, v.group_address_hash} + ) + + accounts_with_activated_votes = + query + |> Repo.all() + |> Enum.map(fn + {account_address_hash, group_address_hash} -> + { + Hash.to_string(account_address_hash), + Hash.to_string(group_address_hash) + } + end) + + requests = + accounts_with_activated_votes + |> Enum.map(fn {account_address_hash, group_address_hash} -> + (block_number - 1)..block_number + |> Enum.map(fn block_number -> + %{ + contract_address: election_contract_address, + method_id: @get_active_votes_for_group_by_account_method_id, + args: [ + group_address_hash, + account_address_hash + ], + block_number: block_number + } + end) + end) + |> Enum.concat() + + {responses, []} = + read_contracts_with_retries( + requests, + @get_active_votes_for_group_by_account_abi, + json_rpc_named_arguments, + @repeated_request_max_retries + ) + + diffs = + responses + |> Enum.chunk_every(2) + |> Enum.map(fn + [ok: [votes_before], ok: [votes_after]] + when is_integer(votes_before) and + is_integer(votes_after) -> + votes_after - votes_before + end) + + # WARN: we do not count Revoked/Activated votes for the last epoch, but + # should we? + # + # See https://github.com/fedor-ivn/celo-blockscout/tree/master/apps/indexer/lib/indexer/fetcher/celo_epoch_data.ex#L179-L187 + # There is no case when those events occur in the epoch block. + rewards = + accounts_with_activated_votes + |> Enum.zip_with( + diffs, + fn {account_address_hash, group_address_hash}, diff -> + %{ + block_hash: block_hash, + account_address_hash: account_address_hash, + amount: diff, + associated_account_address_hash: group_address_hash, + type: :voter + } + end + ) + + ok_or_error = validate_voter_rewards(pending_operation, rewards) + + {ok_or_error, rewards} + end + + # Validates voter rewards by comparing the sum of what we got from the + # `EpochRewardsDistributedToVoters` event and the sum of what we calculated + # manually by fetching the votes for each account that has or had an activated + # vote. + defp validate_voter_rewards(pending_operation, voter_rewards) do + manual_voters_total = voter_rewards |> Enum.map(& &1.amount) |> Enum.sum() + {:ok, election_contract_address} = CeloCoreContracts.get_address(:election, pending_operation.block_number) + + query = + from( + l in Log, + where: + l.block_hash == ^pending_operation.block_hash and + l.address_hash == ^election_contract_address and + l.first_topic == ^@epoch_rewards_distributed_to_voters_topic and + is_nil(l.transaction_hash), + select: l.data + ) + + voter_rewards_from_event_total = + query + |> Repo.all() + |> Enum.map(fn data -> + [amount] = decode_data(data, [{:uint, 256}]) + amount + end) + |> Enum.sum() + + voter_rewards_count = Enum.count(voter_rewards) + voter_rewards_diff = voter_rewards_from_event_total - manual_voters_total + + if voter_rewards_diff < voter_rewards_count or voter_rewards_count == 0 do + :ok + else + Logger.warning(fn -> + [ + "Total voter rewards do not match. ", + "Amount calculated manually: #{manual_voters_total}. ", + "Amount got from `EpochRewardsDistributedToVoters` events: #{voter_rewards_from_event_total}. ", + "Voter rewards count: #{voter_rewards_count}." + ] + end) + + :error + end + end +end diff --git a/apps/indexer/lib/indexer/fetcher/celo/epoch_logs.ex b/apps/indexer/lib/indexer/fetcher/celo/epoch_logs.ex new file mode 100644 index 000000000000..4d12ab9c8a27 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/celo/epoch_logs.ex @@ -0,0 +1,121 @@ +defmodule Indexer.Fetcher.Celo.EpochLogs do + @moduledoc """ + Fetches logs that are not associated which are not linked to transaction, but + to the block. + """ + + import Explorer.Chain.Celo.Helper, only: [epoch_block_number?: 1] + + alias EthereumJSONRPC.Logs + alias Explorer.Chain.Cache.CeloCoreContracts + alias Indexer.Helper, as: IndexerHelper + + alias Explorer.Chain.TokenTransfer + alias Indexer.Transform.Celo.ValidatorEpochPaymentDistributions + + require Logger + + @max_request_retries 3 + + @epoch_block_targets [ + # TargetVotingYieldUpdated + epoch_rewards: "0x49d8cdfe05bae61517c234f65f4088454013bafe561115126a8fe0074dc7700e", + celo_token: TokenTransfer.constant(), + usd_token: TokenTransfer.constant(), + validators: ValidatorEpochPaymentDistributions.signature(), + # ValidatorScoreUpdated + validators: "0xedf9f87e50e10c533bf3ae7f5a7894ae66c23e6cbbe8773d7765d20ad6f995e9", + # EpochRewardsDistributedToVoters + election: "0x91ba34d62474c14d6c623cd322f4256666c7a45b7fdaa3378e009d39dfcec2a7" + ] + + @default_block_targets [ + # GasPriceMinimumUpdated + gas_price_minimum: "0x6e53b2f8b69496c2a175588ad1326dbabe2f66df4d82f817aeca52e3474807fb" + ] + + @spec fetch( + [Indexer.Transform.Blocks.block()], + EthereumJSONRPC.json_rpc_named_arguments() + ) :: Logs.t() + def fetch(blocks, json_rpc_named_arguments) + + def fetch(blocks, json_rpc_named_arguments) do + if Application.get_env(:explorer, :chain_type) == :celo do + do_fetch(blocks, json_rpc_named_arguments) + else + [] + end + end + + defp do_fetch(blocks, json_rpc_named_arguments) do + requests = + blocks + |> Enum.reduce({[], 0}, &blocks_reducer/2) + |> elem(0) + |> Enum.reverse() + |> Enum.concat() + + with {:ok, responses} <- do_requests(requests, json_rpc_named_arguments), + {:ok, logs} <- Logs.from_responses(responses) do + logs + |> Enum.filter(&(&1.transaction_hash == &1.block_hash)) + |> Enum.map(&Map.put(&1, :transaction_hash, nil)) + end + end + + # Workaround in order to fix block fetcher tests. + # + # If the requests is empty, we still send the requests to the JSON RPC + defp do_requests(requests, json_rpc_named_arguments) do + if Enum.empty?(requests) do + {:ok, []} + else + IndexerHelper.repeated_batch_rpc_call( + requests, + json_rpc_named_arguments, + fn message -> "Could not fetch epoch logs: #{message}" end, + @max_request_retries + ) + end + end + + defp blocks_reducer(%{number: number}, {acc, start_request_id}) do + targets = + @default_block_targets ++ + if epoch_block_number?(number) do + @epoch_block_targets + else + [] + end + + requests = + targets + |> Enum.map(fn {contract_atom, topic} -> + res = CeloCoreContracts.get_address(contract_atom, number) + {res, topic} + end) + |> Enum.split_with(&match?({{:ok, _address}, _topic}, &1)) + |> tap(fn {_, not_found} -> + if not Enum.empty?(not_found) do + Logger.info("Could not fetch addresses for the following contract atoms: #{inspect(not_found)}") + end + end) + |> elem(0) + |> Enum.with_index(start_request_id) + |> Enum.map(fn {{{:ok, address}, topic}, request_id} -> + Logs.request( + request_id, + %{ + from_block: number, + to_block: number, + address: address, + topics: [topic] + } + ) + end) + + next_start_request_id = start_request_id + length(requests) + {[requests | acc], next_start_request_id} + end +end diff --git a/apps/indexer/lib/indexer/fetcher/celo/helper.ex b/apps/indexer/lib/indexer/fetcher/celo/helper.ex new file mode 100644 index 000000000000..ec178397cd1f --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/celo/helper.ex @@ -0,0 +1,29 @@ +defmodule Indexer.Fetcher.Celo.Helper do + @moduledoc """ + Helper functions for the Celo fetchers. + """ + + @doc """ + Extracts the method ID from an ABI specification. + + ## Parameters + - `method` ([map()] | map()): The ABI specification, either as a single map + or a list containing one map. + + ## Returns + - `binary()`: The method ID extracted from the ABI specification. + + ## Examples + + iex> Indexer.Fetcher.Celo.Helper.abi_to_method_id([%{"name" => "transfer", "type" => "function", "inputs" => [%{"name" => "to", "type" => "address"}]}]) + <<26, 105, 82, 48>> + + """ + @spec abi_to_method_id([map()] | map()) :: binary() + def abi_to_method_id([method]), do: abi_to_method_id(method) + + def abi_to_method_id(method) when is_map(method) do + [parsed_method] = ABI.parse_specification([method]) + parsed_method.method_id + end +end diff --git a/apps/indexer/lib/indexer/fetcher/celo/validator_group_votes.ex b/apps/indexer/lib/indexer/fetcher/celo/validator_group_votes.ex new file mode 100644 index 000000000000..f51e736e00b4 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/celo/validator_group_votes.ex @@ -0,0 +1,217 @@ +defmodule Indexer.Fetcher.Celo.ValidatorGroupVotes do + @moduledoc """ + Fetches validator group votes from the Celo blockchain. + """ + + use GenServer + use Indexer.Fetcher + + import Explorer.Helper, + only: [ + truncate_address_hash: 1, + safe_parse_non_negative_integer: 1 + ] + + alias EthereumJSONRPC.Logs + alias Explorer.Application.Constants + alias Explorer.Chain + alias Explorer.Chain.Cache.CeloCoreContracts + alias Indexer.Helper, as: IndexerHelper + alias Indexer.Transform.Addresses + + require Logger + + @last_fetched_block_key "celo_validator_group_votes_last_fetched_block_number" + + @max_request_retries 3 + + @validator_group_vote_activated_topic "0x45aac85f38083b18efe2d441a65b9c1ae177c78307cb5a5d4aec8f7dbcaeabfe" + @validator_group_active_vote_revoked_topic "0xae7458f8697a680da6be36406ea0b8f40164915ac9cc40c0dad05a2ff6e8c6a8" + + @spec fetch(block_number :: EthereumJSONRPC.block_number()) :: :ok + def fetch(block_number) do + GenServer.call(__MODULE__, {:fetch, block_number}) + end + + def child_spec(start_link_arguments) do + spec = %{ + id: __MODULE__, + start: {__MODULE__, :start_link, start_link_arguments}, + type: :worker + } + + Supervisor.child_spec(spec, []) + end + + def start_link(args, gen_server_options \\ []) do + GenServer.start_link(__MODULE__, args, Keyword.put_new(gen_server_options, :name, __MODULE__)) + end + + @impl GenServer + def init(args) do + Logger.metadata(fetcher: :celo_validator_group_votes) + + { + :ok, + %{ + config: %{ + batch_size: Application.get_env(:indexer, __MODULE__)[:batch_size], + json_rpc_named_arguments: args[:json_rpc_named_arguments] + }, + data: %{} + }, + {:continue, :ok} + } + end + + @impl GenServer + def handle_continue( + :ok, + %{ + config: %{ + batch_size: batch_size, + json_rpc_named_arguments: json_rpc_named_arguments + } + } = state + ) do + {:ok, latest_block_number} = + EthereumJSONRPC.fetch_block_number_by_tag( + "latest", + json_rpc_named_arguments + ) + + Logger.info("Fetching votes up to latest block number #{latest_block_number}") + + fetch_up_to_block_number(latest_block_number, batch_size, json_rpc_named_arguments) + + {:noreply, state} + end + + @impl GenServer + def handle_info({ref, _result}, state) do + Process.demonitor(ref, [:flush]) + {:noreply, state} + end + + @impl GenServer + def handle_call( + {:fetch, block_number}, + _from, + %{ + config: %{ + batch_size: batch_size, + json_rpc_named_arguments: json_rpc_named_arguments + } + } = state + ) do + Logger.info("Fetching votes on demand up to block number #{block_number}") + + fetch_up_to_block_number(block_number, batch_size, json_rpc_named_arguments) + + {:reply, :ok, state} + end + + defp fetch_up_to_block_number(block_number, batch_size, json_rpc_named_arguments) do + {:ok, last_fetched_block_number} = + @last_fetched_block_key + |> Constants.get_constant_value() + |> case do + nil -> CeloCoreContracts.get_first_update_block_number(:election) + value -> safe_parse_non_negative_integer(value) + end + + if last_fetched_block_number < block_number do + block_range = last_fetched_block_number..block_number + + block_range + |> IndexerHelper.range_chunk_every(batch_size) + |> Enum.each(&process_chunk(&1, block_range, json_rpc_named_arguments)) + + Logger.info("Fetched validator group votes up to block number #{block_number}") + else + Logger.info("No new validator group votes to fetch") + end + end + + defp process_chunk(_..chunk_to_block = chunk, block_range, json_rpc_named_arguments) do + validator_group_votes = + chunk + |> fetch_logs_chunk(block_range, json_rpc_named_arguments) + |> Enum.map(&log_to_entry/1) + + addresses_params = + Addresses.extract_addresses(%{ + celo_validator_group_votes: validator_group_votes + }) + + {:ok, _imported} = + Chain.import(%{ + addresses: %{params: addresses_params}, + celo_validator_group_votes: %{params: validator_group_votes} + }) + + Constants.set_constant_value(@last_fetched_block_key, to_string(chunk_to_block)) + + :ok + end + + defp fetch_logs_chunk( + chunk_from_block..chunk_to_block, + from_block..to_block, + json_rpc_named_arguments + ) do + IndexerHelper.log_blocks_chunk_handling(chunk_from_block, chunk_to_block, from_block, to_block, nil, :L1) + + {:ok, election_contract_address} = CeloCoreContracts.get_address(:election, chunk_from_block) + + requests = + [ + @validator_group_active_vote_revoked_topic, + @validator_group_vote_activated_topic + ] + |> Enum.with_index() + |> Enum.map(fn {topic, request_id} -> + Logs.request( + request_id, + %{ + from_block: chunk_from_block, + to_block: chunk_to_block, + address: election_contract_address, + topics: [topic] + } + ) + end) + + {:ok, responses} = + IndexerHelper.repeated_batch_rpc_call( + requests, + json_rpc_named_arguments, + fn message -> Logger.error("Could not fetch logs: #{message}") end, + @max_request_retries + ) + + {:ok, logs} = Logs.from_responses(responses) + + logs + end + + defp log_to_entry(log) do + type = + case log.first_topic do + @validator_group_vote_activated_topic -> :activated + @validator_group_active_vote_revoked_topic -> :revoked + end + + account_address_hash = truncate_address_hash(log.second_topic) + group_address_hash = truncate_address_hash(log.third_topic) + + %{ + account_address_hash: account_address_hash, + group_address_hash: group_address_hash, + type: type, + block_number: log.block_number, + block_hash: log.block_hash, + transaction_hash: log.transaction_hash + } + end +end diff --git a/apps/indexer/lib/indexer/fetcher/internal_transaction.ex b/apps/indexer/lib/indexer/fetcher/internal_transaction.ex index f847af2ebc86..91d23414d985 100644 --- a/apps/indexer/lib/indexer/fetcher/internal_transaction.ex +++ b/apps/indexer/lib/indexer/fetcher/internal_transaction.ex @@ -10,15 +10,21 @@ defmodule Indexer.Fetcher.InternalTransaction do require Logger - import Indexer.Block.Fetcher, only: [async_import_coin_balances: 2] + import Indexer.Block.Fetcher, + only: [ + async_import_coin_balances: 2, + async_import_token_balances: 2, + token_transfers_merge_token: 2 + ] alias EthereumJSONRPC.Utility.RangesHelper alias Explorer.Chain - alias Explorer.Chain.Block + alias Explorer.Chain.{Block, Hash} alias Explorer.Chain.Cache.{Accounts, Blocks} alias Indexer.{BufferedTask, Tracer} alias Indexer.Fetcher.InternalTransaction.Supervisor, as: InternalTransactionSupervisor - alias Indexer.Transform.Addresses + alias Indexer.Transform.Celo.TransactionTokenTransfers, as: CeloTransactionTokenTransfers + alias Indexer.Transform.{Addresses, AddressTokenBalances} @behaviour BufferedTask @@ -280,8 +286,29 @@ defmodule Indexer.Fetcher.InternalTransaction do internal_transactions_and_empty_block_numbers = internal_transactions_params_marked ++ empty_block_numbers + celo_token_transfers_params = + %{token_transfers: celo_token_transfers, tokens: celo_tokens} = + if Application.get_env(:explorer, :chain_type) == :celo do + block_number_to_block_hash = + unique_numbers + |> Chain.block_hash_by_number() + |> Map.new(fn + {block_number, block_hash} -> + {block_number, Hash.to_string(block_hash)} + end) + + CeloTransactionTokenTransfers.parse_internal_transactions( + internal_transactions_params_marked, + block_number_to_block_hash + ) + else + %{token_transfers: [], tokens: []} + end + imports = Chain.import(%{ + token_transfers: %{params: celo_token_transfers}, + tokens: %{params: celo_tokens}, addresses: %{params: addresses_params}, internal_transactions: %{params: internal_transactions_and_empty_block_numbers, with: :blockless_changeset}, timeout: :infinity @@ -296,6 +323,8 @@ defmodule Indexer.Fetcher.InternalTransaction do address_hash_to_fetched_balance_block_number: address_hash_to_block_number }) + async_import_celo_token_balances(celo_token_transfers_params) + {:error, step, reason, _changes_so_far} -> Logger.error( fn -> @@ -411,4 +440,28 @@ defmodule Indexer.Fetcher.InternalTransaction do metadata: [fetcher: :internal_transaction] ] end + + defp async_import_celo_token_balances(%{token_transfers: token_transfers, tokens: tokens}) do + if Application.get_env(:explorer, :chain_type) == :celo do + token_transfers_with_token = token_transfers_merge_token(token_transfers, tokens) + + address_token_balances = + %{token_transfers_params: token_transfers_with_token} + |> AddressTokenBalances.params_set() + |> Enum.map(fn %{address_hash: address_hash, token_contract_address_hash: token_contract_address_hash} = entry -> + with {:ok, address_hash} <- Hash.Address.cast(address_hash), + {:ok, token_contract_address_hash} <- Hash.Address.cast(token_contract_address_hash) do + entry + |> Map.put(:address_hash, address_hash) + |> Map.put(:token_contract_address_hash, token_contract_address_hash) + else + error -> Logger.error("Failed to cast string to hash: #{inspect(error)}") + end + end) + + async_import_token_balances(%{address_token_balances: address_token_balances}, false) + else + :ok + end + end end diff --git a/apps/indexer/lib/indexer/helper.ex b/apps/indexer/lib/indexer/helper.ex index 23043f7802c0..b5a143e76b1a 100644 --- a/apps/indexer/lib/indexer/helper.ex +++ b/apps/indexer/lib/indexer/helper.ex @@ -200,6 +200,36 @@ defmodule Indexer.Helper do ] end + @doc """ + Splits a given range into chunks of the specified size. + + ## Parameters + - `range`: The range to be split into chunks. + - `chunk_size`: The size of each chunk. + + ## Returns + - A stream of ranges, each representing a chunk of the specified size. + + ## Examples + + iex> Indexer.Helper.range_chunk_every(1..10, 3) + #Stream<...> + + iex> Enum.to_list(Indexer.Helper.range_chunk_every(1..10, 3)) + [1..3, 4..6, 7..9, 10..10] + """ + @spec range_chunk_every(Range.t(), non_neg_integer()) :: Enum.t() + def range_chunk_every(from..to, chunk_size) do + chunks_number = floor((to - from + 1) / chunk_size) + + 0..chunks_number + |> Stream.map(fn current_chunk -> + chunk_start = from + chunk_size * current_chunk + chunk_end = min(chunk_start + chunk_size - 1, to) + chunk_start..chunk_end + end) + end + @doc """ Retrieves event logs from Ethereum-like blockchains within a specified block range for a given address and set of topics using JSON-RPC. @@ -314,26 +344,35 @@ defmodule Indexer.Helper do end @doc """ - Retrieves decoded results of `eth_call` requests to contracts, with retry logic for handling errors. + Retrieves decoded results of `eth_call` requests to contracts, with retry + logic for handling errors. - The function attempts the specified number of retries, with a progressive delay between - each retry, for each `eth_call` request. If, after all retries, some requests remain - unsuccessful, it returns a list of unique error messages encountered. + The function attempts the specified number of retries, with a progressive + delay between each retry, for each `eth_call` request. If, after all + retries, some requests remain unsuccessful, it returns a list of unique + error messages encountered. ## Parameters - - `requests`: A list of `EthereumJSONRPC.Contract.call()` instances describing the parameters - for `eth_call`, including the contract address and method selector. - - `abi`: A list of maps providing the ABI that describes the input parameters and output - format for the contract functions. - - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. - - `retries_left`: The number of retries allowed for any `eth_call` that returns an error. + - `requests`: A list of `EthereumJSONRPC.Contract.call()` instances + describing the parameters for `eth_call`, including the + contract address and method selector. + - `abi`: A list of maps providing the ABI that describes the input + parameters and output format for the contract functions. + - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC + connection. + - `retries_left`: The number of retries allowed for any `eth_call` that + returns an error. + - `log_error?` (optional): A boolean indicating whether to log error + messages on retries. Defaults to `true`. ## Returns - `{responses, errors}` where: - - `responses`: A list of tuples `{status, result}`, where `result` is the decoded response - from the corresponding `eth_call` if `status` is `:ok`, or the error message - if `status` is `:error`. - - `errors`: A list of error messages, if any element in `responses` contains `:error`. + - `responses`: A list of tuples `{status, result}`, where `result` is the + decoded response from the corresponding `eth_call` if + `status` is `:ok`, or the error message if `status` is + `:error`. + - `errors`: A list of error messages, if any element in `responses` + contains `:error`. """ @spec read_contracts_with_retries( [EthereumJSONRPC.Contract.call()], @@ -341,12 +380,12 @@ defmodule Indexer.Helper do EthereumJSONRPC.json_rpc_named_arguments(), integer() ) :: {[{:ok | :error, any()}], list()} - def read_contracts_with_retries(requests, abi, json_rpc_named_arguments, retries_left) + def read_contracts_with_retries(requests, abi, json_rpc_named_arguments, retries_left, log_error? \\ true) when is_list(requests) and is_list(abi) and is_integer(retries_left) do - do_read_contracts_with_retries(requests, abi, json_rpc_named_arguments, retries_left, 0) + do_read_contracts_with_retries(requests, abi, json_rpc_named_arguments, retries_left, 0, log_error?) end - defp do_read_contracts_with_retries(requests, abi, json_rpc_named_arguments, retries_left, retries_done) do + defp do_read_contracts_with_retries(requests, abi, json_rpc_named_arguments, retries_left, retries_done, log_error?) do responses = ContractReader.query_contracts(requests, abi, json_rpc_named_arguments: json_rpc_named_arguments) error_messages = @@ -359,18 +398,28 @@ defmodule Indexer.Helper do end end) - if error_messages == [] do - {responses, []} - else - retries_left = retries_left - 1 + retries_left = retries_left - 1 - if retries_left <= 0 do + cond do + error_messages == [] -> + {responses, []} + + retries_left <= 0 -> + if log_error?, do: Logger.error("#{List.first(error_messages)}.") {responses, Enum.uniq(error_messages)} - else - Logger.error("#{List.first(error_messages)}. Retrying...") + + true -> + if log_error?, do: Logger.error("#{List.first(error_messages)}. Retrying...") pause_before_retry(retries_done) - do_read_contracts_with_retries(requests, abi, json_rpc_named_arguments, retries_left, retries_done + 1) - end + + do_read_contracts_with_retries( + requests, + abi, + json_rpc_named_arguments, + retries_left, + retries_done + 1, + log_error? + ) end end @@ -387,7 +436,7 @@ defmodule Indexer.Helper do - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. - `error_message_generator`: A function that generates a string containing the error message returned by the RPC call. - - `retries_left`: The number of retries allowed for any RPC call that returns an error. + - `max_retries`: The number of retries allowed for any RPC call that returns an error. ## Returns - `{:ok, responses}`: When all calls are successful, `responses` is a list of standard @@ -399,9 +448,9 @@ defmodule Indexer.Helper do """ @spec repeated_batch_rpc_call([Transport.request()], EthereumJSONRPC.json_rpc_named_arguments(), fun(), integer()) :: {:error, any()} | {:ok, any()} - def repeated_batch_rpc_call(requests, json_rpc_named_arguments, error_message_generator, retries_left) - when is_list(requests) and is_function(error_message_generator) and is_integer(retries_left) do - do_repeated_batch_rpc_call(requests, json_rpc_named_arguments, error_message_generator, retries_left, 0) + def repeated_batch_rpc_call(requests, json_rpc_named_arguments, error_message_generator, max_retries) + when is_list(requests) and is_function(error_message_generator) and is_integer(max_retries) do + do_repeated_batch_rpc_call(requests, json_rpc_named_arguments, error_message_generator, max_retries, 0) end # credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex index ecb32417fbbe..641b4ac65493 100644 --- a/apps/indexer/lib/indexer/supervisor.ex +++ b/apps/indexer/lib/indexer/supervisor.ex @@ -189,6 +189,12 @@ defmodule Indexer.Supervisor do configure(ArbitrumRollupMessagesCatchup.Supervisor, [ [json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor] ]), + configure(Indexer.Fetcher.Celo.ValidatorGroupVotes.Supervisor, [ + [json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor] + ]), + configure(Indexer.Fetcher.Celo.EpochBlockOperations.Supervisor, [ + [json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor] + ]), {Indexer.Fetcher.Beacon.Blob.Supervisor, [[memory_monitor: memory_monitor]]}, # Out-of-band fetchers @@ -282,6 +288,7 @@ defmodule Indexer.Supervisor do end defp configure(process, opts) do + # todo: shouldn't we pay attention to process.disabled?() predicate? if Application.get_env(:indexer, process)[:enabled] do [{process, opts}] else diff --git a/apps/indexer/lib/indexer/transform/address_coin_balances.ex b/apps/indexer/lib/indexer/transform/address_coin_balances.ex index 9f1935da4638..e85cd1ffa1aa 100644 --- a/apps/indexer/lib/indexer/transform/address_coin_balances.ex +++ b/apps/indexer/lib/indexer/transform/address_coin_balances.ex @@ -90,15 +90,39 @@ defmodule Indexer.Transform.AddressCoinBalances do ) when is_integer(block_number) and is_binary(from_address_hash) do # a transaction MUST have a `from_address_hash` - acc = MapSet.put(initial, %{address_hash: from_address_hash, block_number: block_number}) - - # `to_address_hash` is optional - case transaction_params do - %{to_address_hash: to_address_hash} when is_binary(to_address_hash) -> - MapSet.put(acc, %{address_hash: to_address_hash, block_number: block_number}) + initial + |> MapSet.put(%{address_hash: from_address_hash, block_number: block_number}) + |> (&(case transaction_params do + %{to_address_hash: to_address_hash} when is_binary(to_address_hash) -> + MapSet.put(&1, %{address_hash: to_address_hash, block_number: block_number}) + + _ -> + &1 + end)).() + |> (&transactions_params_chain_type_fields_reducer(transaction_params, &1)).() + end - _ -> - acc + if Application.compile_env(:explorer, :chain_type) == :celo do + import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + + @burn_address_hash_string burn_address_hash_string() + + # todo: subject for deprecation, since celo transactions with + # gatewayFeeRecipient are deprecated + defp transactions_params_chain_type_fields_reducer( + %{ + block_number: block_number, + gas_fee_recipient_address_hash: recipient_address_hash, + gas_token_contract_address_hash: nil + }, + initial + ) + when is_integer(block_number) and + is_binary(recipient_address_hash) and + recipient_address_hash != @burn_address_hash_string do + MapSet.put(initial, %{address_hash: recipient_address_hash, block_number: block_number}) end end + + defp transactions_params_chain_type_fields_reducer(_, acc), do: acc end diff --git a/apps/indexer/lib/indexer/transform/addresses.ex b/apps/indexer/lib/indexer/transform/addresses.ex index af645ba34457..73c48375f879 100644 --- a/apps/indexer/lib/indexer/transform/addresses.ex +++ b/apps/indexer/lib/indexer/transform/addresses.ex @@ -155,6 +155,19 @@ defmodule Indexer.Transform.Addresses do [ %{from: :l2_token_address, to: :hash} ] + ], + celo_election_rewards: [ + [ + %{from: :account_address_hash, to: :hash} + ] + ], + celo_validator_group_votes: [ + [ + %{from: :account_address_hash, to: :hash} + ], + [ + %{from: :group_address_hash, to: :hash} + ] ] } @@ -176,7 +189,7 @@ defmodule Indexer.Transform.Addresses do Blocks have their `miner_hash` extracted. - iex> Indexer.Addresses.extract_addresses( + iex> Indexer.Transform.Addresses.extract_addresses( ...> %{ ...> blocks: [ ...> %{ @@ -196,7 +209,7 @@ defmodule Indexer.Transform.Addresses do Internal transactions can have their `from_address_hash`, `to_address_hash` and/or `created_contract_address_hash` extracted. - iex> Indexer.Addresses.extract_addresses( + iex> Indexer.Transform.Addresses.extract_addresses( ...> %{ ...> internal_transactions: [ ...> %{ @@ -233,7 +246,7 @@ defmodule Indexer.Transform.Addresses do Transactions can have their `from_address_hash` and/or `to_address_hash` extracted. - iex> Indexer.Addresses.extract_addresses( + iex> Indexer.Transform.Addresses.extract_addresses( ...> %{ ...> transactions: [ ...> %{ @@ -269,7 +282,7 @@ defmodule Indexer.Transform.Addresses do Logs can have their `address_hash` extracted. - iex> Indexer.Addresses.extract_addresses( + iex> Indexer.Transform.Addresses.extract_addresses( ...> %{ ...> logs: [ ...> %{ @@ -288,7 +301,7 @@ defmodule Indexer.Transform.Addresses do When the same address is mentioned multiple times, the greatest `block_number` is used - iex> Indexer.Addresses.extract_addresses( + iex> Indexer.Transform.Addresses.extract_addresses( ...> %{ ...> blocks: [ ...> %{ @@ -341,7 +354,7 @@ defmodule Indexer.Transform.Addresses do When a contract is created and then used in internal transactions and transaction in the same fetched data, the `created_contract_code` is merged with the greatest `block_number` - iex> Indexer.Addresses.extract_addresses( + iex> Indexer.Transform.Addresses.extract_addresses( ...> %{ ...> internal_transactions: [ ...> %{ @@ -467,6 +480,17 @@ defmodule Indexer.Transform.Addresses do %{ optional(:l2_token_address) => String.t() } + ], + optional(:celo_election_rewards) => [ + %{ + required(:account_address_hash) => String.t() + } + ], + optional(:celo_validator_group_votes) => [ + %{ + required(:account_address_hash) => String.t(), + required(:group_address_hash) => String.t() + } ] }) :: [params] def extract_addresses(fetched_data, options \\ []) when is_map(fetched_data) and is_list(options) do diff --git a/apps/indexer/lib/indexer/transform/celo/transaction_gas_tokens.ex b/apps/indexer/lib/indexer/transform/celo/transaction_gas_tokens.ex new file mode 100644 index 000000000000..7857a932157d --- /dev/null +++ b/apps/indexer/lib/indexer/transform/celo/transaction_gas_tokens.ex @@ -0,0 +1,42 @@ +defmodule Indexer.Transform.Celo.TransactionGasTokens do + @moduledoc """ + Helper functions for extracting tokens specified as gas fee currency. + """ + + alias Explorer.Chain.Hash + + @doc """ + Parses transactions and extracts tokens specified as gas fee currency. + """ + @spec parse([ + %{ + optional(:gas_token_contract_address_hash) => Hash.Address.t() | nil + } + ]) :: [ + %{ + contract_address_hash: String.t(), + type: String.t() + } + ] + def parse(transactions) do + if Application.get_env(:explorer, :chain_type) == :celo do + transactions + |> Enum.reduce( + MapSet.new(), + fn + %{gas_token_contract_address_hash: address_hash}, acc when not is_nil(address_hash) -> + MapSet.put(acc, %{ + contract_address_hash: address_hash, + type: "ERC-20" + }) + + _, acc -> + acc + end + ) + |> MapSet.to_list() + else + [] + end + end +end diff --git a/apps/indexer/lib/indexer/transform/celo/transaction_token_transfers.ex b/apps/indexer/lib/indexer/transform/celo/transaction_token_transfers.ex new file mode 100644 index 000000000000..2efa6b74de26 --- /dev/null +++ b/apps/indexer/lib/indexer/transform/celo/transaction_token_transfers.ex @@ -0,0 +1,138 @@ +defmodule Indexer.Transform.Celo.TransactionTokenTransfers do + @moduledoc """ + Helper functions for generating ERC20 token transfers from native Celo coin + transfers. + + CELO has a feature referred to as "token duality", where the native chain + asset (CELO) can be used as both a native chain currency and as an ERC-20 + token. Unfortunately native chain asset transfers do not emit ERC-20 transfer + events, which requires the artificial creation of entries in the + `token_transfers` table. + """ + require Logger + + import Indexer.Transform.TokenTransfers, + only: [ + filter_tokens_for_supply_update: 1 + ] + + alias Explorer.Chain.Cache.CeloCoreContracts + alias Explorer.Chain.Hash + alias Indexer.Fetcher.TokenTotalSupplyUpdater + @token_type "ERC-20" + @transaction_buffer_size 20_000 + + @doc """ + In order to avoid conflicts with real token transfers, for native token + transfers we put a negative `log_index`. + + Each transaction within the block is assigned a so-called _buffer_ of + #{@transaction_buffer_size} entries. Thus, according to the formula, + transactions with indices 0, 1, 2 would have log indices -20000, -40000, + -60000. + + The spare intervals between the log indices (0..-19_999, -20_001..-39_999, + -40_001..59_999) are reserved for native token transfers fetched from + internal transactions. + """ + @spec parse_transactions([ + %{ + required(:value) => non_neg_integer(), + optional(:to_address_hash) => Hash.Address.t() | nil, + optional(:created_contract_address_hash) => Hash.Address.t() | nil + } + ]) :: %{ + token_transfers: list(), + tokens: list() + } + def parse_transactions(transactions) do + token_transfers = + if Application.get_env(:explorer, :chain_type) == :celo do + transactions + |> Enum.filter(fn tx -> tx.value > 0 end) + |> Enum.map(fn tx -> + to_address_hash = Map.get(tx, :to_address_hash) || Map.get(tx, :created_contract_address_hash) + log_index = -1 * (tx.index + 1) * @transaction_buffer_size + {:ok, celo_token_address} = CeloCoreContracts.get_address(:celo_token, tx.block_number) + + %{ + amount: Decimal.new(tx.value), + block_hash: tx.block_hash, + block_number: tx.block_number, + from_address_hash: tx.from_address_hash, + log_index: log_index, + to_address_hash: to_address_hash, + token_contract_address_hash: celo_token_address, + token_ids: nil, + token_type: @token_type, + transaction_hash: tx.hash + } + end) + |> tap(&Logger.debug("Found #{length(&1)} Celo token transfers.")) + else + [] + end + + token_transfers + |> filter_tokens_for_supply_update() + |> TokenTotalSupplyUpdater.add_tokens() + + %{ + token_transfers: token_transfers, + tokens: to_tokens(token_transfers) + } + end + + def parse_internal_transactions(internal_transactions, block_number_to_block_hash) do + token_transfers = + internal_transactions + |> Enum.filter(fn itx -> + itx.value > 0 && + itx.index > 0 && + not Map.has_key?(itx, :error) && + (not Map.has_key?(itx, :call_type) || itx.call_type != "delegatecall") + end) + |> Enum.map(fn itx -> + to_address_hash = Map.get(itx, :to_address_hash) || Map.get(itx, :created_contract_address_hash) + log_index = -1 * (itx.transaction_index * @transaction_buffer_size + itx.index) + {:ok, celo_token_address} = CeloCoreContracts.get_address(:celo_token, itx.block_number) + + %{ + amount: Decimal.new(itx.value), + block_hash: block_number_to_block_hash[itx.block_number], + block_number: itx.block_number, + from_address_hash: itx.from_address_hash, + log_index: log_index, + to_address_hash: to_address_hash, + token_contract_address_hash: celo_token_address, + token_ids: nil, + token_type: @token_type, + transaction_hash: itx.transaction_hash + } + end) + + Logger.debug("Found #{length(token_transfers)} Celo token transfers from internal transactions.") + + token_transfers + |> filter_tokens_for_supply_update() + |> TokenTotalSupplyUpdater.add_tokens() + + %{ + token_transfers: token_transfers, + tokens: to_tokens(token_transfers) + } + end + + defp to_tokens([]), do: [] + + defp to_tokens(token_transfers) do + token_transfers + |> Enum.map( + &%{ + contract_address_hash: &1.token_contract_address_hash, + type: @token_type + } + ) + |> Enum.uniq() + end +end diff --git a/apps/indexer/lib/indexer/transform/celo/validator_epoch_payment_distributions.ex b/apps/indexer/lib/indexer/transform/celo/validator_epoch_payment_distributions.ex new file mode 100644 index 000000000000..69ace6db3bd2 --- /dev/null +++ b/apps/indexer/lib/indexer/transform/celo/validator_epoch_payment_distributions.ex @@ -0,0 +1,72 @@ +defmodule Indexer.Transform.Celo.ValidatorEpochPaymentDistributions do + @moduledoc """ + Extracts data from `ValidatorEpochPaymentDistributed` event logs of the + `Validators` Celo core contract. + """ + alias ABI.FunctionSelector + + alias Explorer.Chain.Cache.CeloCoreContracts + alias Explorer.Chain.{Hash, Log} + + require Logger + + @event_signature "0x6f5937add2ec38a0fa4959bccd86e3fcc2aafb706cd3e6c0565f87a7b36b9975" + + @event_abi [ + %{ + "name" => "ValidatorEpochPaymentDistributed", + "type" => "event", + "anonymous" => false, + "inputs" => [ + %{ + "indexed" => true, + "name" => "validator", + "type" => "address" + }, + %{ + "indexed" => false, + "name" => "validatorPayment", + "type" => "uint256" + }, + %{ + "indexed" => true, + "name" => "group", + "type" => "address" + }, + %{ + "indexed" => false, + "name" => "groupPayment", + "type" => "uint256" + } + ] + } + ] + + def signature, do: @event_signature + + def parse(logs) do + logs + |> Enum.filter(fn log -> + {:ok, validators_contract_address} = CeloCoreContracts.get_address(:validators, log.block_number) + + Hash.to_string(log.address_hash) == validators_contract_address and + Hash.to_string(log.first_topic) == @event_signature + end) + |> Enum.map(fn log -> + {:ok, %FunctionSelector{}, + [ + {"validator", "address", true, validator_address}, + {"validatorPayment", "uint256", false, validator_payment}, + {"group", "address", true, group_address}, + {"groupPayment", "uint256", false, group_payment} + ]} = Log.find_and_decode(@event_abi, log, log.block_hash) + + %{ + validator_address: "0x" <> Base.encode16(validator_address, case: :lower), + validator_payment: validator_payment, + group_address: "0x" <> Base.encode16(group_address, case: :lower), + group_payment: group_payment + } + end) + end +end diff --git a/apps/indexer/lib/indexer/transform/token_transfers.ex b/apps/indexer/lib/indexer/transform/token_transfers.ex index 161e8dfdcb2e..8ac1af300c75 100644 --- a/apps/indexer/lib/indexer/transform/token_transfers.ex +++ b/apps/indexer/lib/indexer/transform/token_transfers.ex @@ -63,14 +63,7 @@ defmodule Indexer.Transform.TokenTransfers do token_transfers = sanitize_weth_transfers(tokens, rough_token_transfers, weth_transfers.token_transfers) token_transfers - |> Enum.filter(fn token_transfer -> - token_transfer.to_address_hash == burn_address_hash_string() || - token_transfer.from_address_hash == burn_address_hash_string() - end) - |> Enum.map(fn token_transfer -> - token_transfer.token_contract_address_hash - end) - |> Enum.uniq() + |> filter_tokens_for_supply_update() |> TokenTotalSupplyUpdater.add_tokens() tokens_uniq = tokens |> Enum.uniq() @@ -484,6 +477,16 @@ defmodule Indexer.Transform.TokenTransfers do end end + def filter_tokens_for_supply_update(token_transfers) do + token_transfers + |> Enum.filter(fn token_transfer -> + token_transfer.to_address_hash == burn_address_hash_string() || + token_transfer.from_address_hash == burn_address_hash_string() + end) + |> Enum.map(& &1.token_contract_address_hash) + |> Enum.uniq() + end + defp encode_address_hash(binary) do "0x" <> Base.encode16(binary, case: :lower) end diff --git a/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs b/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs index 8d9650a762ea..fc81b000867e 100644 --- a/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs +++ b/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs @@ -34,6 +34,8 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisorTest do setup do initial_env = Application.get_env(:indexer, :block_ranges) on_exit(fn -> Application.put_env(:indexer, :block_ranges, initial_env) end) + + set_celo_core_contracts_env_var() end # See https://github.com/poanetwork/blockscout/issues/597 @@ -416,6 +418,9 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisorTest do setup context do initial_env = Application.get_env(:indexer, :block_ranges) on_exit(fn -> Application.put_env(:indexer, :block_ranges, initial_env) end) + + set_celo_core_contracts_env_var() + # force to use `Mox`, so we can manipulate `latest_block_number` put_in(context.json_rpc_named_arguments[:transport], EthereumJSONRPC.Mox) end @@ -592,4 +597,28 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisorTest do {:ok, %{pid: pid}} end + + defp set_celo_core_contracts_env_var do + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, + contracts: %{ + "addresses" => %{ + "Accounts" => [], + "Election" => [], + "EpochRewards" => [], + "FeeHandler" => [], + "GasPriceMinimum" => [], + "GoldToken" => [], + "Governance" => [], + "LockedGold" => [], + "Reserve" => [], + "StableToken" => [], + "Validators" => [] + } + } + ) + + on_exit(fn -> + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, contracts: %{}) + end) + end end diff --git a/apps/indexer/test/indexer/block/catchup/fetcher_test.exs b/apps/indexer/test/indexer/block/catchup/fetcher_test.exs index fcec1ef1998e..292b48183c74 100644 --- a/apps/indexer/test/indexer/block/catchup/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/catchup/fetcher_test.exs @@ -40,6 +40,7 @@ defmodule Indexer.Block.Catchup.FetcherTest do setup do configuration = Application.get_env(:indexer, :last_block) Application.put_env(:indexer, :last_block, 0) + Application.put_env(:indexer, Indexer.Fetcher.Celo.EpochBlockOperations.Supervisor, disabled?: true) on_exit(fn -> Application.put_env(:indexer, :last_block, configuration) @@ -141,6 +142,29 @@ defmodule Indexer.Block.Catchup.FetcherTest do setup do initial_env = Application.get_env(:indexer, :block_ranges) on_exit(fn -> Application.put_env(:indexer, :block_ranges, initial_env) end) + Application.put_env(:indexer, Indexer.Fetcher.Celo.EpochBlockOperations.Supervisor, disabled?: true) + + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, + contracts: %{ + "addresses" => %{ + "Accounts" => [], + "Election" => [], + "EpochRewards" => [], + "FeeHandler" => [], + "GasPriceMinimum" => [], + "GoldToken" => [], + "Governance" => [], + "LockedGold" => [], + "Reserve" => [], + "StableToken" => [], + "Validators" => [] + } + } + ) + + on_exit(fn -> + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, contracts: %{}) + end) end test "ignores fetched beneficiaries with different hash for same number", %{ @@ -617,7 +641,7 @@ defmodule Indexer.Block.Catchup.FetcherTest do MissingRangesManipulator.start_link([]) EthereumJSONRPC.Mox - |> expect(:json_rpc, 2, fn + |> expect(:json_rpc, 1, fn [ %{ id: id_1, @@ -646,9 +670,6 @@ defmodule Indexer.Block.Catchup.FetcherTest do error: %{message: "error"} } ]} - - [], _options -> - {:ok, []} end) Process.sleep(50) diff --git a/apps/indexer/test/indexer/block/fetcher_test.exs b/apps/indexer/test/indexer/block/fetcher_test.exs index ca9ae2a099de..ce5ff1686ae7 100644 --- a/apps/indexer/test/indexer/block/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/fetcher_test.exs @@ -60,6 +60,10 @@ defmodule Indexer.Block.FetcherTest do block_fetcher: %Fetcher{json_rpc_named_arguments: json_rpc_named_arguments} ) + Application.put_env(:indexer, Indexer.Fetcher.Celo.EpochBlockOperations.Supervisor, disabled?: true) + + maybe_set_celo_core_contracts_env() + %{ block_fetcher: %Fetcher{ broadcast: false, @@ -279,6 +283,31 @@ defmodule Indexer.Block.FetcherTest do to_address_hash = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" transaction_hash = "0x53bd884872de3e488692881baeec262e7b95234d3965248c39fe992fffd433e5" + transaction = %{ + "blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd", + "blockNumber" => "0x25", + "chainId" => "0x4d", + "condition" => nil, + "creates" => nil, + "from" => from_address_hash, + "gas" => "0x47b760", + "gasPrice" => "0x174876e800", + "hash" => transaction_hash, + "input" => "0x10855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef", + "nonce" => "0x4", + "publicKey" => + "0xe5d196ad4ceada719d9e592f7166d0c75700f6eab2e3c3de34ba751ea786527cb3f6eb96ad9fdfdb9989ff572df50f1c42ef800af9c5207a38b929aff969b5c9", + "r" => "0xa7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01", + "raw" => + "0xf88a0485174876e8008347b760948bf38d4764929064f2d4d3a56520a76ab3df415b80a410855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef81bea0a7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01a01f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f", + "s" => "0x1f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f", + "standardV" => "0x1", + "to" => to_address_hash, + "transactionIndex" => "0x0", + "v" => "0xbe", + "value" => "0x0" + } + EthereumJSONRPC.Mox |> expect(:json_rpc, fn json, _options -> assert [%{id: id, method: "eth_getBlockByNumber", params: [^block_quantity, true]}] = json @@ -313,32 +342,7 @@ defmodule Indexer.Block.FetcherTest do "step" => "302674398", "timestamp" => "0x5a343956", "totalDifficulty" => "0x24ffffffffffffffffffffffffedf78dfd", - "transactions" => [ - %{ - "blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd", - "blockNumber" => "0x25", - "chainId" => "0x4d", - "condition" => nil, - "creates" => nil, - "from" => from_address_hash, - "gas" => "0x47b760", - "gasPrice" => "0x174876e800", - "hash" => transaction_hash, - "input" => "0x10855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef", - "nonce" => "0x4", - "publicKey" => - "0xe5d196ad4ceada719d9e592f7166d0c75700f6eab2e3c3de34ba751ea786527cb3f6eb96ad9fdfdb9989ff572df50f1c42ef800af9c5207a38b929aff969b5c9", - "r" => "0xa7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01", - "raw" => - "0xf88a0485174876e8008347b760948bf38d4764929064f2d4d3a56520a76ab3df415b80a410855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef81bea0a7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01a01f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f", - "s" => "0x1f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f", - "standardV" => "0x1", - "to" => to_address_hash, - "transactionIndex" => "0x0", - "v" => "0xbe", - "value" => "0x0" - } - ], + "transactions" => [transaction], "transactionsRoot" => "0x68e314a05495f390f9cd0c36267159522e5450d2adf254a74567b452e767bf34", "uncles" => [] } @@ -433,32 +437,7 @@ defmodule Indexer.Block.FetcherTest do "step" => "302674398", "timestamp" => "0x5a343956", "totalDifficulty" => "0x24ffffffffffffffffffffffffedf78dfd", - "transactions" => [ - %{ - "blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd", - "blockNumber" => "0x25", - "chainId" => "0x4d", - "condition" => nil, - "creates" => nil, - "from" => from_address_hash, - "gas" => "0x47b760", - "gasPrice" => "0x174876e800", - "hash" => transaction_hash, - "input" => "0x10855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef", - "nonce" => "0x4", - "publicKey" => - "0xe5d196ad4ceada719d9e592f7166d0c75700f6eab2e3c3de34ba751ea786527cb3f6eb96ad9fdfdb9989ff572df50f1c42ef800af9c5207a38b929aff969b5c9", - "r" => "0xa7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01", - "raw" => - "0xf88a0485174876e8008347b760948bf38d4764929064f2d4d3a56520a76ab3df415b80a410855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef81bea0a7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01a01f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f", - "s" => "0x1f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f", - "standardV" => "0x1", - "to" => to_address_hash, - "transactionIndex" => "0x0", - "v" => "0xbe", - "value" => "0x0" - } - ], + "transactions" => [transaction], "transactionsRoot" => "0x68e314a05495f390f9cd0c36267159522e5450d2adf254a74567b452e767bf34", "uncles" => [] } @@ -705,88 +684,89 @@ defmodule Indexer.Block.FetcherTest do if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do EthereumJSONRPC.Mox - |> expect(:json_rpc, 2, fn requests, _options -> - {:ok, - Enum.map(requests, fn - %{id: id, method: "eth_getBlockByNumber", params: ["0x708677", true]} -> - %{ - id: id, - result: %{ - "author" => "0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c", - "difficulty" => "0x6bc767dd80781", - "extraData" => "0x5050594520737061726b706f6f6c2d6574682d7477", - "gasLimit" => "0x7a121d", - "gasUsed" => "0x79cbe9", - "hash" => "0x1b6fb99af0b51af6685a191b2f7bcba684f8565629bf084c70b2530479407455", - "logsBloom" => - "0x044d42d008801488400e1809190200a80d06105bc0c4100b047895c0d518327048496108388040140010b8208006288102e206160e21052322440924002090c1c808a0817405ab238086d028211014058e949401012403210314896702d06880c815c3060a0f0809987c81044488292cc11d57882c912a808ca10471c84460460040000c0001012804022000a42106591881d34407420ba401e1c08a8d00a000a34c11821a80222818a4102152c8a0c044032080c6462644223104d618e0e544072008120104408205c60510542264808488220403000106281a0290404220112c10b080145028c8000300b18a2c8280701c882e702210b00410834840108084", - "miner" => "0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c", - "mixHash" => "0xda53ae7c2b3c529783d6cdacdb90587fd70eb651c0f04253e8ff17de97844010", - "nonce" => "0x0946e5f01fce12bc", - "number" => "0x708677", - "parentHash" => "0x62543e836e0ef7edfa9e38f26526092c4be97efdf5ba9e0f53a4b0b7d5bc930a", - "receiptsRoot" => "0xa7d2b82bd8526de11736c18bd5cc8cfe2692106c4364526f3310ad56d78669c4", - "sealFields" => [ - "0xa0da53ae7c2b3c529783d6cdacdb90587fd70eb651c0f04253e8ff17de97844010", - "0x880946e5f01fce12bc" - ], - "sha3Uncles" => "0x483a8a21a5825ad270f358b3ea56e060bbb8b3082d9a92ec8fa17a5c7e6fc1b6", - "size" => "0x544c", - "stateRoot" => "0x85daa9cd528004c1609d4cb3520fd958e85983bb4183124a4a9f7137fd39c691", - "timestamp" => "0x5c8bc76e", - "totalDifficulty" => "0x201a42c35142ae94458", - "transactions" => [], - "transactionsRoot" => "0xcd6c12fa43cd4e92ad5c0bf232b30488bbcbfe273c5b4af0366fced0767d54db", - "uncles" => [] + |> expect(:json_rpc, 2, fn + requests, _options -> + {:ok, + Enum.map(requests, fn + %{id: id, method: "eth_getBlockByNumber", params: ["0x708677", true]} -> + %{ + id: id, + result: %{ + "author" => "0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c", + "difficulty" => "0x6bc767dd80781", + "extraData" => "0x5050594520737061726b706f6f6c2d6574682d7477", + "gasLimit" => "0x7a121d", + "gasUsed" => "0x79cbe9", + "hash" => "0x1b6fb99af0b51af6685a191b2f7bcba684f8565629bf084c70b2530479407455", + "logsBloom" => + "0x044d42d008801488400e1809190200a80d06105bc0c4100b047895c0d518327048496108388040140010b8208006288102e206160e21052322440924002090c1c808a0817405ab238086d028211014058e949401012403210314896702d06880c815c3060a0f0809987c81044488292cc11d57882c912a808ca10471c84460460040000c0001012804022000a42106591881d34407420ba401e1c08a8d00a000a34c11821a80222818a4102152c8a0c044032080c6462644223104d618e0e544072008120104408205c60510542264808488220403000106281a0290404220112c10b080145028c8000300b18a2c8280701c882e702210b00410834840108084", + "miner" => "0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c", + "mixHash" => "0xda53ae7c2b3c529783d6cdacdb90587fd70eb651c0f04253e8ff17de97844010", + "nonce" => "0x0946e5f01fce12bc", + "number" => "0x708677", + "parentHash" => "0x62543e836e0ef7edfa9e38f26526092c4be97efdf5ba9e0f53a4b0b7d5bc930a", + "receiptsRoot" => "0xa7d2b82bd8526de11736c18bd5cc8cfe2692106c4364526f3310ad56d78669c4", + "sealFields" => [ + "0xa0da53ae7c2b3c529783d6cdacdb90587fd70eb651c0f04253e8ff17de97844010", + "0x880946e5f01fce12bc" + ], + "sha3Uncles" => "0x483a8a21a5825ad270f358b3ea56e060bbb8b3082d9a92ec8fa17a5c7e6fc1b6", + "size" => "0x544c", + "stateRoot" => "0x85daa9cd528004c1609d4cb3520fd958e85983bb4183124a4a9f7137fd39c691", + "timestamp" => "0x5c8bc76e", + "totalDifficulty" => "0x201a42c35142ae94458", + "transactions" => [], + "transactionsRoot" => "0xcd6c12fa43cd4e92ad5c0bf232b30488bbcbfe273c5b4af0366fced0767d54db", + "uncles" => [] + } } - } - %{id: id, method: "trace_block"} -> - block_quantity = integer_to_quantity(block_number) - _res = eth_block_number_fake_response(block_quantity) + %{id: id, method: "trace_block"} -> + block_quantity = integer_to_quantity(block_number) + _res = eth_block_number_fake_response(block_quantity) - %{ - id: id, - result: [ - %{ - "action" => %{ - "author" => "0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c", - "rewardType" => "block", - "value" => "0x1d7d843dc3b48000" - }, - "blockHash" => "0x1b6fb99af0b51af6685a191b2f7bcba684f8565629bf084c70b2530479407455", - "blockNumber" => block_number, - "subtraces" => 0, - "traceAddress" => [], - "type" => "reward" - }, - %{ - "action" => %{ - "author" => "0xea674fdde714fd979de3edf0f56aa9716b898ec8", - "rewardType" => "uncle", - "value" => "0x14d1120d7b160000" + %{ + id: id, + result: [ + %{ + "action" => %{ + "author" => "0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c", + "rewardType" => "block", + "value" => "0x1d7d843dc3b48000" + }, + "blockHash" => "0x1b6fb99af0b51af6685a191b2f7bcba684f8565629bf084c70b2530479407455", + "blockNumber" => block_number, + "subtraces" => 0, + "traceAddress" => [], + "type" => "reward" }, - "blockHash" => "0x1b6fb99af0b51af6685a191b2f7bcba684f8565629bf084c70b2530479407455", - "blockNumber" => block_number, - "subtraces" => 0, - "traceAddress" => [], - "type" => "reward" - }, - %{ - "action" => %{ - "author" => "0xea674fdde714fd979de3edf0f56aa9716b898ec8", - "rewardType" => "uncle", - "value" => "0x18493fba64ef0000" + %{ + "action" => %{ + "author" => "0xea674fdde714fd979de3edf0f56aa9716b898ec8", + "rewardType" => "uncle", + "value" => "0x14d1120d7b160000" + }, + "blockHash" => "0x1b6fb99af0b51af6685a191b2f7bcba684f8565629bf084c70b2530479407455", + "blockNumber" => block_number, + "subtraces" => 0, + "traceAddress" => [], + "type" => "reward" }, - "blockHash" => "0x1b6fb99af0b51af6685a191b2f7bcba684f8565629bf084c70b2530479407455", - "blockNumber" => block_number, - "subtraces" => 0, - "traceAddress" => [], - "type" => "reward" - } - ] - } - end)} + %{ + "action" => %{ + "author" => "0xea674fdde714fd979de3edf0f56aa9716b898ec8", + "rewardType" => "uncle", + "value" => "0x18493fba64ef0000" + }, + "blockHash" => "0x1b6fb99af0b51af6685a191b2f7bcba684f8565629bf084c70b2530479407455", + "blockNumber" => block_number, + "subtraces" => 0, + "traceAddress" => [], + "type" => "reward" + } + ] + } + end)} end) end @@ -797,6 +777,387 @@ defmodule Indexer.Block.FetcherTest do end end + if Application.compile_env(:explorer, :chain_type) == :celo do + describe "import_range/2 celo" do + setup %{json_rpc_named_arguments: json_rpc_named_arguments} do + CoinBalanceCatchup.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + ContractCode.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + InternalTransaction.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + Token.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + TokenBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + ReplacedTransaction.Supervisor.Case.start_supervised!() + + UncleBlock.Supervisor.Case.start_supervised!( + block_fetcher: %Fetcher{json_rpc_named_arguments: json_rpc_named_arguments} + ) + + Application.put_env(:indexer, Indexer.Fetcher.Celo.EpochBlockOperations.Supervisor, disabled?: true) + + maybe_set_celo_core_contracts_env() + + %{ + block_fetcher: %Fetcher{ + broadcast: false, + callback_module: Indexer.Block.Catchup.Fetcher, + json_rpc_named_arguments: json_rpc_named_arguments + } + } + end + + test "can import range with all synchronous imported schemas", %{ + block_fetcher: %Fetcher{json_rpc_named_arguments: json_rpc_named_arguments} = block_fetcher + } do + block_number = @first_full_block_number + + if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do + case Keyword.fetch!(json_rpc_named_arguments, :variant) do + EthereumJSONRPC.Nethermind -> + block_quantity = integer_to_quantity(block_number) + from_address_hash = "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca" + to_address_hash = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + transaction_hash = "0x53bd884872de3e488692881baeec262e7b95234d3965248c39fe992fffd433e5" + gas_token_contract_address_hash = "0x471ece3750da237f93b8e339c536989b8978a438" + + transaction = %{ + "blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd", + "blockNumber" => "0x25", + "chainId" => "0x4d", + "condition" => nil, + "creates" => nil, + "from" => from_address_hash, + "gas" => "0x47b760", + "gasPrice" => "0x174876e800", + "hash" => transaction_hash, + "input" => "0x10855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef", + "nonce" => "0x4", + "publicKey" => + "0xe5d196ad4ceada719d9e592f7166d0c75700f6eab2e3c3de34ba751ea786527cb3f6eb96ad9fdfdb9989ff572df50f1c42ef800af9c5207a38b929aff969b5c9", + "r" => "0xa7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01", + "raw" => + "0xf88a0485174876e8008347b760948bf38d4764929064f2d4d3a56520a76ab3df415b80a410855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef81bea0a7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01a01f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f", + "s" => "0x1f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f", + "standardV" => "0x1", + "to" => to_address_hash, + "transactionIndex" => "0x0", + "v" => "0xbe", + "value" => "0x0", + # Celo-specific fields + "feeCurrency" => gas_token_contract_address_hash, + "gatewayFeeRecipient" => nil, + "gatewayFee" => "0x0", + "ethCompatible" => false + } + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn json, _options -> + assert [%{id: id, method: "eth_getBlockByNumber", params: [^block_quantity, true]}] = json + + {:ok, + [ + %{ + id: id, + jsonrpc: "2.0", + result: %{ + "author" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca", + "difficulty" => "0xfffffffffffffffffffffffffffffffe", + "extraData" => "0xd5830108048650617269747986312e32322e31826c69", + "gasLimit" => "0x69fe20", + "gasUsed" => "0xc512", + "hash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000200000000000000000000020000000000000000200000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca", + "number" => "0x25", + "parentHash" => "0xc37bbad7057945d1bf128c1ff009fb1ad632110bf6a000aac025a80f7766b66e", + "receiptsRoot" => "0xd300311aab7dcc98c05ac3f1893629b2c9082c189a0a0c76f4f63e292ac419d5", + "sealFields" => [ + "0x84120a71de", + "0xb841fcdb570511ec61edda93849bb7c6b3232af60feb2ea74e4035f0143ab66dfdd00f67eb3eda1adddbb6b572db1e0abd39ce00f9b3ccacb9f47973279ff306fe5401" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "fcdb570511ec61edda93849bb7c6b3232af60feb2ea74e4035f0143ab66dfdd00f67eb3eda1adddbb6b572db1e0abd39ce00f9b3ccacb9f47973279ff306fe5401", + "size" => "0x2cf", + "stateRoot" => "0x2cd84079b0d0c267ed387e3895fd1c1dc21ff82717beb1132adac64276886e19", + "step" => "302674398", + "timestamp" => "0x5a343956", + "totalDifficulty" => "0x24ffffffffffffffffffffffffedf78dfd", + "transactions" => [transaction], + "transactionsRoot" => "0x68e314a05495f390f9cd0c36267159522e5450d2adf254a74567b452e767bf34", + "uncles" => [] + } + } + ]} + end) + |> expect(:json_rpc, fn json, _options -> + assert [ + %{ + id: id, + method: "eth_getTransactionReceipt", + params: ["0x53bd884872de3e488692881baeec262e7b95234d3965248c39fe992fffd433e5"] + } + ] = json + + {:ok, + [ + %{ + id: id, + jsonrpc: "2.0", + result: %{ + "blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd", + "blockNumber" => "0x25", + "contractAddress" => nil, + "cumulativeGasUsed" => "0xc512", + "gasUsed" => "0xc512", + "logs" => [ + %{ + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b", + "blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd", + "blockNumber" => "0x25", + "data" => "0x000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef", + "logIndex" => "0x0", + "topics" => ["0x600bcf04a13e752d1e3670a5a9f1c21177ca2a93c6f5391d4f1298d098097c22"], + "transactionHash" => "0x53bd884872de3e488692881baeec262e7b95234d3965248c39fe992fffd433e5", + "transactionIndex" => "0x0", + "transactionLogIndex" => "0x0" + } + ], + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000200000000000000000000020000000000000000200000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "root" => nil, + "status" => "0x1", + "transactionHash" => "0x53bd884872de3e488692881baeec262e7b95234d3965248c39fe992fffd433e5", + "transactionIndex" => "0x0" + } + } + ]} + end) + |> expect(:json_rpc, fn [%{id: id, method: "trace_block", params: [^block_quantity]}], _options -> + {:ok, [%{id: id, result: []}]} + end) + # async requests need to be grouped in one expect because the order is non-deterministic while multiple expect + # calls on the same name/arity are used in order + |> expect(:json_rpc, 10, fn json, _options -> + case json do + [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: [^block_quantity, true] + } + ] -> + {:ok, + [ + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "author" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca", + "difficulty" => "0xfffffffffffffffffffffffffffffffe", + "extraData" => "0xd5830108048650617269747986312e32322e31826c69", + "gasLimit" => "0x69fe20", + "gasUsed" => "0xc512", + "hash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000200000000000000000000020000000000000000200000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca", + "number" => "0x25", + "parentHash" => "0xc37bbad7057945d1bf128c1ff009fb1ad632110bf6a000aac025a80f7766b66e", + "receiptsRoot" => "0xd300311aab7dcc98c05ac3f1893629b2c9082c189a0a0c76f4f63e292ac419d5", + "sealFields" => [ + "0x84120a71de", + "0xb841fcdb570511ec61edda93849bb7c6b3232af60feb2ea74e4035f0143ab66dfdd00f67eb3eda1adddbb6b572db1e0abd39ce00f9b3ccacb9f47973279ff306fe5401" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "fcdb570511ec61edda93849bb7c6b3232af60feb2ea74e4035f0143ab66dfdd00f67eb3eda1adddbb6b572db1e0abd39ce00f9b3ccacb9f47973279ff306fe5401", + "size" => "0x2cf", + "stateRoot" => "0x2cd84079b0d0c267ed387e3895fd1c1dc21ff82717beb1132adac64276886e19", + "step" => "302674398", + "timestamp" => "0x5a343956", + "totalDifficulty" => "0x24ffffffffffffffffffffffffedf78dfd", + "transactions" => [transaction], + "transactionsRoot" => "0x68e314a05495f390f9cd0c36267159522e5450d2adf254a74567b452e767bf34", + "uncles" => [] + } + } + ]} + + [%{id: id, method: "eth_getBalance", params: [^to_address_hash, ^block_quantity]}] -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x1"}]} + + [%{id: id, method: "eth_getBalance", params: [^from_address_hash, ^block_quantity]}] -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0xd0d4a965ab52d8cd740000"}]} + + [%{id: id, method: "trace_replayBlockTransactions", params: [^block_quantity, ["trace"]]}] -> + {:ok, + [ + %{ + id: id, + jsonrpc: "2.0", + result: [ + %{ + "output" => "0x", + "stateDiff" => nil, + "trace" => [ + %{ + "action" => %{ + "callType" => "call", + "from" => from_address_hash, + "gas" => "0x475ec8", + "input" => + "0x10855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef", + "to" => to_address_hash, + "value" => "0x0" + }, + "result" => %{"gasUsed" => "0x6c7a", "output" => "0x"}, + "subtraces" => 0, + "traceAddress" => [], + "type" => "call" + } + ], + "transactionHash" => transaction_hash, + "vmTrace" => nil + } + ] + } + ]} + + requests -> + {:ok, + Enum.map(requests, fn + %{id: id, method: "eth_call", params: [%{data: "0x313ce567", to: _}, "latest"]} -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000012" + } + + %{id: id, method: "eth_call", params: [%{data: "0x06fdde03", to: _}, "latest"]} -> + %{ + id: id, + result: + "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000642616e636f720000000000000000000000000000000000000000000000000000" + } + + %{id: id, method: "eth_call", params: [%{data: "0x95d89b41", to: _}, "latest"]} -> + %{ + id: id, + result: + "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003424e540000000000000000000000000000000000000000000000000000000000" + } + + %{id: id, method: "eth_call", params: [%{data: "0x18160ddd", to: _}, "latest"]} -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000" + } + end)} + end + end) + + variant -> + raise ArgumentError, "Unsupported variant (#{variant})" + end + end + + case Keyword.fetch!(json_rpc_named_arguments, :variant) do + EthereumJSONRPC.Nethermind -> + gateway_fee_value = Decimal.new(0) + + assert {:ok, + %{ + inserted: %{ + addresses: [ + %Address{ + hash: + %Explorer.Chain.Hash{ + byte_count: 20, + bytes: + <<139, 243, 141, 71, 100, 146, 144, 100, 242, 212, 211, 165, 101, 32, 167, 106, 179, + 223, 65, 91>> + } = first_address_hash + }, + %Address{ + hash: + %Explorer.Chain.Hash{ + byte_count: 20, + bytes: + <<232, 221, 197, 199, 162, 210, 240, 215, 169, 121, 132, 89, 192, 16, 79, 223, 94, + 152, 122, 202>> + } = second_address_hash + } + ], + blocks: [ + %Chain.Block{ + hash: %Explorer.Chain.Hash{ + byte_count: 32, + bytes: + <<246, 180, 184, 200, 141, 243, 235, 210, 82, 236, 71, 99, 40, 51, 77, 192, 38, 207, + 102, 96, 106, 132, 251, 118, 155, 61, 60, 188, 204, 132, 113, 189>> + } + } + ], + logs: [ + %Log{ + index: 0, + transaction_hash: %Explorer.Chain.Hash{ + byte_count: 32, + bytes: + <<83, 189, 136, 72, 114, 222, 62, 72, 134, 146, 136, 27, 174, 236, 38, 46, 123, 149, 35, + 77, 57, 101, 36, 140, 57, 254, 153, 47, 255, 212, 51, 229>> + } + } + ], + transactions: [ + %Transaction{ + block_number: block_number, + index: 0, + hash: %Explorer.Chain.Hash{ + byte_count: 32, + bytes: + <<83, 189, 136, 72, 114, 222, 62, 72, 134, 146, 136, 27, 174, 236, 38, 46, 123, 149, 35, + 77, 57, 101, 36, 140, 57, 254, 153, 47, 255, 212, 51, 229>> + }, + gas_token_contract_address_hash: %Explorer.Chain.Hash{ + byte_count: 20, + bytes: + <<71, 30, 206, 55, 80, 218, 35, 127, 147, 184, 227, 57, 197, 54, 152, 155, 137, 120, + 164, 56>> + }, + gas_fee_recipient_address_hash: nil, + gateway_fee: %Explorer.Chain.Wei{value: ^gateway_fee_value} + } + ] + }, + errors: [] + }} = Fetcher.fetch_and_import_range(block_fetcher, block_number..block_number) + + wait_for_tasks(InternalTransaction) + wait_for_tasks(CoinBalanceCatchup) + + assert Repo.aggregate(Chain.Block, :count, :hash) == 1 + assert Repo.aggregate(Address, :count, :hash) == 2 + assert Chain.log_count() == 1 + assert Repo.aggregate(Transaction, :count, :hash) == 1 + + first_address = Repo.get!(Address, first_address_hash) + + assert first_address.fetched_coin_balance == %Wei{value: Decimal.new(1)} + assert first_address.fetched_coin_balance_block_number == block_number + + second_address = Repo.get!(Address, second_address_hash) + + assert second_address.fetched_coin_balance == %Wei{value: Decimal.new(252_460_837_000_000_000_000_000_000)} + assert second_address.fetched_coin_balance_block_number == block_number + + variant -> + raise ArgumentError, "Unsupported variant (#{variant})" + end + end + end + end + defp wait_until(timeout, producer) do parent = self() ref = make_ref() @@ -861,4 +1222,28 @@ defmodule Indexer.Block.FetcherTest do } } end + + def maybe_set_celo_core_contracts_env do + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, + contracts: %{ + "addresses" => %{ + "Accounts" => [], + "Election" => [], + "EpochRewards" => [], + "FeeHandler" => [], + "GasPriceMinimum" => [], + "GoldToken" => [], + "Governance" => [], + "LockedGold" => [], + "Reserve" => [], + "StableToken" => [], + "Validators" => [] + } + } + ) + + on_exit(fn -> + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, contracts: %{}) + end) + end end diff --git a/apps/indexer/test/indexer/block/realtime/fetcher_test.exs b/apps/indexer/test/indexer/block/realtime/fetcher_test.exs index 71b671ee27df..12e164b634a7 100644 --- a/apps/indexer/test/indexer/block/realtime/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/realtime/fetcher_test.exs @@ -4,11 +4,19 @@ defmodule Indexer.Block.Realtime.FetcherTest do import Mox - alias Explorer.Chain + alias Explorer.{Chain, Factory} alias Explorer.Chain.{Address, Transaction, Wei} alias Indexer.Block.Realtime alias Indexer.Fetcher.CoinBalance.Realtime, as: CoinBalanceRealtime - alias Indexer.Fetcher.{ContractCode, InternalTransaction, ReplacedTransaction, Token, TokenBalance, UncleBlock} + + alias Indexer.Fetcher.{ + ContractCode, + InternalTransaction, + ReplacedTransaction, + Token, + TokenBalance, + UncleBlock + } @moduletag capture_log: true @@ -38,6 +46,30 @@ defmodule Indexer.Block.Realtime.FetcherTest do TokenBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) CoinBalanceRealtime.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + Application.put_env(:indexer, Indexer.Fetcher.Celo.EpochBlockOperations.Supervisor, disabled?: true) + + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, + contracts: %{ + "addresses" => %{ + "Accounts" => [], + "Election" => [], + "EpochRewards" => [], + "FeeHandler" => [], + "GasPriceMinimum" => [], + "GoldToken" => [], + "Governance" => [], + "LockedGold" => [], + "Reserve" => [], + "StableToken" => [], + "Validators" => [] + } + } + ) + + on_exit(fn -> + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, contracts: %{}) + end) + %{block_fetcher: block_fetcher, json_rpc_named_arguments: core_json_rpc_named_arguments} end @@ -59,6 +91,36 @@ defmodule Indexer.Block.Realtime.FetcherTest do ReplacedTransaction.Supervisor.Case.start_supervised!() + # In CELO network, there is a token duality feature where CELO can be used + # as both a native chain currency and as an ERC-20 token (GoldToken). + # Transactions that transfer CELO are also counted as token transfers, and + # the TokenInstance fetcher is called. However, for simplicity, we disable + # it in this test. + Application.put_env(:indexer, Indexer.Fetcher.TokenInstance.Realtime.Supervisor, disabled?: true) + + on_exit(fn -> + Application.put_env(:indexer, Indexer.Fetcher.TokenInstance.Realtime.Supervisor, disabled?: false) + end) + + celo_token_address_hash = Factory.address_hash() + + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, + contracts: %{ + "addresses" => %{ + "GoldToken" => [ + %{ + "address" => to_string(celo_token_address_hash), + "updated_at_block_number" => 3_946_079 + } + ] + } + } + ) + + on_exit(fn -> + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, contracts: %{}) + end) + if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do EthereumJSONRPC.Mox |> expect(:json_rpc, fn [ @@ -474,12 +536,7 @@ defmodule Indexer.Block.Realtime.FetcherTest do assert {:ok, %{ inserted: %{ - addresses: [ - %Address{hash: first_address_hash}, - %Address{hash: second_address_hash}, - %Address{hash: third_address_hash}, - %Address{hash: fourth_address_hash} - ], + addresses: addresses, address_coin_balances: [ %{ address_hash: first_address_hash, @@ -503,6 +560,23 @@ defmodule Indexer.Block.Realtime.FetcherTest do }, errors: [] }} = Indexer.Block.Fetcher.fetch_and_import_range(block_fetcher, 3_946_079..3_946_080) + + unless Application.get_env(:explorer, :chain_type) == :celo do + assert [ + %Address{hash: ^first_address_hash}, + %Address{hash: ^second_address_hash}, + %Address{hash: ^third_address_hash}, + %Address{hash: ^fourth_address_hash} + ] = addresses + else + assert [ + %Address{hash: ^celo_token_address_hash}, + %Address{hash: ^first_address_hash}, + %Address{hash: ^second_address_hash}, + %Address{hash: ^third_address_hash}, + %Address{hash: ^fourth_address_hash} + ] = addresses + end end @tag :no_geth @@ -524,6 +598,36 @@ defmodule Indexer.Block.Realtime.FetcherTest do ReplacedTransaction.Supervisor.Case.start_supervised!() + # In CELO network, there is a token duality feature where CELO can be used + # as both a native chain currency and as an ERC-20 token (GoldToken). + # Transactions that transfer CELO are also counted as token transfers, and + # the TokenInstance fetcher is called. However, for simplicity, we disable + # it in this test. + Application.put_env(:indexer, Indexer.Fetcher.TokenInstance.Realtime.Supervisor, disabled?: true) + + on_exit(fn -> + Application.put_env(:indexer, Indexer.Fetcher.TokenInstance.Realtime.Supervisor, disabled?: false) + end) + + celo_token_address_hash = Factory.address_hash() + + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, + contracts: %{ + "addresses" => %{ + "GoldToken" => [ + %{ + "address" => to_string(celo_token_address_hash), + "updated_at_block_number" => 3_946_079 + } + ] + } + } + ) + + on_exit(fn -> + Application.put_env(:explorer, Explorer.Chain.Cache.CeloCoreContracts, contracts: %{}) + end) + if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do EthereumJSONRPC.Mox |> expect(:json_rpc, fn [ @@ -676,12 +780,7 @@ defmodule Indexer.Block.Realtime.FetcherTest do assert {:ok, %{ inserted: %{ - addresses: [ - %Address{hash: first_address_hash}, - %Address{hash: second_address_hash}, - %Address{hash: third_address_hash}, - %Address{hash: fourth_address_hash} - ], + addresses: addresses, address_coin_balances: [ %{ address_hash: first_address_hash, @@ -718,6 +817,23 @@ defmodule Indexer.Block.Realtime.FetcherTest do errors: [] }} = Indexer.Block.Fetcher.fetch_and_import_range(block_fetcher, 3_946_079..3_946_080) + unless Application.get_env(:explorer, :chain_type) == :celo do + assert [ + %Address{hash: ^first_address_hash}, + %Address{hash: ^second_address_hash}, + %Address{hash: ^third_address_hash}, + %Address{hash: ^fourth_address_hash} + ] = addresses + else + assert [ + %Address{hash: ^celo_token_address_hash}, + %Address{hash: ^first_address_hash}, + %Address{hash: ^second_address_hash}, + %Address{hash: ^third_address_hash}, + %Address{hash: ^fourth_address_hash} + ] = addresses + end + Application.put_env(:indexer, :fetch_rewards_way, nil) end end diff --git a/apps/indexer/test/indexer/fetcher/celo/epoch_block_operations_test.exs b/apps/indexer/test/indexer/fetcher/celo/epoch_block_operations_test.exs new file mode 100644 index 000000000000..d812cc92d3d7 --- /dev/null +++ b/apps/indexer/test/indexer/fetcher/celo/epoch_block_operations_test.exs @@ -0,0 +1,46 @@ +defmodule Indexer.Fetcher.Celo.EpochBlockOperationsTest do + # MUST be `async: false` so that {:shared, pid} is set for connection to allow CoinBalanceFetcher's self-send to have + # connection allowed immediately. + # use EthereumJSONRPC.Case, async: false + + use EthereumJSONRPC.Case + use Explorer.DataCase + + import Mox + # import EthereumJSONRPC, only: [integer_to_quantity: 1] + + import Explorer.Chain.Celo.Helper, only: [blocks_per_epoch: 0] + + alias Indexer.Fetcher.Celo.EpochBlockOperations + + # @moduletag :capture_log + + # MUST use global mode because we aren't guaranteed to get `start_supervised`'s pid back fast enough to `allow` it to + # use expectations and stubs from test's pid. + setup :set_mox_global + + setup :verify_on_exit! + + if Application.compile_env(:explorer, :chain_type) == :celo do + describe "init/3" do + test "buffers blocks with pending epoch operation", %{ + json_rpc_named_arguments: json_rpc_named_arguments + } do + unfetched = insert(:block, number: 1 * blocks_per_epoch()) + insert(:celo_pending_epoch_block_operation, block: unfetched) + + assert [ + %{ + block_number: unfetched.number, + block_hash: unfetched.hash + } + ] == + EpochBlockOperations.init( + [], + fn block_number, acc -> [block_number | acc] end, + json_rpc_named_arguments + ) + end + end + end +end diff --git a/apps/indexer/test/indexer/fetcher/internal_transaction_test.exs b/apps/indexer/test/indexer/fetcher/internal_transaction_test.exs index fada8686e3cd..66de4d813a21 100644 --- a/apps/indexer/test/indexer/fetcher/internal_transaction_test.exs +++ b/apps/indexer/test/indexer/fetcher/internal_transaction_test.exs @@ -10,7 +10,7 @@ defmodule Indexer.Fetcher.InternalTransactionTest do alias Explorer.Chain.{Block, PendingBlockOperation} alias Explorer.Chain.Import.Runner.Blocks alias Indexer.Fetcher.CoinBalance.Catchup, as: CoinBalanceCatchup - alias Indexer.Fetcher.{InternalTransaction, PendingTransaction} + alias Indexer.Fetcher.{InternalTransaction, PendingTransaction, TokenBalance} # MUST use global mode because we aren't guaranteed to get PendingTransactionFetcher's pid back fast enough to `allow` # it to use expectations and stubs from test's pid. @@ -68,6 +68,7 @@ defmodule Indexer.Fetcher.InternalTransactionTest do CoinBalanceCatchup.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) PendingTransaction.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + start_token_balance_fetcher(json_rpc_named_arguments) wait_for_results(fn -> Repo.one!(from(transaction in Explorer.Chain.Transaction, where: is_nil(transaction.block_hash), limit: 1)) @@ -106,6 +107,8 @@ defmodule Indexer.Fetcher.InternalTransactionTest do block = insert(:block, number: block_number) insert(:pending_block_operation, block_hash: block.hash, block_number: block.number) + start_token_balance_fetcher(json_rpc_named_arguments) + assert :ok = InternalTransaction.run([block_number], json_rpc_named_arguments) assert InternalTransaction.init( @@ -174,6 +177,8 @@ defmodule Indexer.Fetcher.InternalTransactionTest do block_hash = block.hash insert(:pending_block_operation, block_hash: block_hash, block_number: block.number) + start_token_balance_fetcher(json_rpc_named_arguments) + assert %{block_hash: block_hash} = Repo.get(PendingBlockOperation, block_hash) assert :ok == InternalTransaction.run([block.number], json_rpc_named_arguments) @@ -278,6 +283,7 @@ defmodule Indexer.Fetcher.InternalTransactionTest do end CoinBalanceCatchup.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + start_token_balance_fetcher(json_rpc_named_arguments) assert %{block_hash: block_hash} = Repo.get(PendingBlockOperation, block_hash) @@ -608,4 +614,15 @@ defmodule Indexer.Fetcher.InternalTransactionTest do assert last_int_tx.call_type == :invalid end end + + # Due to token-duality feature in Celo network (native coin transfers are + # treated as token transfers), we need to fetch updated token balances after + # parsing the internal transactions + if Application.compile_env(:explorer, :chain_type) == :celo do + defp start_token_balance_fetcher(json_rpc_named_arguments) do + TokenBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + end + else + defp start_token_balance_fetcher(_json_rpc_named_arguments), do: :ok + end end diff --git a/apps/indexer/test/support/indexer/fetcher/celo_epoch_rewards_supervisor_case.ex b/apps/indexer/test/support/indexer/fetcher/celo_epoch_rewards_supervisor_case.ex new file mode 100644 index 000000000000..507981237dd2 --- /dev/null +++ b/apps/indexer/test/support/indexer/fetcher/celo_epoch_rewards_supervisor_case.ex @@ -0,0 +1,17 @@ +defmodule Indexer.Fetcher.Celo.EpochBlockOperations.Supervisor.Case do + alias Indexer.Fetcher.Celo.EpochBlockOperations + + def start_supervised!(fetcher_arguments \\ []) when is_list(fetcher_arguments) do + merged_fetcher_arguments = + Keyword.merge( + fetcher_arguments, + flush_interval: 50, + max_batch_size: 1, + max_concurrency: 1 + ) + + [merged_fetcher_arguments] + |> EpochBlockOperations.Supervisor.child_spec() + |> ExUnit.Callbacks.start_supervised!() + end +end diff --git a/config/config_helper.exs b/config/config_helper.exs index d75f9aaba7e2..31570bd05339 100644 --- a/config/config_helper.exs +++ b/config/config_helper.exs @@ -21,6 +21,7 @@ defmodule ConfigHelper do :filecoin -> base_repos ++ [Explorer.Repo.Filecoin] :stability -> base_repos ++ [Explorer.Repo.Stability] :zksync -> base_repos ++ [Explorer.Repo.ZkSync] + :celo -> base_repos ++ [Explorer.Repo.Celo] :arbitrum -> base_repos ++ [Explorer.Repo.Arbitrum] _ -> base_repos end @@ -252,7 +253,7 @@ defmodule ConfigHelper do end @spec parse_json_env_var(String.t(), String.t()) :: any() - def parse_json_env_var(env_var, default_value) do + def parse_json_env_var(env_var, default_value \\ "{}") do env_var |> safe_get_env(default_value) |> Jason.decode!() @@ -292,7 +293,8 @@ defmodule ConfigHelper do "stability", "suave", "zetachain", - "zksync" + "zksync", + "celo" ] @spec chain_type() :: atom() | nil diff --git a/config/runtime.exs b/config/runtime.exs index db25f492158e..22943edddc2d 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -451,6 +451,9 @@ config :explorer, Explorer.Chain.Cache.Uncles, ttl_check_interval: ConfigHelper.cache_ttl_check_interval(disable_indexer?), global_ttl: ConfigHelper.cache_global_ttl(disable_indexer?) +config :explorer, Explorer.Chain.Cache.CeloCoreContracts, + contracts: ConfigHelper.parse_json_env_var("CELO_CORE_CONTRACTS") + config :explorer, Explorer.ThirdPartyIntegrations.Sourcify, server_url: System.get_env("SOURCIFY_SERVER_URL") || "https://sourcify.dev/server", enabled: ConfigHelper.parse_bool_env_var("SOURCIFY_INTEGRATION_ENABLED"), @@ -1003,6 +1006,19 @@ config :indexer, Indexer.Fetcher.PolygonZkevm.TransactionBatch.Supervisor, ConfigHelper.chain_type() == :polygon_zkevm && ConfigHelper.parse_bool_env_var("INDEXER_POLYGON_ZKEVM_BATCHES_ENABLED") +config :indexer, Indexer.Fetcher.Celo.ValidatorGroupVotes, + batch_size: ConfigHelper.parse_integer_env_var("INDEXER_CELO_VALIDATOR_GROUP_VOTES_BATCH_SIZE", 200_000) + +celo_epoch_fetchers_enabled? = + ConfigHelper.chain_type() == :celo and + not ConfigHelper.parse_bool_env_var("INDEXER_DISABLE_CELO_EPOCH_FETCHER") + +config :indexer, Indexer.Fetcher.Celo.ValidatorGroupVotes.Supervisor, enabled: celo_epoch_fetchers_enabled? + +config :indexer, Indexer.Fetcher.Celo.EpochBlockOperations.Supervisor, + enabled: celo_epoch_fetchers_enabled?, + disabled?: not celo_epoch_fetchers_enabled? + Code.require_file("#{config_env()}.exs", "config/runtime") for config <- "../apps/*/config/runtime/#{config_env()}.exs" |> Path.expand(__DIR__) |> Path.wildcard() do diff --git a/config/runtime/dev.exs b/config/runtime/dev.exs index 2c831a5f36e4..b4ce37657a90 100644 --- a/config/runtime/dev.exs +++ b/config/runtime/dev.exs @@ -126,6 +126,15 @@ config :explorer, Explorer.Repo.ZkSync, # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type pool_size: 1 +# Configure Celo database +config :explorer, Explorer.Repo.Celo, + database: database, + hostname: hostname, + url: System.get_env("DATABASE_URL"), + # actually this repo is not started, and its pool size remains unused. + # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type + pool_size: 1 + # Configure Rootstock database config :explorer, Explorer.Repo.RSK, database: database, diff --git a/config/runtime/prod.exs b/config/runtime/prod.exs index b88b35471925..6f240fee4848 100644 --- a/config/runtime/prod.exs +++ b/config/runtime/prod.exs @@ -96,6 +96,14 @@ config :explorer, Explorer.Repo.ZkSync, pool_size: 1, ssl: ExplorerConfigHelper.ssl_enabled?() +# Configures Celo database +config :explorer, Explorer.Repo.Celo, + url: System.get_env("DATABASE_URL"), + # actually this repo is not started, and its pool size remains unused. + # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type + pool_size: 1, + ssl: ExplorerConfigHelper.ssl_enabled?() + # Configures Rootstock database config :explorer, Explorer.Repo.RSK, url: System.get_env("DATABASE_URL"), diff --git a/cspell.json b/cspell.json index 91a90a03e643..d6dee164e4af 100644 --- a/cspell.json +++ b/cspell.json @@ -19,6 +19,7 @@ "AIRTABLE", "Aiubo", "alloc", + "alfajores", "amzootyukbugmx", "anytrust", "apikey", @@ -83,6 +84,7 @@ "CBOR", "Celestia", "cellspacing", + "celo", "certifi", "cfasync", "chainid", @@ -168,6 +170,7 @@ "enetunreach", "enoent", "epns", + "epochrewards", "Erigon", "errora", "errorb", @@ -192,9 +195,10 @@ "falala", "feelin", "FEVM", - "filecoin", "Filecoin", "Filesize", + "filecoin", + "fixidity", "fkey", "Floki", "fontawesome", @@ -221,6 +225,7 @@ "gettxinfo", "gettxreceiptstatus", "giga", + "goldtoken", "gqz", "granitegrey", "graphiql", @@ -284,6 +289,7 @@ "listcontracts", "lkve", "llhauc", + "lockedgold", "loggable", "LPAD", "LUKSO", @@ -428,6 +434,7 @@ "redix", "refetched", "regclass", + "registryproxy", "REINDEX", "relname", "relpages", @@ -476,6 +483,7 @@ "sourcify", "splitted", "srcset", + "stabletoken", "staker", "stakers", "stateroot", diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 2b23e63402d7..205bf8407e16 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -248,6 +248,9 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_ARBITRUM_BRIDGE_MESSAGES_TRACKING_ENABLED= # INDEXER_ARBITRUM_TRACKING_MESSAGES_ON_L1_RECHECK_INTERVAL= # INDEXER_ARBITRUM_MISSED_MESSAGES_RECHECK_INTERVAL= +# CELO_CORE_CONTRACTS= +# INDEXER_CELO_VALIDATOR_GROUP_VOTES_BATCH_SIZE=200000 +# INDEXER_DISABLE_CELO_EPOCH_FETCHER=false # INDEXER_ARBITRUM_MISSED_MESSAGES_BLOCKS_DEPTH= # INDEXER_REALTIME_FETCHER_MAX_GAP= # INDEXER_FETCHER_INIT_QUERY_LIMIT= @@ -410,4 +413,4 @@ TENDERLY_CHAIN_PATH= # SANITIZE_INCORRECT_WETH_CONCURRENCY=1 # PUBLIC_METRICS_ENABLED= # PUBLIC_METRICS_UPDATE_PERIOD_HOURS= -# CSV_EXPORT_LIMIT= \ No newline at end of file +# CSV_EXPORT_LIMIT= From 3ab691d0e0c3bad274ef5f597d8624ae54e26660 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Tue, 6 Aug 2024 20:34:54 +0300 Subject: [PATCH 057/363] fix: Fix :checkout_timeout error on NFT fetching (#10429) --- apps/indexer/lib/indexer/application.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/indexer/lib/indexer/application.ex b/apps/indexer/lib/indexer/application.ex index 4003de22f9e8..2a688a1a6f03 100644 --- a/apps/indexer/lib/indexer/application.ex +++ b/apps/indexer/lib/indexer/application.ex @@ -44,7 +44,9 @@ defmodule Indexer.Application do ) + token_instance_fetcher_pool_size(Indexer.Fetcher.TokenInstance.LegacySanitize, nil) + token_instance_fetcher_pool_size(Indexer.Fetcher.TokenInstance.SanitizeERC1155, nil) + - token_instance_fetcher_pool_size(Indexer.Fetcher.TokenInstance.SanitizeERC721, nil) + token_instance_fetcher_pool_size(Indexer.Fetcher.TokenInstance.SanitizeERC721, nil) + 1 + + # + 1 (above in pool_size calculation) for the Indexer.Fetcher.OnDemand.TokenInstanceMetadataRefetch base_children = [ :hackney_pool.child_spec(:token_instance_fetcher, max_connections: pool_size), From 25339b9479e79bb51805cca44cd3f047bd09e7a9 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 7 Aug 2024 11:47:34 +0300 Subject: [PATCH 058/363] fix: Sanitize topic value before making db query (#10481) * Sanitize topic value before making db query * Process review comment --- apps/explorer/lib/explorer/etherscan/logs.ex | 42 ++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/apps/explorer/lib/explorer/etherscan/logs.ex b/apps/explorer/lib/explorer/etherscan/logs.ex index f463f8232c68..b22dbbd3b885 100644 --- a/apps/explorer/lib/explorer/etherscan/logs.ex +++ b/apps/explorer/lib/explorer/etherscan/logs.ex @@ -275,6 +275,8 @@ defmodule Explorer.Etherscan.Logs do } defp where_topic_match(query, filter) do + filter = sanitize_filter_topics(filter) + case Enum.filter(@topics, &filter[&1]) do [] -> query @@ -287,6 +289,46 @@ defmodule Explorer.Etherscan.Logs do end end + defp sanitize_filter_topics(filter) do + @topics + |> Enum.reduce(filter, fn topic, acc -> + topic_value = filter[topic] + + sanitized_value = + topic_value + |> List.wrap() + |> Enum.map(&sanitize_topic_value/1) + |> Enum.reject(&is_nil/1) + |> case do + [] -> nil + [topic] -> topic + topics -> topics + end + + Map.put(acc, topic, sanitized_value) + end) + end + + defp sanitize_topic_value(topic_value) do + case topic_value do + %Explorer.Chain.Hash{} -> + topic_value + + _ -> + sanitize_string_topic_value(topic_value) + end + end + + defp sanitize_string_topic_value(topic_value) do + case Chain.string_to_block_hash(topic_value) do + {:ok, _} -> + topic_value + + _ -> + nil + end + end + defp where_multiple_topics_match(query, filter) do Enum.reduce(Map.keys(@topic_operations), query, fn topic_operation, acc_query -> where_multiple_topics_match(acc_query, filter, topic_operation, filter[topic_operation]) From 0f7aba63370adf98ce7121ac31f32818dc591c1a Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 7 Aug 2024 11:48:38 +0300 Subject: [PATCH 059/363] fix: Fetch contract methods decoding candidates sorted by inserted_at (#10529) * Fetch contract methods decoding candidates sorted by inserted_at * Add contract_methods inserted_at B-tree index --- apps/explorer/lib/explorer/chain/contract_method.ex | 1 + ...240806162644_add_contract_methods_inserted_at_index.exs | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 apps/explorer/priv/repo/migrations/20240806162644_add_contract_methods_inserted_at_index.exs diff --git a/apps/explorer/lib/explorer/chain/contract_method.ex b/apps/explorer/lib/explorer/chain/contract_method.ex index c4716cde0043..8d1c3815716f 100644 --- a/apps/explorer/lib/explorer/chain/contract_method.ex +++ b/apps/explorer/lib/explorer/chain/contract_method.ex @@ -72,6 +72,7 @@ defmodule Explorer.Chain.ContractMethod do from( contract_method in __MODULE__, where: contract_method.identifier == ^method_id, + order_by: [asc: contract_method.inserted_at], limit: ^limit ) end diff --git a/apps/explorer/priv/repo/migrations/20240806162644_add_contract_methods_inserted_at_index.exs b/apps/explorer/priv/repo/migrations/20240806162644_add_contract_methods_inserted_at_index.exs new file mode 100644 index 000000000000..b025f59ebe37 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20240806162644_add_contract_methods_inserted_at_index.exs @@ -0,0 +1,7 @@ +defmodule Explorer.Repo.Migrations.AddContractMethodsInsertedAtIndex do + use Ecto.Migration + + def change do + create(index(:contract_methods, [:inserted_at])) + end +end From 678b544d7eb382d20ec7567c290e6963319ca6ac Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 7 Aug 2024 12:31:31 +0300 Subject: [PATCH 060/363] feat: No rate limit API key (#10515) * feat: No rate limit API key * Refactor, add tests * Add tests --- .../block_scout_web/views/access_helper.ex | 80 ++++++++++++------- .../views/access_helper_test.exs | 70 ++++++++++++++++ config/runtime.exs | 1 + docker-compose/envs/common-blockscout.env | 1 + 4 files changed, 124 insertions(+), 28 deletions(-) create mode 100644 apps/block_scout_web/test/block_scout_web/views/access_helper_test.exs diff --git a/apps/block_scout_web/lib/block_scout_web/views/access_helper.ex b/apps/block_scout_web/lib/block_scout_web/views/access_helper.ex index d56b2dd8c831..440ca2efed15 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/access_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/access_helper.ex @@ -53,13 +53,23 @@ defmodule BlockScoutWeb.AccessHelper do |> Conn.halt() end + @doc """ + Checks, if rate limit reached before making a new request. It is applied to GraphQL API. + """ + @spec check_rate_limit(Plug.Conn.t(), list()) :: :ok | :rate_limit_reached | true | false def check_rate_limit(conn, graphql?: true) do rate_limit_config = Application.get_env(:block_scout_web, Api.GraphQL) + no_rate_limit_api_key = rate_limit_config[:no_rate_limit_api_key] - if rate_limit_config[:rate_limit_disabled?] do - :ok - else - check_graphql_rate_limit_inner(conn, rate_limit_config) + cond do + rate_limit_config[:rate_limit_disabled?] -> + :ok + + check_no_rate_limit_api_key(conn, no_rate_limit_api_key) -> + :ok + + true -> + check_graphql_rate_limit_inner(conn, rate_limit_config) end end @@ -74,31 +84,48 @@ defmodule BlockScoutWeb.AccessHelper do ip_string = conn_to_ip_string(conn) plan = get_plan(conn.query_params) + user_api_key = get_api_key(conn) + cond do - check_api_key(conn) && get_api_key(conn) == static_api_key -> - rate_limit(static_api_key, limit_by_key, time_interval_limit) + check_api_key(conn) && user_api_key == static_api_key -> + rate_limit(static_api_key, time_interval_limit, limit_by_key) check_api_key(conn) && !is_nil(plan) -> - conn - |> get_api_key() - |> rate_limit(min(plan.max_req_per_second, limit_by_key), time_interval_limit) + rate_limit(user_api_key, time_interval_limit, min(plan.max_req_per_second, limit_by_key)) true -> rate_limit("graphql_#{ip_string}", limit_by_ip, time_interval_by_ip) == :ok && - rate_limit("graphql", global_limit, time_interval_limit) == :ok + rate_limit("graphql", time_interval_limit, global_limit) == :ok end end + @doc """ + Checks, if rate limit reached before making a new request. It is applied to API v1, ETH RPC API. + """ + @spec check_rate_limit(Plug.Conn.t()) :: :ok | :rate_limit_reached def check_rate_limit(conn) do rate_limit_config = Application.get_env(:block_scout_web, :api_rate_limit) + no_rate_limit_api_key = rate_limit_config[:no_rate_limit_api_key] - if rate_limit_config[:disabled] do - :ok - else - check_rate_limit_inner(conn, rate_limit_config) + cond do + rate_limit_config[:disabled] -> + :ok + + check_no_rate_limit_api_key(conn, no_rate_limit_api_key) -> + :ok + + true -> + check_rate_limit_inner(conn, rate_limit_config) end end + defp check_no_rate_limit_api_key(conn, no_rate_limit_api_key) do + user_api_key = get_api_key(conn) + + check_api_key(conn) && !is_nil(user_api_key) && String.trim(user_api_key) !== "" && + user_api_key == no_rate_limit_api_key + end + # credo:disable-for-next-line /Complexity/ defp check_rate_limit_inner(conn, rate_limit_config) do global_limit = rate_limit_config[:global_limit] @@ -117,26 +144,26 @@ defmodule BlockScoutWeb.AccessHelper do user_agent = get_user_agent(conn) + user_api_key = get_api_key(conn) + cond do - check_api_key(conn) && get_api_key(conn) == static_api_key -> - rate_limit(static_api_key, limit_by_key, time_interval_limit) + check_api_key(conn) && user_api_key == static_api_key -> + rate_limit(static_api_key, time_interval_limit, limit_by_key) check_api_key(conn) && !is_nil(plan) -> - conn - |> get_api_key() - |> rate_limit(plan.max_req_per_second, time_interval_limit) + rate_limit(user_api_key, time_interval_limit, plan.max_req_per_second) Enum.member?(whitelisted_ips(rate_limit_config), ip_string) -> - rate_limit(ip_string, limit_by_whitelisted_ip, time_interval_limit) + rate_limit(ip_string, time_interval_limit, limit_by_whitelisted_ip) api_v2_request?(conn) && !is_nil(token) && !is_nil(user_agent) -> - rate_limit(token, api_v2_ui_limit, time_interval_limit) + rate_limit(token, time_interval_limit, api_v2_ui_limit) api_v2_request?(conn) && !is_nil(user_agent) -> - rate_limit(ip_string, limit_by_ip, time_interval_by_ip) + rate_limit(ip_string, time_interval_by_ip, limit_by_ip) true -> - rate_limit("api", global_limit, time_interval_limit) + rate_limit("api", time_interval_limit, global_limit) end end @@ -156,11 +183,8 @@ defmodule BlockScoutWeb.AccessHelper do end end - defp rate_limit(key, limit, time_interval) do - rate_limit_inner(key, time_interval, limit) - end - - defp rate_limit_inner(key, time_interval, limit) do + @spec rate_limit(String.t(), integer(), integer()) :: :ok | :rate_limit_reached + defp rate_limit(key, time_interval, limit) do case Hammer.check_rate(key, time_interval, limit) do {:allow, _count} -> :ok diff --git a/apps/block_scout_web/test/block_scout_web/views/access_helper_test.exs b/apps/block_scout_web/test/block_scout_web/views/access_helper_test.exs new file mode 100644 index 000000000000..c18cb955e1d2 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/access_helper_test.exs @@ -0,0 +1,70 @@ +defmodule BlockScoutWeb.AccessHelperTest do + alias BlockScoutWeb.AccessHelper + use BlockScoutWeb.ConnCase + import Mox + + setup :verify_on_exit! + + setup do + configuration = Application.get_env(:block_scout_web, :api_rate_limit) + + on_exit(fn -> + Application.put_env(:block_scout_web, :api_rate_limit, configuration) + end) + + :ok + end + + describe "check_rate_limit/1" do + test "rate_limit_disabled", %{conn: conn} do + Application.put_env(:block_scout_web, :api_rate_limit, + global_limit: 0, + limit_by_key: 0, + limit_by_whitelisted_ip: 0, + time_interval_limit: 1_000, + disabled: true + ) + + assert AccessHelper.check_rate_limit(conn) == :ok + end + + test "no_rate_limit_api_key", %{conn: conn} do + Application.put_env(:block_scout_web, :api_rate_limit, + global_limit: 0, + limit_by_key: 0, + limit_by_whitelisted_ip: 0, + time_interval_limit: 1_000, + no_rate_limit_api_key: "123" + ) + + conn = %{conn | query_params: %{"apikey" => "123"}} + assert AccessHelper.check_rate_limit(conn) == :ok + end + + test "rate limit, if no_rate_limit_api_key is nil", %{conn: conn} do + Application.put_env(:block_scout_web, :api_rate_limit, + global_limit: 0, + limit_by_key: 0, + limit_by_whitelisted_ip: 0, + time_interval_limit: 1_000, + no_rate_limit_api_key: nil + ) + + conn = %{conn | query_params: %{"apikey" => nil}} + assert AccessHelper.check_rate_limit(conn) == :rate_limit_reached + end + + test "rate limit, if no_rate_limit_api_key is empty", %{conn: conn} do + Application.put_env(:block_scout_web, :api_rate_limit, + global_limit: 0, + limit_by_key: 0, + limit_by_whitelisted_ip: 0, + time_interval_limit: 1_000, + no_rate_limit_api_key: "" + ) + + conn = %{conn | query_params: %{"apikey" => " "}} + assert AccessHelper.check_rate_limit(conn) == :rate_limit_reached + end + end +end diff --git a/config/runtime.exs b/config/runtime.exs index 22943edddc2d..341c187423f7 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -105,6 +105,7 @@ config :block_scout_web, :api_rate_limit, limit_by_ip: ConfigHelper.parse_integer_env_var("API_RATE_LIMIT_BY_IP", 3000), time_interval_limit_by_ip: ConfigHelper.parse_time_env_var("API_RATE_LIMIT_BY_IP_TIME_INTERVAL", "5m"), static_api_key: System.get_env("API_RATE_LIMIT_STATIC_API_KEY"), + no_rate_limit_api_key: System.get_env("API_NO_RATE_LIMIT_API_KEY"), whitelisted_ips: System.get_env("API_RATE_LIMIT_WHITELISTED_IPS"), is_blockscout_behind_proxy: ConfigHelper.parse_bool_env_var("API_RATE_LIMIT_IS_BLOCKSCOUT_BEHIND_PROXY"), api_v2_ui_limit: ConfigHelper.parse_integer_env_var("API_RATE_LIMIT_UI_V2_WITH_TOKEN", 5), diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 205bf8407e16..7e7bda27e8eb 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -131,6 +131,7 @@ API_RATE_LIMIT_WHITELISTED_IPS= API_RATE_LIMIT_STATIC_API_KEY= API_RATE_LIMIT_UI_V2_WITH_TOKEN=5 API_RATE_LIMIT_BY_IP=3000 +API_NO_RATE_LIMIT_API_KEY= # API_GRAPHQL_ENABLED= # API_GRAPHQL_MAX_COMPLEXITY= # API_GRAPHQL_TOKEN_LIMIT= From 343b159c779e93559d20b30f5b974436e0005e33 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 8 Aug 2024 11:02:44 +0300 Subject: [PATCH 061/363] chore: Public metrics config API endpoint (#10568) --- .../controllers/api/v2/config_controller.ex | 8 ++++++++ .../lib/block_scout_web/routers/api_router.ex | 1 + 2 files changed, 9 insertions(+) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/config_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/config_controller.ex index dd695a20f37c..543b43d4f8dd 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/config_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/config_controller.ex @@ -16,4 +16,12 @@ defmodule BlockScoutWeb.API.V2.ConfigController do |> put_status(200) |> json(%{limit: limit}) end + + def public_metrics(conn, _params) do + public_metrics_update_period_hours = Application.get_env(:explorer, Explorer.Chain.Metrics)[:update_period_hours] + + conn + |> put_status(200) + |> json(%{update_period_hours: public_metrics_update_period_hours}) + end end diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index b976c38720ff..cc42a8db8f57 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -108,6 +108,7 @@ defmodule BlockScoutWeb.Routers.ApiRouter do scope "/config" do get("/backend-version", V2.ConfigController, :backend_version) get("/csv-export", V2.ConfigController, :csv_export) + get("/public-metrics", V2.ConfigController, :public_metrics) end scope "/transactions" do From 259a143c4f9cad22c04a01ba815b71cbc1d0a8f6 Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Thu, 8 Aug 2024 14:15:42 +0400 Subject: [PATCH 062/363] perf: speed up worlds list query (#10556) --- apps/explorer/lib/explorer/chain/mud.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain/mud.ex b/apps/explorer/lib/explorer/chain/mud.ex index 1ee67ee95c08..386cec09c59f 100644 --- a/apps/explorer/lib/explorer/chain/mud.ex +++ b/apps/explorer/lib/explorer/chain/mud.ex @@ -69,8 +69,9 @@ defmodule Explorer.Chain.Mud do paging_options = Keyword.get(options, :paging_options, Chain.default_paging_options()) Mud - |> select([r], r.address) |> distinct(true) + |> select([r], r.address) + |> where([r], r.table_id == ^@store_tables_table_id) |> page_worlds(paging_options) |> limit(^paging_options.page_size) |> Repo.Mud.all() From a13d43f0a93a8f4239954559604244a30f4dc699 Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Thu, 8 Aug 2024 04:44:24 -0600 Subject: [PATCH 063/363] fix: filter out incorrect L1-to-L2 Arbitrum messages (#10570) --- .../lib/explorer/chain/arbitrum/reader.ex | 15 +++++++++++++-- .../lib/indexer/fetcher/arbitrum/messaging.ex | 14 +++++++------- .../arbitrum/workers/historical_messages_on_l2.ex | 2 ++ 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/arbitrum/reader.ex b/apps/explorer/lib/explorer/chain/arbitrum/reader.ex index 1d5916fcf4ee..881ebe357e38 100644 --- a/apps/explorer/lib/explorer/chain/arbitrum/reader.ex +++ b/apps/explorer/lib/explorer/chain/arbitrum/reader.ex @@ -21,7 +21,11 @@ defmodule Explorer.Chain.Arbitrum.Reader do alias Explorer.Chain.Block, as: FullBlock alias Explorer.Chain.{Hash, Log, Transaction} - @to_l2_messages_transaction_types [100, 105] + # https://github.com/OffchainLabs/go-ethereum/blob/dff302de66598c36b964b971f72d35a95148e650/core/types/transaction.go#L44C2-L50 + @message_to_l2_eth_deposit 100 + @message_to_l2_submit_retryable_tx 105 + + @zero_wei 0 @doc """ Retrieves the number of the latest L1 block where an L1-to-L2 message was discovered. @@ -834,6 +838,10 @@ defmodule Explorer.Chain.Arbitrum.Reader do # table. A message is considered missed if there is a transaction without a # matching message record. # + # For transactions that could be considered ETH deposits, it checks + # that the message value is not zero, as transactions with a zero value + # cannot be a deposit. + # # ## Returns # - A query to retrieve missed L1-to-L2 messages. @spec missed_messages_to_l2_query() :: Ecto.Query.t() @@ -841,7 +849,10 @@ defmodule Explorer.Chain.Arbitrum.Reader do from(rollup_tx in Transaction, left_join: msg in Message, on: rollup_tx.hash == msg.completion_transaction_hash and msg.direction == :to_l2, - where: rollup_tx.type in @to_l2_messages_transaction_types and is_nil(msg.completion_transaction_hash) + where: + (rollup_tx.type == ^@message_to_l2_submit_retryable_tx or + (rollup_tx.type == ^@message_to_l2_eth_deposit and rollup_tx.value != ^@zero_wei)) and + is_nil(msg.completion_transaction_hash) ) end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/messaging.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/messaging.ex index e840f04fee16..29704aad8ca5 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/messaging.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/messaging.ex @@ -62,10 +62,10 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do @doc """ Filters a list of rollup transactions to identify L1-to-L2 messages and composes a map for each with the related message information. - This function filters through a list of rollup transactions, selecting those - with a non-nil `request_id`, indicating they are L1-to-L2 message completions. - These filtered transactions are then processed to construct a detailed message - structure for each. + This function filters a list of rollup transactions, selecting those where + `request_id` is not nil and is below 2^31, indicating they are L1-to-L2 + message completions. These filtered transactions are then processed to + construct a detailed message structure for each. ## Parameters - `transactions`: A list of rollup transaction entries. @@ -78,14 +78,14 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do this context are considered `:relayed` as they represent completed actions from L1 to L2. """ - @spec filter_l1_to_l2_messages(maybe_improper_list(min_transaction, [])) :: [arbitrum_message] - @spec filter_l1_to_l2_messages(maybe_improper_list(min_transaction, []), boolean()) :: [arbitrum_message] + @spec filter_l1_to_l2_messages([min_transaction()]) :: [arbitrum_message] + @spec filter_l1_to_l2_messages([min_transaction()], boolean()) :: [arbitrum_message] def filter_l1_to_l2_messages(transactions, report \\ true) when is_list(transactions) and is_boolean(report) do messages = transactions |> Enum.filter(fn tx -> - tx[:request_id] != nil + tx[:request_id] != nil and Bitwise.bsr(tx[:request_id], 31) == 0 end) |> handle_filtered_l1_to_l2_messages() diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex index 94457cd56941..659a787d3a7e 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex @@ -273,6 +273,8 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do messages ++ messages_acc end) + # Logging of zero messages is left by intent to reveal potential cases when + # not all transactions are recognized as completed L1-to-L2 messages. log_info("#{length(messages)} completions of L1-to-L2 messages will be imported") import_to_db(messages) end From 1fd2a8f3a285233fa782d1225a65b946b594e8fa Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Thu, 8 Aug 2024 17:09:38 +0400 Subject: [PATCH 064/363] fix: Fix internal transactions runner test for zetachain (#10576) --- .github/workflows/config.yml | 2 +- .../runner/internal_transactions_test.exs | 106 ++++++++++++------ 2 files changed, 73 insertions(+), 35 deletions(-) diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml index edefb0f392b7..c02cc24da1ac 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/config.yml @@ -49,7 +49,7 @@ jobs: // Add/remove CI matrix chain types here const defaultChainTypes = ["default"]; - const chainTypes = ["ethereum", "polygon_zkevm", "rsk", "stability", "filecoin", "optimism", "arbitrum", "celo"]; + const chainTypes = ["ethereum", "polygon_zkevm", "rsk", "stability", "filecoin", "optimism", "arbitrum", "celo", "zetachain"]; const extraChainTypes = ["suave", "polygon_edge"]; // Chain type matrix we use in master branch 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 89bdf98ea327..17144ccd2fa5 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 @@ -256,22 +256,83 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do assert PendingBlockOperation |> Repo.get(full_block.hash) |> is_nil() end - test "sets refetch_needed=true for blocks where not all transactions are filled" do - full_block = insert(:block) - transaction_a = insert(:transaction) |> with_block(full_block) - transaction_b = insert(:transaction) |> with_block(full_block) + if Application.compile_env(:explorer, :chain_type) != :zetachain do + test "sets refetch_needed=true for blocks where not all transactions are filled" do + full_block = insert(:block) + transaction_a = insert(:transaction) |> with_block(full_block) + transaction_b = insert(:transaction) |> with_block(full_block) - insert(:pending_block_operation, block_hash: full_block.hash, block_number: full_block.number) + insert(:pending_block_operation, block_hash: full_block.hash, block_number: full_block.number) + + transaction_a_changes = make_internal_transaction_changes(transaction_a, 0, nil) + + assert {:ok, _} = run_internal_transactions([transaction_a_changes]) + + assert from(i in InternalTransaction, where: i.transaction_hash == ^transaction_a.hash) + |> Repo.one() + |> is_nil() + + assert from(i in InternalTransaction, where: i.transaction_hash == ^transaction_b.hash) + |> Repo.one() + |> is_nil() + + assert %{consensus: true, refetch_needed: true} = Repo.get(Block, full_block.hash) + assert not is_nil(Repo.get(PendingBlockOperation, full_block.hash)) + end + + test "does not set refetch_needed=true from non-traceable blocks" do + original_config = Application.get_env(:indexer, :trace_block_ranges) + + full_block = insert(:block) + transaction_a = insert(:transaction) |> with_block(full_block) + transaction_b = insert(:transaction) |> with_block(full_block) + + Application.put_env(:indexer, :trace_block_ranges, "#{full_block.number + 1}..latest") + + insert(:pending_block_operation, block_hash: full_block.hash, block_number: full_block.number) + + transaction_a_changes = make_internal_transaction_changes(transaction_a, 0, nil) + + assert {:ok, _} = run_internal_transactions([transaction_a_changes]) + + assert from(i in InternalTransaction, where: i.transaction_hash == ^transaction_a.hash) + |> Repo.one() + |> is_nil() + + assert from(i in InternalTransaction, where: i.transaction_hash == ^transaction_b.hash) + |> Repo.one() + |> is_nil() + + assert %{consensus: true, refetch_needed: false} = Repo.get(Block, full_block.hash) + + on_exit(fn -> Application.put_env(:indexer, :trace_block_ranges, original_config) end) + end + end - transaction_a_changes = make_internal_transaction_changes(transaction_a, 0, nil) + if Application.compile_env(:explorer, :chain_type) == :zetachain do + test "does not set refetch_needed=true from non-traceable blocks (zetachain)" do + original_config = Application.get_env(:indexer, :trace_block_ranges) - assert {:ok, _} = run_internal_transactions([transaction_a_changes]) + full_block = insert(:block) + transaction = insert(:transaction) |> with_block(full_block) - assert from(i in InternalTransaction, where: i.transaction_hash == ^transaction_a.hash) |> Repo.one() |> is_nil() - assert from(i in InternalTransaction, where: i.transaction_hash == ^transaction_b.hash) |> Repo.one() |> is_nil() + Application.put_env(:indexer, :trace_block_ranges, "#{full_block.number + 1}..latest") - assert %{consensus: true, refetch_needed: true} = Repo.get(Block, full_block.hash) - assert not is_nil(Repo.get(PendingBlockOperation, full_block.hash)) + insert(:pending_block_operation, block_hash: full_block.hash, block_number: full_block.number) + + transaction_changes = + transaction + |> make_internal_transaction_changes(0, nil) + |> Map.put(:block_number, full_block.number - 1) + + assert {:ok, _} = run_internal_transactions([transaction_changes]) + + assert from(i in InternalTransaction, where: i.transaction_hash == ^transaction.hash) |> Repo.one() |> is_nil() + + assert %{consensus: true, refetch_needed: false} = Repo.get(Block, full_block.hash) + + on_exit(fn -> Application.put_env(:indexer, :trace_block_ranges, original_config) end) + end end test "does not remove consensus when block is empty and no transactions are missing" do @@ -311,29 +372,6 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do assert PendingBlockOperation |> Repo.get(full_block.hash) |> is_nil() end - test "does not remove consensus from non-traceable blocks" do - original_config = Application.get_env(:indexer, :trace_block_ranges) - - full_block = insert(:block) - transaction_a = insert(:transaction) |> with_block(full_block) - transaction_b = insert(:transaction) |> with_block(full_block) - - Application.put_env(:indexer, :trace_block_ranges, "#{full_block.number + 1}..latest") - - insert(:pending_block_operation, block_hash: full_block.hash, block_number: full_block.number) - - transaction_a_changes = make_internal_transaction_changes(transaction_a, 0, nil) - - assert {:ok, _} = run_internal_transactions([transaction_a_changes]) - - assert from(i in InternalTransaction, where: i.transaction_hash == ^transaction_a.hash) |> Repo.one() |> is_nil() - assert from(i in InternalTransaction, where: i.transaction_hash == ^transaction_b.hash) |> Repo.one() |> is_nil() - - assert %{consensus: true} = Repo.get(Block, full_block.hash) - - on_exit(fn -> Application.put_env(:indexer, :trace_block_ranges, original_config) end) - end - test "successfully imports internal transaction with stop type" do block = insert(:block) transaction = insert(:transaction) |> with_block(block, status: :ok) From c846237ac804ceba31366f4823fb9b7d60371250 Mon Sep 17 00:00:00 2001 From: varasev <33550681+varasev@users.noreply.github.com> Date: Mon, 12 Aug 2024 23:03:27 +0400 Subject: [PATCH 065/363] chore: Add API endpoint for OP batch blocks (#10566) * API endpoint for OP batch blocks * Minor refactoring * mix format --------- Co-authored-by: POA <33550681+poa@users.noreply.github.com> --- .../controllers/api/v2/block_controller.ex | 28 ++++++++++ .../controllers/api/v2/optimism_controller.ex | 13 ++++- .../lib/block_scout_web/routers/api_router.ex | 4 ++ .../views/api/v2/optimism_view.ex | 3 +- .../lib/explorer/chain/optimism/txn_batch.ex | 51 ++++++++++++++++++- 5 files changed, 96 insertions(+), 3 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex index 4db96f9c11e6..ab2fed6b17c6 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex @@ -35,6 +35,7 @@ defmodule BlockScoutWeb.API.V2.BlockController do alias Explorer.Chain.Celo.EpochReward, as: CeloEpochReward alias Explorer.Chain.Celo.Reader, as: CeloReader alias Explorer.Chain.InternalTransaction + alias Explorer.Chain.Optimism.TxnBatch, as: OptimismTxnBatch case Application.compile_env(:explorer, :chain_type) do :ethereum -> @@ -197,6 +198,33 @@ defmodule BlockScoutWeb.API.V2.BlockController do }) end + @doc """ + Function to handle GET requests to `/api/v2/blocks/optimism-batch/:batch_number` endpoint. + It renders the list of L2 blocks bound to the specified batch. + """ + @spec optimism_batch(Plug.Conn.t(), any()) :: Plug.Conn.t() + def optimism_batch(conn, %{"batch_number" => batch_number} = params) do + full_options = + params + |> select_block_type() + |> Keyword.merge(paging_options(params)) + |> Keyword.merge(@api_true) + + {blocks, next_page} = + batch_number + |> OptimismTxnBatch.batch_blocks(full_options) + |> split_list_by_page() + + next_page_params = next_page |> next_page_params(blocks, delete_parameters_from_next_page_params(params)) + + conn + |> put_status(200) + |> render(:blocks, %{ + blocks: blocks |> maybe_preload_ens() |> maybe_preload_metadata(), + next_page_params: next_page_params + }) + end + @doc """ Function to handle GET requests to `/api/v2/blocks/:block_hash_or_number/transactions` endpoint. """ diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/optimism_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/optimism_controller.ex index d4f9b960ec16..ecd5d35a4553 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/optimism_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/optimism_controller.ex @@ -15,7 +15,16 @@ defmodule BlockScoutWeb.API.V2.OptimismController do alias Explorer.Chain alias Explorer.Chain.Transaction - alias Explorer.Chain.Optimism.{Deposit, DisputeGame, FrameSequence, OutputRoot, TxnBatch, Withdrawal} + + alias Explorer.Chain.Optimism.{ + Deposit, + DisputeGame, + FrameSequence, + FrameSequenceBlob, + OutputRoot, + TxnBatch, + Withdrawal + } action_fallback(BlockScoutWeb.API.V2.FallbackController) @@ -74,10 +83,12 @@ defmodule BlockScoutWeb.API.V2.OptimismController do l2_block_number_from = TxnBatch.edge_l2_block_number(fs.id, :min) l2_block_number_to = TxnBatch.edge_l2_block_number(fs.id, :max) tx_count = Transaction.tx_count_for_block_range(l2_block_number_from..l2_block_number_to) + {batch_data_container, _} = FrameSequenceBlob.list(fs.id, api?: true) fs |> Map.put(:l2_block_range, l2_block_number_from..l2_block_number_to) |> Map.put(:tx_count, tx_count) + |> Map.put(:batch_data_container, batch_data_container) end) end) |> Task.yield_many(:infinity) diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index cc42a8db8f57..e951bdc42ba8 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -164,6 +164,10 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/:block_hash_or_number/epoch", V2.BlockController, :celo_epoch) get("/:block_hash_or_number/election-rewards/:reward_type", V2.BlockController, :celo_election_rewards) end + + if Application.compile_env(:explorer, :chain_type) == :optimism do + get("/optimism-batch/:batch_number", V2.BlockController, :optimism_batch) + end end scope "/addresses" do diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex index 994050687c5c..1b021718d1cd 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex @@ -68,7 +68,8 @@ defmodule BlockScoutWeb.API.V2.OptimismView do "l2_block_start" => from, "l2_block_end" => to, "tx_count" => batch.tx_count, - "l1_tx_hashes" => batch.l1_transaction_hashes + "l1_tx_hashes" => batch.l1_transaction_hashes, + "batch_data_container" => batch.batch_data_container } end) diff --git a/apps/explorer/lib/explorer/chain/optimism/txn_batch.ex b/apps/explorer/lib/explorer/chain/optimism/txn_batch.ex index eb6a159fa81b..a94ef6d3c98b 100644 --- a/apps/explorer/lib/explorer/chain/optimism/txn_batch.ex +++ b/apps/explorer/lib/explorer/chain/optimism/txn_batch.ex @@ -16,8 +16,9 @@ defmodule Explorer.Chain.Optimism.TxnBatch do use Explorer.Schema - import Explorer.Chain, only: [default_paging_options: 0, join_association: 3, select_repo: 1] + import Explorer.Chain, only: [default_paging_options: 0, join_association: 3, join_associations: 2, select_repo: 1] + alias Explorer.Chain.Block alias Explorer.Chain.Optimism.FrameSequence alias Explorer.{PagingOptions, Repo} @@ -136,6 +137,54 @@ defmodule Explorer.Chain.Optimism.TxnBatch do end end + @doc """ + Retrieves a list of rollup blocks included into a specified batch. + + This function constructs and executes a database query to retrieve a list of rollup blocks, + considering pagination options specified in the `options` parameter. These options dictate + the number of items to retrieve and how many items to skip from the top. + + ## Parameters + - `batch_number`: The batch number whose transactions are included on L1. + - `options`: A keyword list of options specifying pagination, association necessity, and + whether to use a replica database. + + ## Returns + - A list of `Explorer.Chain.Block` entries belonging to the specified batch. + """ + @spec batch_blocks(non_neg_integer() | binary(), + necessity_by_association: %{atom() => :optional | :required}, + api?: boolean(), + paging_options: PagingOptions.t() + ) :: [Block.t()] + def batch_blocks(batch_number, options) do + necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) + paging_options = Keyword.get(options, :paging_options, default_paging_options()) + + query = + from( + b in Block, + inner_join: tb in __MODULE__, + on: tb.l2_block_number == b.number and tb.frame_sequence_id == ^batch_number, + where: b.consensus == true + ) + + query + |> page_blocks(paging_options) + |> limit(^paging_options.page_size) + |> order_by(desc: :number) + |> join_associations(necessity_by_association) + |> select_repo(options).all() + end + + defp page_blocks(query, %PagingOptions{key: nil}), do: query + + defp page_blocks(query, %PagingOptions{key: {0}}), do: query + + defp page_blocks(query, %PagingOptions{key: {block_number}}) do + where(query, [block], block.number < ^block_number) + end + @doc """ Decodes EIP-4844 blob to the raw data. Returns `nil` if the blob is invalid. """ From 7c4753d8260ae6a9a5a160035da23c87f1f9b628 Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Tue, 13 Aug 2024 03:03:06 -0600 Subject: [PATCH 066/363] feat: L1 tx associated with Arbitrum message in /api/v2/transactions/{txHash} (#10590) --- .../views/api/v2/arbitrum_view.ex | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex index b9414e6fdecf..16586f3cf031 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex @@ -527,14 +527,15 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do end # Determines if an Arbitrum transaction contains a cross-chain message and extends - # the incoming map with the `contains_message` field to reflect the direction of - # the message. + # the incoming map with fields related to the cross-chain message to reflect the + # direction of the message and the associated L1 transaction. # # ## Parameters # - `arbitrum_tx`: An Arbitrum transaction. # # ## Returns - # - A map extended with a field indicating the direction of the message. + # - A map extended with fields indicating the direction of the message and + # the associated L1 transaction. @spec extend_if_message(map(), %{ :__struct__ => Transaction, :arbitrum_message_to_l2 => any(), @@ -542,15 +543,22 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do optional(any()) => any() }) :: map() defp extend_if_message(arbitrum_json, %Transaction{} = arbitrum_tx) do - message_type = + {message_type, l1_tx} = case {APIV2Helper.specified?(Map.get(arbitrum_tx, :arbitrum_message_to_l2)), APIV2Helper.specified?(Map.get(arbitrum_tx, :arbitrum_message_from_l2))} do - {true, false} -> "incoming" - {false, true} -> "outcoming" - _ -> nil + {true, false} -> + {"incoming", APIV2Helper.get_2map_data(arbitrum_tx, :arbitrum_message_to_l2, :originating_transaction_hash)} + + {false, true} -> + {"outcoming", APIV2Helper.get_2map_data(arbitrum_tx, :arbitrum_message_from_l2, :completion_transaction_hash)} + + _ -> + {nil, nil} end - Map.put(arbitrum_json, "contains_message", message_type) + arbitrum_json + |> Map.put("contains_message", message_type) + |> Map.put("message_related_l1_transaction", l1_tx) end # Extends the output JSON with information from Arbitrum-specific fields of the transaction. From af88ab547ff8e25edb5baf3b7cef4ac0e6f007b6 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 13 Aug 2024 12:15:13 +0300 Subject: [PATCH 067/363] feat: Add block number to token transfer object in API v2 endpoint (#10591) --- .../lib/block_scout_web/views/api/v2/transaction_view.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex index 0b43013a4a20..d05a35e4c52f 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex @@ -258,6 +258,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do ), "method" => method_name(token_transfer.transaction, decoded_input, true), "block_hash" => to_string(token_transfer.block_hash), + "block_number" => to_string(token_transfer.block_number), "log_index" => to_string(token_transfer.log_index) } end From ebc99bfff90705c09328bd41f3ec38f51828cc99 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Tue, 13 Aug 2024 13:06:36 +0300 Subject: [PATCH 068/363] feat: Add DELETE /api/v2/import/token-info method (#10580) * feat: Add DELETE /api/v2/import/token-info method * Process review comments * Unify params case * Fix test * Fix test --- .../controllers/api/v2/import_controller.ex | 47 ++++++++-- .../lib/block_scout_web/routers/api_router.ex | 2 + .../api/v2/import_controller_test.exs | 92 ++++++++++++++++++- apps/explorer/lib/explorer/chain/token.ex | 12 +++ 4 files changed, 145 insertions(+), 8 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/import_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/import_controller.ex index c7d21eff87c5..e41abe047b57 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/import_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/import_controller.ex @@ -3,9 +3,10 @@ defmodule BlockScoutWeb.API.V2.ImportController do alias BlockScoutWeb.API.V2.ApiView alias Explorer.Chain - alias Explorer.Chain.{Data, SmartContract} + alias Explorer.Chain.{Data, SmartContract, Token} alias Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand alias Explorer.SmartContract.EthBytecodeDBInterface + alias Indexer.Fetcher.TokenUpdater import Explorer.SmartContract.Helper, only: [prepare_bytecode_for_microservice: 3, contract_creation_input: 1] @@ -23,12 +24,7 @@ defmodule BlockScoutWeb.API.V2.ImportController do "tokenName" => token_name } = params ) do - with {:sensitive_endpoints_api_key, api_key} when not is_nil(api_key) <- - {:sensitive_endpoints_api_key, Application.get_env(:block_scout_web, :sensitive_endpoints_api_key)}, - {:api_key, ^api_key} <- {:api_key, params["api_key"]}, - {:format_address, {:ok, address_hash}} <- - {:format_address, Chain.string_to_address_hash(token_address_hash_string)}, - {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)} do + with {:ok, token} <- validate_api_key_address_hash_and_token(token_address_hash_string, params["api_key"]) do changeset = %{is_verified_via_admin_panel: true} |> put_icon_url(icon_url) @@ -96,6 +92,32 @@ defmodule BlockScoutWeb.API.V2.ImportController do end end + def delete_token_info( + conn, + %{ + "token_address_hash" => token_address_hash_string + } = params + ) do + with {:ok, token} <- validate_api_key_address_hash_and_token(token_address_hash_string, params["api_key"]) do + case Token.drop_token_info(token) do + {:ok, _} -> + TokenUpdater.run([token], []) + + conn + |> put_view(ApiView) + |> render(:message, %{message: "Success"}) + + error -> + Logger.warning(fn -> ["Error on deleting token info: ", inspect(error)] end) + + conn + |> put_view(ApiView) + |> put_status(:bad_request) + |> render(:message, %{message: "Error"}) + end + end + end + defp params_to_contract_search_options(%{"import_from" => "verifier_alliance"}) do [only_verifier_alliance?: true] end @@ -142,4 +164,15 @@ defmodule BlockScoutWeb.API.V2.ImportController do nil end end + + defp validate_api_key_address_hash_and_token(token_address_hash_string, provided_api_key) do + with {:sensitive_endpoints_api_key, api_key} when not is_nil(api_key) <- + {:sensitive_endpoints_api_key, Application.get_env(:block_scout_web, :sensitive_endpoints_api_key)}, + {:api_key, ^api_key} <- {:api_key, provided_api_key}, + {:format_address, {:ok, address_hash}} <- + {:format_address, Chain.string_to_address_hash(token_address_hash_string)}, + {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)} do + {:ok, token} + end + end end diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index e951bdc42ba8..3fb8d1f0a2df 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -93,6 +93,8 @@ defmodule BlockScoutWeb.Routers.ApiRouter do pipe_through(:api_v2_no_session) post("/token-info", V2.ImportController, :import_token_info) + delete("/token-info", V2.ImportController, :delete_token_info) + get("/smart-contracts/:address_hash_param", V2.ImportController, :try_to_search_contract) end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/import_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/import_controller_test.exs index 52ea55071b61..1c4d6d72e5b7 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/import_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/import_controller_test.exs @@ -1,7 +1,9 @@ defmodule BlockScoutWeb.API.V2.ImportControllerTest do use BlockScoutWeb.ConnCase - describe "/import/token-info" do + import Mox + + describe "POST /import/token-info" do test "return error on misconfigured api key", %{conn: conn} do request = post(conn, "/api/v2/import/token-info", %{ @@ -73,4 +75,92 @@ defmodule BlockScoutWeb.API.V2.ImportControllerTest do Application.put_env(:block_scout_web, :sensitive_endpoints_api_key, nil) end end + + describe "DELETE /import/token-info" do + test "return error on misconfigured api key", %{conn: conn} do + request = + delete(conn, "/api/v2/import/token-info", %{ + "token_address_hash" => build(:address).hash + }) + + assert %{"message" => "API key not configured on the server"} = json_response(request, 403) + end + + test "return error on wrong api key", %{conn: conn} do + Application.put_env(:block_scout_web, :sensitive_endpoints_api_key, "abc") + body = %{"token_address_hash" => build(:address).hash} + request = delete(conn, "/api/v2/import/token-info", Map.merge(body, %{"api_key" => "123"})) + + assert %{"message" => "Wrong API key"} = json_response(request, 401) + + Application.put_env(:block_scout_web, :sensitive_endpoints_api_key, nil) + end + + test "success delete token info", %{conn: conn} do + insert(:token, + name: "old", + symbol: "OLD", + decimals: 10, + cataloged: true, + updated_at: DateTime.add(DateTime.utc_now(), -:timer.hours(50), :millisecond) + ) + + expect( + EthereumJSONRPC.Mox, + :json_rpc, + 1, + fn requests, _opts -> + {:ok, + Enum.map(requests, fn + %{id: id, method: "eth_call", params: [%{data: "0x313ce567", to: _}, "latest"]} -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000012" + } + + %{id: id, method: "eth_call", params: [%{data: "0x06fdde03", to: _}, "latest"]} -> + %{ + id: id, + result: + "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000096e657720746f6b656e0000000000000000000000000000000000000000000000" + } + + %{id: id, method: "eth_call", params: [%{data: "0x95d89b41", to: _}, "latest"]} -> + %{ + id: id, + result: + "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034e45570000000000000000000000000000000000000000000000000000000000" + } + + %{id: id, method: "eth_call", params: [%{data: "0x18160ddd", to: _}, "latest"]} -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000" + } + end)} + end + ) + + api_key = "abc123" + icon_url = nil + token_symbol = "NEW" + token_name = "new token" + + Application.put_env(:block_scout_web, :sensitive_endpoints_api_key, api_key) + + token_address = to_string(insert(:token).contract_address_hash) + + body = %{ + "token_address_hash" => token_address + } + + request = delete(conn, "/api/v2/import/token-info", Map.merge(body, %{"api_key" => api_key})) + assert %{"message" => "Success"} = json_response(request, 200) + + request = get(conn, "/api/v2/tokens/#{token_address}") + assert %{"icon_url" => ^icon_url, "name" => ^token_name, "symbol" => ^token_symbol} = json_response(request, 200) + + Application.put_env(:block_scout_web, :sensitive_endpoints_api_key, nil) + end + end end diff --git a/apps/explorer/lib/explorer/chain/token.ex b/apps/explorer/lib/explorer/chain/token.ex index c89e07788142..7e8286dd4fe3 100644 --- a/apps/explorer/lib/explorer/chain/token.ex +++ b/apps/explorer/lib/explorer/chain/token.ex @@ -278,4 +278,16 @@ defmodule Explorer.Chain.Token do timeout: @timeout ) end + + @doc """ + Drops token info for the given token: + Sets is_verified_via_admin_panel to false, icon_url to nil, symbol to nil, name to nil. + Don't forget to set/update token's symbol and name after this function. + """ + @spec drop_token_info(t()) :: {:ok, t()} | {:error, Changeset.t()} + def drop_token_info(token) do + token + |> Changeset.change(%{is_verified_via_admin_panel: false, icon_url: nil, symbol: nil, name: nil}) + |> Repo.update() + end end From f4540a3601a389fbadbcf8e000f40390c561f287 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 13 Aug 2024 13:50:58 +0300 Subject: [PATCH 069/363] feat: Add token decimals to token transfers CSV export (#10589) * Add token decimals to token transfers CSV export * Fix tests * Process review comments --- .../csv_export/address_token_transfer_csv_exporter.ex | 2 ++ .../address_token_transfer_csv_exporter_test.exs | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/apps/explorer/lib/explorer/chain/csv_export/address_token_transfer_csv_exporter.ex b/apps/explorer/lib/explorer/chain/csv_export/address_token_transfer_csv_exporter.ex index 8b83bcf75776..f249204ee0e1 100644 --- a/apps/explorer/lib/explorer/chain/csv_export/address_token_transfer_csv_exporter.ex +++ b/apps/explorer/lib/explorer/chain/csv_export/address_token_transfer_csv_exporter.ex @@ -55,6 +55,7 @@ defmodule Explorer.Chain.CSVExport.AddressTokenTransferCsvExporter do "ToAddress", "TokenContractAddress", "Type", + "TokenDecimals", "TokenSymbol", "TokensTransferred", "TransactionFee", @@ -73,6 +74,7 @@ defmodule Explorer.Chain.CSVExport.AddressTokenTransferCsvExporter do Address.checksum(token_transfer.to_address_hash), Address.checksum(token_transfer.token_contract_address_hash), type(token_transfer, address_hash), + token_transfer.token.decimals, token_transfer.token.symbol, token_transfer.amount, fee(token_transfer.transaction), diff --git a/apps/explorer/test/explorer/chain/csv_export/address_token_transfer_csv_exporter_test.exs b/apps/explorer/test/explorer/chain/csv_export/address_token_transfer_csv_exporter_test.exs index 88eeb2982530..87bc3e37c9e6 100644 --- a/apps/explorer/test/explorer/chain/csv_export/address_token_transfer_csv_exporter_test.exs +++ b/apps/explorer/test/explorer/chain/csv_export/address_token_transfer_csv_exporter_test.exs @@ -15,6 +15,7 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporterTest do token_transfer = insert(:token_transfer, transaction: transaction, from_address: address, block_number: transaction.block_number) + |> Repo.preload([:token, :transaction]) from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime) to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) @@ -39,6 +40,8 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporterTest do _, [[], type], _, + [[], token_decimals], + _, [[], token_symbol], _, [[], tokens_transferred], @@ -58,6 +61,7 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporterTest do to_address: to_address, token_contract_address: token_contract_address, type: type, + token_decimals: token_decimals, token_symbol: token_symbol, tokens_transferred: tokens_transferred, transaction_fee: transaction_fee, @@ -71,6 +75,11 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporterTest do assert result.from_address == Address.checksum(token_transfer.from_address_hash) assert result.to_address == Address.checksum(token_transfer.to_address_hash) assert result.timestamp == to_string(transaction.block_timestamp) + assert result.token_symbol == to_string(token_transfer.token.symbol) + assert result.token_decimals == to_string(token_transfer.token.decimals) + assert result.tokens_transferred == to_string(token_transfer.amount) + assert result.status == to_string(token_transfer.transaction.status) + assert result.err_code == to_string(token_transfer.transaction.error) assert result.type == "OUT" end From 63805dbc1b08cc40ba176107e6203ae5a1b7350d Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Tue, 13 Aug 2024 14:13:16 +0300 Subject: [PATCH 070/363] feat: Add internal_transactions to Tx interpreter request (#10347) --- .../transaction_interpretation.ex | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex index 9f89707e6f7b..83b4abfa59cf 100644 --- a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex +++ b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex @@ -6,7 +6,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do alias BlockScoutWeb.API.V2.{Helper, TokenView, TransactionView} alias Ecto.Association.NotLoaded alias Explorer.Chain - alias Explorer.Chain.{Data, Log, TokenTransfer, Transaction} + alias Explorer.Chain.{Data, InternalTransaction, Log, TokenTransfer, Transaction} alias HTTPoison.Response import Explorer.Utility.Microservice, only: [base_url: 2, check_enabled: 2] @@ -17,6 +17,13 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do @request_error_msg "Error while sending request to Transaction Interpretation Service" @api_true api?: true @items_limit 50 + @internal_transaction_necessity_by_association [ + necessity_by_association: %{ + [created_contract_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional + } + ] @doc """ Interpret transaction or user operation @@ -100,6 +107,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do transaction = Chain.select_repo(@api_true).preload(transaction, [ :transaction_actions, + :block, to_address: [:names, :smart_contract], from_address: [:names, :smart_contract], created_contract_address: [:names, :token, :smart_contract] @@ -129,7 +137,8 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do tx_types: TransactionView.tx_types(transaction), raw_input: transaction.input, decoded_input: decoded_input_data, - token_transfers: prepare_token_transfers(transaction, decoded_input) + token_transfers: prepare_token_transfers(transaction, decoded_input), + internal_transactions: prepare_internal_transactions(transaction) }, logs_data: %{items: prepare_logs(transaction)} } @@ -152,6 +161,17 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do |> Enum.map(&TransactionView.prepare_token_transfer(&1, nil, decoded_input)) end + defp prepare_internal_transactions(transaction) do + full_options = + @internal_transaction_necessity_by_association + |> Keyword.merge(@api_true) + + transaction.hash + |> InternalTransaction.transaction_to_internal_transactions(full_options) + |> Enum.take(@items_limit) + |> Enum.map(&TransactionView.prepare_internal_transaction(&1, transaction.block)) + end + defp user_op_to_logs_and_token_transfers(user_op, decoded_input) do log_options = [ From 8382c357f4240b3e3c7704d2fb88986d685b0a6f Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 13 Aug 2024 16:21:07 +0300 Subject: [PATCH 071/363] fix: Fix internal transaction validation (#10443) * Fix internal transaction validation * Add parsing function clause for failed internal transaction, add logging of to internal transaction changeset validation --- apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex | 44 +++++++++++++++---- .../lib/ethereum_jsonrpc/geth/call.ex | 34 +++++++++++++- 2 files changed, 69 insertions(+), 9 deletions(-) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex index e960e08cf86a..3e3afe9dde6a 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex @@ -580,15 +580,43 @@ defmodule EthereumJSONRPC do defp chunk_requests(requests, nil), do: requests defp chunk_requests(requests, chunk_size), do: Enum.chunk_every(requests, chunk_size) - def put_if_present(result, map, keys) do - Enum.reduce(keys, result, fn {from_key, to_key}, acc -> - value = map[from_key] + def put_if_present(result, transaction, keys) do + Enum.reduce(keys, result, fn key, acc -> + key_list = key |> Tuple.to_list() + from_key = Enum.at(key_list, 0) + to_key = Enum.at(key_list, 1) + opts = if Enum.count(key_list) > 2, do: Enum.at(key_list, 2), else: %{} - if value do - Map.put(acc, to_key, value) - else - acc - end + value = transaction[from_key] + + validate_key(acc, to_key, value, opts) end) end + + defp validate_key(acc, _to_key, nil, _opts), do: acc + + defp validate_key(acc, to_key, value, %{:validation => validation}) do + case validation do + :address_hash -> + if address_correct?(value), do: Map.put(acc, to_key, value), else: acc + + _ -> + Map.put(acc, to_key, value) + end + end + + defp validate_key(acc, to_key, value, _validation) do + Map.put(acc, to_key, value) + end + + # todo: The similar function exists in Indexer application: + # Here is the room for future refactoring to keep a single function. + @spec address_correct?(binary()) :: boolean() + defp address_correct?(address) when is_binary(address) do + String.match?(address, ~r/^0x[[:xdigit:]]{40}$/i) + end + + defp address_correct?(_address) do + false + end end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth/call.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth/call.ex index 2d8e2723b5ab..2835c61431ea 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth/call.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth/call.ex @@ -363,6 +363,38 @@ defmodule EthereumJSONRPC.Geth.Call do ]) end + # failed internal transaction + defp elixir_to_internal_transaction_params(%{ + "blockNumber" => block_number, + "transactionIndex" => transaction_index, + "transactionHash" => transaction_hash, + "index" => index, + "traceAddress" => trace_address, + "type" => type, + "from" => from_address_hash, + "gas" => gas, + "gasUsed" => gas_used, + "init" => init, + "value" => value, + "error" => error + }) + when type in ~w(create create2) and not is_nil(error) do + %{ + block_number: block_number, + transaction_index: transaction_index, + transaction_hash: transaction_hash, + index: index, + trace_address: trace_address, + type: type, + from_address_hash: from_address_hash, + gas: gas, + gas_used: gas_used, + init: init, + value: value, + error: error + } + end + defp elixir_to_internal_transaction_params( %{ "blockNumber" => block_number, @@ -394,7 +426,7 @@ defmodule EthereumJSONRPC.Geth.Call do } |> put_if_present(params, [ {"error", :error}, - {"createdContractAddressHash", :created_contract_address_hash}, + {"createdContractAddressHash", :created_contract_address_hash, %{validation: :address_hash}}, {"createdContractCode", :created_contract_code} ]) end From 96c37ff2230572e99826801898a739e45188fa42 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Wed, 14 Aug 2024 11:15:07 +0300 Subject: [PATCH 072/363] CI workflow for split setup for Shibarium --- .../publish-docker-image-for-shibarium.yml | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-docker-image-for-shibarium.yml b/.github/workflows/publish-docker-image-for-shibarium.yml index 3b5aaa4b9f13..32d6cfb3b588 100644 --- a/.github/workflows/publish-docker-image-for-shibarium.yml +++ b/.github/workflows/publish-docker-image-for-shibarium.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - - name: Build and push Docker image + - name: Build and push Docker image (indexer + API) uses: docker/build-push-action@v5 with: context: . @@ -48,4 +48,48 @@ jobs: CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=shibarium + + - name: Build and push Docker image (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=shibarium + + - name: Build and push Docker image (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=shibarium \ No newline at end of file From 6caf8148f30c0d3fb24b21f62f6ba1920c6bab43 Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Wed, 14 Aug 2024 09:32:14 -0600 Subject: [PATCH 073/363] feat: more descriptive status for Arbitrum message for the transaction view (#10593) * more descriptive status for message in transaction view * fix dialyzer issue --- .../views/api/v2/arbitrum_view.ex | 58 ++++++++++++++++--- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex index 16586f3cf031..e41089b65fd2 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex @@ -528,14 +528,14 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do # Determines if an Arbitrum transaction contains a cross-chain message and extends # the incoming map with fields related to the cross-chain message to reflect the - # direction of the message and the associated L1 transaction. + # direction of the message, its status and the associated L1 transaction. # # ## Parameters # - `arbitrum_tx`: An Arbitrum transaction. # # ## Returns - # - A map extended with fields indicating the direction of the message and - # the associated L1 transaction. + # - A map extended with fields indicating the direction of the message, its status + # and the associated L1 transaction. @spec extend_if_message(map(), %{ :__struct__ => Transaction, :arbitrum_message_to_l2 => any(), @@ -543,22 +543,64 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do optional(any()) => any() }) :: map() defp extend_if_message(arbitrum_json, %Transaction{} = arbitrum_tx) do - {message_type, l1_tx} = + {message_type, message_data} = case {APIV2Helper.specified?(Map.get(arbitrum_tx, :arbitrum_message_to_l2)), APIV2Helper.specified?(Map.get(arbitrum_tx, :arbitrum_message_from_l2))} do {true, false} -> - {"incoming", APIV2Helper.get_2map_data(arbitrum_tx, :arbitrum_message_to_l2, :originating_transaction_hash)} + {"incoming", l1_tx_and_status_for_message(arbitrum_tx, :incoming)} {false, true} -> - {"outcoming", APIV2Helper.get_2map_data(arbitrum_tx, :arbitrum_message_from_l2, :completion_transaction_hash)} + {"outcoming", l1_tx_and_status_for_message(arbitrum_tx, :outcoming)} _ -> - {nil, nil} + {nil, %{}} end arbitrum_json |> Map.put("contains_message", message_type) - |> Map.put("message_related_l1_transaction", l1_tx) + |> Map.put("message_related_info", message_data) + end + + # Determines the associated L1 transaction and its status for the given message direction. + @spec l1_tx_and_status_for_message( + %{ + :__struct__ => Transaction, + :arbitrum_message_to_l2 => any(), + :arbitrum_message_from_l2 => any(), + optional(any()) => any() + }, + :incoming | :outcoming + ) :: map() + defp l1_tx_and_status_for_message(arbitrum_tx, message_direction) do + {l1_tx, status} = + case message_direction do + :incoming -> + l1_tx = APIV2Helper.get_2map_data(arbitrum_tx, :arbitrum_message_to_l2, :originating_transaction_hash) + + if is_nil(l1_tx) do + {nil, "Syncing with base layer"} + else + {l1_tx, "Relayed"} + end + + :outcoming -> + case APIV2Helper.get_2map_data(arbitrum_tx, :arbitrum_message_from_l2, :status) do + :initiated -> + {nil, "Settlement pending"} + + :sent -> + {nil, "Waiting for confirmation"} + + :confirmed -> + {nil, "Ready for relay"} + + :relayed -> + {APIV2Helper.get_2map_data(arbitrum_tx, :arbitrum_message_from_l2, :completion_transaction_hash), + "Relayed"} + end + end + + %{"associated_l1_transaction" => l1_tx, "message_status" => status} end # Extends the output JSON with information from Arbitrum-specific fields of the transaction. From c9ddc19b22052a4bae55556db7ebe4ca83162fe9 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Wed, 14 Aug 2024 18:49:26 +0300 Subject: [PATCH 074/363] feat: Add /api/v2/proxy/metadata/addresses endpoint (#10585) * Init * feat: Add /api/v2/proxy/metadata/addresses endpoint * Process review comments --- .../api/v2/proxy/metadata_controller.ex | 30 ++++++++ .../lib/block_scout_web/routers/api_router.ex | 4 ++ .../views/api/v2/proxy/metadata_view.ex | 13 ++++ .../microservice_interfaces/metadata.ex | 71 +++++++++++++++++-- 4 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/metadata_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/proxy/metadata_view.ex diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/metadata_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/metadata_controller.ex new file mode 100644 index 000000000000..b8b68939110c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/metadata_controller.ex @@ -0,0 +1,30 @@ +defmodule BlockScoutWeb.API.V2.Proxy.MetadataController do + @moduledoc """ + Controller for the metadata service + """ + use BlockScoutWeb, :controller + + alias Explorer.MicroserviceInterfaces.Metadata + + action_fallback(BlockScoutWeb.API.V2.FallbackController) + + @doc """ + Function to handle GET requests to `/api/v2/proxy/metadata/addresses` endpoint. + Proxies request to the metadata service. Preloads additional info to the addresses in response. + If some addresses are not found, they'll be discarded. + """ + def addresses(conn, params) do + with {code, body} <- Metadata.get_addresses(params) do + case code do + 200 -> + conn + |> render(:addresses, %{result: body}) + + status_code -> + conn + |> put_status(status_code) + |> json(body) + end + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index 3fb8d1f0a2df..d0e05070afa5 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -314,6 +314,10 @@ defmodule BlockScoutWeb.Routers.ApiRouter do scope "/zerion" do get("/wallets/:address_hash_param/portfolio", V2.Proxy.ZerionController, :wallet_portfolio) end + + scope "/metadata" do + get("/addresses", V2.Proxy.MetadataController, :addresses) + end end scope "/blobs" do diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/proxy/metadata_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/proxy/metadata_view.ex new file mode 100644 index 000000000000..50b8cf2e0399 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/proxy/metadata_view.ex @@ -0,0 +1,13 @@ +defmodule BlockScoutWeb.API.V2.Proxy.MetadataView do + use BlockScoutWeb, :view + + alias BlockScoutWeb.API.V2.AddressView + + def render("addresses.json", %{result: {:ok, %{"addresses" => addresses} = body}}) do + Map.put(body, "addresses", Enum.map(addresses, &AddressView.prepare_address/1)) + end + + def render("addresses.json", %{result: :error}) do + %{error: "Decoding error"} + end +end diff --git a/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex b/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex index 06309b661b07..ad9e692cbe2a 100644 --- a/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex +++ b/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex @@ -3,6 +3,7 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do Module to interact with Metadata microservice """ + alias Explorer.Chain alias Explorer.Chain.{Address.MetadataPreloader, Transaction} alias Explorer.Utility.Microservice alias HTTPoison.Response @@ -10,9 +11,10 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do import Explorer.Chain.Address.MetadataPreloader, only: [maybe_preload_meta: 3] require Logger - @post_timeout :timer.seconds(5) + @request_timeout :timer.seconds(5) @tags_per_address_limit 5 + @page_size 50 @request_error_msg "Error while sending request to Metadata microservice" @spec get_addresses_tags([String.t()]) :: {:error, :disabled | <<_::416>> | Jason.DecodeError.t()} | {:ok, any()} @@ -28,12 +30,27 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do end end - defp http_get_request(url, params) do - headers = [{"Content-Type", "application/json"}] + @doc """ + Get addresses list from Metadata microservice. Then preloads addresses from local DB. + """ + @spec get_addresses(map()) :: {:error | integer(), any()} + def get_addresses(params) do + with :ok <- Microservice.check_enabled(__MODULE__) do + params = + params + |> Map.put("pageSize", @page_size) + |> Map.put("chainId", Application.get_env(:block_scout_web, :chain_id)) + + http_get_request_for_proxy_method(addresses_url(), params, &prepare_addresses_response/1) + end + end - case HTTPoison.get(url, headers, params: params, recv_timeout: @post_timeout) do + defp http_get_request(url, params, parsing_function \\ &decode_meta/1) do + headers = [] + + case HTTPoison.get(url, headers, params: params, recv_timeout: @request_timeout) do {:ok, %Response{body: body, status_code: 200}} -> - body |> Jason.decode() |> decode_meta() + body |> Jason.decode() |> parsing_function.() {_, error} -> Logger.error(fn -> @@ -47,10 +64,39 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do end end + defp http_get_request_for_proxy_method(url, params, parsing_function) do + case HTTPoison.get(url, [], params: params) do + {:ok, %Response{body: body, status_code: 200}} -> + {200, body |> Jason.decode() |> parsing_function.()} + + {_, %Response{body: body, status_code: status_code} = error} -> + old_truncate = Application.get_env(:logger, :truncate) + Logger.configure(truncate: :infinity) + + Logger.error(fn -> + [ + "Error while sending request to Metadata microservice url: #{url}: ", + inspect(error, limit: :infinity, printable_limit: :infinity) + ] + end) + + Logger.configure(truncate: old_truncate) + {:ok, response_json} = Jason.decode(body) + {status_code, response_json} + + {:error, %HTTPoison.Error{reason: reason}} -> + {500, %{error: reason}} + end + end + defp addresses_metadata_url do "#{base_url()}/metadata" end + defp addresses_url do + "#{base_url()}/addresses" + end + defp base_url do "#{Microservice.base_url(__MODULE__)}/api/v1" end @@ -89,4 +135,19 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do defp decode_meta_in_tag(%{"meta" => meta} = tag) do Map.put(tag, "meta", Jason.decode!(meta)) end + + defp prepare_addresses_response({:ok, %{"addresses" => addresses} = response}) do + {:ok, + Map.put( + response, + "addresses", + addresses + |> Chain.hashes_to_addresses( + necessity_by_association: %{names: :optional, smart_contract: :optional, proxy_implementations: :optional} + ) + |> Enum.map(fn address -> {address, address.transactions_count} end) + )} + end + + defp prepare_addresses_response(_), do: :error end From 5785ea89be842a8a680afd348a2648c1ef06ac25 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Wed, 14 Aug 2024 19:05:44 +0300 Subject: [PATCH 075/363] feat: Add method name to transactions CSV export (#10579) * feat: Add method name to transactions CSV export * Add doc --- .../api/v2/advanced_filter_controller.ex | 4 +- .../controllers/api/v2/utils_controller.ex | 2 +- .../transaction_interpretation.ex | 8 +-- .../views/api/v2/advanced_filter_view.ex | 4 +- .../views/api/v2/suave_view.ex | 7 +- .../views/api/v2/transaction_view.ex | 66 ++++--------------- .../address_transaction_csv_exporter.ex | 26 +++++--- .../lib/explorer/chain/transaction.ex | 56 ++++++++++++++++ .../address_transaction_csv_exporter_test.exs | 33 +++++++++- 9 files changed, 129 insertions(+), 77 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex index 3178aaae5d7d..35975e90ec2d 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex @@ -4,7 +4,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do import BlockScoutWeb.Chain, only: [split_list_by_page: 1, next_page_params: 4] import Explorer.PagingOptions, only: [default_paging_options: 0] - alias BlockScoutWeb.API.V2.{AdvancedFilterView, CSVExportController, TransactionView} + alias BlockScoutWeb.API.V2.{AdvancedFilterView, CSVExportController} alias Explorer.{Chain, PagingOptions} alias Explorer.Chain.{AdvancedFilter, ContractMethod, Data, Token, Transaction} alias Explorer.Chain.CSVExport.Helper, as: CSVHelper @@ -60,7 +60,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do {decoded_transactions, _abi_acc, methods_acc} = advanced_filters |> Enum.map(fn af -> %Transaction{to_address: af.to_address, input: af.input, hash: af.hash} end) - |> TransactionView.decode_transactions(true) + |> Transaction.decode_transactions(true, @api_true) next_page_params = next_page |> next_page_params(advanced_filters, Map.take(params, ["items_count"]), &paging_params/1) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/utils_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/utils_controller.ex index 9de0d519a0d3..1826c67598c1 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/utils_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/utils_controller.ex @@ -34,7 +34,7 @@ defmodule BlockScoutWeb.API.V2.UtilsController do @api_true ) - decoded_input_data = decoded_input |> TransactionView.format_decoded_input() |> TransactionView.decoded_input() + decoded_input_data = decoded_input |> Transaction.format_decoded_input() |> TransactionView.decoded_input() conn |> json(%{result: decoded_input_data}) diff --git a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex index 83b4abfa59cf..591598fd42d7 100644 --- a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex +++ b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex @@ -116,7 +116,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do skip_sig_provider? = false {decoded_input, _abi_acc, _methods_acc} = Transaction.decoded_input_data(transaction, skip_sig_provider?, @api_true) - decoded_input_data = decoded_input |> TransactionView.format_decoded_input() |> TransactionView.decoded_input() + decoded_input_data = decoded_input |> Transaction.format_decoded_input() |> TransactionView.decoded_input() %{ data: %{ @@ -131,7 +131,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do hash: transaction.hash, type: transaction.type, value: transaction.value, - method: TransactionView.method_name(transaction, TransactionView.format_decoded_input(decoded_input)), + method: Transaction.method_name(transaction, Transaction.format_decoded_input(decoded_input)), status: transaction.status, actions: TransactionView.transaction_actions(transaction.transaction_actions), tx_types: TransactionView.tx_types(transaction), @@ -317,7 +317,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do hash: user_op_hash, type: 0, value: "0", - method: TransactionView.method_name(mock_tx, TransactionView.format_decoded_input(decoded_input), true), + method: Transaction.method_name(mock_tx, Transaction.format_decoded_input(decoded_input), true), status: user_op["status"], actions: [], tx_types: [], @@ -350,6 +350,6 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do {decoded_input, _abi_acc, _methods_acc} = Transaction.decoded_input_data(mock_tx, skip_sig_provider?, @api_true) - {mock_tx, decoded_input, decoded_input |> TransactionView.format_decoded_input() |> TransactionView.decoded_input()} + {mock_tx, decoded_input, decoded_input |> Transaction.format_decoded_input() |> TransactionView.decoded_input()} end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/advanced_filter_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/advanced_filter_view.ex index 5e9c9a0423d1..eb602e3412e9 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/advanced_filter_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/advanced_filter_view.ex @@ -112,7 +112,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterView do method: if(advanced_filter.type != "coin_transfer", do: - TransactionView.method_name( + Transaction.method_name( %Transaction{ to_address: %Address{ hash: advanced_filter.token_transfer.token.contract_address_hash, @@ -123,7 +123,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterView do decoded_input ), else: - TransactionView.method_name( + Transaction.method_name( %Transaction{to_address: advanced_filter.to_address, input: advanced_filter.input}, decoded_input ) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/suave_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/suave_view.ex index 8bbee60b6f1c..5d334ee06c84 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/suave_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/suave_view.ex @@ -28,7 +28,7 @@ defmodule BlockScoutWeb.API.V2.SuaveView do wrapped_value = Map.get(transaction, :wrapped_value) {[wrapped_decoded_input], _, _} = - TransactionView.decode_transactions( + Transaction.decode_transactions( [ %Transaction{ to_address: wrapped_to_address, @@ -36,7 +36,8 @@ defmodule BlockScoutWeb.API.V2.SuaveView do hash: wrapped_hash } ], - false + false, + api?: true ) out_json @@ -76,7 +77,7 @@ defmodule BlockScoutWeb.API.V2.SuaveView do "value" => wrapped_value, "hash" => wrapped_hash, "method" => - TransactionView.method_name( + Transaction.method_name( %Transaction{to_address: wrapped_to_address, input: wrapped_input}, wrapped_decoded_input ), diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex index d05a35e4c52f..808ff2196930 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex @@ -30,7 +30,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do watchlist_names: watchlist_names }) do block_height = Chain.block_height(@api_true) - {decoded_transactions, _, _} = decode_transactions(transactions, true) + {decoded_transactions, _, _} = Transaction.decode_transactions(transactions, true, @api_true) %{ "items" => @@ -50,7 +50,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do watchlist_names: watchlist_names }) do block_height = Chain.block_height(@api_true) - {decoded_transactions, _, _} = decode_transactions(transactions, true) + {decoded_transactions, _, _} = Transaction.decode_transactions(transactions, true, @api_true) transactions |> chain_type_transformations() @@ -62,7 +62,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do def render("transactions.json", %{transactions: transactions, next_page_params: next_page_params, conn: conn}) do block_height = Chain.block_height(@api_true) - {decoded_transactions, _, _} = decode_transactions(transactions, true) + {decoded_transactions, _, _} = Transaction.decode_transactions(transactions, true, @api_true) %{ "items" => @@ -82,7 +82,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do def render("transactions.json", %{transactions: transactions, conn: conn}) do block_height = Chain.block_height(@api_true) - {decoded_transactions, _, _} = decode_transactions(transactions, true) + {decoded_transactions, _, _} = Transaction.decode_transactions(transactions, true, @api_true) transactions |> chain_type_transformations() @@ -92,7 +92,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do def render("transaction.json", %{transaction: transaction, conn: conn}) do block_height = Chain.block_height(@api_true) - {[decoded_input], _, _} = decode_transactions([transaction], false) + {[decoded_input], _, _} = Transaction.decode_transactions([transaction], false, @api_true) transaction |> chain_type_transformations() @@ -116,7 +116,8 @@ defmodule BlockScoutWeb.API.V2.TransactionView do end def render("token_transfers.json", %{token_transfers: token_transfers, next_page_params: next_page_params, conn: conn}) do - {decoded_transactions, _, _} = decode_transactions(Enum.map(token_transfers, fn tt -> tt.transaction end), true) + {decoded_transactions, _, _} = + Transaction.decode_transactions(Enum.map(token_transfers, fn tt -> tt.transaction end), true, @api_true) %{ "items" => @@ -128,7 +129,8 @@ defmodule BlockScoutWeb.API.V2.TransactionView do end def render("token_transfers.json", %{token_transfers: token_transfers, conn: conn}) do - {decoded_transactions, _, _} = decode_transactions(Enum.map(token_transfers, fn tt -> tt.transaction end), true) + {decoded_transactions, _, _} = + Transaction.decode_transactions(Enum.map(token_transfers, fn tt -> tt.transaction end), true, @api_true) token_transfers |> Enum.zip(decoded_transactions) @@ -136,7 +138,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do end def render("token_transfer.json", %{token_transfer: token_transfer, conn: conn}) do - {[decoded_transaction], _, _} = decode_transactions([token_transfer.transaction], true) + {[decoded_transaction], _, _} = Transaction.decode_transactions([token_transfer.transaction], true, @api_true) prepare_token_transfer(token_transfer, conn, decoded_transaction) end @@ -231,18 +233,6 @@ defmodule BlockScoutWeb.API.V2.TransactionView do Enum.reverse(result) end - def decode_transactions(transactions, skip_sig_provider?) do - {results, abi_acc, methods_acc} = - Enum.reduce(transactions, {[], %{}, %{}}, fn transaction, {results, abi_acc, methods_acc} -> - {result, abi_acc, methods_acc} = - Transaction.decoded_input_data(transaction, skip_sig_provider?, @api_true, abi_acc, methods_acc) - - {[format_decoded_input(result) | results], abi_acc, methods_acc} - end) - - {Enum.reverse(results), abi_acc, methods_acc} - end - def prepare_token_transfer(token_transfer, _conn, decoded_input) do %{ "tx_hash" => token_transfer.transaction_hash, @@ -256,7 +246,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do do: block_timestamp(token_transfer.transaction), else: block_timestamp(token_transfer.block) ), - "method" => method_name(token_transfer.transaction, decoded_input, true), + "method" => Transaction.method_name(token_transfer.transaction, decoded_input, true), "block_hash" => to_string(token_transfer.block_hash), "block_number" => to_string(token_transfer.block_number), "log_index" => to_string(token_transfer.log_index) @@ -457,7 +447,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do "token_transfers_overflow" => token_transfers_overflow(transaction.token_transfers, single_tx?), "actions" => transaction_actions(transaction.transaction_actions), "exchange_rate" => Market.get_coin_exchange_rate().usd_value, - "method" => method_name(transaction, decoded_input), + "method" => Transaction.method_name(transaction, decoded_input), "tx_types" => tx_types(transaction), "tx_tag" => GetTransactionTags.get_transaction_tags(transaction.hash, current_user(single_tx? && conn)), "has_error_in_internal_txs" => transaction.has_error_in_internal_txs @@ -553,12 +543,6 @@ defmodule BlockScoutWeb.API.V2.TransactionView do defp format_status({:error, reason}), do: reason defp format_status(status), do: status - @spec format_decoded_input(any()) :: nil | map() | tuple() - def format_decoded_input({:error, _, []}), do: nil - def format_decoded_input({:error, _, candidates}), do: Enum.at(candidates, 0) - def format_decoded_input({:ok, _identifier, _text, _mapping} = decoded), do: decoded - def format_decoded_input(_), do: nil - defp format_decoded_log_input({:error, :could_not_decode}), do: nil defp format_decoded_log_input({:ok, _method_id, _text, _mapping} = decoded), do: decoded defp format_decoded_log_input({:error, _, candidates}), do: Enum.at(candidates, 0) @@ -607,32 +591,6 @@ defmodule BlockScoutWeb.API.V2.TransactionView do |> Timex.diff(right, :milliseconds) end - @doc """ - Return method name used in tx - """ - @spec method_name(Transaction.t(), any(), boolean()) :: binary() | nil - def method_name(_, _, skip_sc_check? \\ false) - - def method_name(_, {:ok, _method_id, text, _mapping}, _) do - Transaction.parse_method_name(text, false) - end - - def method_name( - %Transaction{to_address: to_address, input: %{bytes: <>}}, - _, - skip_sc_check? - ) do - if skip_sc_check? || Address.smart_contract?(to_address) do - "0x" <> Base.encode16(method_id, case: :lower) - else - nil - end - end - - def method_name(_, _, _) do - nil - end - @doc """ Returns array of token types for tx. """ diff --git a/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex b/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex index d332cd7dd149..88496d4cd3c0 100644 --- a/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex +++ b/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex @@ -13,8 +13,14 @@ defmodule Explorer.Chain.CSVExport.AddressTransactionCsvExporter do {from_block, to_block} = Helper.block_from_period(from_period, to_period) exchange_rate = Market.get_coin_exchange_rate() - address_hash - |> fetch_transactions(from_block, to_block, filter_type, filter_value, Helper.paging_options()) + transactions = + address_hash + |> fetch_transactions(from_block, to_block, filter_type, filter_value, Helper.paging_options()) + + transactions + |> Transaction.decode_transactions(true, api?: true) + |> elem(0) + |> Enum.zip(transactions) |> to_csv_format(address_hash, exchange_rate) |> Helper.dump_to_stream() end @@ -22,7 +28,7 @@ defmodule Explorer.Chain.CSVExport.AddressTransactionCsvExporter do # sobelow_skip ["DOS.StringToAtom"] def fetch_transactions(address_hash, from_block, to_block, filter_type, filter_value, paging_options) do options = - [] + [necessity_by_association: %{[to_address: :smart_contract] => :optional}] |> DenormalizationHelper.extend_block_necessity(:required) |> Keyword.put(:paging_options, paging_options) |> Keyword.put(:from_block, from_block) @@ -35,7 +41,7 @@ defmodule Explorer.Chain.CSVExport.AddressTransactionCsvExporter do Transaction.address_to_transactions_without_rewards(address_hash, options) end - defp to_csv_format(transactions, address_hash, exchange_rate) do + defp to_csv_format(transactions_with_decoded_data, address_hash, exchange_rate) do row_names = [ "TxHash", "BlockNumber", @@ -50,11 +56,12 @@ defmodule Explorer.Chain.CSVExport.AddressTransactionCsvExporter do "ErrCode", "CurrentPrice", "TxDateOpeningPrice", - "TxDateClosingPrice" + "TxDateClosingPrice", + "MethodName" ] date_to_prices = - Enum.reduce(transactions, %{}, fn tx, acc -> + Enum.reduce(transactions_with_decoded_data, %{}, fn {_decoded_data, tx}, acc -> date = tx |> Transaction.block_timestamp() |> DateTime.to_date() if Map.has_key?(acc, date) do @@ -71,8 +78,8 @@ defmodule Explorer.Chain.CSVExport.AddressTransactionCsvExporter do end) transaction_lists = - transactions - |> Stream.map(fn transaction -> + transactions_with_decoded_data + |> Stream.map(fn {decoded_data, transaction} -> {opening_price, closing_price} = date_to_prices[DateTime.to_date(Transaction.block_timestamp(transaction))] [ @@ -89,7 +96,8 @@ defmodule Explorer.Chain.CSVExport.AddressTransactionCsvExporter do transaction.error, exchange_rate.usd_value, opening_price, - closing_price + closing_price, + Transaction.method_name(transaction, decoded_data) ] end) diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index 2954241ab741..05916ff89445 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -288,6 +288,7 @@ defmodule Explorer.Chain.Transaction do alias Explorer.{Chain, Helper, PagingOptions, Repo, SortingHelper} alias Explorer.Chain.{ + Address, Block, Block.Reward, ContractMethod, @@ -1900,4 +1901,59 @@ defmodule Explorer.Chain.Transaction do timeout: :infinity ) end + + @doc """ + Receives as input list of transactions and returns tuple {decoded_input_data, abi_acc, methods_acc} + Where + - `decoded_input_data` is list of results: either `{:ok, _identifier, _text, _mapping}` or `nil` + - `abi_acc` is list of all smart contracts ABIs fetched during decoding + - `methods_acc` is list of all smart contracts methods fetched from `contract_methods` table during decoding + """ + @spec decode_transactions([Transaction.t()], boolean(), Keyword.t()) :: {[any()], map(), map()} + def decode_transactions(transactions, skip_sig_provider?, opts) do + {results, abi_acc, methods_acc} = + Enum.reduce(transactions, {[], %{}, %{}}, fn transaction, {results, abi_acc, methods_acc} -> + {result, abi_acc, methods_acc} = + decoded_input_data(transaction, skip_sig_provider?, opts, abi_acc, methods_acc) + + {[format_decoded_input(result) | results], abi_acc, methods_acc} + end) + + {Enum.reverse(results), abi_acc, methods_acc} + end + + @doc """ + Receives as input result of decoded_input_data/5, returns either nil or decoded input in format: {:ok, _identifier, _text, _mapping} + """ + @spec format_decoded_input(any()) :: nil | tuple() + def format_decoded_input({:error, _, []}), do: nil + def format_decoded_input({:error, _, candidates}), do: Enum.at(candidates, 0) + def format_decoded_input({:ok, _identifier, _text, _mapping} = decoded), do: decoded + def format_decoded_input(_), do: nil + + @doc """ + Return method name used in tx + """ + @spec method_name(t(), any(), boolean()) :: binary() | nil + def method_name(_, _, skip_sc_check? \\ false) + + def method_name(_, {:ok, _method_id, text, _mapping}, _) do + parse_method_name(text, false) + end + + def method_name( + %__MODULE__{to_address: to_address, input: %{bytes: <>}}, + _, + skip_sc_check? + ) do + if skip_sc_check? || Address.smart_contract?(to_address) do + "0x" <> Base.encode16(method_id, case: :lower) + else + nil + end + end + + def method_name(_, _, _) do + nil + end end diff --git a/apps/explorer/test/explorer/chain/csv_export/address_transaction_csv_exporter_test.exs b/apps/explorer/test/explorer/chain/csv_export/address_transaction_csv_exporter_test.exs index 955cad7a0673..536aa2df211b 100644 --- a/apps/explorer/test/explorer/chain/csv_export/address_transaction_csv_exporter_test.exs +++ b/apps/explorer/test/explorer/chain/csv_export/address_transaction_csv_exporter_test.exs @@ -8,9 +8,34 @@ defmodule Explorer.Chain.AddressTransactionCsvExporterTest do test "exports address transactions to csv" do address = insert(:address) + insert(:contract_method, + identifier: Base.decode16!("731133e9", case: :lower), + abi: %{ + "constant" => false, + "inputs" => [ + %{"name" => "account", "type" => "address"}, + %{"name" => "id", "type" => "uint256"}, + %{"name" => "amount", "type" => "uint256"}, + %{"name" => "data", "type" => "bytes"} + ], + "name" => "mint", + "outputs" => [], + "payable" => false, + "stateMutability" => "nonpayable", + "type" => "function" + } + ) + + to_address = insert(:contract_address) + transaction = :transaction - |> insert(from_address: address) + |> insert( + from_address: address, + to_address: to_address, + input: + "0x731133e9000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000" + ) |> with_block() |> Repo.preload(:token_transfers) @@ -50,6 +75,8 @@ defmodule Explorer.Chain.AddressTransactionCsvExporterTest do [[], op_price], _, [[], cl_price], + _, + [[], method_name], _ ] -> %{ @@ -66,7 +93,8 @@ defmodule Explorer.Chain.AddressTransactionCsvExporterTest do error: error, current_price: cur_price, opening_price: op_price, - closing_price: cl_price + closing_price: cl_price, + method_name: method_name } end) @@ -84,6 +112,7 @@ defmodule Explorer.Chain.AddressTransactionCsvExporterTest do assert result.current_price assert result.opening_price assert result.closing_price + assert result.method_name == "mint" end test "fetches all transactions" do From 8eb6f08f1e1b05692dc0871b94cf909c04114977 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 15 Aug 2024 17:25:30 +0300 Subject: [PATCH 076/363] feat: enhance /api/v2/smart-contracts/:hash API endpoint (#10558) * feat: enhance /api/v2/smart-contracts/:hash API endpoint * Refactor implementation fetch * Refactoring address controller * Process review comment * Fix address overview template * Update apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex Co-authored-by: nikitosing <32202610+nikitosing@users.noreply.github.com> --------- Co-authored-by: nikitosing <32202610+nikitosing@users.noreply.github.com> --- .../controllers/api/v2/address_controller.ex | 18 +- .../api/v2/smart_contract_controller.ex | 11 +- .../templates/address/_tabs.html.eex | 2 +- .../templates/address/overview.html.eex | 8 +- .../address_coin_balance/index.html.eex | 2 +- .../templates/address_contract/index.html.eex | 22 +-- .../index.html.eex | 2 +- .../index.html.eex | 2 +- .../templates/address_logs/index.html.eex | 2 +- .../address_read_contract/index.html.eex | 2 +- .../address_read_proxy/index.html.eex | 2 +- .../templates/address_token/index.html.eex | 2 +- .../address_token_transfer/index.html.eex | 2 +- .../address_transaction/index.html.eex | 2 +- .../address_validation/index.html.eex | 2 +- .../address_withdrawal/index.html.eex | 10 +- .../address_write_contract/index.html.eex | 2 +- .../address_write_proxy/index.html.eex | 2 +- .../block_withdrawal/_withdrawal.html.eex | 2 +- .../tokens/holder/_token_balances.html.eex | 2 +- .../tokens/transfer/_token_transfer.html.eex | 4 +- .../templates/transaction/_actions.html.eex | 8 +- .../transaction/_pending_tile.html.eex | 4 +- .../_total_transfers_from_to.html.eex | 4 +- .../templates/transaction/overview.html.eex | 8 +- .../transaction_state/_state_change.html.eex | 4 +- .../_token_transfer.html.eex | 4 +- .../templates/withdrawal/_withdrawal.html.eex | 2 +- .../views/address_coin_balance_view.ex | 1 + .../views/address_contract_view.ex | 1 + .../views/address_decompiled_contract_view.ex | 1 + .../address_internal_transaction_view.ex | 1 + .../views/address_logs_view.ex | 1 + .../views/address_read_contract_view.ex | 2 + .../views/address_read_proxy_view.ex | 2 + .../views/address_token_transfer_view.ex | 1 + .../views/address_token_view.ex | 1 + .../views/address_transaction_view.ex | 1 + .../views/address_validation_view.ex | 2 +- .../lib/block_scout_web/views/address_view.ex | 35 ++-- .../views/address_withdrawal_view.ex | 2 + .../views/address_write_contract_view.ex | 2 + .../views/address_write_proxy_view.ex | 2 + .../views/api/v2/address_view.ex | 33 +--- .../block_scout_web/views/api/v2/helper.ex | 52 ++---- .../views/api/v2/smart_contract_view.ex | 96 ++++++----- .../views/block_withdrawal_view.ex | 2 + .../views/tokens/holder_view.ex | 2 +- .../views/transaction_state_view.ex | 2 +- .../views/transaction_token_transfer_view.ex | 1 + .../block_scout_web/views/transaction_view.ex | 2 +- .../block_scout_web/views/withdrawal_view.ex | 2 + .../account/api/v2/user_controller_test.exs | 2 + .../address_read_contract_controller_test.exs | 2 - .../address_read_proxy_controller_test.exs | 2 - ...address_write_contract_controller_test.exs | 2 - .../address_write_proxy_controller_test.exs | 2 - .../api/v2/address_controller_test.exs | 3 + .../api/v2/smart_contract_controller_test.exs | 157 ++++++++---------- .../views/address_view_test.exs | 17 -- apps/explorer/lib/explorer/chain.ex | 47 +----- apps/explorer/lib/explorer/chain/address.ex | 23 +++ .../lib/explorer/chain/address/counters.ex | 2 +- .../lib/explorer/chain/smart_contract.ex | 21 +-- .../explorer/chain/smart_contract/proxy.ex | 89 +++++++--- .../proxy/models/implementation.ex | 114 +++++++------ .../lib/explorer/chain/transaction.ex | 2 +- .../lib/explorer/smart_contract/helper.ex | 48 +++++- .../proxy/models/implementation_test.exs | 36 ++-- apps/explorer/test/explorer/chain_test.exs | 5 +- 70 files changed, 487 insertions(+), 471 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index f5c2ef44f44e..59afca890e2c 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -29,6 +29,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do alias Explorer.Chain.{Address, Hash, Transaction} alias Explorer.Chain.Address.Counters alias Explorer.Chain.Token.Instance + alias Explorer.SmartContract.Helper, as: SmartContractHelper alias BlockScoutWeb.API.V2.CeloView alias Explorer.Chain.Celo.ElectionReward, as: CeloElectionReward @@ -118,16 +119,23 @@ defmodule BlockScoutWeb.API.V2.AddressController do action_fallback(BlockScoutWeb.API.V2.FallbackController) def address(conn, %{"address_hash_param" => address_hash_string} = params) do - with {:ok, _address_hash, address} <- validate_address(address_hash_string, params, @address_options), - fully_preloaded_address <- - Address.maybe_preload_smart_contract_associations(address, @contract_address_preloads, @api_true) do - CoinBalanceOnDemand.trigger_fetch(fully_preloaded_address) + with {:ok, _address_hash, address} <- validate_address(address_hash_string, params, @address_options) do + fully_preloaded_address = + Address.maybe_preload_smart_contract_associations(address, @contract_address_preloads, @api_true) + {implementations, proxy_type} = + SmartContractHelper.pre_fetch_implementations(fully_preloaded_address) + + CoinBalanceOnDemand.trigger_fetch(address) ContractCodeOnDemand.trigger_fetch(address) conn |> put_status(200) - |> render(:address, %{address: fully_preloaded_address |> maybe_preload_ens_to_address()}) + |> render(:address, %{ + address: fully_preloaded_address |> maybe_preload_ens_to_address(), + implementations: implementations, + proxy_type: proxy_type + }) end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex index 03d33589e1d8..7d19376428e2 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex @@ -14,6 +14,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do alias Explorer.Chain.{Address, SmartContract} alias Explorer.Chain.SmartContract.AuditReport alias Explorer.Chain.SmartContract.Proxy.Models.Implementation + alias Explorer.SmartContract.Helper, as: SmartContractHelper alias Explorer.SmartContract.{Reader, Writer} alias Explorer.SmartContract.Solidity.PublishHelper alias Explorer.ThirdPartyIntegrations.SolidityScan @@ -22,7 +23,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do necessity_by_association: %{ :contracts_creation_internal_transaction => :optional, [smart_contract: :smart_contract_additional_sources] => :optional, - :contracts_creation_transaction => :optional + :contracts_creation_transaction => :optional, + :proxy_implementations => :optional }, api?: true ] @@ -37,9 +39,12 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do _ <- PublishHelper.sourcify_check(address_hash_string), {:not_found, {:ok, address}} <- {:not_found, Chain.find_contract_address(address_hash, @smart_contract_address_options, false)} do + {implementations, proxy_type} = + SmartContractHelper.pre_fetch_implementations(address) + conn |> put_status(200) - |> render(:smart_contract, %{address: address}) + |> render(:smart_contract, %{address: address, implementations: implementations, proxy_type: proxy_type}) end end @@ -206,7 +211,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do end @doc """ - /api/v2/smart-contracts/${address_hash_string}/solidityscan-report logic + /api/v2/smart-contracts/:address_hash_string/solidityscan-report logic """ @spec solidityscan_report(Plug.Conn.t(), map()) :: {:address, {:error, :not_found}} diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex index a4060c9485d9..67a69145bfad 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex @@ -59,7 +59,7 @@ to: AccessHelper.get_path(@conn, :address_validation_path, :index, @address.hash) ) %> <% end %> - <%= if contract?(@address) do %> + <%= if Address.smart_contract?(@address) do %> <%= link( to: AccessHelper.get_path(@conn, :address_contract_path, :index, @address.hash), class: "card-tab #{tab_status("contracts", @conn.request_path)}") do %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex index dfc00f704859..0e4e20000138 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex @@ -40,7 +40,7 @@ <%= render BlockScoutWeb.CommonComponentsView, "_btn_qr_code.html" %> -

<%= @address %>

+

<%= @address %>

<% from_address_hash = from_address_hash(@address) %> - <%= if contract?(@address) do %> + <%= if Address.smart_contract?(@address) do %>
<%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", @@ -124,7 +124,7 @@ <% end %> <%= if @is_proxy do %> - <% {implementation_addresses, implementation_names} = Implementation.get_implementation(@address.smart_contract) %> + <% {implementation_addresses, implementation_names, _proxy_type} = Implementation.get_implementation(@address.smart_contract) %> <% implementation_address_ = Enum.at(implementation_addresses, 0) %> <% name = Enum.at(implementation_names, 0) %> <% implementation_address = implementation_address_ || "0x0000000000000000000000000000000000000000" %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/index.html.eex index 268b148cdb72..27c10ec6b77b 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/index.html.eex @@ -1,7 +1,7 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex index d5b804f42b3f..7b06783f2ed0 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex @@ -1,12 +1,12 @@ <% contract_creation_code = contract_creation_code(@address) %> <% minimal_proxy_template = EIP1167.get_implementation_smart_contract(@address.hash) %> -<% implementation_or_bytecode_twin_contract = minimal_proxy_template || SmartContract.get_address_verified_bytecode_twin_contract(@address.hash).verified_contract %> +<% bytecode_twin_contract = SmartContract.get_address_verified_bytecode_twin_contract(@address.hash).verified_contract %> <% smart_contract_verified = BlockScoutWeb.AddressView.smart_contract_verified?(@address) %> <% fully_verified = SmartContract.verified_with_full_match?(@address.hash)%> -<% additional_sources = BlockScoutWeb.API.V2.SmartContractView.get_additional_sources(@address.smart_contract, smart_contract_verified, implementation_or_bytecode_twin_contract) %> +<% additional_sources = BlockScoutWeb.API.V2.SmartContractView.get_additional_sources(@address.smart_contract, smart_contract_verified, bytecode_twin_contract) %> <% visualize_sol2uml_enabled = Explorer.Visualize.Sol2uml.enabled?() %>
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> @@ -15,16 +15,16 @@
<%= unless smart_contract_verified do %> <%= if minimal_proxy_template do %> - <%= render BlockScoutWeb.CommonComponentsView, "_minimal_proxy_pattern.html", address_hash: implementation_or_bytecode_twin_contract.address_hash, conn: @conn %> + <%= render BlockScoutWeb.CommonComponentsView, "_minimal_proxy_pattern.html", address_hash: bytecode_twin_contract.address_hash, conn: @conn %> <% else %> - <%= if implementation_or_bytecode_twin_contract do %> + <%= if bytecode_twin_contract do %> <% path = address_verify_contract_path(@conn, :new, @address.hash) %>
<%= render BlockScoutWeb.CommonComponentsView, "_info.html" %> <%= gettext("Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB") %> <%= link( - implementation_or_bytecode_twin_contract.address_hash, - to: address_contract_path(@conn, :index, implementation_or_bytecode_twin_contract.address_hash)) %>.
<%= gettext("All metadata displayed below is from that contract. In order to verify current contract, click") %> <%= gettext("Verify & Publish") %> <%= gettext("button") %>
+ bytecode_twin_contract.address_hash, + to: address_contract_path(@conn, :index, bytecode_twin_contract.address_hash)) %>.
<%= gettext("All metadata displayed below is from that contract. In order to verify current contract, click") %> <%= gettext("Verify & Publish") %> <%= gettext("button") %>
<%= link(gettext("Verify & Publish"), to: path, class: "button button-primary button-sm float-right ml-3", "data-test": "verify_and_publish") %>
@@ -40,8 +40,8 @@
<% end %> <% end %> - <%= if smart_contract_verified || (!smart_contract_verified && implementation_or_bytecode_twin_contract) do %> - <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: implementation_or_bytecode_twin_contract %> + <%= if smart_contract_verified || (!smart_contract_verified && bytecode_twin_contract) do %> + <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: bytecode_twin_contract %> <%= if @address.smart_contract && @address.smart_contract.verified_via_sourcify && @address.smart_contract.partially_verified && smart_contract_verified do %>
<%= gettext("This contract has been partially verified via Sourcify.") %> @@ -240,8 +240,8 @@ <% end %>
- <%= if smart_contract_verified || (!smart_contract_verified && implementation_or_bytecode_twin_contract) do %> - <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: implementation_or_bytecode_twin_contract %> + <%= if smart_contract_verified || (!smart_contract_verified && bytecode_twin_contract) do %> + <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: bytecode_twin_contract %> <%= if target_contract.external_libraries && target_contract.external_libraries != [] do %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/index.html.eex index 5390e7806ef1..d9967f71a83d 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/index.html.eex @@ -1,5 +1,5 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex index 0a5b2bcf94c9..fe847a2a14d3 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex @@ -1,5 +1,5 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex index 3dc20d3a7f78..5b8058fdeeb0 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex @@ -1,5 +1,5 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex index 58ff9c96f561..afd3ab3a7d75 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex @@ -1,6 +1,6 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/index.html.eex index a3577a7cb925..efe8695dec76 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/index.html.eex @@ -1,6 +1,6 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex index 1fd811570f6b..4ecd808911b9 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex @@ -1,5 +1,5 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex index 896ea5f8ab61..3d8e5196a80d 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex @@ -1,5 +1,5 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex index 74f122040a5c..7d8d54178cbd 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex @@ -1,6 +1,6 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex index e435195fe3b1..9c2947ad0612 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex @@ -1,5 +1,5 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_withdrawal/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_withdrawal/index.html.eex index d07c664e6b44..c11adfd87cbb 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_withdrawal/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_withdrawal/index.html.eex @@ -1,6 +1,6 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> @@ -9,17 +9,17 @@ <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %>
- +

<%= gettext "Withdrawals" %>

<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>
- + - +
@@ -54,7 +54,7 @@ <%= gettext "There are no withdrawals for this address." %> - +
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/index.html.eex index a1d915d2ec81..a538e98b5f71 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/index.html.eex @@ -1,6 +1,6 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/index.html.eex index a3577a7cb925..efe8695dec76 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/index.html.eex @@ -1,6 +1,6 @@
- <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + <% is_proxy = SmartContractHelper.address_is_proxy?(@address) %> <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/block_withdrawal/_withdrawal.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/block_withdrawal/_withdrawal.html.eex index 9506edc3319a..5a9ff7c43918 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/block_withdrawal/_withdrawal.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/block_withdrawal/_withdrawal.html.eex @@ -12,7 +12,7 @@ <%= render BlockScoutWeb.AddressView, "_link.html", address: @withdrawal.address, - contract: BlockScoutWeb.AddressView.contract?(@withdrawal.address), + contract: Address.smart_contract?(@withdrawal.address), use_custom_tooltip: false %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex index f8fecf59a639..324741db64a5 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex @@ -2,7 +2,7 @@
- <%= render BlockScoutWeb.AddressView, "_link.html", address: @token_balance.address, contract: BlockScoutWeb.AddressView.contract?(@token_balance.address), use_custom_tooltip: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: @token_balance.address, contract: Address.smart_contract?(@token_balance.address), use_custom_tooltip: false %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex index 73b6bbf5afe5..1e019df78ecb 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex @@ -15,7 +15,7 @@ BlockScoutWeb.AddressView, "_responsive_hash.html", address: @token_transfer.from_address, - contract: BlockScoutWeb.AddressView.contract?(@token_transfer.from_address), + contract: Address.smart_contract?(@token_transfer.from_address), use_custom_tooltip: false ) %> <% end %> @@ -25,7 +25,7 @@ BlockScoutWeb.AddressView, "_responsive_hash.html", address: @token_transfer.to_address, - contract: BlockScoutWeb.AddressView.contract?(@token_transfer.to_address), + contract: Address.smart_contract?(@token_transfer.to_address), use_custom_tooltip: false ) %> <% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_actions.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_actions.html.eex index 9a820f5ad9af..982434fcbd37 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_actions.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_actions.html.eex @@ -84,7 +84,7 @@ <% address_string = Map.get(@action.data, "address") %> <% {address_status, address} = transaction_action_string_to_address(address_string) %> - <% address = if address_status == :ok, do: render_to_string(BlockScoutWeb.AddressView, "_link.html", address: address, contract: BlockScoutWeb.AddressView.contract?(address), use_custom_tooltip: false, trimmed: false), else: render_to_string(BlockScoutWeb.TransactionView, "_actions_address.html", address_string: address_string, action: @action) %> + <% address = if address_status == :ok, do: render_to_string(BlockScoutWeb.AddressView, "_link.html", address: address, contract: Address.smart_contract?(address), use_custom_tooltip: false, trimmed: false), else: render_to_string(BlockScoutWeb.TransactionView, "_actions_address.html", address_string: address_string, action: @action) %> <% to_address = Map.get(@action.data, "to") %> <% to_content = raw(render_to_string BlockScoutWeb.TransactionView, "_actions_to.html", address: to_address) %> <% to = link to: address_path(BlockScoutWeb.Endpoint, :show, to_address), "data-test": "address_hash_link", do: to_content %> @@ -114,11 +114,11 @@ <% address0 = Map.get(@action.data, "address0") %> <% symbol1 = Map.get(@action.data, "symbol1") %> <% address1 = Map.get(@action.data, "address1") %> - + - + <% symbol0 = if symbol0 != "Ether", do: link(symbol0, to: token_path(BlockScoutWeb.Endpoint, :show, address0), "data-test": "token_link"), else: raw(symbol0) %> - + <% symbol1 = if symbol1 != "Ether", do: link(symbol1, to: token_path(BlockScoutWeb.Endpoint, :show, address1), "data-test": "token_link"), else: raw(symbol1) %> <%= if @action.type == :mint do %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_pending_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_pending_tile.html.eex index fd1a1496f183..e3b68d786d76 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_pending_tile.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_pending_tile.html.eex @@ -8,10 +8,10 @@
<%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @transaction.hash %> - <%= render BlockScoutWeb.AddressView, "_link.html", address: @transaction.from_address, contract: BlockScoutWeb.AddressView.contract?(@transaction.from_address), use_custom_tooltip: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: @transaction.from_address, contract: Address.smart_contract?(@transaction.from_address), use_custom_tooltip: false %> → <%= if @transaction.to_address_hash do %> - <%= render BlockScoutWeb.AddressView, "_link.html", address: @transaction.to_address, contract: BlockScoutWeb.AddressView.contract?(@transaction.to_address), use_custom_tooltip: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: @transaction.to_address, contract: Address.smart_contract?(@transaction.to_address), use_custom_tooltip: false %> <% else %> <%= gettext("Contract Address Pending") %> <% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex index 91de91125ad1..db68b6f09527 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex @@ -7,7 +7,7 @@ From
@@ -28,7 +28,7 @@ <% else %> <% end %> <%= if not_negative?(@balance_before) and not_negative?(@balance_after) do %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex index 885990a377ae..8611cb5b02ed 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex @@ -7,9 +7,9 @@
<%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @token_transfer.transaction_hash %> - <%= render BlockScoutWeb.AddressView, "_link.html", address: @token_transfer.from_address, contract: BlockScoutWeb.AddressView.contract?(@token_transfer.from_address), use_custom_tooltip: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: @token_transfer.from_address, contract: Address.smart_contract?(@token_transfer.from_address), use_custom_tooltip: false %> → - <%= render BlockScoutWeb.AddressView, "_link.html", address: @token_transfer.to_address, contract: BlockScoutWeb.AddressView.contract?(@token_transfer.to_address), use_custom_tooltip: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: @token_transfer.to_address, contract: Address.smart_contract?(@token_transfer.to_address), use_custom_tooltip: false %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/withdrawal/_withdrawal.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/withdrawal/_withdrawal.html.eex index e90e6c99d226..cb3928bc0213 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/withdrawal/_withdrawal.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/withdrawal/_withdrawal.html.eex @@ -19,7 +19,7 @@ <%= render BlockScoutWeb.AddressView, "_link.html", address: @withdrawal.address, - contract: BlockScoutWeb.AddressView.contract?(@withdrawal.address), + contract: Address.smart_contract?(@withdrawal.address), use_custom_tooltip: false %> diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_coin_balance_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_coin_balance_view.ex index d2bd7904bc44..6e0363dab30c 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_coin_balance_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_coin_balance_view.ex @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.AddressCoinBalanceView do alias BlockScoutWeb.AccessHelper alias Explorer.Chain.Wei + alias Explorer.SmartContract.Helper, as: SmartContractHelper def format(%Wei{} = value) do format_wei_value(value, :ether) diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex index 01b13e6a71d3..efb3dd168fae 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex @@ -10,6 +10,7 @@ defmodule BlockScoutWeb.AddressContractView do alias Explorer.Chain.{Address, Data, InternalTransaction, Transaction} alias Explorer.Chain.SmartContract alias Explorer.Chain.SmartContract.Proxy.EIP1167 + alias Explorer.SmartContract.Helper, as: SmartContractHelper def render("scripts.html", %{conn: conn}) do render_scripts(conn, "address_contract/code_highlighting.js") diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex index daab6b54b11d..8542b7f0672a 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.AddressDecompiledContractView do use BlockScoutWeb, :view + alias Explorer.SmartContract.Helper, as: SmartContractHelper @colors %{ "\e[95m" => "", diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_internal_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_internal_transaction_view.ex index 28a478b12a35..81cb6ca558b1 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_internal_transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_internal_transaction_view.ex @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.AddressInternalTransactionView do alias BlockScoutWeb.AccessHelper alias Explorer.Chain.Address + alias Explorer.SmartContract.Helper, as: SmartContractHelper def format_current_filter(filter) do case filter do diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex index 0c4daa067d93..e1a4862fe21a 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex @@ -2,6 +2,7 @@ defmodule BlockScoutWeb.AddressLogsView do use BlockScoutWeb, :view alias Explorer.Chain.Address + alias Explorer.SmartContract.Helper, as: SmartContractHelper import BlockScoutWeb.AddressView, only: [decode: 2] end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_read_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_read_contract_view.ex index 559af200bc35..e13b14768e9b 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_read_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_read_contract_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.AddressReadContractView do use BlockScoutWeb, :view + + alias Explorer.SmartContract.Helper, as: SmartContractHelper end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex index e51247fdd8e2..447f362c3c5e 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.AddressReadProxyView do use BlockScoutWeb, :view + + alias Explorer.SmartContract.Helper, as: SmartContractHelper end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_token_transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_token_transfer_view.ex index 6a4eeffafedc..c053e54af567 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_token_transfer_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_token_transfer_view.ex @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.AddressTokenTransferView do alias BlockScoutWeb.AccessHelper alias Explorer.Chain.Address + alias Explorer.SmartContract.Helper, as: SmartContractHelper def format_current_filter(filter) do case filter do diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_token_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_token_view.ex index 83cdf79a7182..941cfe292497 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_token_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_token_view.ex @@ -4,4 +4,5 @@ defmodule BlockScoutWeb.AddressTokenView do alias BlockScoutWeb.{AddressView, ChainView} alias Explorer.Chain alias Explorer.Chain.{Address, Wei} + alias Explorer.SmartContract.Helper, as: SmartContractHelper end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_transaction_view.ex index 1b18148a475e..b6539a49a2db 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_transaction_view.ex @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.AddressTransactionView do alias BlockScoutWeb.AccessHelper alias Explorer.Chain.Address + alias Explorer.SmartContract.Helper, as: SmartContractHelper def format_current_filter(filter) do case filter do diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_validation_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_validation_view.ex index 65f2bb0e9d35..1625fb33cdfd 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_validation_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_validation_view.ex @@ -1,5 +1,5 @@ defmodule BlockScoutWeb.AddressValidationView do use BlockScoutWeb, :view - # import BlockScoutWeb.AddressView, only: [contract?: 1, smart_contract_verified?: 1] + alias Explorer.SmartContract.Helper, as: SmartContractHelper end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex index 6792270e7c51..a8016bdccade 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex @@ -36,7 +36,7 @@ defmodule BlockScoutWeb.AddressView do def address_partial_selector(struct_to_render_from, direction, current_address, truncate \\ false) def address_partial_selector(%Address{} = address, _, current_address, truncate) do - matching_address_check(current_address, address, contract?(address), truncate) + matching_address_check(current_address, address, Address.smart_contract?(address), truncate) end def address_partial_selector( @@ -58,19 +58,19 @@ defmodule BlockScoutWeb.AddressView do end def address_partial_selector(%InternalTransaction{to_address: address}, :to, current_address, truncate) do - matching_address_check(current_address, address, contract?(address), truncate) + matching_address_check(current_address, address, Address.smart_contract?(address), truncate) end def address_partial_selector(%InternalTransaction{from_address: address}, :from, current_address, truncate) do - matching_address_check(current_address, address, contract?(address), truncate) + matching_address_check(current_address, address, Address.smart_contract?(address), truncate) end def address_partial_selector(%TokenTransfer{to_address: address}, :to, current_address, truncate) do - matching_address_check(current_address, address, contract?(address), truncate) + matching_address_check(current_address, address, Address.smart_contract?(address), truncate) end def address_partial_selector(%TokenTransfer{from_address: address}, :from, current_address, truncate) do - matching_address_check(current_address, address, contract?(address), truncate) + matching_address_check(current_address, address, Address.smart_contract?(address), truncate) end def address_partial_selector( @@ -92,11 +92,11 @@ defmodule BlockScoutWeb.AddressView do end def address_partial_selector(%Transaction{to_address: address}, :to, current_address, truncate) do - matching_address_check(current_address, address, contract?(address), truncate) + matching_address_check(current_address, address, Address.smart_contract?(address), truncate) end def address_partial_selector(%Transaction{from_address: address}, :from, current_address, truncate) do - matching_address_check(current_address, address, contract?(address), truncate) + matching_address_check(current_address, address, Address.smart_contract?(address), truncate) end def address_partial_selector(%Reward{address: address}, _, current_address, truncate) do @@ -104,7 +104,7 @@ defmodule BlockScoutWeb.AddressView do end def address_title(%Address{} = address) do - if contract?(address) do + if Address.smart_contract?(address) do gettext("Contract Address") else gettext("Address") @@ -167,12 +167,6 @@ defmodule BlockScoutWeb.AddressView do to_string(fetched_coin_balance_block_number) end - def contract?(%Address{contract_code: nil}), do: false - - def contract?(%Address{contract_code: _}), do: true - - def contract?(nil), do: true - def validator?(val) when val > 0, do: true def validator?(_), do: false @@ -259,14 +253,6 @@ defmodule BlockScoutWeb.AddressView do def read_function?(function), do: Helper.queriable_method?(function) || Helper.read_with_wallet_method?(function) - def smart_contract_is_proxy?(address, options \\ []) - - def smart_contract_is_proxy?(%Address{smart_contract: %SmartContract{} = smart_contract}, options) do - Proxy.proxy_contract?(smart_contract, options) - end - - def smart_contract_is_proxy?(%Address{smart_contract: _}, _), do: false - def smart_contract_with_write_functions?(%Address{smart_contract: %SmartContract{}} = address) do !contract_interaction_disabled?() && Enum.any?( @@ -447,13 +433,14 @@ defmodule BlockScoutWeb.AddressView do def address_page_title(address) do cond do smart_contract_verified?(address) -> "#{address.smart_contract.name} (#{to_string(address)})" - contract?(address) -> "Contract #{to_string(address)}" + Address.smart_contract?(address) -> "Contract #{to_string(address)}" true -> "#{to_string(address)}" end end def smart_contract_is_gnosis_safe_proxy?(%Address{smart_contract: %SmartContract{}} = address) do - address.smart_contract.name == "GnosisSafeProxy" && Proxy.gnosis_safe_contract?(address.smart_contract.abi) + address.smart_contract.name == "GnosisSafeProxy" && + Proxy.gnosis_safe_contract?(address.smart_contract.abi) end def smart_contract_is_gnosis_safe_proxy?(_address), do: false diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_withdrawal_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_withdrawal_view.ex index 9ff659e84757..80572ba12789 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_withdrawal_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_withdrawal_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.AddressWithdrawalView do use BlockScoutWeb, :view + + alias Explorer.SmartContract.Helper, as: SmartContractHelper end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_write_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_write_contract_view.ex index c21e1f51c9bf..dc8d88ce5cc3 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_write_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_write_contract_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.AddressWriteContractView do use BlockScoutWeb, :view + + alias Explorer.SmartContract.Helper, as: SmartContractHelper end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex index 17634e9fb195..b27e5df50319 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.AddressWriteProxyView do use BlockScoutWeb, :view + + alias Explorer.SmartContract.Helper, as: SmartContractHelper end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex index 22841d305916..b39fe2f26f5c 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex @@ -5,11 +5,9 @@ defmodule BlockScoutWeb.API.V2.AddressView do alias BlockScoutWeb.AddressView alias BlockScoutWeb.API.V2.{ApiView, Helper, TokenView} - alias BlockScoutWeb.API.V2.Helper alias Explorer.{Chain, Market} alias Explorer.Chain.Address alias Explorer.Chain.Address.Counters - alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.Chain.Token.Instance @api_true [api?: true] @@ -18,8 +16,8 @@ defmodule BlockScoutWeb.API.V2.AddressView do ApiView.render("message.json", assigns) end - def render("address.json", %{address: address, conn: conn}) do - prepare_address(address, conn) + def render("address.json", %{address: address, implementations: implementations, proxy_type: proxy_type, conn: conn}) do + prepare_address(address, conn, implementations, proxy_type) end def render("token_balances.json", %{token_balances: token_balances}) do @@ -83,29 +81,13 @@ defmodule BlockScoutWeb.API.V2.AddressView do |> Map.put(:coin_balance, if(address.fetched_coin_balance, do: address.fetched_coin_balance.value)) end - def prepare_address(address, conn \\ nil) do + @doc """ + Prepares address properties for rendering in /addresses and /addresses/:address_hash_param API v2 endpoints + """ + @spec prepare_address(Address.t(), Plug.Conn.t() | nil, list(), String.t() | nil) :: map() + def prepare_address(address, conn \\ nil, implementations \\ [], proxy_type \\ nil) do base_info = Helper.address_with_info(conn, address, address.hash, true) - {:ok, address_with_smart_contract} = - Chain.hash_to_address( - address.hash, - [necessity_by_association: %{:smart_contract => :optional}], - false - ) - - is_proxy = AddressView.smart_contract_is_proxy?(address_with_smart_contract, @api_true) - - implementations = - with true <- is_proxy, - {addresses, names} <- - Implementation.get_implementation(address_with_smart_contract.smart_contract, @api_true), - false <- addresses && Enum.empty?(addresses) do - Helper.proxy_object_info(addresses, names) - else - _ -> - [] - end - balance = address.fetched_coin_balance && address.fetched_coin_balance.value exchange_rate = Market.get_coin_exchange_rate().usd_value @@ -134,6 +116,7 @@ defmodule BlockScoutWeb.API.V2.AddressView do extended_info else Map.merge(extended_info, %{ + "proxy_type" => proxy_type, "implementations" => implementations }) end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex index e9d1f963dbad..5f483b191128 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex @@ -4,8 +4,8 @@ defmodule BlockScoutWeb.API.V2.Helper do """ alias Ecto.Association.NotLoaded - alias Explorer.Chain - alias Explorer.Chain.{Address, Hash} + alias Explorer.Chain.Address + alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.Transaction.History.TransactionStats import BlockScoutWeb.Account.AuthController, only: [current_user: 1] @@ -64,26 +64,28 @@ defmodule BlockScoutWeb.API.V2.Helper do def address_with_info(%Address{} = address, _address_hash) do smart_contract? = Address.smart_contract?(address) - {proxy_implementations, implementation_address_hashes, implementation_names} = + {proxy_implementations, implementation_address_hashes, implementation_names, proxy_type} = case address.proxy_implementations do %NotLoaded{} -> - {nil, [], []} + {nil, [], [], nil} nil -> - {nil, [], []} + {nil, [], [], nil} proxy_implementations -> address_hashes = proxy_implementations.address_hashes names = proxy_implementations.names + proxy_type = proxy_implementations.proxy_type - {proxy_implementations, address_hashes, names} + {proxy_implementations, address_hashes, names, proxy_type} end %{ "hash" => Address.checksum(address), "is_contract" => smart_contract?, "name" => address_name(address), - "implementations" => proxy_object_info(implementation_address_hashes, implementation_names), + "proxy_type" => proxy_type, + "implementations" => Proxy.proxy_object_info(implementation_address_hashes, implementation_names), "is_verified" => verified?(address) || verified_minimal_proxy?(proxy_implementations), "ens_domain_name" => address.ens_domain_name, "metadata" => address.metadata @@ -110,6 +112,7 @@ defmodule BlockScoutWeb.API.V2.Helper do "hash" => Address.checksum(address_hash), "is_contract" => false, "name" => nil, + "proxy_type" => nil, "implementations" => [], "is_verified" => nil, "ens_domain_name" => nil, @@ -117,41 +120,6 @@ defmodule BlockScoutWeb.API.V2.Helper do } end - @doc """ - Retrieves formatted proxy object based on its implementation addresses and names. - - ## Parameters - - * `implementation_addresses` - A list of implementation addresses for the proxy object. - * `implementation_names` - A list of implementation names for the proxy object. - - ## Returns - - A list of maps containing information about the proxy object. - - """ - @spec proxy_object_info([String.t() | Hash.Address.t()], [String.t() | nil]) :: [map()] - def proxy_object_info([], []), do: [] - - def proxy_object_info(implementation_addresses, implementation_names) do - implementation_addresses - |> Enum.zip(implementation_names) - |> Enum.reduce([], fn {address, name}, acc -> - case address do - %Hash{} = address_hash -> - [%{"address" => Address.checksum(address_hash), "name" => name} | acc] - - _ -> - with {:ok, address_hash} <- Chain.string_to_address_hash(address), - checksummed_address <- Address.checksum(address_hash) do - [%{"address" => checksummed_address, "name" => name} | acc] - else - _ -> acc - end - end - end) - end - defp minimal_proxy_pattern?(proxy_implementations) do proxy_implementations.proxy_type == :eip1167 end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex index 4a523f9270fd..561788f41f1b 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex @@ -11,18 +11,28 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do alias Ecto.Changeset alias Explorer.Chain alias Explorer.Chain.{Address, SmartContract, SmartContractAdditionalSource} + alias Explorer.SmartContract.Helper, as: SmartContractHelper alias Explorer.Visualize.Sol2uml require Logger @api_true [api?: true] + # Option to skip fetching implementation from the node, + # when checking, if smart-contract is proxy. It is used in smart contract view + # to prevent double request to the JSON RPC node. + @skip_implementation_fetch_true [skip_implementation_fetch?: true] def render("smart_contracts.json", %{smart_contracts: smart_contracts, next_page_params: next_page_params}) do %{"items" => Enum.map(smart_contracts, &prepare_smart_contract_for_list/1), "next_page_params" => next_page_params} end - def render("smart_contract.json", %{address: address, conn: conn}) do - prepare_smart_contract(address, conn) + def render("smart_contract.json", %{ + address: address, + implementations: implementations, + proxy_type: proxy_type, + conn: conn + }) do + prepare_smart_contract(address, implementations, proxy_type, conn) end def render("read_functions.json", %{functions: functions}) do @@ -145,15 +155,20 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do defp prepare_output(output), do: output # credo:disable-for-next-line - def prepare_smart_contract(%Address{smart_contract: %SmartContract{} = smart_contract} = address, conn) do + def prepare_smart_contract( + %Address{smart_contract: %SmartContract{} = smart_contract} = address, + implementations, + proxy_type, + conn + ) do bytecode_twin = SmartContract.get_address_verified_bytecode_twin_contract(address.hash, @api_true) minimal_proxy_address_hash = address.implementation - implementation_or_bytecode_twin_contract = address.implementation || bytecode_twin.verified_contract + bytecode_twin_contract = bytecode_twin.verified_contract smart_contract_verified = AddressView.smart_contract_verified?(address) fully_verified = SmartContract.verified_with_full_match?(address.hash, @api_true) write_methods? = AddressView.smart_contract_with_write_functions?(address) - is_proxy = AddressView.smart_contract_is_proxy?(address, @api_true) + is_proxy = SmartContractHelper.address_is_proxy?(address, Keyword.merge(@api_true, @skip_implementation_fetch_true)) read_custom_abi? = AddressView.has_address_custom_abi_with_read_functions?(conn, address.hash) write_custom_abi? = AddressView.has_address_custom_abi_with_write_functions?(conn, address.hash) @@ -162,57 +177,62 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do get_additional_sources( smart_contract, smart_contract_verified, - implementation_or_bytecode_twin_contract + bytecode_twin_contract ) visualize_sol2uml_enabled = Sol2uml.enabled?() target_contract = - if smart_contract_verified, do: address.smart_contract, else: implementation_or_bytecode_twin_contract + if smart_contract_verified, do: smart_contract, else: bytecode_twin_contract %{ "verified_twin_address_hash" => - implementation_or_bytecode_twin_contract && - Address.checksum(implementation_or_bytecode_twin_contract.address_hash), + bytecode_twin_contract && + Address.checksum(bytecode_twin_contract.address_hash), "is_verified" => smart_contract_verified, - "is_changed_bytecode" => smart_contract_verified && address.smart_contract.is_changed_bytecode, - "is_partially_verified" => address.smart_contract.partially_verified && smart_contract_verified, + "is_changed_bytecode" => smart_contract_verified && smart_contract.is_changed_bytecode, + "is_partially_verified" => smart_contract.partially_verified && smart_contract_verified, "is_fully_verified" => fully_verified, - "is_verified_via_sourcify" => address.smart_contract.verified_via_sourcify && smart_contract_verified, - "is_verified_via_eth_bytecode_db" => address.smart_contract.verified_via_eth_bytecode_db, - "is_verified_via_verifier_alliance" => address.smart_contract.verified_via_verifier_alliance, - "is_vyper_contract" => target_contract.is_vyper_contract, + "is_verified_via_sourcify" => smart_contract.verified_via_sourcify && smart_contract_verified, + "is_verified_via_eth_bytecode_db" => smart_contract.verified_via_eth_bytecode_db, + "is_verified_via_verifier_alliance" => smart_contract.verified_via_verifier_alliance, + "is_vyper_contract" => target_contract && target_contract.is_vyper_contract, "has_custom_methods_read" => read_custom_abi?, "has_custom_methods_write" => write_custom_abi?, "has_methods_read" => AddressView.smart_contract_with_read_only_functions?(address), "has_methods_write" => write_methods?, "has_methods_read_proxy" => is_proxy, "has_methods_write_proxy" => is_proxy && write_methods?, + # todo: remove this property once frontend is bound to "implementations" "minimal_proxy_address_hash" => minimal_proxy_address_hash && Address.checksum(minimal_proxy_address_hash.address_hash), + "proxy_type" => proxy_type, + "implementations" => implementations, "sourcify_repo_url" => - if(address.smart_contract.verified_via_sourcify && smart_contract_verified, - do: AddressContractView.sourcify_repo_url(address.hash, address.smart_contract.partially_verified) + if(smart_contract.verified_via_sourcify && smart_contract_verified, + do: AddressContractView.sourcify_repo_url(address.hash, smart_contract.partially_verified) ), "can_be_visualized_via_sol2uml" => - visualize_sol2uml_enabled && !target_contract.is_vyper_contract && !is_nil(target_contract.abi), - "name" => target_contract.name, - "compiler_version" => target_contract.compiler_version, - "optimization_enabled" => target_contract.optimization, - "optimization_runs" => target_contract.optimization_runs, - "evm_version" => target_contract.evm_version, - "verified_at" => target_contract.inserted_at, - "abi" => target_contract.abi, - "source_code" => target_contract.contract_source_code, - "file_path" => target_contract.file_path, + visualize_sol2uml_enabled && target_contract && !target_contract.is_vyper_contract && + !is_nil(target_contract.abi), + "name" => target_contract && target_contract.name, + "compiler_version" => target_contract && target_contract.compiler_version, + "optimization_enabled" => target_contract && target_contract.optimization, + "optimization_runs" => target_contract && target_contract.optimization_runs, + "evm_version" => target_contract && target_contract.evm_version, + "verified_at" => target_contract && target_contract.inserted_at, + "abi" => target_contract && target_contract.abi, + "source_code" => target_contract && target_contract.contract_source_code, + "file_path" => target_contract && target_contract.file_path, "additional_sources" => (is_list(additional_sources) && Enum.map(additional_sources, &prepare_additional_source/1)) || [], - "compiler_settings" => target_contract.compiler_settings, - "external_libraries" => prepare_external_libraries(target_contract.external_libraries), - "constructor_args" => if(smart_contract_verified, do: target_contract.constructor_arguments), + "compiler_settings" => target_contract && target_contract.compiler_settings, + "external_libraries" => (target_contract && prepare_external_libraries(target_contract.external_libraries)) || [], + "constructor_args" => if(smart_contract_verified, do: target_contract && target_contract.constructor_arguments), "decoded_constructor_args" => if(smart_contract_verified, - do: format_constructor_arguments(target_contract.abi, target_contract.constructor_arguments) + do: + target_contract && format_constructor_arguments(target_contract.abi, target_contract.constructor_arguments) ), "language" => smart_contract_language(smart_contract), "license_type" => smart_contract.license_type, @@ -222,26 +242,28 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do |> Map.merge(bytecode_info(address)) end - def prepare_smart_contract(address, conn) do + def prepare_smart_contract(address, implementations, proxy_type, conn) do read_custom_abi? = AddressView.has_address_custom_abi_with_read_functions?(conn, address.hash) write_custom_abi? = AddressView.has_address_custom_abi_with_write_functions?(conn, address.hash) %{ "has_custom_methods_read" => read_custom_abi?, - "has_custom_methods_write" => write_custom_abi? + "has_custom_methods_write" => write_custom_abi?, + "proxy_type" => proxy_type, + "implementations" => implementations } |> Map.merge(bytecode_info(address)) end @doc """ - Returns additional sources of the smart-contract or from bytecode twin or from implementation, if it fits minimal proxy pattern (EIP-1167, Clone with immutable arguments) + Returns additional sources of the smart-contract from bytecode twin """ @spec get_additional_sources(SmartContract.t(), boolean, SmartContract.t() | nil) :: [SmartContractAdditionalSource.t()] | nil - def get_additional_sources(smart_contract, smart_contract_verified, implementation_or_bytecode_twin_contract) do + def get_additional_sources(smart_contract, smart_contract_verified, bytecode_twin_contract) do cond do - !is_nil(implementation_or_bytecode_twin_contract) -> - implementation_or_bytecode_twin_contract.smart_contract_additional_sources + !is_nil(bytecode_twin_contract) -> + bytecode_twin_contract.smart_contract_additional_sources smart_contract_verified -> smart_contract.smart_contract_additional_sources diff --git a/apps/block_scout_web/lib/block_scout_web/views/block_withdrawal_view.ex b/apps/block_scout_web/lib/block_scout_web/views/block_withdrawal_view.ex index 5fa812f198b8..f6b22b30f7a7 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/block_withdrawal_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/block_withdrawal_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.BlockWithdrawalView do use BlockScoutWeb, :view + + alias Explorer.Chain.Address end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex index 745b041ddd64..e916a69fe2b7 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex @@ -2,7 +2,7 @@ defmodule BlockScoutWeb.Tokens.HolderView do use BlockScoutWeb, :view alias BlockScoutWeb.Tokens.OverviewView - alias Explorer.Chain.Token + alias Explorer.Chain.{Address, Token} @doc """ Checks if the total supply percentage must be shown. diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex index 69d6eaad9f27..73814009ac8a 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex @@ -2,7 +2,7 @@ defmodule BlockScoutWeb.TransactionStateView do use BlockScoutWeb, :view alias Explorer.Chain - alias Explorer.Chain.Wei + alias Explorer.Chain.{Address, Wei} import Explorer.Chain.Transaction.StateChange, only: [from_loss: 1, has_diff?: 1, to_profit: 1] diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex index 66999ad804fc..3e31c0f2c351 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex @@ -2,4 +2,5 @@ defmodule BlockScoutWeb.TransactionTokenTransferView do use BlockScoutWeb, :view alias Explorer.Chain + alias Explorer.Chain.Address end diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex index d89ab6f738ce..64eafbccde88 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex @@ -449,7 +449,7 @@ defmodule BlockScoutWeb.TransactionView do end def involves_contract?(%Transaction{from_address: from_address, to_address: to_address}) do - AddressView.contract?(from_address) || AddressView.contract?(to_address) + Address.smart_contract?(from_address) || Address.smart_contract?(to_address) end def involves_token_transfers?(%Transaction{token_transfers: []}), do: false diff --git a/apps/block_scout_web/lib/block_scout_web/views/withdrawal_view.ex b/apps/block_scout_web/lib/block_scout_web/views/withdrawal_view.ex index bbe7be4fcf51..296304d622ab 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/withdrawal_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/withdrawal_view.ex @@ -1,3 +1,5 @@ defmodule BlockScoutWeb.WithdrawalView do use BlockScoutWeb, :view + + alias Explorer.Chain.Address end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs index 0bdb1d1ef89f..30f44f611bcd 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs @@ -151,6 +151,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do "name" => name, "address" => %{ "hash" => Address.checksum(addr), + "proxy_type" => nil, "implementations" => [], "is_contract" => false, "is_verified" => false, @@ -207,6 +208,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do "name" => name, "address" => %{ "hash" => Address.checksum(addr), + "proxy_type" => nil, "implementations" => [], "is_contract" => false, "is_verified" => false, diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs index a6be3d9058ac..f3d6d5f8185b 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs @@ -78,8 +78,6 @@ defmodule BlockScoutWeb.AddressReadContractControllerTest do block_index: 0 ) - TestHelper.get_eip1967_implementation_zero_addresses() - conn = get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs index a31dccf58c5c..310eb60851e7 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs @@ -77,8 +77,6 @@ defmodule BlockScoutWeb.AddressReadProxyControllerTest do block_index: 0 ) - TestHelper.get_eip1967_implementation_zero_addresses() - conn = get(conn, address_read_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) assert html_response(conn, 404) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs index 63a2a2eaeaf3..20a47ca8ac22 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs @@ -80,8 +80,6 @@ defmodule BlockScoutWeb.AddressWriteContractControllerTest do block_index: 0 ) - TestHelper.get_eip1967_implementation_zero_addresses() - conn = get(conn, address_write_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs index 398317ef22cf..5155ab618dea 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs @@ -78,8 +78,6 @@ defmodule BlockScoutWeb.AddressWriteProxyControllerTest do block_index: 0 ) - TestHelper.get_eip1967_implementation_zero_addresses() - conn = get(conn, address_write_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs index d8feb0c0ecc9..5f38ff2bbdb5 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs @@ -85,6 +85,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do "creation_tx_hash" => nil, "token" => nil, "coin_balance" => nil, + "proxy_type" => nil, "implementations" => [], "block_number_balance_updated_at" => nil, "has_decompiled_code" => false, @@ -220,6 +221,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do "watchlist_names" => [], "creator_address_hash" => ^from, "creation_tx_hash" => ^tx_hash, + "proxy_type" => "eip1167", "implementations" => [ %{"address" => ^checksummed_implementation_contract_address_hash, "name" => ^name} ] @@ -265,6 +267,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do "watchlist_names" => [], "creator_address_hash" => ^from, "creation_tx_hash" => ^tx_hash, + "proxy_type" => "eip1967", "implementations" => [%{"address" => ^implementation_address_hash_string, "name" => nil}] } = json_response(request, 200) end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs index c0e19f0b9c34..a7d01ba118c9 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs @@ -32,13 +32,15 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do test "get unverified smart-contract info", %{conn: conn} do address = insert(:contract_address) - TestHelper.get_eip1967_implementation_zero_addresses() + TestHelper.get_eip1967_implementation_error_response() request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") response = json_response(request, 200) assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -53,11 +55,15 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do ) |> with_block() + TestHelper.get_eip1967_implementation_error_response() + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") response = json_response(request, 200) assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -112,6 +118,10 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do ) |> with_block() + implementation_address = insert(:address) + implementation_address_hash_string = to_string(implementation_address.hash) + formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_address.hash)) + correct_response = %{ "verified_twin_address_hash" => nil, "is_verified" => true, @@ -151,6 +161,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "creation_bytecode" => "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029", "abi" => target_contract.abi, + "proxy_type" => "eip1967", + "implementations" => [%{"address" => formatted_implementation_address_hash_string, "name" => nil}], "is_verified_via_eth_bytecode_db" => target_contract.verified_via_eth_bytecode_db, "is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance, "language" => smart_contract_language(target_contract), @@ -159,8 +171,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "is_blueprint" => false } - implementation_address = insert(:address) - implementation_address_hash_string = to_string(implementation_address.hash) TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string) request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(target_contract.address_hash)}") @@ -257,6 +267,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "creation_bytecode" => "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029", "abi" => target_contract.abi, + "proxy_type" => nil, + "implementations" => [], "is_verified_via_eth_bytecode_db" => target_contract.verified_via_eth_bytecode_db, "is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance, "language" => smart_contract_language(target_contract), @@ -363,6 +375,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "creation_bytecode" => "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029", "abi" => target_contract.abi, + "proxy_type" => nil, + "implementations" => [], "is_verified_via_eth_bytecode_db" => target_contract.verified_via_eth_bytecode_db, "is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance, "language" => smart_contract_language(target_contract), @@ -379,7 +393,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert correct_response == response end - test "get smart-contract multiple additional sources from EIP-1167 implementation", %{conn: conn} do + test "doesn't get smart-contract multiple additional sources from EIP-1167 implementation", %{conn: conn} do implementation_contract = insert(:smart_contract, external_libraries: [], @@ -449,48 +463,18 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do |> with_block(status: :ok) correct_response = %{ - "verified_twin_address_hash" => Address.checksum(implementation_contract.address_hash), - "is_verified" => false, - "is_changed_bytecode" => false, - "is_partially_verified" => implementation_contract.partially_verified, - "is_fully_verified" => false, - "is_verified_via_sourcify" => false, - "is_vyper_contract" => implementation_contract.is_vyper_contract, - "has_methods_read" => true, - "has_methods_write" => true, - "has_methods_read_proxy" => true, - "has_methods_write_proxy" => true, "has_custom_methods_read" => false, "has_custom_methods_write" => false, - "minimal_proxy_address_hash" => Address.checksum(implementation_contract.address_hash), - "sourcify_repo_url" => nil, - "can_be_visualized_via_sol2uml" => false, - "name" => implementation_contract && implementation_contract.name, - "compiler_version" => implementation_contract.compiler_version, - "optimization_enabled" => implementation_contract.optimization, - "optimization_runs" => implementation_contract.optimization_runs, - "evm_version" => implementation_contract.evm_version, - "verified_at" => implementation_contract.inserted_at |> to_string() |> String.replace(" ", "T"), - "source_code" => implementation_contract.contract_source_code, - "file_path" => implementation_contract.file_path, - "additional_sources" => [ - %{"file_path" => "test1", "source_code" => "test2"}, - %{"file_path" => "test3", "source_code" => "test4"} - ], - "compiler_settings" => implementation_contract.compiler_settings, - "external_libraries" => [], - "constructor_args" => nil, - "decoded_constructor_args" => nil, "is_self_destructed" => false, "deployed_bytecode" => proxy_deployed_bytecode, "creation_bytecode" => proxy_tx_input, - "abi" => implementation_contract.abi, - "is_verified_via_eth_bytecode_db" => implementation_contract.verified_via_eth_bytecode_db, - "is_verified_via_verifier_alliance" => implementation_contract.verified_via_verifier_alliance, - "language" => smart_contract_language(implementation_contract), - "license_type" => "bsd_3_clause", - "certified" => false, - "is_blueprint" => false + "proxy_type" => "eip1167", + "implementations" => [ + %{ + "address" => Address.checksum(implementation_contract.address_hash), + "name" => implementation_contract.name + } + ] } request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(proxy_address.hash)}") @@ -551,6 +535,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "creation_bytecode" => "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029", "abi" => target_contract.abi, + "proxy_type" => nil, + "implementations" => [], "is_verified_via_eth_bytecode_db" => target_contract.verified_via_eth_bytecode_db, "is_verified_via_verifier_alliance" => target_contract.verified_via_verifier_alliance, "language" => smart_contract_language(target_contract), @@ -568,7 +554,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do end end - test "get smart-contract implementation for 'Clones with immutable arguments' pattern", %{conn: conn} do + test "doesn't get smart-contract implementation for 'Clones with immutable arguments' pattern", %{conn: conn} do implementation_contract = insert(:smart_contract, external_libraries: [], @@ -631,48 +617,18 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do ) |> with_block(status: :ok) + formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_contract.address_hash)) + correct_response = %{ - "verified_twin_address_hash" => Address.checksum(implementation_contract.address_hash), - "is_verified" => false, - "is_changed_bytecode" => false, - "is_partially_verified" => implementation_contract.partially_verified, - "is_fully_verified" => false, - "is_verified_via_sourcify" => false, - "is_vyper_contract" => implementation_contract.is_vyper_contract, - "has_methods_read" => true, - "has_methods_write" => true, - "has_methods_read_proxy" => true, - "has_methods_write_proxy" => true, + "proxy_type" => "clone_with_immutable_arguments", + "implementations" => [ + %{"address" => formatted_implementation_address_hash_string, "name" => implementation_contract.name} + ], "has_custom_methods_read" => false, "has_custom_methods_write" => false, - "minimal_proxy_address_hash" => Address.checksum(implementation_contract.address_hash), - "sourcify_repo_url" => nil, - "can_be_visualized_via_sol2uml" => false, - "name" => implementation_contract && implementation_contract.name, - "compiler_version" => implementation_contract.compiler_version, - "optimization_enabled" => implementation_contract.optimization, - "optimization_runs" => implementation_contract.optimization_runs, - "evm_version" => implementation_contract.evm_version, - "verified_at" => implementation_contract.inserted_at |> to_string() |> String.replace(" ", "T"), - "source_code" => implementation_contract.contract_source_code, - "file_path" => implementation_contract.file_path, - "additional_sources" => [ - %{"file_path" => "test1", "source_code" => "test2"} - ], - "compiler_settings" => implementation_contract.compiler_settings, - "external_libraries" => [], - "constructor_args" => nil, - "decoded_constructor_args" => nil, "is_self_destructed" => false, "deployed_bytecode" => proxy_deployed_bytecode, - "creation_bytecode" => proxy_tx_input, - "abi" => implementation_contract.abi, - "is_verified_via_eth_bytecode_db" => implementation_contract.verified_via_eth_bytecode_db, - "is_verified_via_verifier_alliance" => implementation_contract.verified_via_verifier_alliance, - "language" => smart_contract_language(implementation_contract), - "license_type" => "bsd_3_clause", - "certified" => false, - "is_blueprint" => false + "creation_bytecode" => proxy_tx_input } request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(proxy_address.hash)}") @@ -752,6 +708,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -837,6 +795,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -969,6 +929,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -1034,7 +996,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do Conn.resp(conn, 200, eth_bytecode_response) end) - TestHelper.get_eip1967_implementation_error_response() + TestHelper.get_eip1967_implementation_zero_addresses() request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") @@ -1056,6 +1018,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -1064,8 +1028,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" } - TestHelper.get_eip1967_implementation_zero_addresses() - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") assert response = json_response(request, 200) @@ -1148,6 +1110,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do implementation_address = insert(:address) implementation_address_hash_string = to_string(implementation_address.hash) + formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_address.hash)) TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string) request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") @@ -1170,6 +1133,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -1180,6 +1145,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") assert response = json_response(request, 200) + assert %{"proxy_type" => "eip1967"} = response + + assert %{"implementations" => [%{"address" => ^formatted_implementation_address_hash_string, "name" => nil}]} = + response + assert %{"is_verified" => true} = response assert %{"is_verified_via_eth_bytecode_db" => true} = response assert %{"is_partially_verified" => true} = response @@ -1265,6 +1235,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do implementation_address = insert(:address) implementation_address_hash_string = to_string(implementation_address.hash) + formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_address.hash)) TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string) request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") @@ -1287,6 +1258,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -1297,6 +1270,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") assert response = json_response(request, 200) + assert %{"proxy_type" => "eip1967"} = response + + assert %{"implementations" => [%{"address" => ^formatted_implementation_address_hash_string, "name" => nil}]} = + response + assert %{"is_verified" => true} = response assert %{"is_verified_via_eth_bytecode_db" => true} = response assert %{"is_partially_verified" => false} = response @@ -1382,6 +1360,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do implementation_address = insert(:address) implementation_address_hash_string = to_string(implementation_address.hash) + formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_address.hash)) TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string) request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") @@ -1404,6 +1383,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ + "proxy_type" => nil, + "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -1414,6 +1395,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") assert response = json_response(request, 200) + assert %{"proxy_type" => "eip1967"} = response + + assert %{"implementations" => [%{"address" => ^formatted_implementation_address_hash_string, "name" => nil}]} = + response + assert %{"is_verified" => true} = response assert %{"is_verified_via_eth_bytecode_db" => true} = response assert %{"is_partially_verified" => false} = response @@ -2260,8 +2246,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do test "return 404 on unverified contract", %{conn: conn} do address = insert(:contract_address) - TestHelper.get_eip1967_implementation_zero_addresses() - request = post(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}/query-read-method", %{ "contract_type" => "regular", @@ -2909,8 +2893,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do custom_abi ) - TestHelper.get_eip1967_implementation_zero_addresses() - expect( EthereumJSONRPC.Mox, :json_rpc, @@ -3050,8 +3032,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do test "return 404 on unverified contract", %{conn: conn} do address = insert(:contract_address) - TestHelper.get_eip1967_implementation_zero_addresses() - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}/methods-read-proxy") assert %{"message" => "Not found"} = json_response(request, 404) end @@ -3421,7 +3401,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do test "return 404 on unverified contract", %{conn: conn} do address = insert(:contract_address) - TestHelper.get_eip1967_implementation_zero_addresses() request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}/methods-write-proxy") assert %{"message" => "Not found"} = json_response(request, 404) end diff --git a/apps/block_scout_web/test/block_scout_web/views/address_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/address_view_test.exs index c8e2393b0bea..e33ea526d7ca 100644 --- a/apps/block_scout_web/test/block_scout_web/views/address_view_test.exs +++ b/apps/block_scout_web/test/block_scout_web/views/address_view_test.exs @@ -176,23 +176,6 @@ defmodule BlockScoutWeb.AddressViewTest do assert "" = AddressView.balance_percentage(address, nil) end - describe "contract?/1" do - test "with a smart contract" do - {:ok, code} = Data.cast("0x000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef") - address = insert(:address, contract_code: code) - assert AddressView.contract?(address) - end - - test "with an account" do - address = insert(:address, contract_code: nil) - refute AddressView.contract?(address) - end - - test "with nil address" do - assert AddressView.contract?(nil) - end - end - describe "hash/1" do test "gives a string version of an address's hash" do address = %Address{ diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 6ce7ec66df06..6af211157cce 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -85,8 +85,6 @@ defmodule Explorer.Chain do alias Explorer.Chain.Fetcher.{CheckBytecodeMatchingOnDemand, LookUpSmartContractSourcesOnDemand} alias Explorer.Chain.Import.Runner alias Explorer.Chain.InternalTransaction.{CallType, Type} - alias Explorer.Chain.SmartContract.Proxy - alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.Market.MarketHistoryCache alias Explorer.{PagingOptions, Repo} @@ -1136,7 +1134,8 @@ defmodule Explorer.Chain do options |> Keyword.get(:necessity_by_association, %{}) |> Map.merge(%{ - [smart_contract: :smart_contract_additional_sources] => :optional + [smart_contract: :smart_contract_additional_sources] => :optional, + :proxy_implementations => :optional }) query = @@ -1171,20 +1170,7 @@ defmodule Explorer.Chain do nil ) - {implementation_address_hashes, _} = - Implementation.get_implementation( - %{ - updated: %SmartContract{ - address_hash: hash - }, - implementation_updated_at: nil, - implementation_address_fetched?: false, - refetch_necessity_checked?: false - }, - Keyword.put(options, :proxy_without_abi?, true) - ) - - add_implementation_and_bytecode_twin_to_result(address_result, implementation_address_hashes, hash, options) + add_bytecode_twin_to_result(address_result, hash, options) end _ -> @@ -1206,31 +1192,12 @@ defmodule Explorer.Chain do end end - defp add_implementation_and_bytecode_twin_to_result(address_result, implementation_address_hashes, hash, options) do - # implementation is added only in the case when mapping proxy to implementation is 1:1 (excluding Diamond proxy) - {implementation_smart_contract, implementation_address_hash} = - if implementation_address_hashes && Enum.count(implementation_address_hashes) == 1 do - implementation_address_hash = implementation_address_hashes |> Enum.at(0) - - implementation_smart_contract = - implementation_address_hash - |> Proxy.implementation_to_smart_contract(options) - - {implementation_smart_contract, implementation_address_hash} - else - {nil, nil} - end - + defp add_bytecode_twin_to_result(address_result, hash, options) do address_verified_bytecode_twin_contract = - implementation_smart_contract || - SmartContract.get_address_verified_bytecode_twin_contract(hash, options).verified_contract + SmartContract.get_address_verified_bytecode_twin_contract(hash, options).verified_contract address_result |> SmartContract.add_bytecode_twin_info_to_contract(address_verified_bytecode_twin_contract, hash) - |> (&if(is_nil(implementation_smart_contract), - do: &1, - else: SmartContract.add_implementation_info_to_contract(&1, implementation_address_hash) - )).() end @spec find_decompiled_contract_address(Hash.Address.t()) :: {:ok, Address.t()} | {:error, :not_found} @@ -1849,10 +1816,6 @@ defmodule Explorer.Chain do Decimal.mult(tokens, fiat_value) end - def contract?(%{contract_code: nil}), do: false - - def contract?(%{contract_code: _}), do: true - @doc """ Returns a stream of unfetched `t:Explorer.Chain.Address.CoinBalance.t/0`. diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index a4749aab9c62..ad8f66980d0a 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -26,6 +26,7 @@ defmodule Explorer.Chain.Address do } alias Explorer.Chain.Cache.{Accounts, NetVersion} + alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.SmartContract.Proxy.Models.Implementation @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce decompiled verified gas_used transactions_count token_transfers_count)a @@ -93,6 +94,8 @@ defmodule Explorer.Chain.Address do field(:gas_used, :integer) field(:ens_domain_name, :string, virtual: true) field(:metadata, :any, virtual: true) + + # todo: remove virtual field for a single implementation when frontend is bound to "implementations" object value in API field(:implementation, :any, virtual: true) has_one(:smart_contract, SmartContract, references: :hash) @@ -460,4 +463,24 @@ defmodule Explorer.Chain.Address do timeout: @timeout ) end + + @doc """ + Prepares implementations object and proxy type from address + """ + @spec parse_implementation_and_proxy_type(__MODULE__.t()) :: {list(), String.t() | nil} + def parse_implementation_and_proxy_type(address) do + with %__MODULE__{ + proxy_implementations: %Implementation{ + address_hashes: address_hashes, + names: names, + proxy_type: proxy_type + } + } <- address, + false <- address_hashes && Enum.empty?(address_hashes) do + {Proxy.proxy_object_info(address_hashes, names), proxy_type} + else + _ -> + {[], nil} + end + end end diff --git a/apps/explorer/lib/explorer/chain/address/counters.ex b/apps/explorer/lib/explorer/chain/address/counters.ex index d58a55ed73bd..9ac501b43480 100644 --- a/apps/explorer/lib/explorer/chain/address/counters.ex +++ b/apps/explorer/lib/explorer/chain/address/counters.ex @@ -228,7 +228,7 @@ defmodule Explorer.Chain.Address.Counters do @spec address_to_gas_usage_count(Address.t()) :: Decimal.t() | nil def address_to_gas_usage_count(address) do - if Chain.contract?(address) do + if Address.smart_contract?(address) do incoming_transaction_gas_usage = address_to_incoming_transaction_gas_usage(address.hash) cond do diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index 121b3c686377..302ed4dda86e 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -594,7 +594,7 @@ defmodule Explorer.Chain.SmartContract do refetch_necessity_checked?: false } - {implementation_address_hash, _} = + {implementation_address_hash, _names, _proxy_type} = Implementation.get_implementation( smart_contract, Keyword.put(options, :proxy_without_abi?, true) @@ -618,7 +618,7 @@ defmodule Explorer.Chain.SmartContract do def compose_address_for_unverified_smart_contract(address_result, _hash, _options), do: address_result def single_implementation_smart_contract_from_proxy(proxy_hash, options) do - {implementation_address_hashes, _} = Implementation.get_implementation(proxy_hash, options) + {implementation_address_hashes, _names, _proxy_type} = Implementation.get_implementation(proxy_hash, options) if implementation_address_hashes && Enum.count(implementation_address_hashes) == 1 do implementation_address_hashes @@ -740,21 +740,6 @@ defmodule Explorer.Chain.SmartContract do |> Map.put(:smart_contract, address_verified_bytecode_twin_contract_updated) end - def add_implementation_info_to_contract(address_result, nil), do: address_result - - def add_implementation_info_to_contract( - %Address{smart_contract: smart_contract} = address_result, - implementation_address_hash - ) - when not is_nil(smart_contract) do - implementation = Map.put(smart_contract, :address_hash, implementation_address_hash) - - address_result - |> Map.put(:implementation, implementation) - end - - def add_implementation_info_to_contract(address_result, _), do: address_result - @doc """ Inserts a new smart contract and associated data into the database. @@ -958,7 +943,7 @@ defmodule Explorer.Chain.SmartContract do with true <- is_nil(current_smart_contract), {:ok, address} <- Chain.hash_to_address(address_hash), - true <- Chain.contract?(address) do + true <- Address.smart_contract?(address) do {implementation_smart_contract, implementation_address_fetched?} = if fetch_implementation? do implementation_smart_contract = diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex index 35c874c6ee51..f4ea719eb789 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex @@ -4,8 +4,9 @@ defmodule Explorer.Chain.SmartContract.Proxy do """ alias EthereumJSONRPC.Contract - alias Explorer.Chain.{Hash, SmartContract} + alias Explorer.Chain.{Address, Hash, SmartContract} alias Explorer.Chain.SmartContract.Proxy + alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.Chain.SmartContract.Proxy.{ Basic, @@ -51,7 +52,7 @@ defmodule Explorer.Chain.SmartContract.Proxy do Fetches into DB proxy contract implementation's address and name from different proxy patterns """ @spec fetch_implementation_address_hash(Hash.Address.t(), list(), options) :: - {[String.t()] | :empty | :error, [String.t()] | :empty | :error} + Implementation.t() | :empty | :error def fetch_implementation_address_hash(proxy_address_hash, proxy_abi, options) when not is_nil(proxy_address_hash) do %{implementation_address_hash_strings: implementation_address_hash_strings, proxy_type: proxy_type} = @@ -70,7 +71,7 @@ defmodule Explorer.Chain.SmartContract.Proxy do end def fetch_implementation_address_hash(_, _, _) do - {:empty, :empty} + :empty end @doc """ @@ -79,32 +80,37 @@ defmodule Explorer.Chain.SmartContract.Proxy do @spec proxy_contract?(SmartContract.t(), Keyword.t()) :: boolean() def proxy_contract?(smart_contract, options \\ []) do {:ok, burn_address_hash} = string_to_address_hash(SmartContract.burn_address_hash_string()) - implementation = get_proxy_implementations(smart_contract.address_hash) + proxy_implementations = get_proxy_implementations(smart_contract.address_hash) - with false <- is_nil(implementation), - false <- Enum.empty?(implementation.address_hashes), - implementation_address_hash = Enum.at(implementation.address_hashes, 0), + with false <- is_nil(proxy_implementations), + false <- Enum.empty?(proxy_implementations.address_hashes), + implementation_address_hash = Enum.at(proxy_implementations.address_hashes, 0), false <- implementation_address_hash.bytes == burn_address_hash.bytes do true else _ -> - {implementation_address_hash_strings, _implementation_names} = get_implementation(smart_contract, options) - - with false <- is_nil(implementation_address_hash_strings), - false <- Enum.empty?(implementation_address_hash_strings) do - implementation_address_hash_strings - |> Enum.reduce_while(false, fn implementation_address_hash_string, acc -> - with {:ok, implementation_address_hash} <- string_to_address_hash(implementation_address_hash_string), - false <- implementation_address_hash.bytes == burn_address_hash.bytes do - {:halt, true} - else - _ -> - {:cont, acc} - end - end) + if options[:skip_implementation_fetch?] do + false else - _ -> - false + {implementation_address_hash_strings, _implementation_names, _proxy_type} = + get_implementation(smart_contract, options) + + with false <- is_nil(implementation_address_hash_strings), + false <- Enum.empty?(implementation_address_hash_strings) do + implementation_address_hash_strings + |> Enum.reduce_while(false, fn implementation_address_hash_string, acc -> + with {:ok, implementation_address_hash} <- string_to_address_hash(implementation_address_hash_string), + false <- implementation_address_hash.bytes == burn_address_hash.bytes do + {:halt, true} + else + _ -> + {:cont, acc} + end + end) + else + _ -> + false + end end end end @@ -138,7 +144,7 @@ defmodule Explorer.Chain.SmartContract.Proxy do options ) when not is_nil(proxy_address_hash) and not is_nil(abi) do - {implementation_address_hash_strings, _names} = get_implementation(smart_contract, options) + {implementation_address_hash_strings, _names, _proxy_type} = get_implementation(smart_contract, options) implementation_address_hash_strings |> Enum.reduce([], fn implementation_address_hash_string, acc -> @@ -452,4 +458,39 @@ defmodule Explorer.Chain.SmartContract.Proxy do |> join_associations(necessity_by_association) |> select_repo(options).one(timeout: 10_000) end + + @doc """ + Retrieves formatted proxy object based on its implementation addresses and names. + + ## Parameters + + * `implementation_addresses` - A list of implementation addresses for the proxy object. + * `implementation_names` - A list of implementation names for the proxy object. + + ## Returns + + A list of maps containing information about the proxy object. + + """ + @spec proxy_object_info([String.t() | Hash.Address.t()], [String.t() | nil]) :: [map()] + def proxy_object_info([], []), do: [] + + def proxy_object_info(implementation_addresses, implementation_names) do + implementation_addresses + |> Enum.zip(implementation_names) + |> Enum.reduce([], fn {address, name}, acc -> + case address do + %Hash{} = address_hash -> + [%{"address" => Address.checksum(address_hash), "name" => name} | acc] + + _ -> + with {:ok, address_hash} <- string_to_address_hash(address), + checksummed_address <- Address.checksum(address_hash) do + [%{"address" => checksummed_address, "name" => name} | acc] + else + _ -> acc + end + end + end) + end end diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex index 91496e870b91..f6e4d9c50199 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex @@ -7,10 +7,17 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do use Explorer.Schema + import Ecto.Query, + only: [ + from: 2, + select: 3 + ] + import Explorer.Chain, only: [select_repo: 1, string_to_address_hash: 1] alias Explorer.Chain.{Address, Hash, SmartContract} alias Explorer.Chain.SmartContract.Proxy + alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.Counters.AverageBlockTime alias Explorer.Repo alias Timex.Duration @@ -78,14 +85,9 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do Returns all implementations for the given smart-contract address hash """ @spec get_proxy_implementations(Hash.Address.t() | nil, Keyword.t()) :: __MODULE__.t() | nil - def get_proxy_implementations(address_hash, options \\ []) do - all_implementations_query = - from( - p in __MODULE__, - where: p.proxy_address_hash == ^address_hash - ) - - all_implementations_query + def get_proxy_implementations(proxy_address_hash, options \\ []) do + proxy_address_hash + |> get_proxy_implementations_query() |> select_repo(options).one() end @@ -93,22 +95,24 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do Returns the last implementation updated_at for the given smart-contract address hash """ @spec get_proxy_implementation_updated_at(Hash.Address.t() | nil, Keyword.t()) :: DateTime.t() - def get_proxy_implementation_updated_at(address_hash, options) do - updated_at_query = - from( - p in __MODULE__, - where: p.proxy_address_hash == ^address_hash, - select: p.updated_at - ) - - updated_at_query + def get_proxy_implementation_updated_at(proxy_address_hash, options) do + proxy_address_hash + |> get_proxy_implementations_query() + |> select([p], p.updated_at) |> select_repo(options).one() end + defp get_proxy_implementations_query(proxy_address_hash) do + from( + p in __MODULE__, + where: p.proxy_address_hash == ^proxy_address_hash + ) + end + @doc """ - Returns implementation address and name of the given SmartContract by hash address + Returns implementation address, name and proxy type for the given SmartContract """ - @spec get_implementation(any(), any()) :: {any(), any()} + @spec get_implementation(any(), any()) :: {any(), any(), atom() | nil} def get_implementation(smart_contract, options \\ []) def get_implementation( @@ -152,6 +156,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do ) end + # credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity def get_implementation( %{ updated: %SmartContract{ @@ -164,14 +169,15 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do }, options ) do - {implementation_addresses_hash_from_db, implementation_names_from_db, implementation_updated_at_from_db} = - implementation_from_db(address_hash, options) + proxy_implementations = get_proxy_implementations(address_hash, options) - implementation_updated_at = implementation_updated_at || implementation_updated_at_from_db + implementation_updated_at = implementation_updated_at || (proxy_implementations && proxy_implementations.updated_at) if fetch_implementation?(implementation_address_fetched?, refetch_necessity_checked?, implementation_updated_at) do get_implementation_address_hash_task = Task.async(fn -> + # Here and only here we fetch implementations for the given address + # using requests to the JSON RPC node for known proxy patterns result = Proxy.fetch_implementation_address_hash(address_hash, abi, options) callback = Keyword.get(options, :callback, nil) @@ -187,41 +193,34 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do case Task.yield(get_implementation_address_hash_task, timeout) || Task.ignore(get_implementation_address_hash_task) do - {:ok, {:empty, :empty}} -> - {[], []} + {:ok, :empty} -> + {[], [], nil} - {:ok, {:error, :error}} -> - {db_implementation_data_converter(implementation_addresses_hash_from_db), - db_implementation_data_converter(implementation_names_from_db)} + {:ok, :error} -> + format_proxy_implementations_response(proxy_implementations) - {:ok, {address_hash, _name} = result} when not is_nil(address_hash) -> - result + {:ok, %Implementation{} = result} -> + format_proxy_implementations_response(result) _ -> - {db_implementation_data_converter(implementation_addresses_hash_from_db), - db_implementation_data_converter(implementation_names_from_db)} + format_proxy_implementations_response(proxy_implementations) end else - {db_implementation_data_converter(implementation_addresses_hash_from_db), - db_implementation_data_converter(implementation_names_from_db)} + format_proxy_implementations_response(proxy_implementations) end end - def get_implementation(_, _), do: {[], []} + def get_implementation(_, _), do: {[], [], nil} defp fetch_implementation?(implementation_address_fetched?, refetch_necessity_checked?, implementation_updated_at) do (!implementation_address_fetched? || !refetch_necessity_checked?) && check_implementation_refetch_necessity(implementation_updated_at) end - defp implementation_from_db(address_hash, options) do - proxy_implementations = get_proxy_implementations(address_hash, options) - - if proxy_implementations do - {proxy_implementations.address_hashes, proxy_implementations.names, proxy_implementations.updated_at} - else - {[], [], nil} - end + defp format_proxy_implementations_response(proxy_implementations) do + {(proxy_implementations && db_implementation_data_converter(proxy_implementations.address_hashes)) || [], + (proxy_implementations && db_implementation_data_converter(proxy_implementations.names)) || [], + proxy_implementations && proxy_implementations.proxy_type} end @doc """ @@ -279,9 +278,9 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do Saves proxy's implementation into the DB """ @spec save_implementation_data([String.t()], Hash.Address.t(), atom() | nil, Keyword.t()) :: - {[String.t()], [String.t()]} | {:empty, :empty} | {:error, :error} + Implementation.t() | :empty | :error def save_implementation_data(:error, _proxy_address_hash, _proxy_type, _options) do - {:error, :error} + :error end def save_implementation_data(implementation_address_hash_strings, proxy_address_hash, proxy_type, options) @@ -289,7 +288,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do implementation_address_hash_strings == [] do upsert_implementation(proxy_address_hash, proxy_type, [], [], options) - {:empty, :empty} + :empty end def save_implementation_data( @@ -301,7 +300,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do when is_burn_signature(empty_implementation_address_hash_string) do upsert_implementation(proxy_address_hash, proxy_type, [], [], options) - {:empty, :empty} + :empty end def save_implementation_data( @@ -331,17 +330,22 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do |> Enum.unzip() if Enum.empty?(implementation_addresses) do - {:empty, :empty} + :empty else - upsert_implementation( - proxy_address_hash, - proxy_type, - implementation_addresses, - implementation_names, - options - ) + case upsert_implementation( + proxy_address_hash, + proxy_type, + implementation_addresses, + implementation_names, + options + ) do + {:ok, result} -> + result - {implementation_addresses, implementation_names} + {:error, error} -> + Logger.error("Error while upserting proxy implementations data into the DB: #{inspect(error)}") + :error + end end end diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index 05916ff89445..e6e48e2fa8fb 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -1873,7 +1873,7 @@ defmodule Explorer.Chain.Transaction do @spec where_transactions_to_from(Hash.Address.t()) :: any() def where_transactions_to_from(address_hash) do with {:ok, address} <- Chain.hash_to_address(address_hash), - true <- Chain.contract?(address) do + true <- Address.smart_contract?(address) do dynamic([transaction], transaction.to_address_hash == ^address_hash) else _ -> diff --git a/apps/explorer/lib/explorer/smart_contract/helper.ex b/apps/explorer/lib/explorer/smart_contract/helper.ex index 38c2026c6159..90530ee756fa 100644 --- a/apps/explorer/lib/explorer/smart_contract/helper.ex +++ b/apps/explorer/lib/explorer/smart_contract/helper.ex @@ -4,10 +4,14 @@ defmodule Explorer.SmartContract.Helper do """ alias Explorer.{Chain, Helper} - alias Explorer.Chain.{Hash, SmartContract} + alias Explorer.Chain.{Address, Hash, SmartContract} + alias Explorer.Chain.SmartContract.Proxy + alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.SmartContract.Writer alias Phoenix.HTML + @api_true [api?: true] + def queriable_method?(method) do method["constant"] || method["stateMutability"] == "view" || method["stateMutability"] == "pure" end @@ -210,4 +214,46 @@ defmodule Explorer.SmartContract.Helper do def prepare_license_type(binary) when is_binary(binary), do: Helper.parse_integer(binary) || binary def prepare_license_type(_), do: nil + + @doc """ + Pre-fetches implementation for unverified smart contract or verified proxy smart-contract + """ + @spec pre_fetch_implementations(Address.t()) :: {any(), atom() | nil} + def pre_fetch_implementations(address) do + {implementation_address_hashes, implementation_names, proxy_type} = + with {:verified_smart_contract, %SmartContract{}} <- {:verified_smart_contract, address.smart_contract}, + {:proxy?, true} <- {:proxy?, address_is_proxy?(address, @api_true)} do + Implementation.get_implementation(address.smart_contract, @api_true) + else + {:verified_smart_contract, _} -> + if Address.smart_contract?(address) do + smart_contract = %SmartContract{ + address_hash: address.hash + } + + Implementation.get_implementation(smart_contract, @api_true) + else + {[], [], nil} + end + + {:proxy?, false} -> + {[], [], nil} + end + + implementations = Proxy.proxy_object_info(implementation_address_hashes, implementation_names) + + {implementations, proxy_type} + end + + @doc """ + Checks if given address is proxy smart contract + """ + @spec address_is_proxy?(Address.t(), list()) :: boolean() + def address_is_proxy?(address, options \\ []) + + def address_is_proxy?(%Address{smart_contract: %SmartContract{} = smart_contract}, options) do + Proxy.proxy_contract?(smart_contract, options) + end + + def address_is_proxy?(%Address{smart_contract: _}, _), do: false end diff --git a/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs b/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs index 477bb788e42d..7893139d5b04 100644 --- a/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs +++ b/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs @@ -28,7 +28,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do # fetch nil implementation and don't save it to db TestHelper.get_eip1967_implementation_zero_addresses() - assert {[], []} = Implementation.get_implementation(smart_contract) + assert {[], [], nil} = Implementation.get_implementation(smart_contract) verify!(EthereumJSONRPC.Mox) assert_empty_implementation(smart_contract.address_hash) @@ -44,7 +44,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do expect_address_in_oz_slot_response(string_implementation_address_hash) - assert {[^string_implementation_address_hash], ["implementation"]} = + assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = Implementation.get_implementation(smart_contract) verify!(EthereumJSONRPC.Mox) @@ -57,7 +57,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do TestHelper.get_eip1967_implementation_error_response() - assert {[^string_implementation_address_hash], ["implementation"]} = + assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = Implementation.get_implementation(smart_contract) verify!(EthereumJSONRPC.Mox) @@ -78,7 +78,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do Application.put_env(:explorer, :proxy, proxy) - assert {[^string_implementation_address_hash], ["implementation"]} = + assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = Implementation.get_implementation(smart_contract) {contract_2, _} = SmartContract.address_hash_to_smart_contract_with_bytecode_twin(smart_contract.address_hash) @@ -96,7 +96,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do TestHelper.get_eip1967_implementation_zero_addresses() - assert {[], []} = Implementation.get_implementation(smart_contract) + assert {[], [], nil} = Implementation.get_implementation(smart_contract) verify!(EthereumJSONRPC.Mox) @@ -106,7 +106,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do test "get_implementation/1 for twins contract" do # return nils for nil - assert {[], []} = Implementation.get_implementation(nil) + assert {[], [], nil} = Implementation.get_implementation(nil) smart_contract = insert(:smart_contract) twin_address = insert(:contract_address) @@ -123,11 +123,11 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do Application.put_env(:explorer, :proxy, proxy) # fetch nil implementation - assert {[], []} = Implementation.get_implementation(bytecode_twin) + assert {[], [], :unknown} = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) - assert {[], []} = Implementation.get_implementation(bytecode_twin) + assert {[], [], :unknown} = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) @@ -143,7 +143,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do expect_address_in_oz_slot_response(string_implementation_address_hash) - assert {[^string_implementation_address_hash], ["implementation"]} = + assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) @@ -158,7 +158,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do refute_implementations(smart_contract.address_hash) - assert {[^string_implementation_address_hash], ["implementation"]} = + assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) @@ -171,11 +171,11 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do _implementation_smart_contract = insert(:smart_contract, name: "implementation") # fetch nil implementation - assert {[], []} = Implementation.get_implementation(bytecode_twin) + assert {[], [], nil} = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) - assert {[], []} = Implementation.get_implementation(bytecode_twin) + assert {[], [], nil} = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) @@ -192,7 +192,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do # expect_address_in_oz_slot_response(string_implementation_address_hash) - # assert {^string_implementation_address_hash, "implementation"} = Implementation.get_implementation(bytecode_twin) + # assert {^string_implementation_address_hash, "implementation", :eip1967} = Implementation.get_implementation(bytecode_twin) # verify!(EthereumJSONRPC.Mox) @@ -200,7 +200,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do # TestHelper.get_eip1967_implementation_error_response() - # assert {^string_implementation_address_hash, "implementation"} = Implementation.get_implementation(bytecode_twin) + # assert {^string_implementation_address_hash, "implementation", :eip1967} = Implementation.get_implementation(bytecode_twin) # verify!(EthereumJSONRPC.Mox) @@ -233,12 +233,12 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do # # fetch nil implementation # TestHelper.get_eip1967_implementation_zero_addresses() - # assert {nil, nil} = Implementation.get_implementation(bytecode_twin) + # assert {[], [], nil} = Implementation.get_implementation(bytecode_twin) # verify!(EthereumJSONRPC.Mox) # refute_implementations(smart_contract.address_hash) # TestHelper.get_eip1967_implementation_zero_addresses() - # assert {nil, nil} = Implementation.get_implementation(bytecode_twin) + # assert {[], [], nil} = Implementation.get_implementation(bytecode_twin) # verify!(EthereumJSONRPC.Mox) # refute_implementations(smart_contract.address_hash) @@ -246,7 +246,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do # expect_address_in_oz_slot_response(string_implementation_address_hash) - # assert {^string_implementation_address_hash, "implementation"} = Implementation.get_implementation(bytecode_twin) + # assert {^string_implementation_address_hash, "implementation", :eip1967} = Implementation.get_implementation(bytecode_twin) # verify!(EthereumJSONRPC.Mox) @@ -254,7 +254,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do # TestHelper.get_eip1967_implementation_zero_addresses() - # assert {nil, nil} = Implementation.get_implementation(bytecode_twin) + # assert {[], [], nil} = Implementation.get_implementation(bytecode_twin) # verify!(EthereumJSONRPC.Mox) diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 3f242e726724..a57dac010c36 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -2599,7 +2599,8 @@ defmodule Explorer.ChainTest do :contracts_creation_internal_transaction, :contracts_creation_transaction, :token, - [smart_contract: :smart_contract_additional_sources] + [smart_contract: :smart_contract_additional_sources], + :proxy_implementations ]) options = [ @@ -2612,8 +2613,6 @@ defmodule Explorer.ChainTest do } ] - TestHelper.get_eip1967_implementation_zero_addresses() - response = Chain.find_contract_address(address.hash, options, true) assert response == {:ok, address} From ec2e25bdd1eebfe034c3f1f91277fcfd07dc1b55 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Thu, 15 Aug 2024 20:13:55 +0300 Subject: [PATCH 077/363] feat: Integrate Cryptorank API (#10550) * feat: Integrate Cryptorank API * Fix config * Fix tests * Process review comments * Add guard * def -> defp * Process review comments * Fix --- apps/explorer/lib/explorer/chain/token.ex | 9 + .../lib/explorer/exchange_rates/source.ex | 45 ++++- .../exchange_rates/source/coin_gecko.ex | 16 +- .../exchange_rates/source/cryptorank.ex | 179 ++++++++++++++++++ .../exchange_rates/token_exchange_rates.ex | 78 +++++++- .../token_exchange_rates_test.exs | 3 +- config/config_helper.exs | 11 ++ config/runtime.exs | 16 +- cspell.json | 2 + docker-compose/envs/common-blockscout.env | 7 + 10 files changed, 347 insertions(+), 19 deletions(-) create mode 100644 apps/explorer/lib/explorer/exchange_rates/source/cryptorank.ex diff --git a/apps/explorer/lib/explorer/chain/token.ex b/apps/explorer/lib/explorer/chain/token.ex index 7e8286dd4fe3..b756d2f301bb 100644 --- a/apps/explorer/lib/explorer/chain/token.ex +++ b/apps/explorer/lib/explorer/chain/token.ex @@ -290,4 +290,13 @@ defmodule Explorer.Chain.Token do |> Changeset.change(%{is_verified_via_admin_panel: false, icon_url: nil, symbol: nil, name: nil}) |> Repo.update() end + + @doc """ + Returns query for token by contract address hash + """ + @spec token_by_contract_address_hash_query(binary() | Hash.Address.t()) :: Ecto.Query.t() + def token_by_contract_address_hash_query(contract_address_hash) do + __MODULE__ + |> where([token], token.contract_address_hash == ^contract_address_hash) + end end diff --git a/apps/explorer/lib/explorer/exchange_rates/source.ex b/apps/explorer/lib/explorer/exchange_rates/source.ex index 52b846b3a75b..e21de5218391 100644 --- a/apps/explorer/lib/explorer/exchange_rates/source.ex +++ b/apps/explorer/lib/explorer/exchange_rates/source.ex @@ -4,7 +4,7 @@ defmodule Explorer.ExchangeRates.Source do """ alias Explorer.Chain.Hash - alias Explorer.ExchangeRates.Source.CoinGecko + alias Explorer.ExchangeRates.Source.{CoinGecko, Cryptorank} alias Explorer.ExchangeRates.Token alias Explorer.Helper alias HTTPoison.{Error, Response} @@ -56,6 +56,26 @@ defmodule Explorer.ExchangeRates.Source do end end + @doc """ + Fetches exchange rates for tokens from cryptorank. + """ + @spec cryptorank_fetch_currencies(integer(), integer()) :: + {:error, any()} + | {:ok, map()} + def cryptorank_fetch_currencies(limit, offset) do + source_url = Cryptorank.source_url(:currencies, limit, offset) + + case http_request(source_url, []) do + {:ok, result} -> + {:ok, + result + |> Cryptorank.format_data()} + + resp -> + resp + end + end + defp fetch_exchange_rates_request(_source, source_url, _headers) when is_nil(source_url), do: {:error, "Source URL is nil"} @@ -147,4 +167,27 @@ defmodule Explorer.ExchangeRates.Source do {:error, "#{status_code}: #{body}"} end end + + @doc """ + Returns `nil` if the date is `nil`, otherwise returns the parsed date. + Date should be in ISO8601 format + """ + @spec maybe_get_date(String.t() | nil) :: DateTime.t() | nil + def maybe_get_date(nil), do: nil + + def maybe_get_date(date) do + {:ok, parsed_date, 0} = DateTime.from_iso8601(date) + parsed_date + end + + @doc """ + Returns `nil` if the url is invalid, otherwise returns the parsed url. + """ + @spec handle_image_url(String.t() | nil) :: String.t() | nil + def handle_image_url(url) do + case Helper.validate_url(url) do + {:ok, url} -> url + _ -> nil + end + end end diff --git a/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex b/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex index 4dd140d5ac1a..c4a2cf962a24 100644 --- a/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex +++ b/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex @@ -3,10 +3,10 @@ defmodule Explorer.ExchangeRates.Source.CoinGecko do Adapter for fetching exchange rates from https://coingecko.com """ - alias Explorer.{Chain, Helper} + alias Explorer.Chain alias Explorer.ExchangeRates.{Source, Token} - import Source, only: [to_decimal: 1] + import Source, only: [to_decimal: 1, maybe_get_date: 1, handle_image_url: 1] @behaviour Source @@ -244,12 +244,7 @@ defmodule Explorer.ExchangeRates.Source.CoinGecko do defp get_last_updated(market_data) do last_updated_data = market_data && market_data["last_updated"] - if last_updated_data do - {:ok, last_updated, 0} = DateTime.from_iso8601(last_updated_data) - last_updated - else - nil - end + maybe_get_date(last_updated_data) end defp get_current_price(market_data) do @@ -268,10 +263,7 @@ defmodule Explorer.ExchangeRates.Source.CoinGecko do nil end - case Helper.validate_url(image_url_raw) do - {:ok, url} -> url - _ -> nil - end + handle_image_url(image_url_raw) end defp get_btc_value(id, market_data) do diff --git a/apps/explorer/lib/explorer/exchange_rates/source/cryptorank.ex b/apps/explorer/lib/explorer/exchange_rates/source/cryptorank.ex new file mode 100644 index 000000000000..8cd89fbc40c0 --- /dev/null +++ b/apps/explorer/lib/explorer/exchange_rates/source/cryptorank.ex @@ -0,0 +1,179 @@ +defmodule Explorer.ExchangeRates.Source.Cryptorank do + @moduledoc """ + Adapter for fetching exchange rates from https://cryptorank.io + """ + + alias Explorer.ExchangeRates.{Source, Token} + alias Explorer.ExchangeRates.Source.CoinGecko + alias Explorer.Market.History.Source.Price, as: SourcePrice + + import Source, only: [to_decimal: 1, maybe_get_date: 1, handle_image_url: 1] + + @spec format_data(term()) :: [Token.t()] | map() + def format_data(%{"data" => %{} = coin}) do + last_updated = maybe_get_date(coin["lastUpdated"]) + + btc_value = + if Application.get_env(:explorer, Explorer.ExchangeRates)[:fetch_btc_value], + do: coin["values"]["BTC"] && coin["values"]["BTC"]["price"] + + image_url = coin["images"] && (coin["images"]["200x200"] || coin["images"]["60x60"]) + usd = coin["values"]["USD"] + + [ + %Token{ + available_supply: to_decimal(coin["circulatingSupply"]), + total_supply: to_decimal(coin["totalSupply"]) || to_decimal(coin["circulatingSupply"]), + btc_value: btc_value, + id: coin["id"], + last_updated: last_updated, + market_cap_usd: to_decimal(usd["marketCap"]), + tvl_usd: nil, + name: coin["name"], + symbol: String.upcase(coin["symbol"]), + usd_value: to_decimal(usd["price"]), + volume_24h_usd: to_decimal(usd["volume24h"]), + image_url: handle_image_url(image_url) + } + ] + end + + def format_data(%{"data" => currencies, "meta" => %{"count" => count}}) when is_list(currencies) do + platform_id = platform_id() + currencies |> Enum.reduce(%{}, &reduce_currency(platform_id, &1, &2)) |> Map.put(:count, count) + end + + @spec format_data(term(), boolean()) :: [SourcePrice.record()] | nil + defp format_data(nil, _), do: nil + + defp format_data(%{"data" => %{"dates" => dates, "prices" => prices}}, secondary_coin?) do + dates + |> Enum.zip(prices) + |> Enum.reverse() + |> Enum.map_reduce(nil, fn {date, price}, next_day_opening -> + if next_day_opening do + {%{ + closing_price: Decimal.new(to_string(next_day_opening)), + date: CoinGecko.date(date), + opening_price: Decimal.new(to_string(price)), + secondary_coin: secondary_coin? + }, price} + else + {%{ + closing_price: Decimal.new(to_string(price)), + date: CoinGecko.date(date), + opening_price: Decimal.new(to_string(price)), + secondary_coin: secondary_coin? + }, price} + end + end) + |> elem(0) + |> Enum.reject(&is_nil/1) + |> Enum.reverse() + end + + def source_url(:market) do + base_url() |> URI.append_path("/global") |> URI.to_string() + end + + def source_url do + if coin_id() do + base_url() |> URI.append_path("/currencies/#{coin_id()}") |> URI.to_string() + end + end + + def source_url(:currencies, limit, offset) do + base_url() + |> URI.append_path("/dedicated/blockscout/currencies/contracts/#{platform_id()}") + |> URI.append_query("limit=#{limit}") + |> URI.append_query("skip=#{offset}") + |> URI.to_string() + end + + @spec history_url(non_neg_integer(), boolean()) :: String.t() + def history_url(previous_days, secondary_coin? \\ false) do + from = Date.utc_today() |> Date.add(-previous_days) |> Date.to_iso8601() + to = Date.utc_today() |> Date.to_iso8601() + coin_id = if secondary_coin?, do: secondary_coin_id(), else: coin_id() + + base_url() + |> URI.append_path("/currencies/#{coin_id}/sparkline") + |> URI.append_query("interval=1d") + |> URI.append_query("from=#{from}") + |> URI.append_query("to=#{to}") + |> URI.to_string() + end + + def headers do + [] + end + + defp base_url do + config()[:base_url] |> URI.parse() |> URI.append_query("api_key=#{api_key()}") + end + + defp api_key do + config()[:api_key] + end + + defp platform_id do + config()[:platform] + end + + defp coin_id do + config()[:coin_id] + end + + defp secondary_coin_id do + config()[:secondary_coin_id] + end + + defp config do + Application.get_env(:explorer, __MODULE__) + end + + @spec fetch_price_history(non_neg_integer(), boolean()) :: {:ok, [SourcePrice.record()]} | :error + def fetch_price_history(previous_days, secondary_coin? \\ false) do + url = history_url(previous_days, secondary_coin?) + + case Source.http_request(url, headers()) do + {:ok, data} -> + result = + data |> format_data(secondary_coin?) + + {:ok, result} + + _ -> + :error + end + end + + defp reduce_currency(platform_id, %{"contracts" => [_ | _] = tokens} = currency, acc) do + Enum.reduce(tokens, acc, fn + %{ + "address" => token_address_hash_string, + "chainId" => ^platform_id + }, + acc -> + market_cap = + currency["priceUSD"] && currency["circulatingSupply"] && + Decimal.mult( + Decimal.new(currency["priceUSD"]), + Decimal.new(currency["circulatingSupply"]) + ) + + Map.put(acc, token_address_hash_string, %{ + fiat_value: currency["priceUSD"], + circulating_market_cap: market_cap, + volume_24h: currency["volume24hUSD"] + }) + + _, acc -> + acc + end) + end + + defp reduce_currency(_, _, acc) do + acc + end +end diff --git a/apps/explorer/lib/explorer/exchange_rates/token_exchange_rates.ex b/apps/explorer/lib/explorer/exchange_rates/token_exchange_rates.ex index 2d3b9cc86db2..08de000af8e5 100644 --- a/apps/explorer/lib/explorer/exchange_rates/token_exchange_rates.ex +++ b/apps/explorer/lib/explorer/exchange_rates/token_exchange_rates.ex @@ -15,7 +15,12 @@ defmodule Explorer.ExchangeRates.TokenExchangeRates do defstruct max_batch_size: @batch_size, interval: @interval, refetch_interval: @refetch_interval, - tokens_to_fetch: nil + tokens_to_fetch: nil, + source: Source.CoinGecko, + cryptorank_limit: 1000, + cryptorank_offset: 0, + cryptorank_total_count: nil, + remaining_tokens: nil @spec start_link(term()) :: GenServer.on_start() def start_link(_) do @@ -28,10 +33,15 @@ defmodule Explorer.ExchangeRates.TokenExchangeRates do state = %__MODULE__{ max_batch_size: Application.get_env(:explorer, __MODULE__)[:max_batch_size] || @batch_size, interval: Application.get_env(:explorer, __MODULE__)[:interval] || @interval, - refetch_interval: Application.get_env(:explorer, __MODULE__)[:refetch_interval] || @refetch_interval + refetch_interval: Application.get_env(:explorer, __MODULE__)[:refetch_interval] || @refetch_interval, + source: Application.get_env(:explorer, __MODULE__)[:source], + cryptorank_limit: Application.get_env(:explorer, Source.Cryptorank)[:limit], + cryptorank_offset: 0, + cryptorank_total_count: nil, + remaining_tokens: nil } - Process.send_after(self(), :fetch, state.interval) + schedule_first_fetching(state) {:ok, state} else @@ -96,6 +106,40 @@ defmodule Explorer.ExchangeRates.TokenExchangeRates do {:noreply, %{state | tokens_to_fetch: fetch_later}} end + def handle_info(:cryptorank_fetch, %{remaining_tokens: remaining_tokens} = state) + when is_integer(remaining_tokens) and remaining_tokens <= 0 do + Process.send_after(self(), :cryptorank_fetch, state.refetch_interval) + + {:noreply, %{state | cryptorank_total_count: nil, cryptorank_offset: 0, remaining_tokens: nil}} + end + + def handle_info(:cryptorank_fetch, state) do + case Source.cryptorank_fetch_currencies(state.cryptorank_limit, state.cryptorank_offset) do + {:ok, result} -> + {count, tokens} = Map.pop(result, :count) + + tokens + |> Enum.each(&update_token/1) + + count = state.cryptorank_total_count || count + cryptorank_offset = state.cryptorank_offset + state.cryptorank_limit + Process.send_after(self(), :cryptorank_fetch, state.interval) + + {:noreply, + %{ + state + | cryptorank_offset: cryptorank_offset, + cryptorank_total_count: count, + remaining_tokens: count - cryptorank_offset + }} + + err -> + Logger.error("Error while fetching cryptorank.io token prices: #{inspect(err)}") + Process.send_after(self(), :cryptorank_fetch, state.refetch_interval) + {:noreply, state} + end + end + defp update_token(%{contract_address_hash: contract_address_hash} = token, token_to_market_data) do case Map.get(token_to_market_data, contract_address_hash) do %{} = market_data -> @@ -107,4 +151,32 @@ defmodule Explorer.ExchangeRates.TokenExchangeRates do nil end end + + defp update_token({nil, _params}) do + :ignore + end + + defp update_token({address_hash, params}) do + address_hash + |> String.downcase() + |> Token.token_by_contract_address_hash_query() + |> Repo.update_all( + set: [ + fiat_value: params[:fiat_value], + circulating_market_cap: params[:circulating_market_cap], + volume_24h: params[:volume_24h], + updated_at: DateTime.utc_now() + ] + ) + end + + defp schedule_first_fetching(state) do + case state.source do + Source.CoinGecko -> + Process.send_after(self(), :fetch, state.interval) + + Source.Cryptorank -> + Process.send_after(self(), :cryptorank_fetch, state.interval) + end + end end diff --git a/apps/explorer/test/explorer/exchange_rates/token_exchange_rates_test.exs b/apps/explorer/test/explorer/exchange_rates/token_exchange_rates_test.exs index a6d3255a8047..5215f209f48a 100644 --- a/apps/explorer/test/explorer/exchange_rates/token_exchange_rates_test.exs +++ b/apps/explorer/test/explorer/exchange_rates/token_exchange_rates_test.exs @@ -19,7 +19,8 @@ defmodule Explorer.TokenExchangeRatesTest do interval: 0, platform: "ethereum", currency: "usd", - enabled: true + enabled: true, + source: Explorer.ExchangeRates.Source.CoinGecko ) on_exit(fn -> diff --git a/config/config_helper.exs b/config/config_helper.exs index 31570bd05339..9098ffdcbeea 100644 --- a/config/config_helper.exs +++ b/config/config_helper.exs @@ -177,6 +177,7 @@ defmodule ConfigHelper do "coin_gecko" -> Source.CoinGecko "coin_market_cap" -> Source.CoinMarketCap "mobula" -> Source.Mobula + "cryptorank" -> Source.Cryptorank _ -> Source.CoinGecko end end @@ -206,6 +207,7 @@ defmodule ConfigHelper do "coin_market_cap" -> Price.CoinMarketCap "crypto_compare" -> Price.CryptoCompare "mobula" -> Price.Mobula + "cryptorank" -> Source.Cryptorank _ -> Price.CryptoCompare end end @@ -217,16 +219,25 @@ defmodule ConfigHelper do cg_secondary_coin_id = System.get_env("EXCHANGE_RATES_COINGECKO_SECONDARY_COIN_ID") cc_secondary_coin_symbol = System.get_env("EXCHANGE_RATES_CRYPTOCOMPARE_SECONDARY_COIN_SYMBOL") mobula_secondary_coin_id = System.get_env("EXCHANGE_RATES_MOBULA_SECONDARY_COIN_ID") + cryptorank_secondary_coin_id = System.get_env("EXCHANGE_RATES_CRYPTORANK_SECONDARY_COIN_ID") cond do cg_secondary_coin_id && cg_secondary_coin_id !== "" -> Price.CoinGecko cmc_secondary_coin_id && cmc_secondary_coin_id !== "" -> Price.CoinMarketCap cc_secondary_coin_symbol && cc_secondary_coin_symbol !== "" -> Price.CryptoCompare mobula_secondary_coin_id && mobula_secondary_coin_id !== "" -> Price.Mobula + cryptorank_secondary_coin_id && cryptorank_secondary_coin_id !== "" -> Source.Cryptorank true -> Price.CryptoCompare end end + def token_exchange_rates_source do + case System.get_env("TOKEN_EXCHANGE_RATES_SOURCE") do + "cryptorank" -> Source.Cryptorank + _ -> Source.CoinGecko + end + end + def block_transformer do block_transformers = %{ "clique" => Blocks.Clique, diff --git a/config/runtime.exs b/config/runtime.exs index 341c187423f7..530062b2822f 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -388,12 +388,24 @@ config :explorer, Explorer.ExchangeRates.TokenExchangeRates, enabled: !ConfigHelper.parse_bool_env_var("DISABLE_TOKEN_EXCHANGE_RATE", "true"), interval: ConfigHelper.parse_time_env_var("TOKEN_EXCHANGE_RATE_INTERVAL", "5s"), refetch_interval: ConfigHelper.parse_time_env_var("TOKEN_EXCHANGE_RATE_REFETCH_INTERVAL", "1h"), - max_batch_size: ConfigHelper.parse_integer_env_var("TOKEN_EXCHANGE_RATE_MAX_BATCH_SIZE", 150) + max_batch_size: ConfigHelper.parse_integer_env_var("TOKEN_EXCHANGE_RATE_MAX_BATCH_SIZE", 150), + source: ConfigHelper.token_exchange_rates_source() + +cryptorank_secondary_coin_id = ConfigHelper.parse_integer_or_nil_env_var("EXCHANGE_RATES_CRYPTORANK_SECONDARY_COIN_ID") + +config :explorer, Explorer.ExchangeRates.Source.Cryptorank, + platform: ConfigHelper.parse_integer_or_nil_env_var("EXCHANGE_RATES_CRYPTORANK_PLATFORM_ID"), + base_url: System.get_env("EXCHANGE_RATES_CRYPTORANK_BASE_URL", "https://api.cryptorank.io/v1/"), + api_key: System.get_env("EXCHANGE_RATES_CRYPTORANK_API_KEY"), + coin_id: ConfigHelper.parse_integer_or_nil_env_var("EXCHANGE_RATES_CRYPTORANK_COIN_ID"), + secondary_coin_id: cryptorank_secondary_coin_id, + limit: ConfigHelper.parse_integer_env_var("EXCHANGE_RATES_CRYPTORANK_LIMIT", 1000) config :explorer, Explorer.Market.History.Cataloger, enabled: !disable_indexer? && !disable_exchange_rates?, history_fetch_interval: ConfigHelper.parse_time_env_var("MARKET_HISTORY_FETCH_INTERVAL", "1h"), - secondary_coin_enabled: cmc_secondary_coin_id || cg_secondary_coin_id || cc_secondary_coin_symbol + secondary_coin_enabled: + cmc_secondary_coin_id || cg_secondary_coin_id || cc_secondary_coin_symbol || cryptorank_secondary_coin_id config :explorer, Explorer.Chain.Transaction, suave_bid_contracts: System.get_env("SUAVE_BID_CONTRACTS", "") diff --git a/cspell.json b/cspell.json index d6dee164e4af..f897d5083702 100644 --- a/cspell.json +++ b/cspell.json @@ -125,6 +125,7 @@ "crosslevel", "crossorigin", "CRYPTOCOMPARE", + "Cryptorank", "ctbs", "ctid", "cumalative", @@ -481,6 +482,7 @@ "someout", "sourcecode", "sourcify", + "sparkline", "splitted", "srcset", "stabletoken", diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 7e7bda27e8eb..9dc79ece9ae7 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -54,6 +54,13 @@ EXCHANGE_RATES_COIN= # EXCHANGE_RATES_COINMARKETCAP_COIN_ID= # EXCHANGE_RATES_COINMARKETCAP_SECONDARY_COIN_ID= # EXCHANGE_RATES_CRYPTOCOMPARE_SECONDARY_COIN_SYMBOL= +# EXCHANGE_RATES_CRYPTORANK_SECONDARY_COIN_ID= +# EXCHANGE_RATES_CRYPTORANK_PLATFORM_ID= +# EXCHANGE_RATES_CRYPTORANK_BASE_URL= +# EXCHANGE_RATES_CRYPTORANK_API_KEY= +# EXCHANGE_RATES_CRYPTORANK_COIN_ID= +# EXCHANGE_RATES_CRYPTORANK_LIMIT= +# TOKEN_EXCHANGE_RATES_SOURCE= POOL_SIZE=80 # EXCHANGE_RATES_COINGECKO_PLATFORM_ID= # TOKEN_EXCHANGE_RATE_INTERVAL= From 8101bfc8083db11083baa67d95d87d9976957d47 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Thu, 15 Aug 2024 22:29:49 +0400 Subject: [PATCH 078/363] chore: Upgrade WS client (#10407) * chore: Upgrade WS client * Websocket client refactoring --- apps/block_scout_web/mix.exs | 1 - .../lib/ethereum_jsonrpc/web_socket.ex | 4 +- .../web_socket/retry_worker.ex | 59 ++++ .../ethereum_jsonrpc/web_socket/supervisor.ex | 66 ++++ .../web_socket/web_socket_client.ex | 303 ++++++++++-------- apps/ethereum_jsonrpc/mix.exs | 4 +- .../web_socket/web_socket_client_test.exs | 22 +- .../ethereum_jsonrpc/web_socket/case/mox.ex | 4 +- apps/explorer/config/dev/besu.exs | 3 +- apps/explorer/config/dev/erigon.exs | 3 +- apps/explorer/config/dev/filecoin.exs | 3 +- apps/explorer/config/dev/ganache.exs | 3 +- apps/explorer/config/dev/geth.exs | 3 +- apps/explorer/config/dev/nethermind.exs | 3 +- apps/explorer/config/dev/rsk.exs | 3 +- apps/explorer/config/prod/besu.exs | 3 +- apps/explorer/config/prod/erigon.exs | 3 +- apps/explorer/config/prod/filecoin.exs | 3 +- apps/explorer/config/prod/ganache.exs | 3 +- apps/explorer/config/prod/geth.exs | 3 +- apps/explorer/config/prod/nethermind.exs | 3 +- apps/explorer/config/prod/rsk.exs | 3 +- apps/indexer/config/dev/besu.exs | 3 +- apps/indexer/config/dev/erigon.exs | 3 +- apps/indexer/config/dev/filecoin.exs | 3 +- apps/indexer/config/dev/ganache.exs | 3 +- apps/indexer/config/dev/geth.exs | 3 +- apps/indexer/config/dev/nethermind.exs | 3 +- apps/indexer/config/dev/rsk.exs | 3 +- apps/indexer/config/prod/besu.exs | 3 +- apps/indexer/config/prod/erigon.exs | 3 +- apps/indexer/config/prod/filecoin.exs | 3 +- apps/indexer/config/prod/ganache.exs | 3 +- apps/indexer/config/prod/geth.exs | 3 +- apps/indexer/config/prod/nethermind.exs | 3 +- apps/indexer/config/prod/rsk.exs | 3 +- .../lib/indexer/block/realtime/supervisor.ex | 5 +- config/runtime.exs | 3 + cspell.json | 2 + docker-compose/envs/common-blockscout.env | 2 + mix.lock | 2 +- 41 files changed, 379 insertions(+), 182 deletions(-) create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/retry_worker.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/supervisor.ex diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs index 4662f1aaf478..f2ebd1589dd8 100644 --- a/apps/block_scout_web/mix.exs +++ b/apps/block_scout_web/mix.exs @@ -153,7 +153,6 @@ defmodule BlockScoutWeb.Mixfile do {:timex, "~> 3.7.1"}, {:wallaby, "~> 0.30", only: :test, runtime: false}, # `:cowboy` `~> 2.0` and Phoenix 1.4 compatibility - {:websocket_client, git: "https://github.com/blockscout/websocket_client.git", branch: "master", override: true}, {:ex_json_schema, "~> 0.10.1"}, {:ueberauth, "~> 0.7"}, {:ueberauth_auth0, "~> 2.0"}, diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket.ex index cb89d0bb05fb..a6d7afccb4f0 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket.ex @@ -8,7 +8,7 @@ defmodule EthereumJSONRPC.WebSocket do @behaviour Transport @enforce_keys ~w(url web_socket)a - defstruct ~w(url web_socket web_socket_options)a + defstruct ~w(url fallback_url web_socket web_socket_options)a @typedoc """ WebSocket name @@ -43,7 +43,7 @@ defmodule EthereumJSONRPC.WebSocket do Starts web socket attached to `url` with `options`. """ # Return is same as `t:GenServer.on_start/0` - @callback start_link([(url :: String.t()) | (options :: term())]) :: + @callback start_link(url :: String.t(), options :: term()) :: {:ok, pid()} | :ignore | {:error, {:already_started, pid()} | (reason :: term())} @doc """ diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/retry_worker.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/retry_worker.ex new file mode 100644 index 000000000000..4a6329edc036 --- /dev/null +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/retry_worker.ex @@ -0,0 +1,59 @@ +defmodule EthereumJSONRPC.WebSocket.RetryWorker do + @moduledoc """ + Stores the unavailable websocket endpoint state and periodically checks if it is already available. + """ + + use GenServer + + require Logger + + alias EthereumJSONRPC.WebSocket.Supervisor, as: WebSocketSupervisor + + def start_link(_) do + GenServer.start_link(__MODULE__, :ok, name: __MODULE__) + end + + def activate(ws_state) do + GenServer.cast(__MODULE__, {:activate, ws_state}) + end + + def deactivate do + GenServer.cast(__MODULE__, :deactivate) + end + + def init(_) do + schedule_next_retry() + + {:ok, %{active?: false, ws_state: nil}} + end + + def handle_cast({:activate, ws_state}, state) do + {:noreply, %{state | active?: true, ws_state: %{ws_state | retry: true}}} + end + + def handle_cast(:deactivate, state) do + {:noreply, %{state | active?: false}} + end + + def handle_info(:retry, %{active?: false} = state) do + schedule_next_retry() + + {:noreply, state} + end + + def handle_info(:retry, %{active?: true, ws_state: ws_state} = state) do + WebSocketSupervisor.start_client(ws_state) + + schedule_next_retry() + + {:noreply, %{state | active?: false}} + end + + defp schedule_next_retry do + Process.send_after(self(), :retry, retry_interval()) + end + + defp retry_interval do + Application.get_env(:ethereum_jsonrpc, __MODULE__)[:retry_interval] + end +end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/supervisor.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/supervisor.ex new file mode 100644 index 000000000000..974b249e18df --- /dev/null +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/supervisor.ex @@ -0,0 +1,66 @@ +defmodule EthereumJSONRPC.WebSocket.Supervisor do + @moduledoc """ + Supervises the processes related to `EthereumJSONRPC.WebSocket`. + """ + + use Supervisor + + alias EthereumJSONRPC.WebSocket.RetryWorker + + def start_link(transport_options) do + Supervisor.start_link(__MODULE__, transport_options, name: __MODULE__) + end + + def start_client(ws_state) do + subscribe_named_arguments = + Application.get_env(:indexer, :realtime_overrides)[:subscribe_named_arguments] || + Application.get_env(:indexer, :subscribe_named_arguments) + + web_socket_module = + subscribe_named_arguments + |> Keyword.fetch!(:transport_options) + |> Keyword.fetch!(:web_socket) + + client_spec = client_spec(web_socket_module, Indexer.Block.Realtime.WebSocketCopy, ws_state.url, nil, ws_state) + + Supervisor.start_child(__MODULE__, client_spec) + end + + def stop_other_client(pid) do + __MODULE__ + |> Supervisor.which_children() + |> Enum.reject(fn {child_id, child_pid, _type, _modules} -> child_pid == pid or child_id == RetryWorker end) + |> Enum.each(fn {child_id, _child_pid, _type, _modules} -> + Supervisor.terminate_child(__MODULE__, child_id) + Supervisor.delete_child(__MODULE__, child_id) + Process.unregister(Indexer.Block.Realtime.WebSocketCopy) + Process.register(pid, Indexer.Block.Realtime.WebSocket) + end) + end + + def init(%{ + url: url, + fallback_url: fallback_url, + web_socket: web_socket_module, + web_socket_options: %{web_socket: web_socket} + }) do + children = [ + {RetryWorker, []}, + client_spec(web_socket_module, web_socket, url, fallback_url) + ] + + Supervisor.init(children, strategy: :one_for_one) + end + + defp client_spec(web_socket_module, name, url, fallback_url, init_state \\ nil) do + %{ + id: name, + start: { + web_socket_module, + :start_link, + [url, [name: name, fallback_url: fallback_url, init_state: init_state]] + }, + restart: :temporary + } + end +end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/web_socket_client.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/web_socket_client.ex index 875519aa2e57..4098e5e42a1c 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/web_socket_client.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/web_socket_client.ex @@ -1,28 +1,28 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do @moduledoc """ - `EthereumJSONRPC.WebSocket` that uses `websocket_client` + `EthereumJSONRPC.WebSocket` that uses `WebSockex` """ require Logger - import EthereumJSONRPC, only: [request: 1] - alias EthereumJSONRPC.{Subscription, Transport, WebSocket} - alias EthereumJSONRPC.WebSocket.Registration + alias EthereumJSONRPC.WebSocket.{Registration, RetryWorker} + alias EthereumJSONRPC.WebSocket.Supervisor, as: WebSocketSupervisor alias EthereumJSONRPC.WebSocket.WebSocketClient.Options - @behaviour :websocket_client @behaviour WebSocket - @reconnect_interval :timer.minutes(1) - @enforce_keys ~w(url)a defstruct connected: false, request_id_to_registration: %{}, subscription_id_to_subscription_reference: %{}, subscription_reference_to_subscription_id: %{}, subscription_reference_to_subscription: %{}, - url: nil + url: nil, + fallback?: false, + fallback_url: nil, + fallback_conn: nil, + retry: false @typedoc """ * `request_id_to_registration` - maps id of requests in flight to their @@ -43,67 +43,29 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do subscription_reference_to_subscription_id: %{reference() => Subscription.id()} } - # Supervisor interface - @impl WebSocket def child_spec(arg) do Supervisor.child_spec(%{id: __MODULE__, start: {__MODULE__, :start_link, [arg]}}, []) end @impl WebSocket - # only allow secure WSS - def start_link(["wss://" <> _ = url, websocket_opts, gen_fsm_options]) when is_list(gen_fsm_options) do - keepalive = websocket_opts[:keepalive] - - fsm_name = - case Keyword.fetch(gen_fsm_options, :name) do - {:ok, name} when is_atom(name) -> {:local, name} - :error -> :undefined - end - - %URI{host: host} = URI.parse(url) - host_charlist = String.to_charlist(host) - - :ssl.start() - - # `:depth`, `:verify`, and `:verify_fun`, are based on `:hackney_connect.ssl_opts_1/2` as we use `:hackney` through - # `:httpoison` and this keeps the SSL rules consistent between HTTP and WebSocket - :websocket_client.start_link( - fsm_name, - url, - __MODULE__, - url, - ssl_verify: :verify_peer, - keepalive: keepalive, - socket_opts: [ - cacerts: :certifi.cacerts(), - depth: 99, - # SNI extension discloses host name in the clear, but allows for compatibility with Virtual Hosting for TLS - server_name_indication: host_charlist, - verify_fun: {&:ssl_verify_hostname.verify_fun/3, [check_hostname: host_charlist]}, - customize_hostname_check: [ - match_fun: :public_key.pkix_verify_hostname_match_fun(:https) - ] - ] - ) - end - - def start_link(["ws://" <> _ = url, websocket_opts, gen_fsm_options]) when is_list(gen_fsm_options) do - keepalive = websocket_opts[:keepalive] + def start_link(url, options) do + case build_conn(url, options) do + {:ok, conn, opts} -> + init_state = + options[:init_state] || + %__MODULE__{ + url: url, + fallback_url: options[:fallback_url], + fallback_conn: build_fallback_conn(options[:fallback_url], options) + } - fsm_name = - case Keyword.fetch(gen_fsm_options, :name) do - {:ok, name} when is_atom(name) -> {:local, name} - :error -> :undefined - end + WebSockex.start_link(conn, __MODULE__, init_state, opts) - :websocket_client.start_link( - fsm_name, - url, - __MODULE__, - url, - keepalive: keepalive - ) + error -> + Logger.error("Unable to build WS main connection: #{inspect(error)}") + :ignore + end end # Client interface @@ -111,41 +73,72 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do @impl WebSocket @spec json_rpc(WebSocket.web_socket(), Transport.request()) :: {:ok, Transport.result()} | {:error, reason :: term()} def json_rpc(web_socket, request) do - GenServer.call(web_socket, {:gen_call, {:json_rpc, request}}) + GenServer.call(web_socket, {:json_rpc, request}) end @impl WebSocket @spec subscribe(WebSocket.web_socket(), Subscription.event(), Subscription.params()) :: {:ok, Subscription.t()} | {:error, reason :: term()} def subscribe(web_socket, event, params) when is_binary(event) and is_list(params) do - GenServer.call(web_socket, {:gen_call, {:subscribe, event, params}}) + GenServer.call(web_socket, {:subscribe, event, params}) end @impl WebSocket @spec unsubscribe(WebSocket.web_socket(), Subscription.t()) :: :ok | {:error, :not_found} def unsubscribe(web_socket, %Subscription{} = subscription) do - GenServer.call(web_socket, {:gen_call, {:unsubscribe, subscription}}) + GenServer.call(web_socket, {:unsubscribe, subscription}) end - @impl :websocket_client - def init(url) do - {:reconnect, %__MODULE__{url: url}} + def handle_connect(_conn, state) do + Logger.metadata(fetcher: :websocket_client) + + unless state.fallback? do + RetryWorker.deactivate() + WebSocketSupervisor.stop_other_client(self()) + end + + {:ok, reconnect(%{state | connected: true, retry: false})} end - @impl :websocket_client - def onconnect(_, %__MODULE__{connected: false} = state) do - {:ok, reconnect(%__MODULE__{state | connected: true})} + def handle_disconnect(_, %{retry: true} = state) do + Logger.metadata(fetcher: :websocket_client) + RetryWorker.activate(state) + Logger.warning("WS endpoint #{state.url} is still unavailable") + {:ok, state} end - @impl :websocket_client - def ondisconnect(_reason, %__MODULE__{request_id_to_registration: request_id_to_registration} = state) do - final_state = Enum.reduce(request_id_to_registration, state, &disconnect_request_id_registration/2) + @attempts_to_reconnect 3 + def handle_disconnect(%{attempt_number: attempt}, state) do + Logger.metadata(fetcher: :websocket_client) + + final_state = + state.request_id_to_registration + |> Enum.reduce(state, &disconnect_request_id_registration/2) + |> Map.put(:connected, false) + + cond do + attempt < @attempts_to_reconnect -> + {:reconnect, final_state} + + state.fallback? -> + Logger.warning("WS fallback endpoint #{state.fallback_url} is unavailable") + {:ok, final_state} - {:reconnect, @reconnect_interval, %__MODULE__{final_state | connected: false}} + not is_nil(state.fallback_conn) -> + RetryWorker.activate(state) + Logger.warning("WS endpoint #{state.url} is unavailable, switching to fallback #{state.fallback_url}") + + {:reconnect, state.fallback_conn, + %{final_state | url: state.fallback_url, fallback?: true, fallback_url: nil, fallback_conn: nil}} + + true -> + RetryWorker.activate(state) + Logger.warning("WS endpoint #{state.url} is unavailable, and no fallback is set, shutting down WS client") + {:ok, final_state} + end end - @impl :websocket_client - def websocket_handle({:text, text}, _request, %__MODULE__{} = state) do + def handle_frame({:text, text}, state) do case Jason.decode(text) do {:ok, json} -> handle_response(json, state) @@ -156,25 +149,96 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do end end - @impl :websocket_client - def websocket_handle({:ping, ""}, _request, %__MODULE__{} = state), do: {:reply, {:pong, ""}, state} + def handle_ping({:ping, ""}, state) do + {:reply, {:pong, ""}, state} + end - @impl :websocket_client - def websocket_handle({:pong, _}, _request, state) do + def handle_pong({:pong, _}, state) do {:ok, state} end - @impl :websocket_client - def websocket_info({{:gen_call, request}, from}, _, %__MODULE__{} = state) do - case handle_call(request, from, state) do - {:reply, _, %__MODULE__{}} = reply -> reply - {:noreply, %__MODULE__{} = new_state} -> {:ok, new_state} + def handle_info({:"$gen_call", from, request}, state) do + case register(request, from, state) do + {:ok, unique_request, updated_state} -> + case state.connected do + true -> + {:reply, frame(unique_request), updated_state} + + false -> + {:ok, updated_state} + end + + {:error, _reason} = error -> + GenServer.reply(from, error) + {:ok, state} + end + end + + def handle_cast({:send_message, frame}, state) do + {:reply, frame, state} + end + + def terminate(reason, state) do + broadcast(reason, state) + end + + defp build_conn(url, options) when is_binary(url) do + common_opts = [ + name: options[:name] || __MODULE__, + async: true, + handle_initial_conn_failure: true + ] + + additional_opts = + case url do + "wss://" <> _ -> + %URI{host: host} = URI.parse(url) + host_charlist = String.to_charlist(host) + + :ssl.start() + + [ + insecure: false, + ssl_options: [ + cacerts: :certifi.cacerts(), + depth: 99, + # SNI extension discloses host name in the clear, but allows for compatibility with Virtual Hosting for TLS + server_name_indication: host_charlist, + verify_fun: {&:ssl_verify_hostname.verify_fun/3, [check_hostname: host_charlist]}, + customize_hostname_check: [ + match_fun: :public_key.pkix_verify_hostname_match_fun(:https) + ] + ] + ] + + _ -> + [] + end + + full_opts = Keyword.merge(common_opts, additional_opts) + + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + case WebSockex.Conn.parse_url(url) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + {:ok, uri} -> {:ok, WebSockex.Conn.new(uri, full_opts), full_opts} + error -> error end end - @impl :websocket_client - def websocket_terminate(close, _request, %__MODULE__{} = state) do - broadcast(close, state) + defp build_fallback_conn(nil, _options) do + Logger.info("WS fallback endpoint is not set") + nil + end + + defp build_fallback_conn(url, options) do + case build_conn(url, options) do + {:ok, conn, _opts} -> + conn + + error -> + Logger.warning("Unable to build WS fallback connection: #{inspect(error)}, continuing without fallback") + nil + end end defp broadcast(message, %__MODULE__{subscription_reference_to_subscription: subscription_reference_to_subscription}) do @@ -183,7 +247,6 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do |> Subscription.broadcast(message) end - # Not re-subscribing after disconnect is the same as a successful unsubscribe defp disconnect_request_id_registration( {request_id, %Registration{ @@ -214,7 +277,6 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do } end - # Re-run in `onconnect\2` defp disconnect_request_id_registration({_request_id, %Registration{type: type}}, state) when type in ~w(json_rpc subscribe)a do state @@ -224,26 +286,9 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do {:text, Jason.encode!(request)} end - defp handle_call(message, from, %__MODULE__{connected: connected} = state) do - case register(message, from, state) do - {:ok, unique_request, updated_state} -> - case connected do - true -> - {:reply, frame(unique_request), updated_state} - - false -> - {:noreply, updated_state} - end - - {:error, _reason} = error -> - GenServer.reply(from, error) - {:noreply, state} - end - end - defp handle_response( %{"method" => "eth_subscription", "params" => %{"result" => result, "subscription" => subscription_id}}, - %__MODULE__{ + %{ subscription_id_to_subscription_reference: subscription_id_to_subscription_reference, subscription_reference_to_subscription: subscription_reference_to_subscription } = state @@ -274,15 +319,15 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do defp handle_response( %{"id" => id} = response, - %__MODULE__{request_id_to_registration: request_id_to_registration} = state + %{request_id_to_registration: request_id_to_registration} = state ) do {registration, new_request_id_to_registration} = Map.pop(request_id_to_registration, id) - new_state = %__MODULE__{state | request_id_to_registration: new_request_id_to_registration} + new_state = %{state | request_id_to_registration: new_request_id_to_registration} respond_to_registration(registration, response, new_state) end - defp handle_response(response, %__MODULE__{} = state) do + defp handle_response(response, state) do Logger.error(fn -> [ "Unexpected JSON response from web socket\n", @@ -296,7 +341,7 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do {:ok, state} end - defp reconnect(%__MODULE__{} = state) do + defp reconnect(state) do state |> rerequest() |> resubscribe() @@ -305,13 +350,13 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do defp register( {:json_rpc, original_request}, from, - %__MODULE__{request_id_to_registration: request_id_to_registration} = state + %{request_id_to_registration: request_id_to_registration} = state ) do unique_id = unique_request_id(state) request = %{original_request | id: unique_id} {:ok, request, - %__MODULE__{ + %{ state | request_id_to_registration: Map.put(request_id_to_registration, unique_id, %Registration{ @@ -325,14 +370,14 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do defp register( {:subscribe, event, params}, from, - %__MODULE__{request_id_to_registration: request_id_to_registration} = state + %{request_id_to_registration: request_id_to_registration} = state ) when is_binary(event) and is_list(params) do unique_id = unique_request_id(state) - request = request(%{id: unique_id, method: "eth_subscribe", params: [event | params]}) + request = EthereumJSONRPC.request(%{id: unique_id, method: "eth_subscribe", params: [event | params]}) {:ok, request, - %__MODULE__{ + %{ state | request_id_to_registration: Map.put(request_id_to_registration, unique_id, %Registration{from: from, type: :subscribe, request: request}) @@ -342,7 +387,7 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do defp register( {:unsubscribe, %Subscription{reference: subscription_reference}}, from, - %__MODULE__{ + %{ request_id_to_registration: request_id_to_registration, subscription_reference_to_subscription_id: subscription_reference_to_subscription_id } = state @@ -350,12 +395,12 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do case subscription_reference_to_subscription_id do %{^subscription_reference => subscription_id} -> unique_id = unique_request_id(state) - request = request(%{id: unique_id, method: "eth_unsubscribe", params: [subscription_id]}) + request = EthereumJSONRPC.request(%{id: unique_id, method: "eth_unsubscribe", params: [subscription_id]}) { :ok, request, - %__MODULE__{ + %{ state | request_id_to_registration: Map.put(request_id_to_registration, unique_id, %Registration{ @@ -373,7 +418,7 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do defp rerequest(%__MODULE__{request_id_to_registration: request_id_to_registration} = state) do Enum.each(request_id_to_registration, fn {_, %Registration{request: request}} -> - :websocket_client.cast(self(), frame(request)) + WebSockex.cast(self(), {:send_message, frame(request)}) end) state @@ -382,7 +427,7 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do defp respond_to_registration( %Registration{type: :json_rpc, from: from}, response, - %__MODULE__{} = state + state ) do reply = case response do @@ -402,7 +447,7 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do request: %{params: [event | params]} }, %{"result" => subscription_id}, - %__MODULE__{ + %{ subscription_id_to_subscription_reference: subscription_id_to_subscription_reference, subscription_reference_to_subscription: subscription_reference_to_subscription, subscription_reference_to_subscription_id: subscription_reference_to_subscription_id, @@ -463,7 +508,7 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do defp respond_to_registration( %Registration{type: :subscribe, from: from}, %{"error" => error}, - %__MODULE__{} = state + state ) do GenServer.reply(from, {:error, error}) @@ -477,7 +522,7 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do request: %{method: "eth_unsubscribe", params: [subscription_id]} }, response, - %__MODULE__{ + %{ subscription_id_to_subscription_reference: subscription_id_to_subscription_reference, subscription_reference_to_subscription: subscription_reference_to_subscription, subscription_reference_to_subscription_id: subscription_reference_to_subscription_id @@ -516,7 +561,7 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do defp respond_to_registration( nil, response, - %__MODULE__{request_id_to_registration: request_id_to_registration} = state + %{request_id_to_registration: request_id_to_registration} = state ) do Logger.error(fn -> [ @@ -549,9 +594,9 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do acc_request_id_to_registration } = acc_state -> request_id = unique_request_id(acc_state) - request = request(%{id: request_id, method: "eth_subscribe", params: [event | params]}) + request = EthereumJSONRPC.request(%{id: request_id, method: "eth_subscribe", params: [event | params]}) - :websocket_client.cast(self(), frame(request)) + WebSockex.cast(self(), {:send_message, frame(request)}) %__MODULE__{ acc_state @@ -565,7 +610,7 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do end) end - defp unique_request_id(%__MODULE__{request_id_to_registration: request_id_to_registration} = state) do + defp unique_request_id(%{request_id_to_registration: request_id_to_registration} = state) do unique_request_id = EthereumJSONRPC.unique_request_id() case request_id_to_registration do diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs index 063d8aff882c..b7c5bee4ad18 100644 --- a/apps/ethereum_jsonrpc/mix.exs +++ b/apps/ethereum_jsonrpc/mix.exs @@ -82,12 +82,12 @@ defmodule EthereumJsonrpc.MixProject do # `:verify_fun` for `Socket.Web.connect` {:ssl_verify_fun, "~> 1.1"}, # `EthereumJSONRPC.WebSocket` - {:websocket_client, git: "https://github.com/blockscout/websocket_client.git", branch: "master", override: true}, {:decimal, "~> 2.0"}, {:decorator, "~> 1.4"}, {:hackney, "~> 1.18"}, {:poolboy, "~> 1.5.2"}, - {:logger_json, "~> 5.1"} + {:logger_json, "~> 5.1"}, + {:websockex, "~> 0.4.3"} ] end end diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/web_socket/web_socket_client_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/web_socket/web_socket_client_test.exs index 51bbcc598f74..245bf124ee1a 100644 --- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/web_socket/web_socket_client_test.exs +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/web_socket/web_socket_client_test.exs @@ -6,13 +6,9 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClientTest do import EthereumJSONRPC, only: [unique_request_id: 0] - describe "ondisconnect/2" do + describe "handle_disconnect/2" do setup :example_state - test "reconnects", %{state: state} do - assert {:reconnect, _, _} = WebSocketClient.ondisconnect({:closed, :remote}, state) - end - test "treats in-progress unsubscribes as successful", %{state: state} do subscription_id = 1 @@ -23,7 +19,7 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClientTest do state = put_registration(state, registration) - assert {_, _, disconnected_state} = WebSocketClient.ondisconnect({:closed, :remote}, state) + assert {_, disconnected_state} = WebSocketClient.handle_disconnect(%{attempt_number: 1}, state) assert Enum.empty?(disconnected_state.request_id_to_registration) assert Enum.empty?(disconnected_state.subscription_id_to_subscription_reference) @@ -36,7 +32,7 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClientTest do test "keeps :json_rpc requests for re-requesting on reconnect", %{state: state} do state = put_registration(state, %{type: :json_rpc, method: "eth_getBlockByNumber", params: [1, true]}) - assert {_, _, disconnected_state} = WebSocketClient.ondisconnect({:closed, :remote}, state) + assert {_, disconnected_state} = WebSocketClient.handle_disconnect(%{attempt_number: 1}, state) assert Enum.count(disconnected_state.request_id_to_registration) == 1 end @@ -44,13 +40,13 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClientTest do test "keeps :subscribe requests for re-requesting on reconnect", %{state: state} do state = put_registration(state, %{type: :subscribe}) - assert {_, _, disconnected_state} = WebSocketClient.ondisconnect({:closed, :remote}, state) + assert {_, disconnected_state} = WebSocketClient.handle_disconnect(%{attempt_number: 1}, state) assert Enum.count(disconnected_state.request_id_to_registration) == 1 end end - describe "websocket_handle/3" do + describe "handle_frame/2" do setup :example_state test "Jason.decode errors are broadcast to all subscribers", %{state: %WebSocketClient{url: url} = state} do @@ -59,12 +55,12 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClientTest do subscription = subscription(%{url: url, reference: subscription_reference}) state = put_subscription(state, subscription_id, subscription) - assert {:ok, ^state} = WebSocketClient.websocket_handle({:text, ""}, nil, state) + assert {:ok, ^state} = WebSocketClient.handle_frame({:text, ""}, state) assert_receive {^subscription, {:error, %Jason.DecodeError{}}} end end - describe "websocket_terminate/3" do + describe "terminate/2" do setup :example_state test "broadcasts close to all subscribers", %{state: %WebSocketClient{url: url} = state} do @@ -73,8 +69,8 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClientTest do subscription = subscription(%{url: url, reference: subscription_reference}) state = put_subscription(state, subscription_id, subscription) - assert {:ok, ^state} = WebSocketClient.websocket_handle({:text, ""}, nil, state) - assert_receive {^subscription, {:error, %Jason.DecodeError{}}} + assert :ok = WebSocketClient.terminate(:close, state) + assert_receive {^subscription, :close} end end diff --git a/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/web_socket/case/mox.ex b/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/web_socket/case/mox.ex index 57546f49f17a..465bd1d2da2b 100644 --- a/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/web_socket/case/mox.ex +++ b/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/web_socket/case/mox.ex @@ -24,12 +24,12 @@ defmodule EthereumJSONRPC.WebSocket.Case.Mox do [] ) end) - |> stub(:start_link, fn _ -> + |> stub(:start_link, fn _, _ -> Task.start_link(__MODULE__, :loop, [%{}]) end) url = "wss://example.com/ws" - web_socket = start_supervised!({web_socket_module, [url, [keepalive: :timer.minutes(10)]]}) + web_socket = start_supervised!(%{id: :ws_client, start: {web_socket_module, :start_link, [url, []]}}) %{ block_interval: @block_interval, diff --git a/apps/explorer/config/dev/besu.exs b/apps/explorer/config/dev/besu.exs index 9b32da34d7b0..e19ee16351b8 100644 --- a/apps/explorer/config/dev/besu.exs +++ b/apps/explorer/config/dev/besu.exs @@ -29,7 +29,8 @@ config :explorer, transport: EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ], variant: EthereumJSONRPC.Besu ] diff --git a/apps/explorer/config/dev/erigon.exs b/apps/explorer/config/dev/erigon.exs index 5304055919e5..4c766d8247fc 100644 --- a/apps/explorer/config/dev/erigon.exs +++ b/apps/explorer/config/dev/erigon.exs @@ -29,7 +29,8 @@ config :explorer, transport: EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ], variant: EthereumJSONRPC.Erigon ] diff --git a/apps/explorer/config/dev/filecoin.exs b/apps/explorer/config/dev/filecoin.exs index ea75c71dc462..f234e5b57000 100644 --- a/apps/explorer/config/dev/filecoin.exs +++ b/apps/explorer/config/dev/filecoin.exs @@ -28,7 +28,8 @@ config :explorer, transport: EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ], variant: EthereumJSONRPC.Filecoin ] diff --git a/apps/explorer/config/dev/ganache.exs b/apps/explorer/config/dev/ganache.exs index ae03df000e59..6ca442c81e69 100644 --- a/apps/explorer/config/dev/ganache.exs +++ b/apps/explorer/config/dev/ganache.exs @@ -26,7 +26,8 @@ config :explorer, transport: EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ], variant: EthereumJSONRPC.Ganache ] diff --git a/apps/explorer/config/dev/geth.exs b/apps/explorer/config/dev/geth.exs index 9d9a166f6329..41d6eb7a16ff 100644 --- a/apps/explorer/config/dev/geth.exs +++ b/apps/explorer/config/dev/geth.exs @@ -29,7 +29,8 @@ config :explorer, transport: EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ], variant: EthereumJSONRPC.Geth ] diff --git a/apps/explorer/config/dev/nethermind.exs b/apps/explorer/config/dev/nethermind.exs index 95ce54a92406..9e44aa811ab5 100644 --- a/apps/explorer/config/dev/nethermind.exs +++ b/apps/explorer/config/dev/nethermind.exs @@ -29,7 +29,8 @@ config :explorer, transport: EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ], variant: EthereumJSONRPC.Nethermind ] diff --git a/apps/explorer/config/dev/rsk.exs b/apps/explorer/config/dev/rsk.exs index 74cb6d98f095..0f8e4444e994 100644 --- a/apps/explorer/config/dev/rsk.exs +++ b/apps/explorer/config/dev/rsk.exs @@ -29,7 +29,8 @@ config :explorer, transport: EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ], variant: EthereumJSONRPC.RSK ] diff --git a/apps/explorer/config/prod/besu.exs b/apps/explorer/config/prod/besu.exs index cb641e7244ec..cafb89d2b0af 100644 --- a/apps/explorer/config/prod/besu.exs +++ b/apps/explorer/config/prod/besu.exs @@ -29,7 +29,8 @@ config :explorer, transport: EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ], variant: EthereumJSONRPC.Besu ] diff --git a/apps/explorer/config/prod/erigon.exs b/apps/explorer/config/prod/erigon.exs index e574b5564663..616a135b32f9 100644 --- a/apps/explorer/config/prod/erigon.exs +++ b/apps/explorer/config/prod/erigon.exs @@ -29,7 +29,8 @@ config :explorer, transport: EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ], variant: EthereumJSONRPC.Erigon ] diff --git a/apps/explorer/config/prod/filecoin.exs b/apps/explorer/config/prod/filecoin.exs index 00c056dbe6dd..bbad83ed26b4 100644 --- a/apps/explorer/config/prod/filecoin.exs +++ b/apps/explorer/config/prod/filecoin.exs @@ -28,7 +28,8 @@ config :explorer, transport: EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ], variant: EthereumJSONRPC.Filecoin ] diff --git a/apps/explorer/config/prod/ganache.exs b/apps/explorer/config/prod/ganache.exs index 7a0581452b3a..0fd0385cada8 100644 --- a/apps/explorer/config/prod/ganache.exs +++ b/apps/explorer/config/prod/ganache.exs @@ -26,7 +26,8 @@ config :explorer, transport: EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ], variant: EthereumJSONRPC.Ganache ] diff --git a/apps/explorer/config/prod/geth.exs b/apps/explorer/config/prod/geth.exs index 90a1a7bb3e80..9cf3bd32abbd 100644 --- a/apps/explorer/config/prod/geth.exs +++ b/apps/explorer/config/prod/geth.exs @@ -29,7 +29,8 @@ config :explorer, transport: EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ], variant: EthereumJSONRPC.Geth ] diff --git a/apps/explorer/config/prod/nethermind.exs b/apps/explorer/config/prod/nethermind.exs index a599d352ab41..d7cf3f3ecc0f 100644 --- a/apps/explorer/config/prod/nethermind.exs +++ b/apps/explorer/config/prod/nethermind.exs @@ -29,7 +29,8 @@ config :explorer, transport: EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ], variant: EthereumJSONRPC.Nethermind ] diff --git a/apps/explorer/config/prod/rsk.exs b/apps/explorer/config/prod/rsk.exs index ce19c3138079..1046aafb0dc3 100644 --- a/apps/explorer/config/prod/rsk.exs +++ b/apps/explorer/config/prod/rsk.exs @@ -29,7 +29,8 @@ config :explorer, transport: EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ], variant: EthereumJSONRPC.RSK ] diff --git a/apps/indexer/config/dev/besu.exs b/apps/indexer/config/dev/besu.exs index e1258bff85cb..bcac1a5640ee 100644 --- a/apps/indexer/config/dev/besu.exs +++ b/apps/indexer/config/dev/besu.exs @@ -39,6 +39,7 @@ config :indexer, EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ] ] diff --git a/apps/indexer/config/dev/erigon.exs b/apps/indexer/config/dev/erigon.exs index 2a7def995444..8a0d81d49891 100644 --- a/apps/indexer/config/dev/erigon.exs +++ b/apps/indexer/config/dev/erigon.exs @@ -39,6 +39,7 @@ config :indexer, EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ] ] diff --git a/apps/indexer/config/dev/filecoin.exs b/apps/indexer/config/dev/filecoin.exs index 65fab05db094..4f3c58b53846 100644 --- a/apps/indexer/config/dev/filecoin.exs +++ b/apps/indexer/config/dev/filecoin.exs @@ -35,6 +35,7 @@ config :indexer, EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ] ] diff --git a/apps/indexer/config/dev/ganache.exs b/apps/indexer/config/dev/ganache.exs index fcb3e127e122..c2c40947da9e 100644 --- a/apps/indexer/config/dev/ganache.exs +++ b/apps/indexer/config/dev/ganache.exs @@ -33,6 +33,7 @@ config :indexer, EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ] ] diff --git a/apps/indexer/config/dev/geth.exs b/apps/indexer/config/dev/geth.exs index c8182d931342..d12f24dc1a19 100644 --- a/apps/indexer/config/dev/geth.exs +++ b/apps/indexer/config/dev/geth.exs @@ -36,6 +36,7 @@ config :indexer, EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ] ] diff --git a/apps/indexer/config/dev/nethermind.exs b/apps/indexer/config/dev/nethermind.exs index 50c7eec22d2e..0e212345294a 100644 --- a/apps/indexer/config/dev/nethermind.exs +++ b/apps/indexer/config/dev/nethermind.exs @@ -56,6 +56,7 @@ config :indexer, EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ] ] diff --git a/apps/indexer/config/dev/rsk.exs b/apps/indexer/config/dev/rsk.exs index d4e4b42630fb..c31b0dfdf4b2 100644 --- a/apps/indexer/config/dev/rsk.exs +++ b/apps/indexer/config/dev/rsk.exs @@ -40,6 +40,7 @@ config :indexer, EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ] ] diff --git a/apps/indexer/config/prod/besu.exs b/apps/indexer/config/prod/besu.exs index 0f98fc1d6775..e87d210895c9 100644 --- a/apps/indexer/config/prod/besu.exs +++ b/apps/indexer/config/prod/besu.exs @@ -38,6 +38,7 @@ config :indexer, EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ] ] diff --git a/apps/indexer/config/prod/erigon.exs b/apps/indexer/config/prod/erigon.exs index f01fbc77409b..1574567cf7f4 100644 --- a/apps/indexer/config/prod/erigon.exs +++ b/apps/indexer/config/prod/erigon.exs @@ -38,6 +38,7 @@ config :indexer, EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ] ] diff --git a/apps/indexer/config/prod/filecoin.exs b/apps/indexer/config/prod/filecoin.exs index fc62d266cc13..82aff88a0865 100644 --- a/apps/indexer/config/prod/filecoin.exs +++ b/apps/indexer/config/prod/filecoin.exs @@ -35,6 +35,7 @@ config :indexer, EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ] ] diff --git a/apps/indexer/config/prod/ganache.exs b/apps/indexer/config/prod/ganache.exs index fcb3e127e122..c2c40947da9e 100644 --- a/apps/indexer/config/prod/ganache.exs +++ b/apps/indexer/config/prod/ganache.exs @@ -33,6 +33,7 @@ config :indexer, EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ] ] diff --git a/apps/indexer/config/prod/geth.exs b/apps/indexer/config/prod/geth.exs index 9e3ccb4e12c3..b690a4409462 100644 --- a/apps/indexer/config/prod/geth.exs +++ b/apps/indexer/config/prod/geth.exs @@ -36,6 +36,7 @@ config :indexer, EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ] ] diff --git a/apps/indexer/config/prod/nethermind.exs b/apps/indexer/config/prod/nethermind.exs index 4ba53bc7d143..3b751ff56fab 100644 --- a/apps/indexer/config/prod/nethermind.exs +++ b/apps/indexer/config/prod/nethermind.exs @@ -38,6 +38,7 @@ config :indexer, EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ] ] diff --git a/apps/indexer/config/prod/rsk.exs b/apps/indexer/config/prod/rsk.exs index ad4ce94b39d6..434344b39772 100644 --- a/apps/indexer/config/prod/rsk.exs +++ b/apps/indexer/config/prod/rsk.exs @@ -40,6 +40,7 @@ config :indexer, EthereumJSONRPC.WebSocket, transport_options: [ web_socket: EthereumJSONRPC.WebSocket.WebSocketClient, - url: System.get_env("ETHEREUM_JSONRPC_WS_URL") + url: System.get_env("ETHEREUM_JSONRPC_WS_URL"), + fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_WS_URL") ] ] diff --git a/apps/indexer/lib/indexer/block/realtime/supervisor.ex b/apps/indexer/lib/indexer/block/realtime/supervisor.ex index 9a7f2b29c815..9a3d3b184a56 100644 --- a/apps/indexer/lib/indexer/block/realtime/supervisor.ex +++ b/apps/indexer/lib/indexer/block/realtime/supervisor.ex @@ -20,16 +20,13 @@ defmodule Indexer.Block.Realtime.Supervisor do web_socket = Indexer.Block.Realtime.WebSocket web_socket_options = %EthereumJSONRPC.WebSocket.WebSocketClient.Options{web_socket: web_socket} transport_options = %EthereumJSONRPC.WebSocket{transport_options | web_socket_options: web_socket_options} - %EthereumJSONRPC.WebSocket{url: url, web_socket: web_socket_module} = transport_options - - keepalive = Keyword.get(subscribe_named_arguments, :keep_alive, :timer.seconds(150)) block_fetcher_subscribe_named_arguments = put_in(subscribe_named_arguments[:transport_options], transport_options) [ {Task.Supervisor, name: Indexer.Block.Realtime.TaskSupervisor}, - {web_socket_module, [url, [keepalive: keepalive], [name: web_socket]]}, + {EthereumJSONRPC.WebSocket.Supervisor, transport_options}, {Indexer.Block.Realtime.Fetcher, [ %{block_fetcher: block_fetcher, subscribe_named_arguments: block_fetcher_subscribe_named_arguments}, diff --git a/config/runtime.exs b/config/runtime.exs index 530062b2822f..a67e0c4881a5 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -201,6 +201,9 @@ config :ethereum_jsonrpc, EthereumJSONRPC.PendingTransaction, config :ethereum_jsonrpc, EthereumJSONRPC.RequestCoordinator, wait_per_timeout: ConfigHelper.parse_time_env_var("ETHEREUM_JSONRPC_WAIT_PER_TIMEOUT", "20s") +config :ethereum_jsonrpc, EthereumJSONRPC.WebSocket.RetryWorker, + retry_interval: ConfigHelper.parse_time_env_var("ETHEREUM_JSONRPC_WS_RETRY_INTERVAL", "1m") + config :ethereum_jsonrpc, EthereumJSONRPC.Utility.EndpointAvailabilityChecker, enabled: true ################ diff --git a/cspell.json b/cspell.json index f897d5083702..7eda2f9f160c 100644 --- a/cspell.json +++ b/cspell.json @@ -476,6 +476,7 @@ "smth", "snapshotted", "snapshotting", + "Sockex", "Sokol", "SOLIDITYSCAN", "soljson", @@ -598,6 +599,7 @@ "watchlist", "watchlisted", "watchlists", + "websockex", "whereis", "whiler", "wysdvjkizxonu", diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 9dc79ece9ae7..44befb0d48af 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -16,6 +16,8 @@ NETWORK= SUBNETWORK=Awesome chain LOGO=/images/blockscout_logo.svg # ETHEREUM_JSONRPC_WS_URL= +# ETHEREUM_JSONRPC_FALLBACK_WS_URL= +# ETHEREUM_JSONRPC_WS_RETRY_INTERVAL= ETHEREUM_JSONRPC_TRANSPORT=http ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES=false # ETHEREUM_JSONRPC_ARCHIVE_BALANCES_WINDOW=200 diff --git a/mix.lock b/mix.lock index 70549d26d1a3..f4841a96400c 100644 --- a/mix.lock +++ b/mix.lock @@ -145,5 +145,5 @@ "varint": {:hex, :varint, "1.4.0", "b7405c8a99db7b95d4341fa9cb15e7c3af6c8dda43e21bbe1c4a9cdff50b6502", [:mix], [], "hexpm", "0fd461901b7120c03467530dff3c58fa3475328fd75ba72c7d3cbf13bce6b0d2"}, "wallaby": {:hex, :wallaby, "0.30.9", "51d60682092c3c428c63b656b818e2258202b9f9a31ec37230659647ae20325b", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}, {:httpoison, "~> 0.12 or ~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_ecto, ">= 3.0.0", [hex: :phoenix_ecto, repo: "hexpm", optional: true]}, {:web_driver_client, "~> 0.2.0", [hex: :web_driver_client, repo: "hexpm", optional: false]}], "hexpm", "62e3ccb89068b231b50ed046219022020516d44f443eebef93a19db4be95b808"}, "web_driver_client": {:hex, :web_driver_client, "0.2.0", "63b76cd9eb3b0716ec5467a0f8bead73d3d9612e63f7560d21357f03ad86e31a", [:mix], [{:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:tesla, "~> 1.3", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "83cc6092bc3e74926d1c8455f0ce927d5d1d36707b74d9a65e38c084aab0350f"}, - "websocket_client": {:git, "https://github.com/blockscout/websocket_client.git", "0b4ecc5b1fb8a0bd1c8352728da787c20add53aa", [branch: "master"]}, + "websockex": {:hex, :websockex, "0.4.3", "92b7905769c79c6480c02daacaca2ddd49de936d912976a4d3c923723b647bf0", [:mix], [], "hexpm", "95f2e7072b85a3a4cc385602d42115b73ce0b74a9121d0d6dbbf557645ac53e4"}, } From 4fbbb3f4b1350f8d501cdadc1e11a467463cefd3 Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Fri, 16 Aug 2024 11:43:50 +0300 Subject: [PATCH 079/363] feat: Add icon for secondary coin (#10241) --- .../controllers/api/v2/stats_controller.ex | 1 + .../channels/exchange_rate_channel_test.exs | 4 +- .../api/rpc/stats_controller_test.exs | 2 +- apps/explorer/config/config.exs | 3 - .../explorer/exchange_rates/exchange_rates.ex | 84 +++++++++++++++---- .../lib/explorer/exchange_rates/source.ex | 21 +++++ .../exchange_rates/source/coin_gecko.ex | 1 + .../exchange_rates/source/coin_market_cap.ex | 12 +-- .../exchange_rates/source/defillama.ex | 5 ++ .../explorer/exchange_rates/source/mobula.ex | 6 ++ .../history/source/price/coin_market_cap.ex | 2 +- apps/explorer/lib/explorer/market/market.ex | 12 +-- .../lib/explorer/market/market_history.ex | 2 +- .../exchange_rates/exchange_rates_test.exs | 24 +++--- .../test/support/fakes/no_op_source.ex | 3 + .../test/support/fakes/one_coin_source.ex | 3 + config/config_helper.exs | 12 ++- config/runtime.exs | 23 +++-- config/runtime/test.exs | 2 + docker-compose/envs/common-blockscout.env | 2 + 20 files changed, 165 insertions(+), 59 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex index ceaf7bbda612..be905edcd06b 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex @@ -65,6 +65,7 @@ defmodule BlockScoutWeb.API.V2.StatsController do "total_transactions" => TransactionCache.estimated_count() |> to_string(), "average_block_time" => AverageBlockTime.average_block_time() |> Duration.to_milliseconds(), "coin_image" => exchange_rate.image_url, + "secondary_coin_image" => secondary_coin_exchange_rate.image_url, "coin_price" => exchange_rate.usd_value, "coin_price_change_percentage" => coin_price_change, "secondary_coin_price" => secondary_coin_exchange_rate.usd_value, diff --git a/apps/block_scout_web/test/block_scout_web/channels/exchange_rate_channel_test.exs b/apps/block_scout_web/test/block_scout_web/channels/exchange_rate_channel_test.exs index 7ae814d1be84..e69b863f563d 100644 --- a/apps/block_scout_web/test/block_scout_web/channels/exchange_rate_channel_test.exs +++ b/apps/block_scout_web/test/block_scout_web/channels/exchange_rate_channel_test.exs @@ -44,7 +44,7 @@ defmodule BlockScoutWeb.ExchangeRateChannelTest do describe "new_rate" do test "subscribed user is notified", %{token: token} do - ExchangeRates.handle_info({nil, {:ok, [token]}}, %{}) + ExchangeRates.handle_info({nil, {:ok, false, [token]}}, %{}) Supervisor.terminate_child(Explorer.Supervisor, {ConCache, Explorer.Market.MarketHistoryCache.cache_name()}) Supervisor.restart_child(Explorer.Supervisor, {ConCache, Explorer.Market.MarketHistoryCache.cache_name()}) @@ -64,7 +64,7 @@ defmodule BlockScoutWeb.ExchangeRateChannelTest do end test "subscribed user is notified with market history", %{token: token} do - ExchangeRates.handle_info({nil, {:ok, [token]}}, %{}) + ExchangeRates.handle_info({nil, {:ok, false, [token]}}, %{}) Supervisor.terminate_child(Explorer.Supervisor, {ConCache, Explorer.Market.MarketHistoryCache.cache_name()}) Supervisor.restart_child(Explorer.Supervisor, {ConCache, Explorer.Market.MarketHistoryCache.cache_name()}) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs index a883a8d96e16..6ec7c2e4b7c7 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs @@ -177,7 +177,7 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do image_url: nil } - ExchangeRates.handle_info({nil, {:ok, [eth]}}, %{}) + ExchangeRates.handle_info({nil, {:ok, false, [eth]}}, %{}) params = %{ "module" => "stats", diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index 27a109720e01..1ffec100ad2d 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -85,9 +85,6 @@ config :explorer, Explorer.Chain.Cache.TransactionActionTokensData, enabled: tru config :explorer, Explorer.Chain.Cache.TransactionActionUniswapPools, enabled: true -config :explorer, Explorer.ExchangeRates, - cache_period: ConfigHelper.parse_time_env_var("CACHE_EXCHANGE_RATES_PERIOD", "10m") - config :explorer, Explorer.ExchangeRates.TokenExchangeRates, enabled: true config :explorer, Explorer.Counters.TokenHoldersCounter, diff --git a/apps/explorer/lib/explorer/exchange_rates/exchange_rates.ex b/apps/explorer/lib/explorer/exchange_rates/exchange_rates.ex index 9bc60675eea5..9e860aa7510d 100644 --- a/apps/explorer/lib/explorer/exchange_rates/exchange_rates.ex +++ b/apps/explorer/lib/explorer/exchange_rates/exchange_rates.ex @@ -13,7 +13,6 @@ defmodule Explorer.ExchangeRates do alias Explorer.Market alias Explorer.ExchangeRates.{Source, Token} - @interval Application.compile_env(:explorer, __MODULE__)[:cache_period] @table_name :exchange_rates @impl GenServer @@ -27,23 +26,30 @@ defmodule Explorer.ExchangeRates do # Callback for successful fetch @impl GenServer - def handle_info({_ref, {:ok, tokens}}, state) do + def handle_info({_ref, {:ok, secondary_coin?, [coin]}}, state) do if store() == :ets do - records = Enum.map(tokens, &Token.to_tuple/1) - :ets.insert(table_name(), records) + :ets.insert(table_name(), {secondary_coin?, coin}) end broadcast_event(:exchange_rate) + unless secondary_coin? do + schedule_next_consolidation() + end + {:noreply, state} end # Callback for errored fetch @impl GenServer - def handle_info({_ref, {:error, reason}}, state) do - Logger.warning(fn -> "Failed to get exchange rates with reason '#{reason}'." end) + def handle_info({_ref, {:error, secondary_coin?, reason}}, state) do + Logger.warning(fn -> + "Failed to get #{if secondary_coin?, do: "secondary", else: ""} exchange rates with reason '#{reason}'." + end) - schedule_next_consolidation() + unless secondary_coin? do + schedule_next_consolidation() + end {:noreply, state} end @@ -57,7 +63,6 @@ defmodule Explorer.ExchangeRates do @impl GenServer def init(_) do send(self(), :update) - :timer.send_interval(@interval, :update) table_opts = [ :set, @@ -79,7 +84,29 @@ defmodule Explorer.ExchangeRates do end defp schedule_next_consolidation do - Process.send_after(self(), :update, :timer.minutes(1)) + if consolidate?() do + Process.send_after(self(), :update, cache_period()) + end + end + + @spec get_coin_exchange_rate() :: Token.t() | nil + def get_coin_exchange_rate do + if store() == :ets && enabled?() do + case :ets.lookup(table_name(), false) do + [{_, coin} | _] -> coin + _ -> nil + end + end + end + + @spec get_secondary_coin_exchange_rate() :: Token.t() | nil + def get_secondary_coin_exchange_rate do + if store() == :ets && enabled?() do + case :ets.lookup(table_name(), true) do + [{_, coin} | _] -> coin + _ -> nil + end + end end @doc """ @@ -123,12 +150,29 @@ defmodule Explorer.ExchangeRates do @spec fetch_rates :: Task.t() defp fetch_rates do - Task.Supervisor.async_nolink(Explorer.MarketTaskSupervisor, fn -> + Task.Supervisor.async_nolink(Explorer.MarketTaskSupervisor, fetch_rates_task(false)) + + if secondary_coin_enabled?() do + Task.Supervisor.async_nolink(Explorer.MarketTaskSupervisor, fetch_rates_task(true)) + end + end + + defp fetch_rates_task(false) do + fn -> case Source.fetch_exchange_rates() do - {:ok, tokens} -> {:ok, add_coin_info_from_db(tokens)} - err -> err + {:ok, coin} -> {:ok, false, add_coin_info_from_db(coin)} + {:error, reason} -> {:error, false, reason} end - end) + end + end + + defp fetch_rates_task(true) do + fn -> + case Source.fetch_secondary_exchange_rates() do + {:ok, coin} -> {:ok, true, coin} + {:error, reason} -> {:error, true, reason} + end + end end defp add_coin_info_from_db(tokens) do @@ -150,7 +194,7 @@ defmodule Explorer.ExchangeRates do defp list_from_store(:ets) do table_name() |> :ets.tab2list() - |> Enum.map(&Token.from_tuple/1) + |> Enum.map(fn {_, coin} -> coin end) |> Enum.sort_by(fn %Token{symbol: symbol} -> symbol end) end @@ -160,7 +204,19 @@ defmodule Explorer.ExchangeRates do config(:store) || :ets end + defp cache_period do + Application.get_env(:explorer, __MODULE__, [])[:cache_period] + end + defp enabled? do Application.get_env(:explorer, __MODULE__, [])[:enabled] == true end + + defp consolidate? do + Application.get_env(:explorer, __MODULE__, [])[:enable_consolidation] + end + + defp secondary_coin_enabled? do + Application.get_env(:explorer, __MODULE__, [])[:secondary_coin_enabled] + end end diff --git a/apps/explorer/lib/explorer/exchange_rates/source.ex b/apps/explorer/lib/explorer/exchange_rates/source.ex index e21de5218391..79875d3809e7 100644 --- a/apps/explorer/lib/explorer/exchange_rates/source.ex +++ b/apps/explorer/lib/explorer/exchange_rates/source.ex @@ -18,6 +18,17 @@ defmodule Explorer.ExchangeRates.Source do fetch_exchange_rates_request(source, source_url, source.headers()) end + @doc """ + Fetches exchange rates for secondary coin. + """ + @spec fetch_secondary_exchange_rates(module) :: {:ok, [Token.t()]} | {:error, any} + def fetch_secondary_exchange_rates(source \\ secondary_exchange_rates_source()) do + case source.secondary_source_url() do + :ignore -> {:error, "Secondary coin fetching is not implemented for source #{inspect(source)}"} + source_url -> fetch_exchange_rates_request(source, source_url, source.headers()) + end + end + @spec fetch_exchange_rates_for_token(String.t()) :: {:ok, [Token.t()]} | {:error, any} def fetch_exchange_rates_for_token(symbol) do source_url = CoinGecko.source_url(symbol) @@ -105,6 +116,11 @@ defmodule Explorer.ExchangeRates.Source do @callback source_url(String.t()) :: String.t() | :ignore + @doc """ + Url for the api to query to get the market info for secondary coin. + """ + @callback secondary_source_url :: String.t() | nil | :ignore + @callback headers :: [any] def headers do @@ -128,6 +144,11 @@ defmodule Explorer.ExchangeRates.Source do config(:source) || Explorer.ExchangeRates.Source.CoinGecko end + @spec secondary_exchange_rates_source() :: module() + defp secondary_exchange_rates_source do + config(:secondary_coin_source) || exchange_rates_source() + end + @spec config(atom()) :: term defp config(key) do Application.get_env(:explorer, __MODULE__, [])[key] diff --git a/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex b/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex index c4a2cf962a24..c40423ddb8e2 100644 --- a/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex +++ b/apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex @@ -106,6 +106,7 @@ defmodule Explorer.ExchangeRates.Source.CoinGecko do "#{source_url}/market_chart?#{URI.encode_query(query_params)}" end + @impl Source def secondary_source_url do id = config(:secondary_coin_id) diff --git a/apps/explorer/lib/explorer/exchange_rates/source/coin_market_cap.ex b/apps/explorer/lib/explorer/exchange_rates/source/coin_market_cap.ex index bb400de04097..855ba7305f61 100644 --- a/apps/explorer/lib/explorer/exchange_rates/source/coin_market_cap.ex +++ b/apps/explorer/lib/explorer/exchange_rates/source/coin_market_cap.ex @@ -71,12 +71,6 @@ defmodule Explorer.ExchangeRates.Source.CoinMarketCap do end end - @impl Source - def source_url(:secondary_coin) do - coin_id = config(:secondary_coin_id) - if coin_id, do: "#{api_quotes_latest_url()}?id=#{coin_id}&CMC_PRO_API_KEY=#{api_key()}", else: nil - end - @impl Source def source_url(input) do case Chain.Hash.Address.cast(input) do @@ -93,6 +87,12 @@ defmodule Explorer.ExchangeRates.Source.CoinMarketCap do end end + @impl Source + def secondary_source_url do + coin_id = config(:secondary_coin_id) + if coin_id, do: "#{api_quotes_latest_url()}?id=#{coin_id}&CMC_PRO_API_KEY=#{api_key()}", else: nil + end + @impl Source def headers do [] diff --git a/apps/explorer/lib/explorer/exchange_rates/source/defillama.ex b/apps/explorer/lib/explorer/exchange_rates/source/defillama.ex index e7cae25e0292..b9c3ecde5aae 100644 --- a/apps/explorer/lib/explorer/exchange_rates/source/defillama.ex +++ b/apps/explorer/lib/explorer/exchange_rates/source/defillama.ex @@ -26,6 +26,11 @@ defmodule Explorer.ExchangeRates.Source.DefiLlama do "" end + @impl Source + def secondary_source_url do + :ignore + end + @impl Source def headers do [] diff --git a/apps/explorer/lib/explorer/exchange_rates/source/mobula.ex b/apps/explorer/lib/explorer/exchange_rates/source/mobula.ex index 9e5484c56fc5..20d1cea213b1 100644 --- a/apps/explorer/lib/explorer/exchange_rates/source/mobula.ex +++ b/apps/explorer/lib/explorer/exchange_rates/source/mobula.ex @@ -81,6 +81,12 @@ defmodule Explorer.ExchangeRates.Source.Mobula do "#{base_url()}/market/data&asset=#{symbol}" end + @impl Source + def secondary_source_url do + id = config(:secondary_coin_id) + if id, do: "#{base_url()}/market/data?asset=#{id}", else: nil + end + @spec secondary_history_source_url() :: String.t() def secondary_history_source_url do id = config(:secondary_coin_id) diff --git a/apps/explorer/lib/explorer/market/history/source/price/coin_market_cap.ex b/apps/explorer/lib/explorer/market/history/source/price/coin_market_cap.ex index c226edfcdd3b..1974b72e7d53 100644 --- a/apps/explorer/lib/explorer/market/history/source/price/coin_market_cap.ex +++ b/apps/explorer/lib/explorer/market/history/source/price/coin_market_cap.ex @@ -13,7 +13,7 @@ defmodule Explorer.Market.History.Source.Price.CoinMarketCap do def fetch_price_history(_previous_days \\ nil, secondary_coin? \\ false) do url = if secondary_coin?, - do: ExchangeRatesSourceCoinMarketCap.source_url(:secondary_coin), + do: ExchangeRatesSourceCoinMarketCap.secondary_source_url(), else: ExchangeRatesSourceCoinMarketCap.source_url() if url do diff --git a/apps/explorer/lib/explorer/market/market.ex b/apps/explorer/lib/explorer/market/market.ex index f00085812ddc..f23f22f34dd5 100644 --- a/apps/explorer/lib/explorer/market/market.ex +++ b/apps/explorer/lib/explorer/market/market.ex @@ -55,7 +55,7 @@ defmodule Explorer.Market do """ @spec get_coin_exchange_rate() :: Token.t() def get_coin_exchange_rate do - get_native_coin_exchange_rate_from_cache() || get_native_coin_exchange_rate_from_db() || Token.null() + ExchangeRates.get_coin_exchange_rate() || get_native_coin_exchange_rate_from_db() || Token.null() end @doc """ @@ -63,7 +63,7 @@ defmodule Explorer.Market do """ @spec get_secondary_coin_exchange_rate() :: Token.t() def get_secondary_coin_exchange_rate do - get_native_coin_exchange_rate_from_db(true) + ExchangeRates.get_secondary_coin_exchange_rate() || get_native_coin_exchange_rate_from_db(true) end @doc false @@ -146,12 +146,4 @@ defmodule Explorer.Market do market_history.closing_price == 0 ) end - - @spec get_native_coin_exchange_rate_from_cache :: Token.t() | nil - defp get_native_coin_exchange_rate_from_cache do - case ExchangeRates.list() do - [native_coin] -> native_coin - _ -> nil - end - end end diff --git a/apps/explorer/lib/explorer/market/market_history.ex b/apps/explorer/lib/explorer/market/market_history.ex index bfcef2da37c3..b29d4b26a195 100644 --- a/apps/explorer/lib/explorer/market/market_history.ex +++ b/apps/explorer/lib/explorer/market/market_history.ex @@ -22,7 +22,7 @@ defmodule Explorer.Market.MarketHistory do field(:opening_price, :decimal) field(:market_cap, :decimal) field(:tvl, :decimal) - field(:secondary_coin, :boolean) + field(:secondary_coin, :boolean, default: false) end @doc """ diff --git a/apps/explorer/test/explorer/exchange_rates/exchange_rates_test.exs b/apps/explorer/test/explorer/exchange_rates/exchange_rates_test.exs index e23e64f07744..36bb6df719bf 100644 --- a/apps/explorer/test/explorer/exchange_rates/exchange_rates_test.exs +++ b/apps/explorer/test/explorer/exchange_rates/exchange_rates_test.exs @@ -64,7 +64,7 @@ defmodule Explorer.ExchangeRatesTest do set_mox_global() assert {:noreply, ^state} = ExchangeRates.handle_info(:update, state) - assert_receive {_, {:ok, [%Token{}]}} + assert_receive {_, {:ok, false, [%Token{}]}} end describe "ticker fetch task" do @@ -89,14 +89,11 @@ defmodule Explorer.ExchangeRatesTest do image_url: nil } - expected_symbol = expected_token.symbol - expected_tuple = Token.to_tuple(expected_token) - state = %{} - assert {:noreply, ^state} = ExchangeRates.handle_info({nil, {:ok, [expected_token]}}, state) + assert {:noreply, ^state} = ExchangeRates.handle_info({nil, {:ok, false, [expected_token]}}, state) - assert [^expected_tuple] = :ets.lookup(ExchangeRates.table_name(), expected_symbol) + assert [false: ^expected_token] = :ets.lookup(ExchangeRates.table_name(), false) end test "with failed fetch" do @@ -114,25 +111,30 @@ defmodule Explorer.ExchangeRatesTest do expect(TestSource, :headers, fn -> [] end) set_mox_global() - assert {:noreply, ^state} = ExchangeRates.handle_info({nil, {:error, "some error"}}, state) + assert {:noreply, ^state} = ExchangeRates.handle_info({nil, {:error, false, "some error"}}, state) assert_receive :update assert {:noreply, ^state} = ExchangeRates.handle_info(:update, state) - assert_receive {_, {:ok, [%Token{}]}} + assert_receive {_, {:ok, false, [%Token{}]}} end end test "list/0" do ExchangeRates.init([]) + rate_a = %Token{Token.null() | symbol: "a"} + rate_z = %Token{Token.null() | symbol: "z"} + rates = [ - %Token{Token.null() | symbol: "z"}, - %Token{Token.null() | symbol: "a"} + rate_z, + rate_a ] expected_rates = Enum.reverse(rates) - for rate <- rates, do: :ets.insert(ExchangeRates.table_name(), Token.to_tuple(rate)) + + :ets.insert(ExchangeRates.table_name(), {false, rate_z}) + :ets.insert(ExchangeRates.table_name(), {true, rate_a}) assert expected_rates == ExchangeRates.list() end diff --git a/apps/explorer/test/support/fakes/no_op_source.ex b/apps/explorer/test/support/fakes/no_op_source.ex index 9fc3d0ec4ba2..ff6365c86bc2 100644 --- a/apps/explorer/test/support/fakes/no_op_source.ex +++ b/apps/explorer/test/support/fakes/no_op_source.ex @@ -14,6 +14,9 @@ defmodule Explorer.ExchangeRates.Source.NoOpSource do @impl Source def source_url(_), do: :ignore + @impl Source + def secondary_source_url, do: :ignore + @impl Source def headers, do: [] end diff --git a/apps/explorer/test/support/fakes/one_coin_source.ex b/apps/explorer/test/support/fakes/one_coin_source.ex index ee38c6f09ba4..18ac3208a835 100644 --- a/apps/explorer/test/support/fakes/one_coin_source.ex +++ b/apps/explorer/test/support/fakes/one_coin_source.ex @@ -32,6 +32,9 @@ defmodule Explorer.ExchangeRates.Source.OneCoinSource do @impl Source def source_url(_), do: :ignore + @impl Source + def secondary_source_url, do: :ignore + @impl Source def headers, do: [] end diff --git a/config/config_helper.exs b/config/config_helper.exs index 9098ffdcbeea..eb7746d81f6e 100644 --- a/config/config_helper.exs +++ b/config/config_helper.exs @@ -173,7 +173,17 @@ defmodule ConfigHelper do @spec exchange_rates_source() :: Source.CoinGecko | Source.CoinMarketCap | Source.Mobula def exchange_rates_source do - case System.get_env("EXCHANGE_RATES_MARKET_CAP_SOURCE") do + case System.get_env("EXCHANGE_RATES_SOURCE") do + "coin_gecko" -> Source.CoinGecko + "coin_market_cap" -> Source.CoinMarketCap + "mobula" -> Source.Mobula + _ -> Source.CoinGecko + end + end + + @spec exchange_rates_secondary_coin_source() :: Source.CoinGecko | Source.CoinMarketCap | Source.Mobula + def exchange_rates_secondary_coin_source do + case System.get_env("EXCHANGE_RATES_SECONDARY_COIN_SOURCE") do "coin_gecko" -> Source.CoinGecko "coin_market_cap" -> Source.CoinMarketCap "mobula" -> Source.Mobula diff --git a/config/runtime.exs b/config/runtime.exs index a67e0c4881a5..bfdd4bbf84cf 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -344,28 +344,34 @@ config :explorer, Explorer.Counters.FreshPendingTransactionsCounter, cache_period: ConfigHelper.parse_time_env_var("CACHE_FRESH_PENDING_TRANSACTIONS_COUNTER_PERIOD", "5m"), enable_consolidation: true +cmc_secondary_coin_id = System.get_env("EXCHANGE_RATES_COINMARKETCAP_SECONDARY_COIN_ID") +cg_secondary_coin_id = System.get_env("EXCHANGE_RATES_COINGECKO_SECONDARY_COIN_ID") +cc_secondary_coin_symbol = System.get_env("EXCHANGE_RATES_CRYPTOCOMPARE_SECONDARY_COIN_SYMBOL") +mobula_secondary_coin_id = System.get_env("EXCHANGE_RATES_MOBULA_SECONDARY_COIN_ID") + config :explorer, Explorer.ExchangeRates, store: :ets, enabled: !disable_exchange_rates?, - fetch_btc_value: ConfigHelper.parse_bool_env_var("EXCHANGE_RATES_FETCH_BTC_VALUE") + enable_consolidation: true, + secondary_coin_enabled: + !disable_exchange_rates? && (cmc_secondary_coin_id || cg_secondary_coin_id || mobula_secondary_coin_id), + fetch_btc_value: ConfigHelper.parse_bool_env_var("EXCHANGE_RATES_FETCH_BTC_VALUE"), + cache_period: ConfigHelper.parse_time_env_var("CACHE_EXCHANGE_RATES_PERIOD", "10m") config :explorer, Explorer.ExchangeRates.Source, source: ConfigHelper.exchange_rates_source(), + secondary_coin_source: ConfigHelper.exchange_rates_secondary_coin_source(), price_source: ConfigHelper.exchange_rates_price_source(), secondary_coin_price_source: ConfigHelper.exchange_rates_secondary_coin_price_source(), market_cap_source: ConfigHelper.exchange_rates_market_cap_source(), tvl_source: ConfigHelper.exchange_rates_tvl_source() -cmc_secondary_coin_id = System.get_env("EXCHANGE_RATES_COINMARKETCAP_SECONDARY_COIN_ID") - config :explorer, Explorer.ExchangeRates.Source.CoinMarketCap, base_url: System.get_env("EXCHANGE_RATES_COINMARKETCAP_BASE_URL"), api_key: System.get_env("EXCHANGE_RATES_COINMARKETCAP_API_KEY"), coin_id: System.get_env("EXCHANGE_RATES_COINMARKETCAP_COIN_ID"), secondary_coin_id: cmc_secondary_coin_id -cg_secondary_coin_id = System.get_env("EXCHANGE_RATES_COINGECKO_SECONDARY_COIN_ID") - config :explorer, Explorer.ExchangeRates.Source.CoinGecko, platform: System.get_env("EXCHANGE_RATES_COINGECKO_PLATFORM_ID"), base_url: System.get_env("EXCHANGE_RATES_COINGECKO_BASE_URL"), @@ -379,12 +385,10 @@ config :explorer, Explorer.ExchangeRates.Source.Mobula, base_url: System.get_env("EXCHANGE_RATES_MOBULA_BASE_URL", "https://api.mobula.io/api/1"), api_key: System.get_env("EXCHANGE_RATES_MOBULA_API_KEY"), coin_id: System.get_env("EXCHANGE_RATES_MOBULA_COIN_ID"), - secondary_coin_id: System.get_env("EXCHANGE_RATES_MOBULA_SECONDARY_COIN_ID") + secondary_coin_id: mobula_secondary_coin_id config :explorer, Explorer.ExchangeRates.Source.DefiLlama, coin_id: System.get_env("EXCHANGE_RATES_DEFILLAMA_COIN_ID") -cc_secondary_coin_symbol = System.get_env("EXCHANGE_RATES_CRYPTOCOMPARE_SECONDARY_COIN_SYMBOL") - config :explorer, Explorer.Market.History.Source.Price.CryptoCompare, secondary_coin_symbol: cc_secondary_coin_symbol config :explorer, Explorer.ExchangeRates.TokenExchangeRates, @@ -408,7 +412,8 @@ config :explorer, Explorer.Market.History.Cataloger, enabled: !disable_indexer? && !disable_exchange_rates?, history_fetch_interval: ConfigHelper.parse_time_env_var("MARKET_HISTORY_FETCH_INTERVAL", "1h"), secondary_coin_enabled: - cmc_secondary_coin_id || cg_secondary_coin_id || cc_secondary_coin_symbol || cryptorank_secondary_coin_id + cmc_secondary_coin_id || cg_secondary_coin_id || cc_secondary_coin_symbol || mobula_secondary_coin_id || + cryptorank_secondary_coin_id config :explorer, Explorer.Chain.Transaction, suave_bid_contracts: System.get_env("SUAVE_BID_CONTRACTS", "") diff --git a/config/runtime/test.exs b/config/runtime/test.exs index 786755f81298..89b65b179274 100644 --- a/config/runtime/test.exs +++ b/config/runtime/test.exs @@ -20,6 +20,8 @@ config :explorer, Explorer.Counters.Transactions24hStats, cache_period: ConfigHelper.parse_time_env_var("CACHE_TRANSACTIONS_24H_STATS_PERIOD", "1h"), enable_consolidation: false +config :explorer, Explorer.ExchangeRates, enable_consolidation: false + variant = Variant.get() Code.require_file("#{variant}.exs", "apps/explorer/config/test") diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 44befb0d48af..0ecb6367e569 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -43,6 +43,8 @@ EMISSION_FORMAT=DEFAULT # SUPPLY_MODULE= COIN= EXCHANGE_RATES_COIN= +# EXCHANGE_RATES_SOURCE= +# EXCHANGE_RATES_SECONDARY_COIN_SOURCE= # EXCHANGE_RATES_MARKET_CAP_SOURCE= # EXCHANGE_RATES_TVL_SOURCE= # EXCHANGE_RATES_PRICE_SOURCE= From 5d98e8ba336868097245001cdb3b184f3a0ec9b3 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Fri, 16 Aug 2024 14:22:01 +0400 Subject: [PATCH 080/363] chore: Shrink internal transactions (#10567) * chore: Shrink internal transactions * Optimize shrink internal transactions migration * Shrink internal transactions refactoring * Fix internal transactions input shrink * Update apps/explorer/lib/explorer/migrator/shrink_internal_transactions.ex Co-authored-by: Victor Baranov * Update apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex Co-authored-by: Victor Baranov * Shrink internal transactions function docs --------- Co-authored-by: Victor Baranov --- .../api/v2/transaction_controller.ex | 25 ++++---- .../transaction_raw_trace_controller.ex | 19 ++---- .../transaction_raw_trace/_card_body.html.eex | 8 +-- .../views/api/v2/transaction_view.ex | 6 +- .../views/transaction_raw_trace_view.ex | 7 +-- .../api/v2/transaction_controller_test.exs | 44 +++++++++++++ apps/block_scout_web/test/test_helper.exs | 1 + apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex | 10 +++ .../lib/ethereum_jsonrpc/besu.ex | 5 ++ .../lib/ethereum_jsonrpc/erigon.ex | 5 ++ .../lib/ethereum_jsonrpc/filecoin.ex | 18 ++++++ .../lib/ethereum_jsonrpc/ganache.ex | 8 +++ .../lib/ethereum_jsonrpc/geth.ex | 17 +++++ .../lib/ethereum_jsonrpc/nethermind.ex | 5 ++ .../lib/ethereum_jsonrpc/rsk.ex | 6 +- .../trace_replay_block_transactions.ex | 18 ++++++ .../lib/ethereum_jsonrpc/variant.ex | 15 +++++ apps/explorer/config/dev.exs | 2 + apps/explorer/config/prod.exs | 5 ++ apps/explorer/config/test.exs | 3 +- apps/explorer/lib/explorer/application.ex | 6 +- apps/explorer/lib/explorer/chain.ex | 13 ++++ .../import/runner/internal_transactions.ex | 37 ++++++++++- ...ddress_current_token_balance_token_type.ex | 13 ++-- .../address_token_balance_token_type.ex | 13 ++-- .../explorer/migrator/filling_migration.ex | 16 ++--- .../migrator/shrink_internal_transactions.ex | 63 +++++++++++++++++++ .../token_transfer_block_consensus.ex | 13 ++-- .../migrator/token_transfer_token_type.ex | 13 ++-- .../migrator/transaction_block_consensus.ex | 13 ++-- .../migrator/transactions_denormalization.ex | 13 ++-- apps/explorer/lib/explorer/repo.ex | 10 +++ ...move_internal_transactions_constraints.exs | 18 ++++++ apps/explorer/test/test_helper.exs | 1 + .../indexer/fetcher/on_demand/first_trace.ex | 13 +++- config/config_helper.exs | 3 +- config/runtime.exs | 8 ++- config/runtime/dev.exs | 7 +++ config/runtime/prod.exs | 8 +++ docker-compose/envs/common-blockscout.env | 3 + 40 files changed, 425 insertions(+), 86 deletions(-) create mode 100644 apps/explorer/lib/explorer/migrator/shrink_internal_transactions.ex create mode 100644 apps/explorer/priv/shrunk_internal_transactions/migrations/20240717080512_remove_internal_transactions_constraints.exs diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex index 0ebb3a4c6b29..43179c26c823 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex @@ -33,6 +33,8 @@ defmodule BlockScoutWeb.API.V2.TransactionController do preload: 2 ] + require Logger + alias BlockScoutWeb.AccessHelper alias BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation, as: TransactionInterpretationService alias BlockScoutWeb.Models.TransactionStateHelper @@ -338,27 +340,24 @@ defmodule BlockScoutWeb.API.V2.TransactionController do """ @spec raw_trace(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} def raw_trace(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do - with {:ok, transaction, transaction_hash} <- validate_transaction(transaction_hash_string, params) do + with {:ok, transaction, _transaction_hash} <- validate_transaction(transaction_hash_string, params) do if is_nil(transaction.block_number) do conn |> put_status(200) |> render(:raw_trace, %{internal_transactions: []}) else - internal_transactions = - InternalTransaction.all_transaction_to_internal_transactions(transaction_hash, @api_true) + FirstTraceOnDemand.maybe_trigger_fetch(transaction, @api_true) - first_trace_exists = - Enum.find_index(internal_transactions, fn trace -> - trace.index == 0 - end) + case Chain.fetch_transaction_raw_traces(transaction) do + {:ok, raw_traces} -> + conn + |> put_status(200) + |> render(:raw_trace, %{raw_traces: raw_traces}) - if !first_trace_exists do - FirstTraceOnDemand.trigger_fetch(transaction) + {:error, error} -> + Logger.error("Raw trace fetching failed: #{inspect(error)}") + {500, "Error while raw trace fetching"} end - - conn - |> put_status(200) - |> render(:raw_trace, %{internal_transactions: internal_transactions}) end end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex index 05c3da6d8db4..748281903a38 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex @@ -8,7 +8,6 @@ defmodule BlockScoutWeb.TransactionRawTraceController do alias BlockScoutWeb.{AccessHelper, TransactionController} alias EthereumJSONRPC alias Explorer.{Chain, Market} - alias Explorer.Chain.InternalTransaction alias Indexer.Fetcher.OnDemand.FirstTrace, as: FirstTraceOnDemand def index(conn, %{"transaction_id" => hash_string} = params) do @@ -30,18 +29,12 @@ defmodule BlockScoutWeb.TransactionRawTraceController do if is_nil(transaction.block_number) do render_raw_trace(conn, [], transaction, hash) else - internal_transactions = InternalTransaction.all_transaction_to_internal_transactions(hash) + FirstTraceOnDemand.maybe_trigger_fetch(transaction) - first_trace_exists = - Enum.find_index(internal_transactions, fn trace -> - trace.index == 0 - end) - - if !first_trace_exists do - FirstTraceOnDemand.trigger_fetch(transaction) + case Chain.fetch_transaction_raw_traces(transaction) do + {:ok, raw_traces} -> render_raw_trace(conn, raw_traces, transaction, hash) + _error -> unprocessable_entity(conn) end - - render_raw_trace(conn, internal_transactions, transaction, hash) end else {:restricted_access, _} -> @@ -55,12 +48,12 @@ defmodule BlockScoutWeb.TransactionRawTraceController do end end - defp render_raw_trace(conn, internal_transactions, transaction, hash) do + defp render_raw_trace(conn, raw_traces, transaction, hash) do render( conn, "index.html", exchange_rate: Market.get_coin_exchange_rate(), - internal_transactions: internal_transactions, + raw_traces: raw_traces, block_height: Chain.block_height(), current_user: current_user(conn), show_token_transfers: Chain.transaction_has_token_transfers?(hash), diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_raw_trace/_card_body.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_raw_trace/_card_body.html.eex index 1d105bf4ba7b..55cbe19bdb28 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction_raw_trace/_card_body.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_raw_trace/_card_body.html.eex @@ -1,6 +1,6 @@

<%= gettext "Raw Trace" %> - <%= unless Enum.empty?(@internal_transactions) do %> - <% raw_trace_text = for {line, _} <- raw_traces_with_lines(@internal_transactions), do: line %> + <%= unless Enum.empty?(@raw_traces) do %> + <% raw_trace_text = for {line, _} <- raw_traces_with_lines(@raw_traces), do: line %> <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", id: "tx-raw-trace-input", additional_classes: ["tx-raw-input", "transaction-input"], @@ -10,10 +10,10 @@ style: "float: right;" %> <% end %>

-<%= if Enum.empty?(@internal_transactions) do %> +<%= if Enum.empty?(@raw_traces) do %>
<%= gettext "No trace entries found." %>
<% else %> -
<%= for {line, number} <- raw_traces_with_lines(@internal_transactions) do %>
<%= line %>
<% end %>
+
<%= for {line, number} <- raw_traces_with_lines(@raw_traces) do %>
<%= line %>
<% end %>
<% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex index 808ff2196930..c950a69fa53c 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex @@ -9,7 +9,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do alias BlockScoutWeb.TransactionStateView alias Ecto.Association.NotLoaded alias Explorer.{Chain, Market} - alias Explorer.Chain.{Address, Block, InternalTransaction, Log, Token, Transaction, Wei} + alias Explorer.Chain.{Address, Block, Log, Token, Transaction, Wei} alias Explorer.Chain.Block.Reward alias Explorer.Chain.Transaction.StateChange alias Explorer.Counters.AverageBlockTime @@ -99,8 +99,8 @@ defmodule BlockScoutWeb.API.V2.TransactionView do |> prepare_transaction(conn, true, block_height, decoded_input) end - def render("raw_trace.json", %{internal_transactions: internal_transactions}) do - InternalTransaction.internal_transactions_to_raw(internal_transactions) + def render("raw_trace.json", %{raw_traces: raw_traces}) do + raw_traces end def render("decoded_log_input.json", %{method_id: method_id, text: text, mapping: mapping}) do diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_raw_trace_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_raw_trace_view.ex index 3adab1697416..09fd46168b29 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_raw_trace_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_raw_trace_view.ex @@ -2,15 +2,12 @@ defmodule BlockScoutWeb.TransactionRawTraceView do use BlockScoutWeb, :view @dialyzer :no_match - alias Explorer.Chain.InternalTransaction - def render("scripts.html", %{conn: conn}) do render_scripts(conn, "raw-trace/code_highlighting.js") end - def raw_traces_with_lines(internal_transactions) do - internal_transactions - |> InternalTransaction.internal_transactions_to_raw() + def raw_traces_with_lines(raw_traces) do + raw_traces |> Jason.encode!(pretty: true) |> String.split("\n") |> Enum.with_index(1) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs index 148d17226543..312a92a7e4bb 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs @@ -2,6 +2,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do use BlockScoutWeb.ConnCase import Explorer.Chain, only: [hash_to_lower_case_string: 1] + import Mox alias BlockScoutWeb.Models.UserFromAuth alias Explorer.Account.WatchlistAddress @@ -1282,6 +1283,49 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end end + describe "/transactions/{tx_hash}/raw-trace" do + test "returns raw trace from node", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block(status: :ok) + + raw_trace = %{ + "traceAddress" => [], + "type" => "call", + "callType" => "call", + "from" => "0xa931c862e662134b85e4dc4baf5c70cc9ba74db4", + "to" => "0x1469b17ebf82fedf56f04109e5207bdc4554288c", + "gas" => "0x8600", + "gasUsed" => "0x7d37", + "input" => "0xb118e2db0000000000000000000000000000000000000000000000000000000000000008", + "output" => "0x", + "value" => "0x174876e800" + } + + expect(EthereumJSONRPC.Mox, :json_rpc, fn _, _ -> {:ok, raw_trace} end) + + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/raw-trace") + + assert response = json_response(request, 200) + assert response == raw_trace + end + + test "returns correct error", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block(status: :ok) + + expect(EthereumJSONRPC.Mox, :json_rpc, fn _, _ -> {:error, "error"} end) + + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/raw-trace") + + assert response = json_response(request, 500) + assert response == "Error while raw trace fetching" + end + end + if Application.compile_env(:explorer, :chain_type) == :stability do @first_topic_hex_string_1 "0x99e7b0ba56da2819c37c047f0511fd2bf6c9b4e27b4a979a19d6da0f74be8155" diff --git a/apps/block_scout_web/test/test_helper.exs b/apps/block_scout_web/test/test_helper.exs index d03847979fce..1a77c4a155bb 100644 --- a/apps/block_scout_web/test/test_helper.exs +++ b/apps/block_scout_web/test/test_helper.exs @@ -36,6 +36,7 @@ Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.Stability, :manual) Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.BridgedTokens, :manual) Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.Filecoin, :manual) Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.Mud, :manual) +Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.ShrunkInternalTransactions, :manual) Absinthe.Test.prime(BlockScoutWeb.GraphQL.Schema) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex index 3e3afe9dde6a..954036218db5 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex @@ -386,6 +386,16 @@ defmodule EthereumJSONRPC do Keyword.fetch!(json_rpc_named_arguments, :variant).fetch_pending_transactions(json_rpc_named_arguments) end + @doc """ + Retrieves raw traces from Ethereum JSON RPC variant API. + """ + def fetch_transaction_raw_traces(transaction_params, json_rpc_named_arguments) do + Keyword.fetch!(json_rpc_named_arguments, :variant).fetch_transaction_raw_traces( + transaction_params, + json_rpc_named_arguments + ) + end + @spec fetch_transaction_receipts( [ %{required(:gas) => non_neg_integer(), required(:hash) => hash, optional(atom) => any} diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/besu.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/besu.ex index 67b38e85c8dc..0c54fecd5848 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/besu.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/besu.ex @@ -55,6 +55,11 @@ defmodule EthereumJSONRPC.Besu do PendingTransaction.fetch_pending_transactions_besu(json_rpc_named_arguments) end + @impl EthereumJSONRPC.Variant + def fetch_transaction_raw_traces(transaction_params, json_rpc_named_arguments) do + TraceReplayBlockTransactions.fetch_transaction_raw_traces(transaction_params, json_rpc_named_arguments) + end + defp block_numbers_to_params_list(block_numbers) when is_list(block_numbers) do Enum.map(block_numbers, &%{block_quantity: integer_to_quantity(&1)}) end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/erigon.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/erigon.ex index 8173a98df2a5..57f490162270 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/erigon.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/erigon.ex @@ -50,6 +50,11 @@ defmodule EthereumJSONRPC.Erigon do PendingTransaction.fetch_pending_transactions_geth(json_rpc_named_arguments) end + @impl EthereumJSONRPC.Variant + def fetch_transaction_raw_traces(transaction_params, json_rpc_named_arguments) do + TraceReplayBlockTransactions.fetch_transaction_raw_traces(transaction_params, json_rpc_named_arguments) + end + defp block_numbers_to_params_list(block_numbers) when is_list(block_numbers) do Enum.map(block_numbers, &%{block_quantity: integer_to_quantity(&1)}) end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/filecoin.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/filecoin.ex index 4afbd33c6cee..c221c71f4b51 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/filecoin.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/filecoin.ex @@ -5551,6 +5551,24 @@ defmodule EthereumJSONRPC.Filecoin do end end + @doc """ + Fetches the raw traces from the FEVM trace URL. + """ + @impl EthereumJSONRPC.Variant + def fetch_transaction_raw_traces(%{hash: transaction_hash, block_number: block_number}, json_rpc_named_arguments) do + request = debug_trace_block_by_number_request({0, block_number}) + transaction_hash_string = to_string(transaction_hash) + + case json_rpc(request, json_rpc_named_arguments) do + {:ok, response} -> + {:ok, Enum.filter(response, &(&1["transactionHash"] == transaction_hash_string))} + + {:error, error} -> + Logger.error(inspect(error)) + {:error, error} + end + end + defp to_transactions_params(blocks_responses, id_to_params) do Enum.reduce(blocks_responses, [], fn %{id: id, result: tx_result}, blocks_acc -> extract_transactions_params(Map.fetch!(id_to_params, id), tx_result) ++ blocks_acc diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/ganache.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/ganache.ex index 6d9716a70633..c555135234a7 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/ganache.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/ganache.ex @@ -44,4 +44,12 @@ defmodule EthereumJSONRPC.Ganache do """ @impl EthereumJSONRPC.Variant def fetch_first_trace(_transactions_params, _json_rpc_named_arguments), do: :ignore + + @doc """ + Traces are not supported currently for Ganache. + + To signal to the caller that fetching is not supported, `:ignore` is returned. + """ + @impl EthereumJSONRPC.Variant + def fetch_transaction_raw_traces(_transaction_params, _json_rpc_named_arguments), do: :ignore end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex index 2033f5f95f18..d30131f1284c 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex @@ -113,6 +113,23 @@ defmodule EthereumJSONRPC.Geth do end end + @doc """ + Fetches the raw traces from the Geth trace URL. + """ + @impl EthereumJSONRPC.Variant + def fetch_transaction_raw_traces(%{hash: transaction_hash}, json_rpc_named_arguments) do + request = debug_trace_transaction_request(%{id: 0, hash_data: to_string(transaction_hash)}, false) + + case json_rpc(request, json_rpc_named_arguments) do + {:ok, traces} -> + {:ok, traces} + + {:error, error} -> + Logger.error(inspect(error)) + {:error, error} + end + end + @spec check_errors_exist(list(), %{non_neg_integer() => any()}) :: :ok | {:error, list()} def check_errors_exist(blocks_responses, id_to_params) do blocks_responses diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/nethermind.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/nethermind.ex index 1359e97f39bd..5f9118670951 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/nethermind.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/nethermind.ex @@ -59,6 +59,11 @@ defmodule EthereumJSONRPC.Nethermind do end end + @impl EthereumJSONRPC.Variant + def fetch_transaction_raw_traces(transaction_params, json_rpc_named_arguments) do + TraceReplayBlockTransactions.fetch_transaction_raw_traces(transaction_params, json_rpc_named_arguments) + end + defp block_numbers_to_params_list(block_numbers) when is_list(block_numbers) do Enum.map(block_numbers, &%{block_quantity: integer_to_quantity(&1)}) end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/rsk.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/rsk.ex index e0d1eeda0fce..baff2def36f8 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/rsk.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/rsk.ex @@ -4,7 +4,7 @@ defmodule EthereumJSONRPC.RSK do """ alias EthereumJSONRPC.RSK.Traces - alias EthereumJSONRPC.TraceBlock + alias EthereumJSONRPC.{Geth, TraceBlock} @behaviour EthereumJSONRPC.Variant @@ -18,4 +18,8 @@ defmodule EthereumJSONRPC.RSK do def fetch_beneficiaries(_, _), do: :ignore def fetch_first_trace(_transactions_params, _json_rpc_named_arguments), do: :ignore + + def fetch_transaction_raw_traces(transaction_params, json_rpc_named_arguments) do + Geth.fetch_transaction_raw_traces(transaction_params, json_rpc_named_arguments) + end end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/trace_replay_block_transactions.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/trace_replay_block_transactions.ex index 53eda23e438e..79748a2aee69 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/trace_replay_block_transactions.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/trace_replay_block_transactions.ex @@ -51,6 +51,24 @@ defmodule EthereumJSONRPC.TraceReplayBlockTransactions do end end + @doc """ + Fetches the raw traces of transaction by `trace_replayTransaction` JSON RPC method. + """ + @spec fetch_transaction_raw_traces(map(), EthereumJSONRPC.json_rpc_named_arguments()) :: + {:ok, [map()]} | {:error, any()} + def fetch_transaction_raw_traces(%{hash: transaction_hash}, json_rpc_named_arguments) do + request = trace_replay_transaction_request(%{id: 0, hash_data: to_string(transaction_hash)}) + + case json_rpc(request, json_rpc_named_arguments) do + {:ok, response} -> + {:ok, Map.get(response, "trace", [])} + + {:error, error} -> + Logger.error(inspect(error)) + {:error, error} + end + end + defp trace_replay_block_transactions_responses_to_internal_transactions_params(responses, id_to_params, traces_module) when is_list(responses) and is_map(id_to_params) do with {:ok, traces} <- trace_replay_block_transactions_responses_to_traces(responses, id_to_params) do diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/variant.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/variant.ex index d6fabf09ea43..c4991fe97c92 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/variant.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/variant.ex @@ -95,6 +95,21 @@ defmodule EthereumJSONRPC.Variant do EthereumJSONRPC.json_rpc_named_arguments() ) :: {:ok, [raw_trace_params]} | {:error, reason :: term} | :ignore + @doc """ + Fetches the raw traces from the variant of the Ethereum JSONRPC API. + + ## Returns + + * `{:ok, [raw_traces]}` - raw traces were successfully fetched for all transactions + * `{:error, reason}` - there was one or more errors with `reason` in fetching at least one of the transaction's + raw traces + * `:ignore` - the variant does not support fetching internal transactions. + """ + @callback fetch_transaction_raw_traces( + %{hash: EthereumJSONRPC.hash(), block_number: EthereumJSONRPC.block_number()}, + EthereumJSONRPC.json_rpc_named_arguments() + ) :: {:ok, [raw_trace_params]} | {:error, reason :: term} | :ignore + def get do default_variant = get_default_variant() diff --git a/apps/explorer/config/dev.exs b/apps/explorer/config/dev.exs index 47e5689bffab..71bfcbe32928 100644 --- a/apps/explorer/config/dev.exs +++ b/apps/explorer/config/dev.exs @@ -44,6 +44,8 @@ config :explorer, Explorer.Repo.Stability, timeout: :timer.seconds(80) config :explorer, Explorer.Repo.Mud, timeout: :timer.seconds(80) +config :explorer, Explorer.Repo.ShrunkInternalTransactions, timeout: :timer.seconds(80) + config :explorer, Explorer.Tracer, env: "dev", disabled?: true config :logger, :explorer, diff --git a/apps/explorer/config/prod.exs b/apps/explorer/config/prod.exs index 7af6ee216926..e27374333564 100644 --- a/apps/explorer/config/prod.exs +++ b/apps/explorer/config/prod.exs @@ -89,6 +89,11 @@ config :explorer, Explorer.Repo.Mud, timeout: :timer.seconds(60), ssl_opts: [verify: :verify_none] +config :explorer, Explorer.Repo.ShrunkInternalTransactions, + prepare: :unnamed, + timeout: :timer.seconds(60), + ssl_opts: [verify: :verify_none] + config :explorer, Explorer.Tracer, env: "production", disabled?: true config :logger, :explorer, diff --git a/apps/explorer/config/test.exs b/apps/explorer/config/test.exs index 6687e8df4df4..f47582559d25 100644 --- a/apps/explorer/config/test.exs +++ b/apps/explorer/config/test.exs @@ -67,7 +67,8 @@ for repo <- [ Explorer.Repo.BridgedTokens, Explorer.Repo.Filecoin, Explorer.Repo.Stability, - Explorer.Repo.Mud + Explorer.Repo.Mud, + Explorer.Repo.ShrunkInternalTransactions ] do config :explorer, repo, database: database, diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index 59951fae2ce7..2e7638cfd07e 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -142,7 +142,8 @@ defmodule Explorer.Application do configure(Explorer.Migrator.TransactionBlockConsensus), configure(Explorer.Migrator.TokenTransferBlockConsensus), configure(Explorer.Migrator.RestoreOmittedWETHTransfers), - configure_chain_type_dependent_process(Explorer.Chain.Cache.StabilityValidatorsCounters, :stability) + configure_chain_type_dependent_process(Explorer.Chain.Cache.StabilityValidatorsCounters, :stability), + configure(Explorer.Migrator.ShrinkInternalTransactions) ] |> List.flatten() @@ -164,7 +165,8 @@ defmodule Explorer.Application do Explorer.Repo.Arbitrum, Explorer.Repo.BridgedTokens, Explorer.Repo.Filecoin, - Explorer.Repo.Stability + Explorer.Repo.Stability, + Explorer.Repo.ShrunkInternalTransactions ] else [] diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 6af211157cce..899bc290a393 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -3047,6 +3047,19 @@ defmodule Explorer.Chain do end end + @doc """ + Fetches the raw traces of transaction. + """ + @spec fetch_transaction_raw_traces(map()) :: {:ok, [map()]} | {:error, any()} + def fetch_transaction_raw_traces(%{hash: hash, block_number: block_number}) do + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + EthereumJSONRPC.fetch_transaction_raw_traces( + %{hash: to_string(hash), block_number: block_number}, + json_rpc_named_arguments + ) + end + @doc """ Parses the revert reason from an error returned by JSON RPC node during eth_call. Returns the formatted revert reason as a hex or utf8 string. 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 d3a29f2111d5..1446ec927acf 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex @@ -119,14 +119,30 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do :valid_internal_transactions_without_first_traces_of_trivial_transactions ) end) + |> Multi.run(:maybe_shrink_internal_transactions_params, fn _, + %{ + valid_internal_transactions_without_first_traces_of_trivial_transactions: + valid_internal_transactions_without_first_traces_of_trivial_transactions + } -> + Instrumenter.block_import_stage_runner( + fn -> + maybe_shrink_internal_transactions_params( + valid_internal_transactions_without_first_traces_of_trivial_transactions + ) + end, + :block_pending, + :internal_transactions, + :maybe_shrink_internal_transactions_params + ) + end) |> Multi.run(:internal_transactions, fn repo, %{ - valid_internal_transactions_without_first_traces_of_trivial_transactions: - valid_internal_transactions_without_first_traces_of_trivial_transactions + maybe_shrink_internal_transactions_params: + shrink_internal_transactions_params } -> Instrumenter.block_import_stage_runner( fn -> - insert(repo, valid_internal_transactions_without_first_traces_of_trivial_transactions, insert_options) + insert(repo, shrink_internal_transactions_params, insert_options) end, :block_pending, :internal_transactions, @@ -434,6 +450,21 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do end end + defp maybe_shrink_internal_transactions_params(internal_transactions) do + if Application.get_env(:explorer, :shrink_internal_transactions_enabled) do + shrunk_internal_transactions = + Enum.map(internal_transactions, fn it -> + it + |> Map.delete(:output) + |> Map.replace(:input, it[:input] && Map.put(it[:input], :bytes, binary_slice(it[:input].bytes, 0, 4))) + end) + + {:ok, shrunk_internal_transactions} + else + {:ok, internal_transactions} + end + end + def defer_internal_transactions_primary_key(repo) do # Allows internal_transactions primary key to not be checked during the # DB transactions and instead be checked only at the end of it. diff --git a/apps/explorer/lib/explorer/migrator/address_current_token_balance_token_type.ex b/apps/explorer/lib/explorer/migrator/address_current_token_balance_token_type.ex index 93226db73fce..70ae0cf74124 100644 --- a/apps/explorer/lib/explorer/migrator/address_current_token_balance_token_type.ex +++ b/apps/explorer/lib/explorer/migrator/address_current_token_balance_token_type.ex @@ -18,13 +18,16 @@ defmodule Explorer.Migrator.AddressCurrentTokenBalanceTokenType do def migration_name, do: @migration_name @impl FillingMigration - def last_unprocessed_identifiers do + def last_unprocessed_identifiers(state) do limit = batch_size() * concurrency() - unprocessed_data_query() - |> select([ctb], ctb.id) - |> limit(^limit) - |> Repo.all(timeout: :infinity) + ids = + unprocessed_data_query() + |> select([ctb], ctb.id) + |> limit(^limit) + |> Repo.all(timeout: :infinity) + + {ids, state} end @impl FillingMigration diff --git a/apps/explorer/lib/explorer/migrator/address_token_balance_token_type.ex b/apps/explorer/lib/explorer/migrator/address_token_balance_token_type.ex index 9427db73ed60..f5977455692c 100644 --- a/apps/explorer/lib/explorer/migrator/address_token_balance_token_type.ex +++ b/apps/explorer/lib/explorer/migrator/address_token_balance_token_type.ex @@ -18,13 +18,16 @@ defmodule Explorer.Migrator.AddressTokenBalanceTokenType do def migration_name, do: @migration_name @impl FillingMigration - def last_unprocessed_identifiers do + def last_unprocessed_identifiers(state) do limit = batch_size() * concurrency() - unprocessed_data_query() - |> select([tb], tb.id) - |> limit(^limit) - |> Repo.all(timeout: :infinity) + ids = + unprocessed_data_query() + |> select([tb], tb.id) + |> limit(^limit) + |> Repo.all(timeout: :infinity) + + {ids, state} end @impl FillingMigration diff --git a/apps/explorer/lib/explorer/migrator/filling_migration.ex b/apps/explorer/lib/explorer/migrator/filling_migration.ex index 37d1264810a0..b150dcec3e19 100644 --- a/apps/explorer/lib/explorer/migrator/filling_migration.ex +++ b/apps/explorer/lib/explorer/migrator/filling_migration.ex @@ -4,8 +4,8 @@ defmodule Explorer.Migrator.FillingMigration do """ @callback migration_name :: String.t() - @callback unprocessed_data_query :: Ecto.Query.t() - @callback last_unprocessed_identifiers :: [any()] + @callback unprocessed_data_query :: Ecto.Query.t() | nil + @callback last_unprocessed_identifiers(map()) :: {[any()], map()} @callback update_batch([any()]) :: any() @callback update_cache :: any() @@ -51,21 +51,21 @@ defmodule Explorer.Migrator.FillingMigration do @impl true def handle_info(:migrate_batch, state) do - case last_unprocessed_identifiers() do - [] -> + case last_unprocessed_identifiers(state) do + {[], new_state} -> update_cache() MigrationStatus.set_status(migration_name(), "completed") - {:stop, :normal, state} + {:stop, :normal, new_state} - hashes -> - hashes + {identifiers, new_state} -> + identifiers |> Enum.chunk_every(batch_size()) |> Enum.map(&run_task/1) |> Task.await_many(:infinity) schedule_batch_migration() - {:noreply, state} + {:noreply, new_state} end end diff --git a/apps/explorer/lib/explorer/migrator/shrink_internal_transactions.ex b/apps/explorer/lib/explorer/migrator/shrink_internal_transactions.ex new file mode 100644 index 000000000000..414f330b674f --- /dev/null +++ b/apps/explorer/lib/explorer/migrator/shrink_internal_transactions.ex @@ -0,0 +1,63 @@ +defmodule Explorer.Migrator.ShrinkInternalTransactions do + @moduledoc """ + Removes the content of output field and leaves first 4 bytes signature in input field in internal transactions. + This migration is disabled unless SHRINK_INTERNAL_TRANSACTIONS_ENABLED env variable is set to true. + """ + + use Explorer.Migrator.FillingMigration + + import Ecto.Query + + alias Explorer.Chain.{Block, InternalTransaction} + alias Explorer.Migrator.FillingMigration + alias Explorer.Repo + + @migration_name "shrink_internal_transactions" + + @impl FillingMigration + def migration_name, do: @migration_name + + @impl FillingMigration + def last_unprocessed_identifiers(%{max_block_number: -1} = state), do: {[], state} + + def last_unprocessed_identifiers(%{max_block_number: from_block_number} = state) do + limit = batch_size() * concurrency() + to_block_number = max(from_block_number - limit + 1, 0) + + {Enum.to_list(from_block_number..to_block_number), %{state | max_block_number: to_block_number - 1}} + end + + def last_unprocessed_identifiers(state) do + query = + from( + it in InternalTransaction, + where: fragment("length(?) > 4", it.input) or not is_nil(it.output), + select: max(it.block_number) + ) + + max_block_number = Repo.one(query, timeout: :infinity) + + state + |> Map.put(:max_block_number, max_block_number || -1) + |> last_unprocessed_identifiers() + end + + @impl FillingMigration + def unprocessed_data_query, do: nil + + @impl FillingMigration + def update_batch(block_numbers) do + block_hashes_query = from(b in Block, where: b.number in ^block_numbers, select: b.hash) + + query = + from(it in InternalTransaction, + where: it.block_hash in subquery(block_hashes_query), + update: [set: [input: fragment("substring(? FOR 4)", it.input), output: nil]] + ) + + Repo.update_all(query, [], timeout: :infinity) + end + + @impl FillingMigration + def update_cache, do: :ok +end diff --git a/apps/explorer/lib/explorer/migrator/token_transfer_block_consensus.ex b/apps/explorer/lib/explorer/migrator/token_transfer_block_consensus.ex index f2660501fb98..98711870271b 100644 --- a/apps/explorer/lib/explorer/migrator/token_transfer_block_consensus.ex +++ b/apps/explorer/lib/explorer/migrator/token_transfer_block_consensus.ex @@ -17,13 +17,16 @@ defmodule Explorer.Migrator.TokenTransferBlockConsensus do def migration_name, do: @migration_name @impl FillingMigration - def last_unprocessed_identifiers do + def last_unprocessed_identifiers(state) do limit = batch_size() * concurrency() - unprocessed_data_query() - |> select([tt], {tt.transaction_hash, tt.block_hash, tt.log_index}) - |> limit(^limit) - |> Repo.all(timeout: :infinity) + ids = + unprocessed_data_query() + |> select([tt], {tt.transaction_hash, tt.block_hash, tt.log_index}) + |> limit(^limit) + |> Repo.all(timeout: :infinity) + + {ids, state} end @impl FillingMigration diff --git a/apps/explorer/lib/explorer/migrator/token_transfer_token_type.ex b/apps/explorer/lib/explorer/migrator/token_transfer_token_type.ex index bb6ee477ab96..81371d6bb2e9 100644 --- a/apps/explorer/lib/explorer/migrator/token_transfer_token_type.ex +++ b/apps/explorer/lib/explorer/migrator/token_transfer_token_type.ex @@ -18,13 +18,16 @@ defmodule Explorer.Migrator.TokenTransferTokenType do def migration_name, do: @migration_name @impl FillingMigration - def last_unprocessed_identifiers do + def last_unprocessed_identifiers(state) do limit = batch_size() * concurrency() - unprocessed_data_query() - |> select([tt], {tt.transaction_hash, tt.block_hash, tt.log_index}) - |> limit(^limit) - |> Repo.all(timeout: :infinity) + ids = + unprocessed_data_query() + |> select([tt], {tt.transaction_hash, tt.block_hash, tt.log_index}) + |> limit(^limit) + |> Repo.all(timeout: :infinity) + + {ids, state} end @impl FillingMigration diff --git a/apps/explorer/lib/explorer/migrator/transaction_block_consensus.ex b/apps/explorer/lib/explorer/migrator/transaction_block_consensus.ex index 11c55111d3e4..6024b10fba35 100644 --- a/apps/explorer/lib/explorer/migrator/transaction_block_consensus.ex +++ b/apps/explorer/lib/explorer/migrator/transaction_block_consensus.ex @@ -17,13 +17,16 @@ defmodule Explorer.Migrator.TransactionBlockConsensus do def migration_name, do: @migration_name @impl FillingMigration - def last_unprocessed_identifiers do + def last_unprocessed_identifiers(state) do limit = batch_size() * concurrency() - unprocessed_data_query() - |> select([t], t.hash) - |> limit(^limit) - |> Repo.all(timeout: :infinity) + ids = + unprocessed_data_query() + |> select([t], t.hash) + |> limit(^limit) + |> Repo.all(timeout: :infinity) + + {ids, state} end @impl FillingMigration diff --git a/apps/explorer/lib/explorer/migrator/transactions_denormalization.ex b/apps/explorer/lib/explorer/migrator/transactions_denormalization.ex index 4e8493ad0a0c..26c5e33804f5 100644 --- a/apps/explorer/lib/explorer/migrator/transactions_denormalization.ex +++ b/apps/explorer/lib/explorer/migrator/transactions_denormalization.ex @@ -18,13 +18,16 @@ defmodule Explorer.Migrator.TransactionsDenormalization do def migration_name, do: @migration_name @impl FillingMigration - def last_unprocessed_identifiers do + def last_unprocessed_identifiers(state) do limit = batch_size() * concurrency() - unprocessed_data_query() - |> select([t], t.hash) - |> limit(^limit) - |> Repo.all(timeout: :infinity) + ids = + unprocessed_data_query() + |> select([t], t.hash) + |> limit(^limit) + |> Repo.all(timeout: :infinity) + + {ids, state} end @impl FillingMigration diff --git a/apps/explorer/lib/explorer/repo.ex b/apps/explorer/lib/explorer/repo.ex index a0ae857fa4bf..08a2a924bee0 100644 --- a/apps/explorer/lib/explorer/repo.ex +++ b/apps/explorer/lib/explorer/repo.ex @@ -276,4 +276,14 @@ defmodule Explorer.Repo do ConfigHelper.init_repo_module(__MODULE__, opts) end end + + defmodule ShrunkInternalTransactions do + use Ecto.Repo, + otp_app: :explorer, + adapter: Ecto.Adapters.Postgres + + def init(_, opts) do + ConfigHelper.init_repo_module(__MODULE__, opts) + end + end end diff --git a/apps/explorer/priv/shrunk_internal_transactions/migrations/20240717080512_remove_internal_transactions_constraints.exs b/apps/explorer/priv/shrunk_internal_transactions/migrations/20240717080512_remove_internal_transactions_constraints.exs new file mode 100644 index 000000000000..05a7b5323d06 --- /dev/null +++ b/apps/explorer/priv/shrunk_internal_transactions/migrations/20240717080512_remove_internal_transactions_constraints.exs @@ -0,0 +1,18 @@ +defmodule Explorer.Repo.ShrunkInternalTransactions.Migrations.RemoveInternalTransactionsConstraints do + use Ecto.Migration + + def change do + drop(constraint(:internal_transactions, :call_has_input, check: "type != 'call' OR input IS NOT NULL")) + + drop( + constraint(:internal_transactions, :call_has_error_or_result, + check: """ + type != 'call' OR + (gas IS NOT NULL AND + ((error IS NULL AND gas_used IS NOT NULL AND output IS NOT NULL) OR + (error IS NOT NULL AND output is NULL))) + """ + ) + ) + end +end diff --git a/apps/explorer/test/test_helper.exs b/apps/explorer/test/test_helper.exs index c5eda8f3e457..fbdaa9f7c1d3 100644 --- a/apps/explorer/test/test_helper.exs +++ b/apps/explorer/test/test_helper.exs @@ -23,6 +23,7 @@ Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.BridgedTokens, :auto) Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.Filecoin, :auto) Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.Stability, :auto) Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.Mud, :auto) +Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.ShrunkInternalTransactions, :auto) Mox.defmock(Explorer.ExchangeRates.Source.TestSource, for: Explorer.ExchangeRates.Source) Mox.defmock(Explorer.Market.History.Source.Price.TestSource, for: Explorer.Market.History.Source.Price) diff --git a/apps/indexer/lib/indexer/fetcher/on_demand/first_trace.ex b/apps/indexer/lib/indexer/fetcher/on_demand/first_trace.ex index 4dd280df9fe1..4a5bf1187a3f 100644 --- a/apps/indexer/lib/indexer/fetcher/on_demand/first_trace.ex +++ b/apps/indexer/lib/indexer/fetcher/on_demand/first_trace.ex @@ -7,11 +7,22 @@ defmodule Indexer.Fetcher.OnDemand.FirstTrace do use Indexer.Fetcher, restart: :permanent alias Explorer.Chain - alias Explorer.Chain.Import + alias Explorer.Chain.{Import, InternalTransaction} alias Explorer.Chain.Import.Runner.InternalTransactions require Logger + def maybe_trigger_fetch(transaction, opts \\ []) do + unless Application.get_env(:explorer, :shrink_internal_transactions_enabled) do + transaction.hash + |> InternalTransaction.all_transaction_to_internal_transactions(opts) + |> Enum.any?(&(&1.index == 0)) + |> unless do + trigger_fetch(transaction) + end + end + end + def trigger_fetch(transaction) do GenServer.cast(__MODULE__, {:fetch, transaction}) end diff --git a/config/config_helper.exs b/config/config_helper.exs index eb7746d81f6e..ffaf0ad0dbcb 100644 --- a/config/config_helper.exs +++ b/config/config_helper.exs @@ -29,7 +29,8 @@ defmodule ConfigHelper do ext_repos = [ {parse_bool_env_var("BRIDGED_TOKENS_ENABLED"), Explorer.Repo.BridgedTokens}, - {parse_bool_env_var("MUD_INDEXER_ENABLED"), Explorer.Repo.Mud} + {parse_bool_env_var("MUD_INDEXER_ENABLED"), Explorer.Repo.Mud}, + {parse_bool_env_var("SHRINK_INTERNAL_TRANSACTIONS_ENABLED"), Explorer.Repo.ShrunkInternalTransactions} ] |> Enum.filter(&elem(&1, 0)) |> Enum.map(&elem(&1, 1)) diff --git a/config/runtime.exs b/config/runtime.exs index bfdd4bbf84cf..4bbb936c3d7f 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -238,7 +238,8 @@ config :explorer, checksum_function: checksum_function && String.to_atom(checksum_function), elasticity_multiplier: ConfigHelper.parse_integer_env_var("EIP_1559_ELASTICITY_MULTIPLIER", 2), base_fee_max_change_denominator: ConfigHelper.parse_integer_env_var("EIP_1559_BASE_FEE_MAX_CHANGE_DENOMINATOR", 8), - csv_export_limit: ConfigHelper.parse_integer_env_var("CSV_EXPORT_LIMIT", 10_000) + csv_export_limit: ConfigHelper.parse_integer_env_var("CSV_EXPORT_LIMIT", 10_000), + shrink_internal_transactions_enabled: ConfigHelper.parse_bool_env_var("SHRINK_INTERNAL_TRANSACTIONS_ENABLED") config :explorer, :proxy, caching_implementation_data_enabled: true, @@ -613,6 +614,11 @@ config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, batch_size: ConfigHelper.parse_integer_env_var("MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_BATCH_SIZE", 50), timeout: ConfigHelper.parse_time_env_var("MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_TIMEOUT", "250ms") +config :explorer, Explorer.Migrator.ShrinkInternalTransactions, + enabled: ConfigHelper.parse_bool_env_var("SHRINK_INTERNAL_TRANSACTIONS_ENABLED"), + batch_size: ConfigHelper.parse_integer_env_var("SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE", 1000), + concurrency: ConfigHelper.parse_integer_env_var("SHRINK_INTERNAL_TRANSACTIONS_CONCURRENCY", 1) + config :explorer, Explorer.Chain.BridgedToken, eth_omni_bridge_mediator: System.get_env("BRIDGED_TOKENS_ETH_OMNI_BRIDGE_MEDIATOR"), bsc_omni_bridge_mediator: System.get_env("BRIDGED_TOKENS_BSC_OMNI_BRIDGE_MEDIATOR"), diff --git a/config/runtime/dev.exs b/config/runtime/dev.exs index b4ce37657a90..cdd35cc636c4 100644 --- a/config/runtime/dev.exs +++ b/config/runtime/dev.exs @@ -192,6 +192,13 @@ config :explorer, Explorer.Repo.Mud, pool_size: ConfigHelper.parse_integer_env_var("MUD_POOL_SIZE", 10), queue_target: queue_target +# Configures ShrunkInternalTransactions database +config :explorer, Explorer.Repo.ShrunkInternalTransactions, + database: database, + hostname: hostname, + url: System.get_env("DATABASE_URL"), + pool_size: 1 + variant = Variant.get() Code.require_file("#{variant}.exs", "apps/explorer/config/dev") diff --git a/config/runtime/prod.exs b/config/runtime/prod.exs index 6f240fee4848..2b10f79f287c 100644 --- a/config/runtime/prod.exs +++ b/config/runtime/prod.exs @@ -151,6 +151,14 @@ config :explorer, Explorer.Repo.Mud, ssl: ExplorerConfigHelper.ssl_enabled?(), queue_target: queue_target +# Configures ShrunkInternalTransactions database +config :explorer, Explorer.Repo.ShrunkInternalTransactions, + url: System.get_env("DATABASE_URL"), + # actually this repo is not started, and its pool size remains unused. + # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type + pool_size: 1, + ssl: ExplorerConfigHelper.ssl_enabled?() + variant = Variant.get() Code.require_file("#{variant}.exs", "apps/explorer/config/prod") diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 0ecb6367e569..df233cddedf9 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -426,3 +426,6 @@ TENDERLY_CHAIN_PATH= # PUBLIC_METRICS_ENABLED= # PUBLIC_METRICS_UPDATE_PERIOD_HOURS= # CSV_EXPORT_LIMIT= +# SHRINK_INTERNAL_TRANSACTIONS_ENABLED= +# SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE= +# SHRINK_INTERNAL_TRANSACTIONS_CONCURRENCY= From c4a13a726e2806ba1211749fc68f3f86e227b2d8 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 16 Aug 2024 15:58:52 +0300 Subject: [PATCH 081/363] feat: Support smart-contract verification in zkSync (#10500) * db changes for zksync smart-contract verification support * Update SmartContract model * Pass zk_compiler_version value in API v1/v2 endpoints * Extend sc verification config with config for zk compilers * Fix constructor arguments saving * Enhance api/v2/smart-contracts/verification/config for zkSync * Process review comments * creationMatch -> runtimeMatch * Fix merging conflicts * Finishing touches --- .github/workflows/config.yml | 2 +- .../api/rpc/contract_controller.ex | 10 +- .../api/v2/verification_controller.ex | 77 +- .../block_scout_web/graphql/schema/types.ex | 65 +- .../routers/smart_contracts_api_v2_router.ex | 15 +- .../views/api/rpc/contract_view.ex | 37 +- .../views/api/v2/smart_contract_view.ex | 55 +- .../api/rpc/contract_controller_test.exs | 78 +- .../api/v2/smart_contract_controller_test.exs | 1549 +++++------ .../api/v2/verification_controller_test.exs | 570 ++-- .../lib/explorer/chain/smart_contract.ex | 246 +- .../lib/explorer/chain_spec/genesis_data.ex | 1 + .../smart_contract/compiler_version.ex | 53 +- .../lib/explorer/smart_contract/helper.ex | 29 +- .../rust_verifier_interface_behaviour.ex | 60 +- .../smart_contract/solidity/publisher.ex | 161 +- .../smart_contract/solidity/verifier.ex | 26 +- .../third_party_integrations/sourcify.ex | 10 +- ...zk_compiler_version_to_smart_contracts.exs | 10 + .../solidity/publisher_test.exs | 410 +-- .../smart_contract/solidity/verifier_test.exs | 2364 +++++++++-------- .../smart_contract/vyper/publisher_test.exs | 114 +- apps/explorer/test/support/factory.ex | 8 +- 23 files changed, 3215 insertions(+), 2735 deletions(-) create mode 100644 apps/explorer/priv/zk_sync/migrations/20240716095237_add_zk_compiler_version_to_smart_contracts.exs diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml index c02cc24da1ac..33237b7202aa 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/config.yml @@ -49,7 +49,7 @@ jobs: // Add/remove CI matrix chain types here const defaultChainTypes = ["default"]; - const chainTypes = ["ethereum", "polygon_zkevm", "rsk", "stability", "filecoin", "optimism", "arbitrum", "celo", "zetachain"]; + const chainTypes = ["ethereum", "polygon_zkevm", "rsk", "stability", "filecoin", "optimism", "arbitrum", "celo", "zetachain", "zksync", "shibarium"]; const extraChainTypes = ["suave", "polygon_edge"]; // Chain type matrix we use in master branch diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex index c37f0c4e4bf4..e9d113a205e1 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex @@ -17,6 +17,12 @@ defmodule BlockScoutWeb.API.RPC.ContractController do alias Explorer.ThirdPartyIntegrations.Sourcify import BlockScoutWeb.API.V2.AddressController, only: [validate_address: 2, validate_address: 3] + if Application.compile_env(:explorer, :chain_type) == :zksync do + @optimization_runs "0" + else + @optimization_runs 200 + end + @smth_went_wrong "Something went wrong while publishing the contract" @verified "Smart-contract already verified." @invalid_address "Invalid address hash" @@ -661,7 +667,7 @@ defmodule BlockScoutWeb.API.RPC.ContractController do {:ok, Map.put(opts, "optimization_runs", runs_int)} _ -> - {:ok, Map.put(opts, "optimization_runs", 200)} + {:ok, Map.put(opts, "optimization_runs", @optimization_runs)} end end @@ -670,7 +676,7 @@ defmodule BlockScoutWeb.API.RPC.ContractController do end defp parse_optimization_runs({:ok, opts}) do - {:ok, Map.put(opts, "optimization_runs", 200)} + {:ok, Map.put(opts, "optimization_runs", @optimization_runs)} end defp parse_optimization_runs(other), do: other diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex index cb040711a856..94cfb3b6c202 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex @@ -18,12 +18,43 @@ defmodule BlockScoutWeb.API.V2.VerificationController do @api_true [api?: true] @sc_verification_started "Smart-contract verification started" + @zk_optimization_modes ["0", "1", "2", "3", "s", "z"] + + if Application.compile_env(:explorer, :chain_type) == :zksync do + @optimization_runs "0" + else + @optimization_runs 200 + end def config(conn, _params) do solidity_compiler_versions = CompilerVersion.fetch_version_list(:solc) vyper_compiler_versions = CompilerVersion.fetch_version_list(:vyper) - verification_options = + verification_options = get_verification_options() + + base_config = + %{ + solidity_evm_versions: CodeCompiler.evm_versions(:solidity), + solidity_compiler_versions: solidity_compiler_versions, + vyper_compiler_versions: vyper_compiler_versions, + verification_options: verification_options, + vyper_evm_versions: CodeCompiler.evm_versions(:vyper), + is_rust_verifier_microservice_enabled: RustVerifierInterface.enabled?(), + license_types: Enum.into(SmartContract.license_types_enum(), %{}) + } + + config = + base_config + |> maybe_add_zk_options() + + conn + |> json(config) + end + + defp get_verification_options do + if Application.get_env(:explorer, :chain_type) == :zksync do + ["standard-input"] + else ["flattened-code", "standard-input", "vyper-code"] |> (&if(Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled], do: ["sourcify" | &1], @@ -33,17 +64,19 @@ defmodule BlockScoutWeb.API.V2.VerificationController do do: ["multi-part", "vyper-multi-part", "vyper-standard-input"] ++ &1, else: &1 )).() + end + end - conn - |> json(%{ - solidity_evm_versions: CodeCompiler.evm_versions(:solidity), - solidity_compiler_versions: solidity_compiler_versions, - vyper_compiler_versions: vyper_compiler_versions, - verification_options: verification_options, - vyper_evm_versions: CodeCompiler.evm_versions(:vyper), - is_rust_verifier_microservice_enabled: RustVerifierInterface.enabled?(), - license_types: Enum.into(SmartContract.license_types_enum(), %{}) - }) + defp maybe_add_zk_options(config) do + if Application.get_env(:explorer, :chain_type) == :zksync do + zk_compiler_versions = CompilerVersion.fetch_version_list(:zk) + + config + |> Map.put(:zk_compiler_versions, zk_compiler_versions) + |> Map.put(:zk_optimization_modes, @zk_optimization_modes) + else + config + end end def verification_via_flattened_code( @@ -62,7 +95,7 @@ defmodule BlockScoutWeb.API.V2.VerificationController do } |> Map.put("optimization", Map.get(params, "is_optimization_enabled", false)) |> (&if(params |> Map.get("is_optimization_enabled", false) |> parse_boolean(), - do: Map.put(&1, "optimization_runs", Map.get(params, "optimization_runs", 200)), + do: Map.put(&1, "optimization_runs", Map.get(params, "optimization_runs", @optimization_runs)), else: &1 )).() |> Map.put("evm_version", Map.get(params, "evm_version", "default")) @@ -89,15 +122,27 @@ defmodule BlockScoutWeb.API.V2.VerificationController do Logger.info("API v2 smart-contract #{address_hash_string} verification via standard json input") with {:json_input, json_input} <- validate_params_standard_json_input(params) do + constructor_arguments = + if Application.get_env(:explorer, :chain_type) == :zksync do + zksync_get_constructor_arguments(address_hash_string) + else + Map.get(params, "constructor_args", "") + end + verification_params = %{ "address_hash" => String.downcase(address_hash_string), "compiler_version" => compiler_version } |> Map.put("autodetect_constructor_args", Map.get(params, "autodetect_constructor_args", true)) - |> Map.put("constructor_arguments", Map.get(params, "constructor_args", "")) + # + |> Map.put("constructor_arguments", constructor_arguments) |> Map.put("name", Map.get(params, "contract_name", "")) |> Map.put("license_type", Map.get(params, "license_type")) + |> (&if(Application.get_env(:explorer, :chain_type) == :zksync, + do: Map.put(&1, "zk_compiler_version", Map.get(params, "zk_compiler_version")), + else: &1 + )).() log_sc_verification_started(address_hash_string) Que.add(SolidityPublisherWorker, {"json_api_v2", verification_params, json_input}) @@ -150,7 +195,7 @@ defmodule BlockScoutWeb.API.V2.VerificationController do } |> Map.put("optimization", Map.get(params, "is_optimization_enabled", false)) |> (&if(params |> Map.get("is_optimization_enabled", false) |> parse_boolean(), - do: Map.put(&1, "optimization_runs", Map.get(params, "optimization_runs", 200)), + do: Map.put(&1, "optimization_runs", Map.get(params, "optimization_runs", @optimization_runs)), else: &1 )).() |> Map.put("evm_version", Map.get(params, "evm_version", "default")) @@ -275,6 +320,10 @@ defmodule BlockScoutWeb.API.V2.VerificationController do end end + defp zksync_get_constructor_arguments(address_hash_string) do + Chain.contract_creation_input_data(address_hash_string) + end + # sobelow_skip ["Traversal.FileModule"] defp validate_params_standard_json_input(%{"files" => files} = params) do with :validated <- validate_address(params), diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex b/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex index 22b1d26e8239..192d38d2f170 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex @@ -1,6 +1,49 @@ +defmodule BlockScoutWeb.GraphQL.Schema.SmartContracts do + @moduledoc false + case Application.compile_env(:explorer, :chain_type) do + :zksync -> + @chain_type_fields quote( + do: [ + field(:optimization_runs, :string), + field(:zk_compiler_version, :string) + ] + ) + + _ -> + @chain_type_fields quote(do: [field(:optimization_runs, :integer)]) + end + + defmacro generate do + quote do + object :smart_contract do + field(:name, :string) + field(:compiler_version, :string) + field(:optimization, :boolean) + field(:contract_source_code, :string) + field(:abi, :json) + field(:address_hash, :address_hash) + field(:constructor_arguments, :string) + field(:evm_version, :string) + field(:external_libraries, :json) + field(:verified_via_sourcify, :boolean) + field(:partially_verified, :boolean) + field(:file_path, :string) + field(:is_vyper_contract, :boolean) + field(:is_changed_bytecode, :boolean) + field(:compiler_settings, :json) + field(:verified_via_eth_bytecode_db, :boolean) + + unquote_splicing(@chain_type_fields) + end + end + end +end + defmodule BlockScoutWeb.GraphQL.Schema.Types do @moduledoc false + require BlockScoutWeb.GraphQL.Schema.SmartContracts + use Absinthe.Schema.Notation use Absinthe.Relay.Schema.Notation, :modern @@ -11,6 +54,8 @@ defmodule BlockScoutWeb.GraphQL.Schema.Types do Transaction } + alias BlockScoutWeb.GraphQL.Schema.SmartContracts, as: SmartContractsSchema + import_types(Absinthe.Type.Custom) import_types(BlockScoutWeb.GraphQL.Schema.Scalars) @@ -100,25 +145,7 @@ defmodule BlockScoutWeb.GraphQL.Schema.Types do blockchain." http://solidity.readthedocs.io/en/v0.4.24/introduction-to-smart-contracts.html """ - object :smart_contract do - field(:name, :string) - field(:compiler_version, :string) - field(:optimization, :boolean) - field(:contract_source_code, :string) - field(:abi, :json) - field(:address_hash, :address_hash) - field(:constructor_arguments, :string) - field(:optimization_runs, :integer) - field(:evm_version, :string) - field(:external_libraries, :json) - field(:verified_via_sourcify, :boolean) - field(:partially_verified, :boolean) - field(:file_path, :string) - field(:is_vyper_contract, :boolean) - field(:is_changed_bytecode, :boolean) - field(:compiler_settings, :json) - field(:verified_via_eth_bytecode_db, :boolean) - end + SmartContractsSchema.generate() @desc """ Represents a token transfer between addresses. diff --git a/apps/block_scout_web/lib/block_scout_web/routers/smart_contracts_api_v2_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/smart_contracts_api_v2_router.ex index 1a0e8922cb51..479c83d24785 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/smart_contracts_api_v2_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/smart_contracts_api_v2_router.ex @@ -69,12 +69,15 @@ defmodule BlockScoutWeb.Routers.SmartContractsApiV2Router do scope "/:address_hash/verification/via", as: :api_v2 do pipe_through(:api_v2_no_forgery_protect) - post("/flattened-code", V2.VerificationController, :verification_via_flattened_code) post("/standard-input", V2.VerificationController, :verification_via_standard_input) - post("/sourcify", V2.VerificationController, :verification_via_sourcify) - post("/multi-part", V2.VerificationController, :verification_via_multi_part) - post("/vyper-code", V2.VerificationController, :verification_via_vyper_code) - post("/vyper-multi-part", V2.VerificationController, :verification_via_vyper_multipart) - post("/vyper-standard-input", V2.VerificationController, :verification_via_vyper_standard_input) + + if Application.compile_env(:explorer, :chain_type) !== :zksync do + post("/flattened-code", V2.VerificationController, :verification_via_flattened_code) + post("/sourcify", V2.VerificationController, :verification_via_sourcify) + post("/multi-part", V2.VerificationController, :verification_via_multi_part) + post("/vyper-code", V2.VerificationController, :verification_via_vyper_code) + post("/vyper-multi-part", V2.VerificationController, :verification_via_vyper_multipart) + post("/vyper-standard-input", V2.VerificationController, :verification_via_vyper_standard_input) + end end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex index 6891c66a2b4a..93a341379289 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex @@ -174,6 +174,16 @@ defmodule BlockScoutWeb.API.RPC.ContractView do |> Map.put_new(:EVMVersion, Map.get(contract, :evm_version, "")) |> Map.put_new(:FileName, Map.get(contract, :file_path, "") || "") |> insert_additional_sources(address) + |> add_zksync_info(contract) + end + end + + defp add_zksync_info(smart_contract_info, contract) do + if Application.get_env(:explorer, :chain_type) == :zksync do + smart_contract_info + |> Map.put_new(:ZkCompilerVersion, Map.get(contract, :zk_compiler_version, "")) + else + smart_contract_info end end @@ -216,13 +226,26 @@ defmodule BlockScoutWeb.API.RPC.ContractView do hash: hash, smart_contract: %SmartContract{} = contract }) do - %{ - "Address" => to_string(hash), - "ABI" => Jason.encode!(contract.abi), - "ContractName" => contract.name, - "CompilerVersion" => contract.compiler_version, - "OptimizationUsed" => if(contract.optimization, do: "1", else: "0") - } + smart_contract_info = + %{ + "Address" => to_string(hash), + "ABI" => Jason.encode!(contract.abi), + "ContractName" => contract.name, + "CompilerVersion" => contract.compiler_version, + "OptimizationUsed" => if(contract.optimization, do: "1", else: "0") + } + + smart_contract_info + |> merge_zksync_info(contract) + end + + defp merge_zksync_info(smart_contract_info, contract) do + if Application.get_env(:explorer, :chain_type) == :zksync do + smart_contract_info + |> Map.merge(%{"ZkCompilerVersion" => contract.zk_compiler_version}) + else + smart_contract_info + end end defp latest_decompiled_smart_contract(%NotLoaded{}), do: nil diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex index 561788f41f1b..c653d40c7865 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex @@ -240,6 +240,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do "is_blueprint" => if(smart_contract.is_blueprint, do: smart_contract.is_blueprint, else: false) } |> Map.merge(bytecode_info(address)) + |> add_zksync_info(target_contract) end def prepare_smart_contract(address, implementations, proxy_type, conn) do @@ -291,6 +292,16 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do end end + defp add_zksync_info(smart_contract_info, target_contract) do + if Application.get_env(:explorer, :chain_type) == :zksync do + Map.merge(smart_contract_info, %{ + "zk_compiler_version" => target_contract.zk_compiler_version + }) + else + smart_contract_info + end + end + defp prepare_external_libraries(libraries) when is_list(libraries) do Enum.map(libraries, fn %Explorer.Chain.SmartContract.ExternalLibrary{name: name, address_hash: address_hash} -> {:ok, hash} = Chain.string_to_address_hash(address_hash) @@ -338,26 +349,30 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do defp prepare_smart_contract_for_list(%SmartContract{} = smart_contract) do token = smart_contract.address.token - %{ - "address" => - Helper.address_with_info( - nil, - %Address{smart_contract.address | smart_contract: smart_contract}, - smart_contract.address.hash, - false - ), - "compiler_version" => smart_contract.compiler_version, - "optimization_enabled" => smart_contract.optimization, - "tx_count" => smart_contract.address.transactions_count, - "language" => smart_contract_language(smart_contract), - "verified_at" => smart_contract.inserted_at, - "market_cap" => token && token.circulating_market_cap, - "has_constructor_args" => !is_nil(smart_contract.constructor_arguments), - "coin_balance" => - if(smart_contract.address.fetched_coin_balance, do: smart_contract.address.fetched_coin_balance.value), - "license_type" => smart_contract.license_type, - "certified" => if(smart_contract.certified, do: smart_contract.certified, else: false) - } + smart_contract_info = + %{ + "address" => + Helper.address_with_info( + nil, + %Address{smart_contract.address | smart_contract: smart_contract}, + smart_contract.address.hash, + false + ), + "compiler_version" => smart_contract.compiler_version, + "optimization_enabled" => smart_contract.optimization, + "tx_count" => smart_contract.address.transactions_count, + "language" => smart_contract_language(smart_contract), + "verified_at" => smart_contract.inserted_at, + "market_cap" => token && token.circulating_market_cap, + "has_constructor_args" => !is_nil(smart_contract.constructor_arguments), + "coin_balance" => + if(smart_contract.address.fetched_coin_balance, do: smart_contract.address.fetched_coin_balance.value), + "license_type" => smart_contract.license_type, + "certified" => if(smart_contract.certified, do: smart_contract.certified, else: false) + } + + smart_contract_info + |> add_zksync_info(smart_contract) end defp smart_contract_language(smart_contract) do diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs index 63006359924b..7812d118810f 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs @@ -7,6 +7,12 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do setup :verify_on_exit! + if Application.compile_env(:explorer, :chain_type) == :zksync do + @optimization_runs "0" + else + @optimization_runs 200 + end + def prepare_contracts do insert(:contract_address) {:ok, dt_1, _} = DateTime.from_iso8601("2022-09-20 10:00:00Z") @@ -108,7 +114,11 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["message"] == "OK" assert response["status"] == "1" - assert response["result"] == [result(contract)] + result_props = result(contract) |> Map.keys() + + for prop <- result_props do + assert Enum.at(response["result"], 0)[prop] == result(contract)[prop] + end assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end @@ -179,7 +189,11 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["message"] == "OK" assert response["status"] == "1" - assert response["result"] == [result(contract)] + result_props = result(contract) |> Map.keys() + + for prop <- result_props do + assert Enum.at(response["result"], 0)[prop] == result(contract)[prop] + end assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end @@ -204,7 +218,11 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["message"] == "OK" assert response["status"] == "1" - assert response["result"] == [result(contract_2)] + result_props = result(contract_2) |> Map.keys() + + for prop <- result_props do + assert Enum.at(response["result"], 0)[prop] == result(contract_2)[prop] + end assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end @@ -229,7 +247,12 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["message"] == "OK" assert response["status"] == "1" - assert response["result"] == [result(contract_2), result(contract_3)] + result_props = result(contract_2) |> Map.keys() + + for prop <- result_props do + assert Enum.at(response["result"], 0)[prop] == result(contract_2)[prop] + assert Enum.at(response["result"], 1)[prop] == result(contract_3)[prop] + end assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end @@ -254,7 +277,12 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert response["message"] == "OK" assert response["status"] == "1" - assert response["result"] == [result(contract_1), result(contract_2)] + result_props = result(contract_1) |> Map.keys() + + for prop <- result_props do + assert Enum.at(response["result"], 0)[prop] == result(contract_1)[prop] + assert Enum.at(response["result"], 1)[prop] == result(contract_2)[prop] + end assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) end @@ -497,7 +525,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do contract = insert(:smart_contract, optimization: true, - optimization_runs: 200, + optimization_runs: @optimization_runs, evm_version: "default", contract_code_md5: "123" ) @@ -519,7 +547,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do # for `OptimizationUsed` is "1". If it was false, the expected value # would be "0". "OptimizationUsed" => "true", - "OptimizationRuns" => 200, + "OptimizationRuns" => @optimization_runs, "EVMVersion" => "default", "FileName" => "", "IsProxy" => "false" @@ -533,7 +561,12 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do |> get("/api", params) |> json_response(200) - assert response["result"] == expected_result + result_props = Enum.at(expected_result, 0) |> Map.keys() + + for prop <- result_props do + assert Enum.at(response["result"], 0)[prop] == Enum.at(expected_result, 0)[prop] + end + assert response["status"] == "1" assert response["message"] == "OK" assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response) @@ -751,7 +784,12 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do } ] - assert response["result"] == expected_result + result_props = Enum.at(expected_result, 0) |> Map.keys() + + for prop <- result_props do + assert Enum.at(response["result"], 0)[prop] == Enum.at(expected_result, 0)[prop] + end + assert response["status"] == "1" assert response["message"] == "OK" assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response) @@ -761,7 +799,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do contract = insert(:smart_contract, optimization: true, - optimization_runs: 200, + optimization_runs: @optimization_runs, evm_version: "default", constructor_arguments: "00000000000000000000000008e7592ce0d7ebabf42844b62ee6a878d4e1913e000000000000000000000000e1b6037da5f1d756499e184ca15254a981c92546", @@ -782,7 +820,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "ContractName" => contract.name, "CompilerVersion" => contract.compiler_version, "OptimizationUsed" => "true", - "OptimizationRuns" => 200, + "OptimizationRuns" => @optimization_runs, "EVMVersion" => "default", "ConstructorArguments" => "00000000000000000000000008e7592ce0d7ebabf42844b62ee6a878d4e1913e000000000000000000000000e1b6037da5f1d756499e184ca15254a981c92546", @@ -798,7 +836,12 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do |> get("/api", params) |> json_response(200) - assert response["result"] == expected_result + result_props = Enum.at(expected_result, 0) |> Map.keys() + + for prop <- result_props do + assert Enum.at(response["result"], 0)[prop] == Enum.at(expected_result, 0)[prop] + end + assert response["status"] == "1" assert response["message"] == "OK" assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response) @@ -859,7 +902,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do } ], optimization: true, - optimization_runs: 200, + optimization_runs: @optimization_runs, evm_version: "default" } @@ -884,7 +927,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do "ContractName" => contract.name, "CompilerVersion" => contract.compiler_version, "OptimizationUsed" => "true", - "OptimizationRuns" => 200, + "OptimizationRuns" => @optimization_runs, "EVMVersion" => "default", "ExternalLibraries" => [ %{"name" => "Test", "address_hash" => "0xb18aed9518d735482badb4e8b7fd8d2ba425ce95"}, @@ -902,7 +945,12 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do |> get("/api", params) |> json_response(200) - assert response["result"] == expected_result + result_props = Enum.at(expected_result, 0) |> Map.keys() + + for prop <- result_props do + assert Enum.at(response["result"], 0)[prop] == Enum.at(expected_result, 0)[prop] + end + assert response["status"] == "1" assert response["message"] == "OK" assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs index a7d01ba118c9..03534093c5fd 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs @@ -176,7 +176,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(target_contract.address_hash)}") response = json_response(request, 200) - assert correct_response == response + result_props = correct_response |> Map.keys() + + for prop <- result_props do + assert correct_response[prop] == response[prop] + end end test "get smart-contract with decoded constructor", %{conn: conn} do @@ -282,7 +286,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(target_contract.address_hash)}") response = json_response(request, 200) - assert correct_response == response + result_props = correct_response |> Map.keys() + + for prop <- result_props do + assert correct_response[prop] == response[prop] + end end test "get smart-contract data from bytecode twin without constructor args", %{conn: conn} do @@ -390,7 +398,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") response = json_response(request, 200) - assert correct_response == response + result_props = correct_response |> Map.keys() + + for prop <- result_props do + assert correct_response[prop] == response[prop] + end end test "doesn't get smart-contract multiple additional sources from EIP-1167 implementation", %{conn: conn} do @@ -480,7 +492,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(proxy_address.hash)}") response = json_response(request, 200) - assert correct_response == response + result_props = correct_response |> Map.keys() + + for prop <- result_props do + assert correct_response[prop] == response[prop] + end end test "get smart-contract which is blueprint", %{conn: conn} do @@ -550,7 +566,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(target_contract.address_hash)}") response = json_response(request, 200) - assert correct_response == response + result_props = correct_response |> Map.keys() + + for prop <- result_props do + assert correct_response[prop] == response[prop] + end end end @@ -634,947 +654,954 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(proxy_address.hash)}") response = json_response(request, 200) - assert correct_response == response - end + result_props = correct_response |> Map.keys() - describe "/smart-contracts/{address_hash} <> eth_bytecode_db" do - setup do - old_interval_env = Application.get_env(:explorer, Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand) + for prop <- result_props do + assert correct_response[prop] == response[prop] + end + end - :ok + if Application.compile_env(:explorer, :chain_type) !== :zksync do + describe "/smart-contracts/{address_hash} <> eth_bytecode_db" do + setup do + old_interval_env = Application.get_env(:explorer, Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand) - on_exit(fn -> - Application.put_env(:explorer, Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand, old_interval_env) - end) - end + :ok - test "automatically verify contract", %{conn: conn} do - {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) - old_chain_id = Application.get_env(:block_scout_web, :chain_id) + on_exit(fn -> + Application.put_env(:explorer, Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand, old_interval_env) + end) + end - Application.put_env(:block_scout_web, :chain_id, 5) + test "automatically verify contract", %{conn: conn} do + {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) + old_chain_id = Application.get_env(:block_scout_web, :chain_id) - bypass = Bypass.open() - eth_bytecode_response = File.read!("./test/support/fixture/smart_contract/eth_bytecode_db_search_response.json") + Application.put_env(:block_scout_web, :chain_id, 5) - old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) + bypass = Bypass.open() + eth_bytecode_response = File.read!("./test/support/fixture/smart_contract/eth_bytecode_db_search_response.json") - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, - service_url: "http://localhost:#{bypass.port}", - enabled: true, - type: "eth_bytecode_db", - eth_bytecode_db?: true - ) + old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) - address = insert(:contract_address) + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, + service_url: "http://localhost:#{bypass.port}", + enabled: true, + type: "eth_bytecode_db", + eth_bytecode_db?: true + ) - insert(:transaction, - created_contract_address_hash: address.hash, - input: - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" - ) - |> with_block() + address = insert(:contract_address) - topic = "addresses:#{address.hash}" + insert(:transaction, + created_contract_address_hash: address.hash, + input: + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" + ) + |> with_block() - {:ok, _reply, _socket} = - BlockScoutWeb.UserSocketV2 - |> socket("no_id", %{}) - |> subscribe_and_join(topic) + topic = "addresses:#{address.hash}" - Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search_all", fn conn -> - Conn.resp(conn, 200, eth_bytecode_response) - end) + {:ok, _reply, _socket} = + BlockScoutWeb.UserSocketV2 + |> socket("no_id", %{}) + |> subscribe_and_join(topic) - TestHelper.get_eip1967_implementation_error_response() + Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search_all", fn conn -> + Conn.resp(conn, 200, eth_bytecode_response) + end) - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + TestHelper.get_eip1967_implementation_error_response() - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "eth_bytecode_db_lookup_started", - topic: ^topic - }, - :timer.seconds(1) + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "smart_contract_was_verified", - topic: ^topic - }, - :timer.seconds(1) + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "eth_bytecode_db_lookup_started", + topic: ^topic + }, + :timer.seconds(1) - response = json_response(request, 200) + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "smart_contract_was_verified", + topic: ^topic + }, + :timer.seconds(1) - assert response == - %{ - "proxy_type" => nil, - "implementations" => [], - "has_custom_methods_read" => false, - "has_custom_methods_write" => false, - "is_self_destructed" => false, - "deployed_bytecode" => to_string(address.contract_code), - "creation_bytecode" => - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" - } + response = json_response(request, 200) - TestHelper.get_eip1967_implementation_zero_addresses() + assert response == + %{ + "proxy_type" => nil, + "implementations" => [], + "has_custom_methods_read" => false, + "has_custom_methods_write" => false, + "is_self_destructed" => false, + "deployed_bytecode" => to_string(address.contract_code), + "creation_bytecode" => + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" + } - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - assert response = json_response(request, 200) - assert %{"is_verified" => true} = response - assert %{"is_verified_via_eth_bytecode_db" => true} = response - assert %{"is_partially_verified" => true} = response - assert %{"is_fully_verified" => false} = response - - Application.put_env(:block_scout_web, :chain_id, old_chain_id) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) - Bypass.down(bypass) - GenServer.stop(pid) - end + TestHelper.get_eip1967_implementation_zero_addresses() - test "automatically verify contract using search-all (ethBytecodeDbSources) endpoint", %{conn: conn} do - {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) - old_chain_id = Application.get_env(:block_scout_web, :chain_id) + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + assert response = json_response(request, 200) + assert %{"is_verified" => true} = response + assert %{"is_verified_via_eth_bytecode_db" => true} = response + assert %{"is_partially_verified" => true} = response + assert %{"is_fully_verified" => false} = response - Application.put_env(:block_scout_web, :chain_id, 5) + Application.put_env(:block_scout_web, :chain_id, old_chain_id) + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) + Bypass.down(bypass) + GenServer.stop(pid) + end - bypass = Bypass.open() + test "automatically verify contract using search-all (ethBytecodeDbSources) endpoint", %{conn: conn} do + {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) + old_chain_id = Application.get_env(:block_scout_web, :chain_id) - eth_bytecode_response = - File.read!("./test/support/fixture/smart_contract/eth_bytecode_db_search_all_local_sources_response.json") + Application.put_env(:block_scout_web, :chain_id, 5) - old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) + bypass = Bypass.open() - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, - service_url: "http://localhost:#{bypass.port}", - enabled: true, - type: "eth_bytecode_db", - eth_bytecode_db?: true - ) + eth_bytecode_response = + File.read!("./test/support/fixture/smart_contract/eth_bytecode_db_search_all_local_sources_response.json") - address = insert(:contract_address) + old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) - insert(:transaction, - created_contract_address_hash: address.hash, - input: - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" - ) - |> with_block() + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, + service_url: "http://localhost:#{bypass.port}", + enabled: true, + type: "eth_bytecode_db", + eth_bytecode_db?: true + ) - topic = "addresses:#{address.hash}" + address = insert(:contract_address) - {:ok, _reply, _socket} = - BlockScoutWeb.UserSocketV2 - |> socket("no_id", %{}) - |> subscribe_and_join(topic) + insert(:transaction, + created_contract_address_hash: address.hash, + input: + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" + ) + |> with_block() - Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search_all", fn conn -> - Conn.resp(conn, 200, eth_bytecode_response) - end) + topic = "addresses:#{address.hash}" - TestHelper.get_eip1967_implementation_error_response() + {:ok, _reply, _socket} = + BlockScoutWeb.UserSocketV2 + |> socket("no_id", %{}) + |> subscribe_and_join(topic) - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search_all", fn conn -> + Conn.resp(conn, 200, eth_bytecode_response) + end) - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "eth_bytecode_db_lookup_started", - topic: ^topic - }, - :timer.seconds(1) + TestHelper.get_eip1967_implementation_error_response() - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "smart_contract_was_verified", - topic: ^topic - }, - :timer.seconds(1) + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - response = json_response(request, 200) + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "eth_bytecode_db_lookup_started", + topic: ^topic + }, + :timer.seconds(1) - assert response == - %{ - "proxy_type" => nil, - "implementations" => [], - "has_custom_methods_read" => false, - "has_custom_methods_write" => false, - "is_self_destructed" => false, - "deployed_bytecode" => to_string(address.contract_code), - "creation_bytecode" => - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" - } + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "smart_contract_was_verified", + topic: ^topic + }, + :timer.seconds(1) - TestHelper.get_eip1967_implementation_zero_addresses() + response = json_response(request, 200) - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - assert response = json_response(request, 200) - assert %{"is_verified" => true} = response - assert %{"is_verified_via_eth_bytecode_db" => true} = response - assert %{"is_partially_verified" => true} = response - assert %{"is_fully_verified" => false} = response - - smart_contract = Jason.decode!(eth_bytecode_response)["ethBytecodeDbSources"] |> List.first() - assert response["compiler_settings"] == Jason.decode!(smart_contract["compilerSettings"]) - assert response["name"] == smart_contract["contractName"] - assert response["compiler_version"] == smart_contract["compilerVersion"] - assert response["file_path"] == smart_contract["fileName"] - assert response["constructor_args"] == smart_contract["constructorArguments"] - assert response["abi"] == Jason.decode!(smart_contract["abi"]) - - assert response["decoded_constructor_args"] == [ - [ - "0xc35dadb65012ec5796536bd9864ed8773abc74c4", + assert response == %{ - "internalType" => "address", - "name" => "_factory", - "type" => "address" + "proxy_type" => nil, + "implementations" => [], + "has_custom_methods_read" => false, + "has_custom_methods_write" => false, + "is_self_destructed" => false, + "deployed_bytecode" => to_string(address.contract_code), + "creation_bytecode" => + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" } - ], - [ - "0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6", + + TestHelper.get_eip1967_implementation_zero_addresses() + + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + assert response = json_response(request, 200) + assert %{"is_verified" => true} = response + assert %{"is_verified_via_eth_bytecode_db" => true} = response + assert %{"is_partially_verified" => true} = response + assert %{"is_fully_verified" => false} = response + + smart_contract = Jason.decode!(eth_bytecode_response)["ethBytecodeDbSources"] |> List.first() + assert response["compiler_settings"] == Jason.decode!(smart_contract["compilerSettings"]) + assert response["name"] == smart_contract["contractName"] + assert response["compiler_version"] == smart_contract["compilerVersion"] + assert response["file_path"] == smart_contract["fileName"] + assert response["constructor_args"] == smart_contract["constructorArguments"] + assert response["abi"] == Jason.decode!(smart_contract["abi"]) + + assert response["decoded_constructor_args"] == [ + [ + "0xc35dadb65012ec5796536bd9864ed8773abc74c4", + %{ + "internalType" => "address", + "name" => "_factory", + "type" => "address" + } + ], + [ + "0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6", + %{ + "internalType" => "address", + "name" => "_WETH", + "type" => "address" + } + ] + ] + + assert response["source_code"] == smart_contract["sourceFiles"][smart_contract["fileName"]] + + assert response["external_libraries"] == [ %{ - "internalType" => "address", - "name" => "_WETH", - "type" => "address" + "address_hash" => "0x00000000D41867734BBee4C6863D9255b2b06aC1", + "name" => "__CACHE_BREAKER__" } ] - ] - assert response["source_code"] == smart_contract["sourceFiles"][smart_contract["fileName"]] + additional_sources = + for file_name <- Map.keys(smart_contract["sourceFiles"]), smart_contract["fileName"] != file_name do + %{ + "source_code" => smart_contract["sourceFiles"][file_name], + "file_path" => file_name + } + end - assert response["external_libraries"] == [ - %{ - "address_hash" => "0x00000000D41867734BBee4C6863D9255b2b06aC1", - "name" => "__CACHE_BREAKER__" - } - ] + assert response["additional_sources"] |> Enum.sort_by(fn x -> x["file_path"] end) == + additional_sources |> Enum.sort_by(fn x -> x["file_path"] end) - additional_sources = - for file_name <- Map.keys(smart_contract["sourceFiles"]), smart_contract["fileName"] != file_name do - %{ - "source_code" => smart_contract["sourceFiles"][file_name], - "file_path" => file_name - } - end + Application.put_env(:block_scout_web, :chain_id, old_chain_id) + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) + Bypass.down(bypass) + GenServer.stop(pid) + end - assert response["additional_sources"] |> Enum.sort_by(fn x -> x["file_path"] end) == - additional_sources |> Enum.sort_by(fn x -> x["file_path"] end) + test "automatically verify contract using search-all (sourcifySources) endpoint", %{conn: conn} do + {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) + old_chain_id = Application.get_env(:block_scout_web, :chain_id) - Application.put_env(:block_scout_web, :chain_id, old_chain_id) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) - Bypass.down(bypass) - GenServer.stop(pid) - end + Application.put_env(:block_scout_web, :chain_id, 5) - test "automatically verify contract using search-all (sourcifySources) endpoint", %{conn: conn} do - {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) - old_chain_id = Application.get_env(:block_scout_web, :chain_id) + bypass = Bypass.open() - Application.put_env(:block_scout_web, :chain_id, 5) + eth_bytecode_response = + File.read!("./test/support/fixture/smart_contract/eth_bytecode_db_search_all_sourcify_sources_response.json") - bypass = Bypass.open() + old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) - eth_bytecode_response = - File.read!("./test/support/fixture/smart_contract/eth_bytecode_db_search_all_sourcify_sources_response.json") + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, + service_url: "http://localhost:#{bypass.port}", + enabled: true, + type: "eth_bytecode_db", + eth_bytecode_db?: true + ) - old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) + address = insert(:contract_address) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, - service_url: "http://localhost:#{bypass.port}", - enabled: true, - type: "eth_bytecode_db", - eth_bytecode_db?: true - ) + insert(:transaction, + created_contract_address_hash: address.hash, + input: + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" + ) + |> with_block() - address = insert(:contract_address) + topic = "addresses:#{address.hash}" - insert(:transaction, - created_contract_address_hash: address.hash, - input: - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" - ) - |> with_block() + {:ok, _reply, _socket} = + BlockScoutWeb.UserSocketV2 + |> socket("no_id", %{}) + |> subscribe_and_join(topic) - topic = "addresses:#{address.hash}" + Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search_all", fn conn -> + Conn.resp(conn, 200, eth_bytecode_response) + end) - {:ok, _reply, _socket} = - BlockScoutWeb.UserSocketV2 - |> socket("no_id", %{}) - |> subscribe_and_join(topic) + TestHelper.get_eip1967_implementation_zero_addresses() - Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search_all", fn conn -> - Conn.resp(conn, 200, eth_bytecode_response) - end) + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - TestHelper.get_eip1967_implementation_zero_addresses() + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "eth_bytecode_db_lookup_started", + topic: ^topic + }, + :timer.seconds(1) - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "smart_contract_was_verified", + topic: ^topic + }, + :timer.seconds(1) - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "eth_bytecode_db_lookup_started", - topic: ^topic - }, - :timer.seconds(1) + response = json_response(request, 200) - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "smart_contract_was_verified", - topic: ^topic - }, - :timer.seconds(1) + assert response == + %{ + "proxy_type" => nil, + "implementations" => [], + "has_custom_methods_read" => false, + "has_custom_methods_write" => false, + "is_self_destructed" => false, + "deployed_bytecode" => to_string(address.contract_code), + "creation_bytecode" => + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" + } - response = json_response(request, 200) + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + assert response = json_response(request, 200) + assert %{"is_verified" => true} = response + assert %{"is_verified_via_eth_bytecode_db" => true} = response + assert %{"is_verified_via_sourcify" => true} = response + assert %{"is_partially_verified" => true} = response + assert %{"is_fully_verified" => false} = response + assert response["file_path"] == "Test.sol" + + Application.put_env(:block_scout_web, :chain_id, old_chain_id) + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) + Bypass.down(bypass) + GenServer.stop(pid) + end - assert response == - %{ - "proxy_type" => nil, - "implementations" => [], - "has_custom_methods_read" => false, - "has_custom_methods_write" => false, - "is_self_destructed" => false, - "deployed_bytecode" => to_string(address.contract_code), - "creation_bytecode" => - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" - } + test "automatically verify contract using search-all (sourcifySources with libraries) endpoint", %{conn: conn} do + {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) + old_chain_id = Application.get_env(:block_scout_web, :chain_id) - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - assert response = json_response(request, 200) - assert %{"is_verified" => true} = response - assert %{"is_verified_via_eth_bytecode_db" => true} = response - assert %{"is_verified_via_sourcify" => true} = response - assert %{"is_partially_verified" => true} = response - assert %{"is_fully_verified" => false} = response - assert response["file_path"] == "Test.sol" - - Application.put_env(:block_scout_web, :chain_id, old_chain_id) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) - Bypass.down(bypass) - GenServer.stop(pid) - end + Application.put_env(:block_scout_web, :chain_id, 5) - test "automatically verify contract using search-all (sourcifySources with libraries) endpoint", %{conn: conn} do - {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) - old_chain_id = Application.get_env(:block_scout_web, :chain_id) + bypass = Bypass.open() - Application.put_env(:block_scout_web, :chain_id, 5) + eth_bytecode_response = + File.read!( + "./test/support/fixture/smart_contract/eth_bytecode_db_search_all_sourcify_sources_with_libs_response.json" + ) - bypass = Bypass.open() + old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) - eth_bytecode_response = - File.read!( - "./test/support/fixture/smart_contract/eth_bytecode_db_search_all_sourcify_sources_with_libs_response.json" + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, + service_url: "http://localhost:#{bypass.port}", + enabled: true, + type: "eth_bytecode_db", + eth_bytecode_db?: true ) - old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) + address = insert(:contract_address) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, - service_url: "http://localhost:#{bypass.port}", - enabled: true, - type: "eth_bytecode_db", - eth_bytecode_db?: true - ) + insert(:transaction, + created_contract_address_hash: address.hash, + input: + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" + ) + |> with_block() - address = insert(:contract_address) + topic = "addresses:#{address.hash}" - insert(:transaction, - created_contract_address_hash: address.hash, - input: - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" - ) - |> with_block() + {:ok, _reply, _socket} = + BlockScoutWeb.UserSocketV2 + |> socket("no_id", %{}) + |> subscribe_and_join(topic) - topic = "addresses:#{address.hash}" + Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search_all", fn conn -> + Conn.resp(conn, 200, eth_bytecode_response) + end) - {:ok, _reply, _socket} = - BlockScoutWeb.UserSocketV2 - |> socket("no_id", %{}) - |> subscribe_and_join(topic) + TestHelper.get_eip1967_implementation_error_response() - Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search_all", fn conn -> - Conn.resp(conn, 200, eth_bytecode_response) - end) + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - TestHelper.get_eip1967_implementation_zero_addresses() + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "eth_bytecode_db_lookup_started", + topic: ^topic + }, + :timer.seconds(1) - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "smart_contract_was_verified", + topic: ^topic + }, + :timer.seconds(1) - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "eth_bytecode_db_lookup_started", - topic: ^topic - }, - :timer.seconds(1) + response = json_response(request, 200) - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "smart_contract_was_verified", - topic: ^topic - }, - :timer.seconds(1) + assert response == + %{ + "proxy_type" => nil, + "implementations" => [], + "has_custom_methods_read" => false, + "has_custom_methods_write" => false, + "is_self_destructed" => false, + "deployed_bytecode" => to_string(address.contract_code), + "creation_bytecode" => + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" + } - response = json_response(request, 200) + TestHelper.get_eip1967_implementation_zero_addresses() - assert response == - %{ - "proxy_type" => nil, - "implementations" => [], - "has_custom_methods_read" => false, - "has_custom_methods_write" => false, - "is_self_destructed" => false, - "deployed_bytecode" => to_string(address.contract_code), - "creation_bytecode" => - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" - } + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + assert response = json_response(request, 200) - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - assert response = json_response(request, 200) + smart_contract = Jason.decode!(eth_bytecode_response)["sourcifySources"] |> List.first() + assert %{"is_verified" => true} = response + assert %{"is_verified_via_eth_bytecode_db" => true} = response + assert %{"is_verified_via_sourcify" => true} = response + assert %{"is_partially_verified" => true} = response + assert %{"is_fully_verified" => false} = response + assert response["file_path"] == "src/zkbob/ZkBobPool.sol" - smart_contract = Jason.decode!(eth_bytecode_response)["sourcifySources"] |> List.first() + assert response["external_libraries"] == [ + %{ + "address_hash" => "0x22DE6B06544Ee5Cd907813a04bcdEd149A2f49D2", + "name" => "lib/base58-solidity/contracts/Base58.sol:Base58" + }, + %{ + "address_hash" => "0x019d3788F00a7087234f3844CB1ceCe1F9982B7A", + "name" => "src/libraries/ZkAddress.sol:ZkAddress" + } + ] - assert %{"is_verified" => true} = response - assert %{"is_verified_via_eth_bytecode_db" => true} = response - assert %{"is_verified_via_sourcify" => true} = response - assert %{"is_partially_verified" => true} = response - assert %{"is_fully_verified" => false} = response - assert response["file_path"] == "src/zkbob/ZkBobPool.sol" + additional_sources = + for file_name <- Map.keys(smart_contract["sourceFiles"]), smart_contract["fileName"] != file_name do + %{ + "source_code" => smart_contract["sourceFiles"][file_name], + "file_path" => file_name + } + end - assert response["external_libraries"] == [ - %{ - "address_hash" => "0x22DE6B06544Ee5Cd907813a04bcdEd149A2f49D2", - "name" => "lib/base58-solidity/contracts/Base58.sol:Base58" - }, - %{ - "address_hash" => "0x019d3788F00a7087234f3844CB1ceCe1F9982B7A", - "name" => "src/libraries/ZkAddress.sol:ZkAddress" - } - ] + assert response["additional_sources"] |> Enum.sort_by(fn x -> x["file_path"] end) == + additional_sources |> Enum.sort_by(fn x -> x["file_path"] end) - additional_sources = - for file_name <- Map.keys(smart_contract["sourceFiles"]), smart_contract["fileName"] != file_name do - %{ - "source_code" => smart_contract["sourceFiles"][file_name], - "file_path" => file_name - } - end + Application.put_env(:block_scout_web, :chain_id, old_chain_id) + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) + Bypass.down(bypass) + GenServer.stop(pid) + end - assert response["additional_sources"] |> Enum.sort_by(fn x -> x["file_path"] end) == - additional_sources |> Enum.sort_by(fn x -> x["file_path"] end) + test "automatically verify contract using search-all (allianceSources) endpoint", %{conn: conn} do + {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) + old_chain_id = Application.get_env(:block_scout_web, :chain_id) - Application.put_env(:block_scout_web, :chain_id, old_chain_id) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) - Bypass.down(bypass) - GenServer.stop(pid) - end + Application.put_env(:block_scout_web, :chain_id, 5) - test "automatically verify contract using search-all (allianceSources) endpoint", %{conn: conn} do - {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) - old_chain_id = Application.get_env(:block_scout_web, :chain_id) + bypass = Bypass.open() - Application.put_env(:block_scout_web, :chain_id, 5) + eth_bytecode_response = + File.read!("./test/support/fixture/smart_contract/eth_bytecode_db_search_all_alliance_sources_response.json") - bypass = Bypass.open() + old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) - eth_bytecode_response = - File.read!("./test/support/fixture/smart_contract/eth_bytecode_db_search_all_alliance_sources_response.json") + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, + service_url: "http://localhost:#{bypass.port}", + enabled: true, + type: "eth_bytecode_db", + eth_bytecode_db?: true + ) - old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) + address = insert(:contract_address) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, - service_url: "http://localhost:#{bypass.port}", - enabled: true, - type: "eth_bytecode_db", - eth_bytecode_db?: true - ) + insert(:transaction, + created_contract_address_hash: address.hash, + input: + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" + ) + |> with_block() - address = insert(:contract_address) + topic = "addresses:#{address.hash}" - insert(:transaction, - created_contract_address_hash: address.hash, - input: - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" - ) - |> with_block() + {:ok, _reply, _socket} = + BlockScoutWeb.UserSocketV2 + |> socket("no_id", %{}) + |> subscribe_and_join(topic) - topic = "addresses:#{address.hash}" + Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search_all", fn conn -> + Conn.resp(conn, 200, eth_bytecode_response) + end) - {:ok, _reply, _socket} = - BlockScoutWeb.UserSocketV2 - |> socket("no_id", %{}) - |> subscribe_and_join(topic) + implementation_address = insert(:address) + implementation_address_hash_string = to_string(implementation_address.hash) + formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_address.hash)) + TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string) - Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search_all", fn conn -> - Conn.resp(conn, 200, eth_bytecode_response) - end) + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - implementation_address = insert(:address) - implementation_address_hash_string = to_string(implementation_address.hash) - formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_address.hash)) - TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string) + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "eth_bytecode_db_lookup_started", + topic: ^topic + }, + :timer.seconds(1) - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "smart_contract_was_verified", + topic: ^topic + }, + :timer.seconds(1) - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "eth_bytecode_db_lookup_started", - topic: ^topic - }, - :timer.seconds(1) + response = json_response(request, 200) - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "smart_contract_was_verified", - topic: ^topic - }, - :timer.seconds(1) + assert response == + %{ + "proxy_type" => nil, + "implementations" => [], + "has_custom_methods_read" => false, + "has_custom_methods_write" => false, + "is_self_destructed" => false, + "deployed_bytecode" => to_string(address.contract_code), + "creation_bytecode" => + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" + } - response = json_response(request, 200) + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + assert response = json_response(request, 200) + assert %{"proxy_type" => "eip1967"} = response - assert response == - %{ - "proxy_type" => nil, - "implementations" => [], - "has_custom_methods_read" => false, - "has_custom_methods_write" => false, - "is_self_destructed" => false, - "deployed_bytecode" => to_string(address.contract_code), - "creation_bytecode" => - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" - } + assert %{"implementations" => [%{"address" => ^formatted_implementation_address_hash_string, "name" => nil}]} = + response - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - assert response = json_response(request, 200) - assert %{"proxy_type" => "eip1967"} = response + assert %{"is_verified" => true} = response + assert %{"is_verified_via_eth_bytecode_db" => true} = response + assert %{"is_partially_verified" => true} = response + assert %{"is_verified_via_sourcify" => false} = response + assert %{"is_verified_via_verifier_alliance" => true} = response + assert %{"is_fully_verified" => false} = response - assert %{"implementations" => [%{"address" => ^formatted_implementation_address_hash_string, "name" => nil}]} = - response + smart_contract = Jason.decode!(eth_bytecode_response)["allianceSources"] |> List.first() + assert response["compiler_settings"] == Jason.decode!(smart_contract["compilerSettings"]) + assert response["name"] == smart_contract["contractName"] + assert response["compiler_version"] == smart_contract["compilerVersion"] + assert response["file_path"] == smart_contract["fileName"] + assert response["constructor_args"] == smart_contract["constructorArguments"] + assert response["abi"] == Jason.decode!(smart_contract["abi"]) - assert %{"is_verified" => true} = response - assert %{"is_verified_via_eth_bytecode_db" => true} = response - assert %{"is_partially_verified" => true} = response - assert %{"is_verified_via_sourcify" => false} = response - assert %{"is_verified_via_verifier_alliance" => true} = response - assert %{"is_fully_verified" => false} = response + assert response["source_code"] == smart_contract["sourceFiles"][smart_contract["fileName"]] - smart_contract = Jason.decode!(eth_bytecode_response)["allianceSources"] |> List.first() - assert response["compiler_settings"] == Jason.decode!(smart_contract["compilerSettings"]) - assert response["name"] == smart_contract["contractName"] - assert response["compiler_version"] == smart_contract["compilerVersion"] - assert response["file_path"] == smart_contract["fileName"] - assert response["constructor_args"] == smart_contract["constructorArguments"] - assert response["abi"] == Jason.decode!(smart_contract["abi"]) + assert response["external_libraries"] == [ + %{ + "address_hash" => "0x00000000D41867734BBee4C6863D9255b2b06aC1", + "name" => "__CACHE_BREAKER__" + } + ] - assert response["source_code"] == smart_contract["sourceFiles"][smart_contract["fileName"]] + additional_sources = + for file_name <- Map.keys(smart_contract["sourceFiles"]), smart_contract["fileName"] != file_name do + %{ + "source_code" => smart_contract["sourceFiles"][file_name], + "file_path" => file_name + } + end - assert response["external_libraries"] == [ - %{ - "address_hash" => "0x00000000D41867734BBee4C6863D9255b2b06aC1", - "name" => "__CACHE_BREAKER__" - } - ] + assert response["additional_sources"] |> Enum.sort_by(fn x -> x["file_path"] end) == + additional_sources |> Enum.sort_by(fn x -> x["file_path"] end) - additional_sources = - for file_name <- Map.keys(smart_contract["sourceFiles"]), smart_contract["fileName"] != file_name do - %{ - "source_code" => smart_contract["sourceFiles"][file_name], - "file_path" => file_name - } - end + Application.put_env(:block_scout_web, :chain_id, old_chain_id) + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) + Bypass.down(bypass) + GenServer.stop(pid) + end - assert response["additional_sources"] |> Enum.sort_by(fn x -> x["file_path"] end) == - additional_sources |> Enum.sort_by(fn x -> x["file_path"] end) + test "automatically verify contract using search-all (prefer sourcify FULL match) endpoint", %{conn: conn} do + {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) + old_chain_id = Application.get_env(:block_scout_web, :chain_id) - Application.put_env(:block_scout_web, :chain_id, old_chain_id) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) - Bypass.down(bypass) - GenServer.stop(pid) - end + Application.put_env(:block_scout_web, :chain_id, 5) - test "automatically verify contract using search-all (prefer sourcify FULL match) endpoint", %{conn: conn} do - {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) - old_chain_id = Application.get_env(:block_scout_web, :chain_id) + bypass = Bypass.open() - Application.put_env(:block_scout_web, :chain_id, 5) + eth_bytecode_response = + File.read!( + "./test/support/fixture/smart_contract/eth_bytecode_db_search_all_alliance_sources_partial_response.json" + ) - bypass = Bypass.open() + old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) - eth_bytecode_response = - File.read!( - "./test/support/fixture/smart_contract/eth_bytecode_db_search_all_alliance_sources_partial_response.json" + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, + service_url: "http://localhost:#{bypass.port}", + enabled: true, + type: "eth_bytecode_db", + eth_bytecode_db?: true ) - old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) + address = insert(:contract_address) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, - service_url: "http://localhost:#{bypass.port}", - enabled: true, - type: "eth_bytecode_db", - eth_bytecode_db?: true - ) + insert(:transaction, + created_contract_address_hash: address.hash, + input: + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" + ) + |> with_block() - address = insert(:contract_address) + topic = "addresses:#{address.hash}" - insert(:transaction, - created_contract_address_hash: address.hash, - input: - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" - ) - |> with_block() + {:ok, _reply, _socket} = + BlockScoutWeb.UserSocketV2 + |> socket("no_id", %{}) + |> subscribe_and_join(topic) - topic = "addresses:#{address.hash}" + Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search_all", fn conn -> + Conn.resp(conn, 200, eth_bytecode_response) + end) - {:ok, _reply, _socket} = - BlockScoutWeb.UserSocketV2 - |> socket("no_id", %{}) - |> subscribe_and_join(topic) + implementation_address = insert(:address) + implementation_address_hash_string = to_string(implementation_address.hash) + formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_address.hash)) + TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string) - Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search_all", fn conn -> - Conn.resp(conn, 200, eth_bytecode_response) - end) + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - implementation_address = insert(:address) - implementation_address_hash_string = to_string(implementation_address.hash) - formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_address.hash)) - TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string) + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "eth_bytecode_db_lookup_started", + topic: ^topic + }, + :timer.seconds(1) - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "smart_contract_was_verified", + topic: ^topic + }, + :timer.seconds(1) - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "eth_bytecode_db_lookup_started", - topic: ^topic - }, - :timer.seconds(1) + response = json_response(request, 200) - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "smart_contract_was_verified", - topic: ^topic - }, - :timer.seconds(1) + assert response == + %{ + "proxy_type" => nil, + "implementations" => [], + "has_custom_methods_read" => false, + "has_custom_methods_write" => false, + "is_self_destructed" => false, + "deployed_bytecode" => to_string(address.contract_code), + "creation_bytecode" => + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" + } - response = json_response(request, 200) + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + assert response = json_response(request, 200) + assert %{"proxy_type" => "eip1967"} = response - assert response == - %{ - "proxy_type" => nil, - "implementations" => [], - "has_custom_methods_read" => false, - "has_custom_methods_write" => false, - "is_self_destructed" => false, - "deployed_bytecode" => to_string(address.contract_code), - "creation_bytecode" => - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" - } + assert %{"implementations" => [%{"address" => ^formatted_implementation_address_hash_string, "name" => nil}]} = + response - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - assert response = json_response(request, 200) - assert %{"proxy_type" => "eip1967"} = response + assert %{"is_verified" => true} = response + assert %{"is_verified_via_eth_bytecode_db" => true} = response + assert %{"is_partially_verified" => false} = response + assert %{"is_verified_via_sourcify" => true} = response + assert %{"is_verified_via_verifier_alliance" => false} = response + assert %{"is_fully_verified" => true} = response - assert %{"implementations" => [%{"address" => ^formatted_implementation_address_hash_string, "name" => nil}]} = - response + smart_contract = Jason.decode!(eth_bytecode_response)["sourcifySources"] |> List.first() + assert response["compiler_settings"] == Jason.decode!(smart_contract["compilerSettings"]) + assert response["name"] == smart_contract["contractName"] + assert response["compiler_version"] == smart_contract["compilerVersion"] + assert response["file_path"] == smart_contract["fileName"] + assert response["constructor_args"] == smart_contract["constructorArguments"] + assert response["abi"] == Jason.decode!(smart_contract["abi"]) - assert %{"is_verified" => true} = response - assert %{"is_verified_via_eth_bytecode_db" => true} = response - assert %{"is_partially_verified" => false} = response - assert %{"is_verified_via_sourcify" => true} = response - assert %{"is_verified_via_verifier_alliance" => false} = response - assert %{"is_fully_verified" => true} = response + assert response["source_code"] == smart_contract["sourceFiles"][smart_contract["fileName"]] - smart_contract = Jason.decode!(eth_bytecode_response)["sourcifySources"] |> List.first() - assert response["compiler_settings"] == Jason.decode!(smart_contract["compilerSettings"]) - assert response["name"] == smart_contract["contractName"] - assert response["compiler_version"] == smart_contract["compilerVersion"] - assert response["file_path"] == smart_contract["fileName"] - assert response["constructor_args"] == smart_contract["constructorArguments"] - assert response["abi"] == Jason.decode!(smart_contract["abi"]) + assert response["external_libraries"] == [ + %{ + "address_hash" => "0x00000000D41867734BBee4C6863D9255b2b06aC1", + "name" => "__CACHE_BREAKER__" + } + ] - assert response["source_code"] == smart_contract["sourceFiles"][smart_contract["fileName"]] + additional_sources = + for file_name <- Map.keys(smart_contract["sourceFiles"]), smart_contract["fileName"] != file_name do + %{ + "source_code" => smart_contract["sourceFiles"][file_name], + "file_path" => file_name + } + end - assert response["external_libraries"] == [ - %{ - "address_hash" => "0x00000000D41867734BBee4C6863D9255b2b06aC1", - "name" => "__CACHE_BREAKER__" - } - ] + assert response["additional_sources"] |> Enum.sort_by(fn x -> x["file_path"] end) == + additional_sources |> Enum.sort_by(fn x -> x["file_path"] end) - additional_sources = - for file_name <- Map.keys(smart_contract["sourceFiles"]), smart_contract["fileName"] != file_name do - %{ - "source_code" => smart_contract["sourceFiles"][file_name], - "file_path" => file_name - } - end + Application.put_env(:block_scout_web, :chain_id, old_chain_id) + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) + Bypass.down(bypass) + GenServer.stop(pid) + end - assert response["additional_sources"] |> Enum.sort_by(fn x -> x["file_path"] end) == - additional_sources |> Enum.sort_by(fn x -> x["file_path"] end) + test "automatically verify contract using search-all (take eth bytecode db FULL match) endpoint", %{conn: conn} do + {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) + old_chain_id = Application.get_env(:block_scout_web, :chain_id) - Application.put_env(:block_scout_web, :chain_id, old_chain_id) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) - Bypass.down(bypass) - GenServer.stop(pid) - end + Application.put_env(:block_scout_web, :chain_id, 5) - test "automatically verify contract using search-all (take eth bytecode db FULL match) endpoint", %{conn: conn} do - {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) - old_chain_id = Application.get_env(:block_scout_web, :chain_id) + bypass = Bypass.open() - Application.put_env(:block_scout_web, :chain_id, 5) + eth_bytecode_response = + File.read!( + "./test/support/fixture/smart_contract/eth_bytecode_db_search_all_alliance_sources_partial_response_eth_bdb_full.json" + ) - bypass = Bypass.open() + old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) - eth_bytecode_response = - File.read!( - "./test/support/fixture/smart_contract/eth_bytecode_db_search_all_alliance_sources_partial_response_eth_bdb_full.json" + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, + service_url: "http://localhost:#{bypass.port}", + enabled: true, + type: "eth_bytecode_db", + eth_bytecode_db?: true ) - old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) + address = insert(:contract_address) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, - service_url: "http://localhost:#{bypass.port}", - enabled: true, - type: "eth_bytecode_db", - eth_bytecode_db?: true - ) + insert(:transaction, + created_contract_address_hash: address.hash, + input: + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" + ) + |> with_block() - address = insert(:contract_address) + topic = "addresses:#{address.hash}" - insert(:transaction, - created_contract_address_hash: address.hash, - input: - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" - ) - |> with_block() + {:ok, _reply, _socket} = + BlockScoutWeb.UserSocketV2 + |> socket("no_id", %{}) + |> subscribe_and_join(topic) - topic = "addresses:#{address.hash}" + Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search_all", fn conn -> + Conn.resp(conn, 200, eth_bytecode_response) + end) - {:ok, _reply, _socket} = - BlockScoutWeb.UserSocketV2 - |> socket("no_id", %{}) - |> subscribe_and_join(topic) + implementation_address = insert(:address) + implementation_address_hash_string = to_string(implementation_address.hash) + formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_address.hash)) + TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string) - Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search_all", fn conn -> - Conn.resp(conn, 200, eth_bytecode_response) - end) + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - implementation_address = insert(:address) - implementation_address_hash_string = to_string(implementation_address.hash) - formatted_implementation_address_hash_string = to_string(Address.checksum(implementation_address.hash)) - TestHelper.get_eip1967_implementation_non_zero_address(implementation_address_hash_string) + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "eth_bytecode_db_lookup_started", + topic: ^topic + }, + :timer.seconds(1) - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "smart_contract_was_verified", + topic: ^topic + }, + :timer.seconds(1) - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "eth_bytecode_db_lookup_started", - topic: ^topic - }, - :timer.seconds(1) + response = json_response(request, 200) - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "smart_contract_was_verified", - topic: ^topic - }, - :timer.seconds(1) + assert response == + %{ + "proxy_type" => nil, + "implementations" => [], + "has_custom_methods_read" => false, + "has_custom_methods_write" => false, + "is_self_destructed" => false, + "deployed_bytecode" => to_string(address.contract_code), + "creation_bytecode" => + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" + } - response = json_response(request, 200) + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + assert response = json_response(request, 200) + assert %{"proxy_type" => "eip1967"} = response - assert response == - %{ - "proxy_type" => nil, - "implementations" => [], - "has_custom_methods_read" => false, - "has_custom_methods_write" => false, - "is_self_destructed" => false, - "deployed_bytecode" => to_string(address.contract_code), - "creation_bytecode" => - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" - } + assert %{"implementations" => [%{"address" => ^formatted_implementation_address_hash_string, "name" => nil}]} = + response - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - assert response = json_response(request, 200) - assert %{"proxy_type" => "eip1967"} = response + assert %{"is_verified" => true} = response + assert %{"is_verified_via_eth_bytecode_db" => true} = response + assert %{"is_partially_verified" => false} = response + assert %{"is_verified_via_sourcify" => false} = response + assert %{"is_verified_via_verifier_alliance" => false} = response + assert %{"is_fully_verified" => true} = response - assert %{"implementations" => [%{"address" => ^formatted_implementation_address_hash_string, "name" => nil}]} = - response + smart_contract = Jason.decode!(eth_bytecode_response)["ethBytecodeDbSources"] |> List.first() + assert response["compiler_settings"] == Jason.decode!(smart_contract["compilerSettings"]) + assert response["name"] == smart_contract["contractName"] + assert response["compiler_version"] == smart_contract["compilerVersion"] + assert response["file_path"] == smart_contract["fileName"] + assert response["constructor_args"] == smart_contract["constructorArguments"] + assert response["abi"] == Jason.decode!(smart_contract["abi"]) - assert %{"is_verified" => true} = response - assert %{"is_verified_via_eth_bytecode_db" => true} = response - assert %{"is_partially_verified" => false} = response - assert %{"is_verified_via_sourcify" => false} = response - assert %{"is_verified_via_verifier_alliance" => false} = response - assert %{"is_fully_verified" => true} = response + assert response["source_code"] == smart_contract["sourceFiles"][smart_contract["fileName"]] - smart_contract = Jason.decode!(eth_bytecode_response)["ethBytecodeDbSources"] |> List.first() - assert response["compiler_settings"] == Jason.decode!(smart_contract["compilerSettings"]) - assert response["name"] == smart_contract["contractName"] - assert response["compiler_version"] == smart_contract["compilerVersion"] - assert response["file_path"] == smart_contract["fileName"] - assert response["constructor_args"] == smart_contract["constructorArguments"] - assert response["abi"] == Jason.decode!(smart_contract["abi"]) + assert response["external_libraries"] == [ + %{ + "address_hash" => "0x00000000D41867734BBee4C6863D9255b2b06aC1", + "name" => "__CACHE_BREAKER__" + } + ] - assert response["source_code"] == smart_contract["sourceFiles"][smart_contract["fileName"]] + additional_sources = + for file_name <- Map.keys(smart_contract["sourceFiles"]), smart_contract["fileName"] != file_name do + %{ + "source_code" => smart_contract["sourceFiles"][file_name], + "file_path" => file_name + } + end - assert response["external_libraries"] == [ - %{ - "address_hash" => "0x00000000D41867734BBee4C6863D9255b2b06aC1", - "name" => "__CACHE_BREAKER__" - } - ] + assert response["additional_sources"] |> Enum.sort_by(fn x -> x["file_path"] end) == + additional_sources |> Enum.sort_by(fn x -> x["file_path"] end) - additional_sources = - for file_name <- Map.keys(smart_contract["sourceFiles"]), smart_contract["fileName"] != file_name do - %{ - "source_code" => smart_contract["sourceFiles"][file_name], - "file_path" => file_name - } - end + Application.put_env(:block_scout_web, :chain_id, old_chain_id) + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) + Bypass.down(bypass) + GenServer.stop(pid) + end - assert response["additional_sources"] |> Enum.sort_by(fn x -> x["file_path"] end) == - additional_sources |> Enum.sort_by(fn x -> x["file_path"] end) + test "check fetch interval for LookUpSmartContractSourcesOnDemand and use sources:search endpoint since chain_id is unset", + %{conn: conn} do + {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) + old_chain_id = Application.get_env(:block_scout_web, :chain_id) - Application.put_env(:block_scout_web, :chain_id, old_chain_id) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) - Bypass.down(bypass) - GenServer.stop(pid) - end + Application.put_env(:block_scout_web, :chain_id, nil) - test "check fetch interval for LookUpSmartContractSourcesOnDemand and use sources:search endpoint since chain_id is unset", - %{conn: conn} do - {:ok, pid} = Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand.start_link([]) - old_chain_id = Application.get_env(:block_scout_web, :chain_id) + bypass = Bypass.open() + address = insert(:contract_address) + topic = "addresses:#{address.hash}" - Application.put_env(:block_scout_web, :chain_id, nil) + {:ok, _reply, _socket} = + BlockScoutWeb.UserSocketV2 + |> socket("no_id", %{}) + |> subscribe_and_join(topic) - bypass = Bypass.open() - address = insert(:contract_address) - topic = "addresses:#{address.hash}" + insert(:transaction, + created_contract_address_hash: address.hash, + input: + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" + ) + |> with_block() - {:ok, _reply, _socket} = - BlockScoutWeb.UserSocketV2 - |> socket("no_id", %{}) - |> subscribe_and_join(topic) + old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) - insert(:transaction, - created_contract_address_hash: address.hash, - input: - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582061b7676067d537e410bb704932a9984739a959416170ea17bda192ac1218d2790029" - ) - |> with_block() + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, + service_url: "http://localhost:#{bypass.port}", + enabled: true, + type: "eth_bytecode_db", + eth_bytecode_db?: true + ) - old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) + old_interval_env = Application.get_env(:explorer, Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, - service_url: "http://localhost:#{bypass.port}", - enabled: true, - type: "eth_bytecode_db", - eth_bytecode_db?: true - ) + Application.put_env(:explorer, Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand, fetch_interval: 0) - old_interval_env = Application.get_env(:explorer, Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand) + Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search", fn conn -> + Conn.resp(conn, 200, "{\"sources\": []}") + end) - Application.put_env(:explorer, Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand, fetch_interval: 0) + TestHelper.get_eip1967_implementation_zero_addresses() - Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search", fn conn -> - Conn.resp(conn, 200, "{\"sources\": []}") - end) + _request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - TestHelper.get_eip1967_implementation_zero_addresses() + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "eth_bytecode_db_lookup_started", + topic: ^topic + }, + :timer.seconds(1) + + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "smart_contract_was_not_verified", + topic: ^topic + }, + :timer.seconds(1) + + :timer.sleep(10) + + Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search", fn conn -> + Conn.resp(conn, 200, "{\"sources\": []}") + end) + + _request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "eth_bytecode_db_lookup_started", + topic: ^topic + }, + :timer.seconds(1) + + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "smart_contract_was_not_verified", + topic: ^topic + }, + :timer.seconds(1) + + :timer.sleep(10) + + Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search", fn conn -> + Conn.resp(conn, 200, "{\"sources\": []}") + end) + + _request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - _request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "eth_bytecode_db_lookup_started", - topic: ^topic - }, - :timer.seconds(1) - - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "smart_contract_was_not_verified", - topic: ^topic - }, - :timer.seconds(1) - - :timer.sleep(10) - - Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search", fn conn -> - Conn.resp(conn, 200, "{\"sources\": []}") - end) - - _request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "eth_bytecode_db_lookup_started", - topic: ^topic - }, - :timer.seconds(1) - - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "smart_contract_was_not_verified", - topic: ^topic - }, - :timer.seconds(1) - - :timer.sleep(10) - - Bypass.expect_once(bypass, "POST", "/api/v2/bytecodes/sources_search", fn conn -> - Conn.resp(conn, 200, "{\"sources\": []}") - end) - - _request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "eth_bytecode_db_lookup_started", - topic: ^topic - }, - :timer.seconds(1) - - assert_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "smart_contract_was_not_verified", - topic: ^topic - }, - :timer.seconds(1) - - :timer.sleep(10) - - Application.put_env(:explorer, Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand, fetch_interval: 10000) - - _request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") - - refute_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "eth_bytecode_db_lookup_started", - topic: ^topic - }, - :timer.seconds(1) - - refute_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "smart_contract_was_not_verified", - topic: ^topic - }, - :timer.seconds(1) - - refute_receive %Phoenix.Socket.Message{ - payload: %{}, - event: "smart_contract_was_verified", - topic: ^topic - }, - :timer.seconds(1) - - Application.put_env(:block_scout_web, :chain_id, old_chain_id) - Application.put_env(:explorer, Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand, old_interval_env) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) - Bypass.down(bypass) - GenServer.stop(pid) + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "eth_bytecode_db_lookup_started", + topic: ^topic + }, + :timer.seconds(1) + + assert_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "smart_contract_was_not_verified", + topic: ^topic + }, + :timer.seconds(1) + + :timer.sleep(10) + + Application.put_env(:explorer, Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand, fetch_interval: 10000) + + _request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(address.hash)}") + + refute_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "eth_bytecode_db_lookup_started", + topic: ^topic + }, + :timer.seconds(1) + + refute_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "smart_contract_was_not_verified", + topic: ^topic + }, + :timer.seconds(1) + + refute_receive %Phoenix.Socket.Message{ + payload: %{}, + event: "smart_contract_was_verified", + topic: ^topic + }, + :timer.seconds(1) + + Application.put_env(:block_scout_web, :chain_id, old_chain_id) + Application.put_env(:explorer, Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand, old_interval_env) + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) + Bypass.down(bypass) + GenServer.stop(pid) + end end end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/verification_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/verification_controller_test.exs index a2199d26c7c1..f8ff110a5fe7 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/verification_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/verification_controller_test.exs @@ -34,271 +34,340 @@ defmodule BlockScoutWeb.API.V2.VerificationControllerTest do end end - describe "/api/v2/smart-contracts/{address_hash}/verification/via/flattened-code" do - test "get 200 for verified contract", %{conn: conn} do - contract = insert(:smart_contract) + if Application.compile_env(:explorer, :chain_type) !== :zksync do + describe "/api/v2/smart-contracts/{address_hash}/verification/via/flattened-code" do + test "get 200 for verified contract", %{conn: conn} do + contract = insert(:smart_contract) - params = %{"compiler_version" => "", "source_code" => ""} - request = post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/verification/via/flattened-code", params) + params = %{"compiler_version" => "", "source_code" => ""} + request = post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/verification/via/flattened-code", params) - assert %{"message" => "Already verified"} = json_response(request, 200) - end + assert %{"message" => "Already verified"} = json_response(request, 200) + end - test "success verification", %{conn: conn} do - before = Application.get_env(:explorer, :solc_bin_api_url) + test "success verification", %{conn: conn} do + before = Application.get_env(:explorer, :solc_bin_api_url) - Application.put_env(:explorer, :solc_bin_api_url, "https://solc-bin.ethereum.org") + Application.put_env(:explorer, :solc_bin_api_url, "https://solc-bin.ethereum.org") - path = File.cwd!() <> "/../explorer/test/support/fixture/smart_contract/solidity_0.5.9_smart_contract.sol" - contract = File.read!(path) + path = File.cwd!() <> "/../explorer/test/support/fixture/smart_contract/solidity_0.5.9_smart_contract.sol" + contract = File.read!(path) - constructor_arguments = - "0000000000000000000000000000000000000000000000000003635c9adc5dea0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000954657374546f6b656e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005546f6b656e000000000000000000000000000000000000000000000000000000" + constructor_arguments = + "0000000000000000000000000000000000000000000000000003635c9adc5dea0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000954657374546f6b656e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005546f6b656e000000000000000000000000000000000000000000000000000000" - bytecode = - "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a723058202bede3d06720cdf63e8e43fa1d96f228a476cc899ae007bf684e802f2484ce7664736f6c63430005090032" + bytecode = + "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a723058202bede3d06720cdf63e8e43fa1d96f228a476cc899ae007bf684e802f2484ce7664736f6c63430005090032" - input = - "0x60806040526040518060400160405280600381526020017f302e3100000000000000000000000000000000000000000000000000000000008152506006908051906020019062000051929190620001e2565b503480156200005f57600080fd5b506040516200105b3803806200105b833981810160405260808110156200008557600080fd5b81019080805190602001909291908051640100000000811115620000a857600080fd5b82810190506020810184811115620000bf57600080fd5b8151856001820283011164010000000082111715620000dd57600080fd5b50509291906020018051906020019092919080516401000000008111156200010457600080fd5b828101905060208101848111156200011b57600080fd5b81518560018202830111640100000000821117156200013957600080fd5b5050929190505050836000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550836002819055508260039080519060200190620001a3929190620001e2565b5081600460006101000a81548160ff021916908360ff1602179055508060059080519060200190620001d7929190620001e2565b505050505062000291565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200022557805160ff191683800117855562000256565b8280016001018555821562000256579182015b828111156200025557825182559160200191906001019062000238565b5b50905062000265919062000269565b5090565b6200028e91905b808211156200028a57600081600090555060010162000270565b5090565b90565b610dba80620002a16000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a723058202bede3d06720cdf63e8e43fa1d96f228a476cc899ae007bf684e802f2484ce7664736f6c63430005090032" + input = + "0x60806040526040518060400160405280600381526020017f302e3100000000000000000000000000000000000000000000000000000000008152506006908051906020019062000051929190620001e2565b503480156200005f57600080fd5b506040516200105b3803806200105b833981810160405260808110156200008557600080fd5b81019080805190602001909291908051640100000000811115620000a857600080fd5b82810190506020810184811115620000bf57600080fd5b8151856001820283011164010000000082111715620000dd57600080fd5b50509291906020018051906020019092919080516401000000008111156200010457600080fd5b828101905060208101848111156200011b57600080fd5b81518560018202830111640100000000821117156200013957600080fd5b5050929190505050836000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550836002819055508260039080519060200190620001a3929190620001e2565b5081600460006101000a81548160ff021916908360ff1602179055508060059080519060200190620001d7929190620001e2565b505050505062000291565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200022557805160ff191683800117855562000256565b8280016001018555821562000256579182015b828111156200025557825182559160200191906001019062000238565b5b50905062000265919062000269565b5090565b6200028e91905b808211156200028a57600081600090555060010162000270565b5090565b90565b610dba80620002a16000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a723058202bede3d06720cdf63e8e43fa1d96f228a476cc899ae007bf684e802f2484ce7664736f6c63430005090032" - contract_address = insert(:contract_address, contract_code: bytecode) + contract_address = insert(:contract_address, contract_code: bytecode) - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: input <> constructor_arguments - ) - |> with_block(status: :ok) + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: input <> constructor_arguments + ) + |> with_block(status: :ok) - topic = "addresses:#{contract_address.hash}" + topic = "addresses:#{contract_address.hash}" - {:ok, _reply, _socket} = - BlockScoutWeb.UserSocketV2 - |> socket("no_id", %{}) - |> subscribe_and_join(topic) + {:ok, _reply, _socket} = + BlockScoutWeb.UserSocketV2 + |> socket("no_id", %{}) + |> subscribe_and_join(topic) - params = %{ - "source_code" => contract, - "compiler_version" => "v0.5.9+commit.e560f70d", - "evm_version" => "petersburg", - "contract_name" => "TestToken", - "is_optimization_enabled" => false - } + params = %{ + "source_code" => contract, + "compiler_version" => "v0.5.9+commit.e560f70d", + "evm_version" => "petersburg", + "contract_name" => "TestToken", + "is_optimization_enabled" => false + } - request = post(conn, "/api/v2/smart-contracts/#{contract_address.hash}/verification/via/flattened-code", params) + request = post(conn, "/api/v2/smart-contracts/#{contract_address.hash}/verification/via/flattened-code", params) - assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) + assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) - assert_receive %Phoenix.Socket.Message{ - payload: %{status: "success"}, - event: "verification_result", - topic: ^topic - }, - :timer.seconds(300) + assert_receive %Phoenix.Socket.Message{ + payload: %{status: "success"}, + event: "verification_result", + topic: ^topic + }, + :timer.seconds(300) - Application.put_env(:explorer, :solc_bin_api_url, before) - end + Application.put_env(:explorer, :solc_bin_api_url, before) + end - test "get error on empty contract name", %{conn: conn} do - before = Application.get_env(:explorer, :solc_bin_api_url) + test "get error on empty contract name", %{conn: conn} do + before = Application.get_env(:explorer, :solc_bin_api_url) - Application.put_env(:explorer, :solc_bin_api_url, "https://solc-bin.ethereum.org") + Application.put_env(:explorer, :solc_bin_api_url, "https://solc-bin.ethereum.org") - contract_address = insert(:contract_address, contract_code: "0x") + contract_address = insert(:contract_address, contract_code: "0x") - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: "0x" - ) - |> with_block(status: :ok) + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: "0x" + ) + |> with_block(status: :ok) - topic = "addresses:#{contract_address.hash}" + topic = "addresses:#{contract_address.hash}" - {:ok, _reply, _socket} = - BlockScoutWeb.UserSocketV2 - |> socket("no_id", %{}) - |> subscribe_and_join(topic) + {:ok, _reply, _socket} = + BlockScoutWeb.UserSocketV2 + |> socket("no_id", %{}) + |> subscribe_and_join(topic) - params = %{ - "source_code" => "123", - "compiler_version" => "v0.5.9+commit.e560f70d", - "evm_version" => "petersburg", - "contract_name" => "", - "is_optimization_enabled" => false - } + params = %{ + "source_code" => "123", + "compiler_version" => "v0.5.9+commit.e560f70d", + "evm_version" => "petersburg", + "contract_name" => "", + "is_optimization_enabled" => false + } - request = post(conn, "/api/v2/smart-contracts/#{contract_address.hash}/verification/via/flattened-code", params) + request = post(conn, "/api/v2/smart-contracts/#{contract_address.hash}/verification/via/flattened-code", params) - assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) + assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) - assert_receive %Phoenix.Socket.Message{ - payload: %{status: "error", errors: %{name: ["Wrong contract name, please try again."]}}, - event: "verification_result", - topic: ^topic - }, - :timer.seconds(2) + assert_receive %Phoenix.Socket.Message{ + payload: %{status: "error", errors: %{name: ["Wrong contract name, please try again."]}}, + event: "verification_result", + topic: ^topic + }, + :timer.seconds(2) - Application.put_env(:explorer, :solc_bin_api_url, before) + Application.put_env(:explorer, :solc_bin_api_url, before) + end end - end - describe "/api/v2/smart-contracts/{address_hash}/verification/via/standard-input" do - test "get 200 for verified contract", %{conn: conn} do - contract = insert(:smart_contract) + describe "/api/v2/smart-contracts/{address_hash}/verification/via/sourcify" do + test "get 200 for verified contract", %{conn: conn} do + contract = insert(:smart_contract) + + params = %{"files" => ""} + request = post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/verification/via/sourcify", params) + + assert %{"message" => "Already verified"} = json_response(request, 200) + end + + test "verify contract from sourcify repo", %{conn: conn} do + address = "0xf26594F585De4EB0Ae9De865d9053FEe02ac6eF1" + + _contract = insert(:address, hash: address, contract_code: "0x01") + + topic = "addresses:#{String.downcase(address)}" + + {:ok, _reply, _socket} = + UserSocketV2 + |> socket("no_id", %{}) + |> subscribe_and_join(topic) + + multipart = + Multipart.new() + |> Multipart.add_file_content("content", "name.json", + name: "files[0]", + headers: [{"content-type", "application/json"}] + ) + + body = + multipart + |> Multipart.body() + |> Enum.to_list() + |> to_str() + + [{name, value}] = Multipart.headers(multipart) + + request = + post( + conn + |> Plug.Conn.put_req_header( + name, + value + ), + "/api/v2/smart-contracts/#{address}/verification/via/sourcify", + body + ) + + assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) + + assert_receive %Phoenix.Socket.Message{ + payload: %{status: "success"}, + event: "verification_result", + topic: ^topic + }, + :timer.seconds(120) + end + end - params = %{"compiler_version" => "", "files" => ""} - request = post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/verification/via/standard-input", params) + describe "/api/v2/smart-contracts/{address_hash}/verification/via/multi-part" do + test "get 404", %{conn: conn} do + contract = insert(:smart_contract) - assert %{"message" => "Already verified"} = json_response(request, 200) + params = %{"compiler_version" => "", "files" => ""} + request = post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/verification/via/multi-part", params) + + assert %{"message" => "Not found"} = json_response(request, 404) + end end - test "success verification", %{conn: conn} do - before = Application.get_env(:explorer, :solc_bin_api_url) + describe "/api/v2/smart-contracts/{address_hash}/verification/via/vyper-code" do + test "get 200 for verified contract", %{conn: conn} do + contract = insert(:smart_contract) - Application.put_env(:explorer, :solc_bin_api_url, "https://solc-bin.ethereum.org") + params = %{"compiler_version" => "", "source_code" => ""} + request = post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/verification/via/vyper-code", params) - path = File.cwd!() <> "/../explorer/test/support/fixture/smart_contract/standard_input.json" - json = File.read!(path) + assert %{"message" => "Already verified"} = json_response(request, 200) + end - bytecode = - "0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063893d20e81461003b578063a6f9dae11461005a575b600080fd5b600054604080516001600160a01b039092168252519081900360200190f35b61006d61006836600461011e565b61006f565b005b6000546001600160a01b031633146100c35760405162461bcd60e51b815260206004820152601360248201527221b0b63632b91034b9903737ba1037bbb732b960691b604482015260640160405180910390fd5b600080546040516001600160a01b03808516939216917f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73591a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006020828403121561013057600080fd5b81356001600160a01b038116811461014757600080fd5b939250505056fea26469706673582212206570365ac95ba46c8d0928763befe51fb6fc8a93499f7dabfda28d18ee673a3e64736f6c63430008110033" + test "success verification", %{conn: conn} do + before = Application.get_env(:explorer, :solc_bin_api_url) - input = - "0x608060405234801561001057600080fd5b5060405161026438038061026483398101604081905261002f91610076565b600080546001600160a01b0319163390811782556040519091907f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a735908290a35050506100d1565b60008060006060848603121561008b57600080fd5b83516001600160701b03811681146100a257600080fd5b60208501519093506001600160a01b03811681146100bf57600080fd5b80925050604084015190509250925092565b610184806100e06000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063893d20e81461003b578063a6f9dae11461005a575b600080fd5b600054604080516001600160a01b039092168252519081900360200190f35b61006d61006836600461011e565b61006f565b005b6000546001600160a01b031633146100c35760405162461bcd60e51b815260206004820152601360248201527221b0b63632b91034b9903737ba1037bbb732b960691b604482015260640160405180910390fd5b600080546040516001600160a01b03808516939216917f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73591a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006020828403121561013057600080fd5b81356001600160a01b038116811461014757600080fd5b939250505056fea26469706673582212206570365ac95ba46c8d0928763befe51fb6fc8a93499f7dabfda28d18ee673a3e64736f6c6343000811003300000000000000000000000000000000000000000000000000000002d2982db3000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e666f6f0000000000000000000000000000000000000000000000000000000000" + Application.put_env(:explorer, :solc_bin_api_url, "https://solc-bin.ethereum.org") - contract_address = insert(:contract_address, contract_code: bytecode) + path = File.cwd!() <> "/../explorer/test/support/fixture/smart_contract/vyper.vy" + contract = File.read!(path) - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: input - ) - |> with_block(status: :ok) + bytecode = + "0x600436101561000d57610572565b600035601c52600051341561002157600080fd5b63a9059cbb8114156100785760043560a01c1561003d57600080fd5b3361014052600435610160526024356101805261018051610160516101405160065801610578565b6101e0526101e050600160005260206000f35b6323b872dd8114156101195760043560a01c1561009457600080fd5b60243560a01c156100a457600080fd5b60043561014052602435610160526044356101805261018051610160516101405160065801610578565b6101e0526101e050600460043560e05260c052604060c0203360e05260c052604060c02080546044358082101561010457600080fd5b80820390509050815550600160005260206000f35b63095ea7b381141561020a5760043560a01c1561013557600080fd5b600854602435111515156101ad576308c379a061014052602061016052603a610180527f43616e7420417070726f7665206d6f7265207468616e20312528313030204d696101a0527f6c6c696f6e2920546f6b656e7320666f72207472616e736665720000000000006101c05261018050608461015cfd5b60243560043360e05260c052604060c02060043560e05260c052604060c0205560243561014052600435337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9256020610140a3600160005260206000f35b6340c10f198114156102c65760043560a01c1561022657600080fd5b600654331461023457600080fd5b60006004351861024357600080fd5b6005805460243581818301101561025957600080fd5b80820190509050815550600360043560e05260c052604060c020805460243581818301101561028757600080fd5b808201905090508155506024356101405260043560007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6020610140a3005b6342966c688114156102f45733610140526004356101605261016051610140516006580161074d565b600050005b6379cc679081141561036c5760043560a01c1561031057600080fd5b600460043560e05260c052604060c0203360e05260c052604060c02080546024358082101561033e57600080fd5b80820390509050815550600435610140526024356101605261016051610140516006580161074d565b600050005b6306fdde038114156104115760008060c052602060c020610180602082540161012060006003818352015b826101205160200211156103aa576103cc565b61012051850154610120516020028501525b8151600101808352811415610397575b50505050505061018051806101a001818260206001820306601f82010390500336823750506020610160526040610180510160206001820306601f8201039050610160f35b6395d89b418114156104b65760018060c052602060c020610180602082540161012060006002818352015b8261012051602002111561044f57610471565b61012051850154610120516020028501525b815160010180835281141561043c575b50505050505061018051806101a001818260206001820306601f82010390500336823750506020610160526040610180510160206001820306601f8201039050610160f35b63313ce5678114156104ce5760025460005260206000f35b6370a082318114156105045760043560a01c156104ea57600080fd5b600360043560e05260c052604060c0205460005260206000f35b63dd62ed3e8114156105585760043560a01c1561052057600080fd5b60243560a01c1561053057600080fd5b600460043560e05260c052604060c02060243560e05260c052604060c0205460005260206000f35b6318160ddd8114156105705760055460005260206000f35b505b60006000fd5b6101a0526101405261016052610180526008546101805111151515610601576308c379a06101c05260206101e0526028610200527f5472616e73666572206c696d6974206f6620312528313030204d696c6c696f6e610220527f2920546f6b656e73000000000000000000000000000000000000000000000000610240526102005060846101dcfd5b60036101605160e05260c052604060c020546101805181818301101561062657600080fd5b808201905090506101c0526008546101c051111515156106aa576308c379a06101e052602061020052603a610220527f53696e676c652077616c6c65742063616e6e6f7420686f6c64206d6f72652074610240527f68616e20312528313030204d696c6c696f6e2920546f6b656e73000000000000610260526102205060846101fcfd5b60036101405160e05260c052604060c020805461018051808210156106ce57600080fd5b8082039050905081555060036101605160e05260c052604060c0208054610180518181830110156106fe57600080fd5b80820190509050815550610180516101e05261016051610140517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206101e0a360016000526000516101a051565b6101805261014052610160526000610140511861076957600080fd5b60058054610160518082101561077e57600080fd5b8082039050905081555060036101405160e05260c052604060c020805461016051808210156107ac57600080fd5b80820390509050815550610160516101a0526000610140517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206101a0a36101805156" - topic = "addresses:#{contract_address.hash}" + input = + "0x6402540be4006007556305f5e10060085560126002556006610140527f4b6f6f6f706100000000000000000000000000000000000000000000000000006101605261014080600060c052602060c020602082510161012060006002818352015b8261012051602002111561007257610094565b61012051602002850151610120518501555b815160010180835281141561005f575b5050505050506003610140527f4b4f4f00000000000000000000000000000000000000000000000000000000006101605261014080600160c052602060c020602082510161012060006002818352015b826101205160200211156100f757610119565b61012051602002850151610120518501555b81516001018083528114156100e4575b505050505050600754604e6002541061013157600080fd5b600254600a0a808202821582848304141761014b57600080fd5b80905090509050610140526101405160033360e05260c052604060c02055610140516005553360065561014051610160523360007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6020610160a361099c56600436101561000d57610572565b600035601c52600051341561002157600080fd5b63a9059cbb8114156100785760043560a01c1561003d57600080fd5b3361014052600435610160526024356101805261018051610160516101405160065801610578565b6101e0526101e050600160005260206000f35b6323b872dd8114156101195760043560a01c1561009457600080fd5b60243560a01c156100a457600080fd5b60043561014052602435610160526044356101805261018051610160516101405160065801610578565b6101e0526101e050600460043560e05260c052604060c0203360e05260c052604060c02080546044358082101561010457600080fd5b80820390509050815550600160005260206000f35b63095ea7b381141561020a5760043560a01c1561013557600080fd5b600854602435111515156101ad576308c379a061014052602061016052603a610180527f43616e7420417070726f7665206d6f7265207468616e20312528313030204d696101a0527f6c6c696f6e2920546f6b656e7320666f72207472616e736665720000000000006101c05261018050608461015cfd5b60243560043360e05260c052604060c02060043560e05260c052604060c0205560243561014052600435337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9256020610140a3600160005260206000f35b6340c10f198114156102c65760043560a01c1561022657600080fd5b600654331461023457600080fd5b60006004351861024357600080fd5b6005805460243581818301101561025957600080fd5b80820190509050815550600360043560e05260c052604060c020805460243581818301101561028757600080fd5b808201905090508155506024356101405260043560007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6020610140a3005b6342966c688114156102f45733610140526004356101605261016051610140516006580161074d565b600050005b6379cc679081141561036c5760043560a01c1561031057600080fd5b600460043560e05260c052604060c0203360e05260c052604060c02080546024358082101561033e57600080fd5b80820390509050815550600435610140526024356101605261016051610140516006580161074d565b600050005b6306fdde038114156104115760008060c052602060c020610180602082540161012060006003818352015b826101205160200211156103aa576103cc565b61012051850154610120516020028501525b8151600101808352811415610397575b50505050505061018051806101a001818260206001820306601f82010390500336823750506020610160526040610180510160206001820306601f8201039050610160f35b6395d89b418114156104b65760018060c052602060c020610180602082540161012060006002818352015b8261012051602002111561044f57610471565b61012051850154610120516020028501525b815160010180835281141561043c575b50505050505061018051806101a001818260206001820306601f82010390500336823750506020610160526040610180510160206001820306601f8201039050610160f35b63313ce5678114156104ce5760025460005260206000f35b6370a082318114156105045760043560a01c156104ea57600080fd5b600360043560e05260c052604060c0205460005260206000f35b63dd62ed3e8114156105585760043560a01c1561052057600080fd5b60243560a01c1561053057600080fd5b600460043560e05260c052604060c02060243560e05260c052604060c0205460005260206000f35b6318160ddd8114156105705760055460005260206000f35b505b60006000fd5b6101a0526101405261016052610180526008546101805111151515610601576308c379a06101c05260206101e0526028610200527f5472616e73666572206c696d6974206f6620312528313030204d696c6c696f6e610220527f2920546f6b656e73000000000000000000000000000000000000000000000000610240526102005060846101dcfd5b60036101605160e05260c052604060c020546101805181818301101561062657600080fd5b808201905090506101c0526008546101c051111515156106aa576308c379a06101e052602061020052603a610220527f53696e676c652077616c6c65742063616e6e6f7420686f6c64206d6f72652074610240527f68616e20312528313030204d696c6c696f6e2920546f6b656e73000000000000610260526102205060846101fcfd5b60036101405160e05260c052604060c020805461018051808210156106ce57600080fd5b8082039050905081555060036101605160e05260c052604060c0208054610180518181830110156106fe57600080fd5b80820190509050815550610180516101e05261016051610140517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206101e0a360016000526000516101a051565b6101805261014052610160526000610140511861076957600080fd5b60058054610160518082101561077e57600080fd5b8082039050905081555060036101405160e05260c052604060c020805461016051808210156107ac57600080fd5b80820390509050815550610160516101a0526000610140517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206101a0a361018051565b6101ab61099c036101ab6000396101ab61099c036000f3" - {:ok, _reply, _socket} = - BlockScoutWeb.UserSocketV2 - |> socket("no_id", %{}) - |> subscribe_and_join(topic) + contract_address = insert(:contract_address, contract_code: bytecode) - multipart = - Multipart.new() - |> Multipart.add_field("compiler_version", "v0.8.17+commit.8df45f5f") - |> Multipart.add_file_content(json, "name.json", - name: "files[0]", - headers: [{"content-type", "application/json"}] + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: input ) + |> with_block(status: :ok) - body = - multipart - |> Multipart.body() - |> Enum.to_list() - |> to_str() + topic = "addresses:#{contract_address.hash}" - [{name, value}] = Multipart.headers(multipart) + {:ok, _reply, _socket} = + BlockScoutWeb.UserSocketV2 + |> socket("no_id", %{}) + |> subscribe_and_join(topic) - request = - post( - conn - |> Plug.Conn.put_req_header( - name, - value - ), - "/api/v2/smart-contracts/#{contract_address.hash}/verification/via/standard-input", - body - ) + params = %{ + "source_code" => contract, + "compiler_version" => "v0.2.12", + "contract_name" => "abc" + } - assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) + request = post(conn, "/api/v2/smart-contracts/#{contract_address.hash}/verification/via/vyper-code", params) - assert_receive %Phoenix.Socket.Message{ - payload: %{status: "success"}, - event: "verification_result", - topic: ^topic - }, - :timer.seconds(300) + assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) - Application.put_env(:explorer, :solc_bin_api_url, before) - end - end + assert_receive %Phoenix.Socket.Message{ + payload: %{status: "success"}, + event: "verification_result", + topic: ^topic + }, + :timer.seconds(300) - describe "/api/v2/smart-contracts/{address_hash}/verification/via/sourcify" do - test "get 200 for verified contract", %{conn: conn} do - contract = insert(:smart_contract) + Application.put_env(:explorer, :solc_bin_api_url, before) + end - params = %{"files" => ""} - request = post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/verification/via/sourcify", params) + test "blueprint contract verification", %{conn: conn} do + bypass = Bypass.open() - assert %{"message" => "Already verified"} = json_response(request, 200) - end + sc_verifier_response = + File.read!( + "./test/support/fixture/smart_contract/smart_contract_verifier_vyper_multi_part_blueprint_response.json" + ) - test "verify contract from sourcify repo", %{conn: conn} do - address = "0xf26594F585De4EB0Ae9De865d9053FEe02ac6eF1" + old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) - _contract = insert(:address, hash: address, contract_code: "0x01") + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, + service_url: "http://localhost:#{bypass.port}", + enabled: true, + type: "sc_verifier", + eth_bytecode_db?: true + ) - topic = "addresses:#{String.downcase(address)}" + Bypass.expect_once(bypass, "POST", "/api/v2//verifier/vyper/sources%3Averify-multi-part", fn conn -> + Conn.resp(conn, 200, sc_verifier_response) + end) - {:ok, _reply, _socket} = - UserSocketV2 - |> socket("no_id", %{}) - |> subscribe_and_join(topic) + bytecode = + "0xfe7100346100235760206100995f395f516001555f5f5561005f61002760003961005f6000f35b5f80fd5f3560e01c60026001821660011b61005b01601e395f51565b63158ef93e81186100535734610057575f5460405260206040f3610053565b633fa4f245811861005357346100575760015460405260206040f35b5f5ffd5b5f80fd0018003784185f810400a16576797065728300030a0013" - multipart = - Multipart.new() - |> Multipart.add_file_content("content", "name.json", - name: "files[0]", - headers: [{"content-type", "application/json"}] + input = + "0x61009c3d81600a3d39f3fe7100346100235760206100995f395f516001555f5f5561005f61002760003961005f6000f35b5f80fd5f3560e01c60026001821660011b61005b01601e395f51565b63158ef93e81186100535734610057575f5460405260206040f3610053565b633fa4f245811861005357346100575760015460405260206040f35b5f5ffd5b5f80fd0018003784185f810400a16576797065728300030a0013" + + contract_address = insert(:contract_address, contract_code: bytecode) + + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: input ) + |> with_block(status: :ok) - body = - multipart - |> Multipart.body() - |> Enum.to_list() - |> to_str() + topic = "addresses:#{contract_address.hash}" - [{name, value}] = Multipart.headers(multipart) + {:ok, _reply, _socket} = + BlockScoutWeb.UserSocketV2 + |> socket("no_id", %{}) + |> subscribe_and_join(topic) - request = - post( - conn - |> Plug.Conn.put_req_header( - name, - value - ), - "/api/v2/smart-contracts/#{address}/verification/via/sourcify", - body - ) + # We can actually use any params here, as verification service response is defined in `sc_verifier_response` + params = %{ + "source_code" => "some_valid_source_code", + "compiler_version" => "v0.3.10", + "contract_name" => "abc" + } - assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) + request = post(conn, "/api/v2/smart-contracts/#{contract_address.hash}/verification/via/vyper-code", params) - assert_receive %Phoenix.Socket.Message{ - payload: %{status: "success"}, - event: "verification_result", - topic: ^topic - }, - :timer.seconds(120) + assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) + + assert_receive %Phoenix.Socket.Message{ + payload: %{status: "success"}, + event: "verification_result", + topic: ^topic + }, + :timer.seconds(300) + + # Assert that the `is_blueprint=true` is stored in the database after verification + TestHelper.get_eip1967_implementation_zero_addresses() + + request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(contract_address.hash)}") + response = json_response(request, 200) + + assert response["is_blueprint"] == true + + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) + Bypass.down(bypass) + end end - end - describe "/api/v2/smart-contracts/{address_hash}/verification/via/multi-part" do - test "get 404", %{conn: conn} do - contract = insert(:smart_contract) + describe "/api/v2/smart-contracts/{address_hash}/verification/via/vyper-multi-part" do + test "get 404", %{conn: conn} do + contract = insert(:smart_contract) - params = %{"compiler_version" => "", "files" => ""} - request = post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/verification/via/multi-part", params) + params = %{"compiler_version" => "", "files" => ""} - assert %{"message" => "Not found"} = json_response(request, 404) + request = + post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/verification/via/vyper-multi-part", params) + + assert %{"message" => "Not found"} = json_response(request, 404) + end end end - describe "/api/v2/smart-contracts/{address_hash}/verification/via/vyper-code" do + describe "/api/v2/smart-contracts/{address_hash}/verification/via/standard-input" do test "get 200 for verified contract", %{conn: conn} do contract = insert(:smart_contract) - params = %{"compiler_version" => "", "source_code" => ""} - request = post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/verification/via/vyper-code", params) + params = %{"compiler_version" => "", "files" => ""} + request = post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/verification/via/standard-input", params) assert %{"message" => "Already verified"} = json_response(request, 200) end @@ -308,14 +377,14 @@ defmodule BlockScoutWeb.API.V2.VerificationControllerTest do Application.put_env(:explorer, :solc_bin_api_url, "https://solc-bin.ethereum.org") - path = File.cwd!() <> "/../explorer/test/support/fixture/smart_contract/vyper.vy" - contract = File.read!(path) + path = File.cwd!() <> "/../explorer/test/support/fixture/smart_contract/standard_input.json" + json = File.read!(path) bytecode = - "0x600436101561000d57610572565b600035601c52600051341561002157600080fd5b63a9059cbb8114156100785760043560a01c1561003d57600080fd5b3361014052600435610160526024356101805261018051610160516101405160065801610578565b6101e0526101e050600160005260206000f35b6323b872dd8114156101195760043560a01c1561009457600080fd5b60243560a01c156100a457600080fd5b60043561014052602435610160526044356101805261018051610160516101405160065801610578565b6101e0526101e050600460043560e05260c052604060c0203360e05260c052604060c02080546044358082101561010457600080fd5b80820390509050815550600160005260206000f35b63095ea7b381141561020a5760043560a01c1561013557600080fd5b600854602435111515156101ad576308c379a061014052602061016052603a610180527f43616e7420417070726f7665206d6f7265207468616e20312528313030204d696101a0527f6c6c696f6e2920546f6b656e7320666f72207472616e736665720000000000006101c05261018050608461015cfd5b60243560043360e05260c052604060c02060043560e05260c052604060c0205560243561014052600435337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9256020610140a3600160005260206000f35b6340c10f198114156102c65760043560a01c1561022657600080fd5b600654331461023457600080fd5b60006004351861024357600080fd5b6005805460243581818301101561025957600080fd5b80820190509050815550600360043560e05260c052604060c020805460243581818301101561028757600080fd5b808201905090508155506024356101405260043560007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6020610140a3005b6342966c688114156102f45733610140526004356101605261016051610140516006580161074d565b600050005b6379cc679081141561036c5760043560a01c1561031057600080fd5b600460043560e05260c052604060c0203360e05260c052604060c02080546024358082101561033e57600080fd5b80820390509050815550600435610140526024356101605261016051610140516006580161074d565b600050005b6306fdde038114156104115760008060c052602060c020610180602082540161012060006003818352015b826101205160200211156103aa576103cc565b61012051850154610120516020028501525b8151600101808352811415610397575b50505050505061018051806101a001818260206001820306601f82010390500336823750506020610160526040610180510160206001820306601f8201039050610160f35b6395d89b418114156104b65760018060c052602060c020610180602082540161012060006002818352015b8261012051602002111561044f57610471565b61012051850154610120516020028501525b815160010180835281141561043c575b50505050505061018051806101a001818260206001820306601f82010390500336823750506020610160526040610180510160206001820306601f8201039050610160f35b63313ce5678114156104ce5760025460005260206000f35b6370a082318114156105045760043560a01c156104ea57600080fd5b600360043560e05260c052604060c0205460005260206000f35b63dd62ed3e8114156105585760043560a01c1561052057600080fd5b60243560a01c1561053057600080fd5b600460043560e05260c052604060c02060243560e05260c052604060c0205460005260206000f35b6318160ddd8114156105705760055460005260206000f35b505b60006000fd5b6101a0526101405261016052610180526008546101805111151515610601576308c379a06101c05260206101e0526028610200527f5472616e73666572206c696d6974206f6620312528313030204d696c6c696f6e610220527f2920546f6b656e73000000000000000000000000000000000000000000000000610240526102005060846101dcfd5b60036101605160e05260c052604060c020546101805181818301101561062657600080fd5b808201905090506101c0526008546101c051111515156106aa576308c379a06101e052602061020052603a610220527f53696e676c652077616c6c65742063616e6e6f7420686f6c64206d6f72652074610240527f68616e20312528313030204d696c6c696f6e2920546f6b656e73000000000000610260526102205060846101fcfd5b60036101405160e05260c052604060c020805461018051808210156106ce57600080fd5b8082039050905081555060036101605160e05260c052604060c0208054610180518181830110156106fe57600080fd5b80820190509050815550610180516101e05261016051610140517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206101e0a360016000526000516101a051565b6101805261014052610160526000610140511861076957600080fd5b60058054610160518082101561077e57600080fd5b8082039050905081555060036101405160e05260c052604060c020805461016051808210156107ac57600080fd5b80820390509050815550610160516101a0526000610140517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206101a0a36101805156" + "0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063893d20e81461003b578063a6f9dae11461005a575b600080fd5b600054604080516001600160a01b039092168252519081900360200190f35b61006d61006836600461011e565b61006f565b005b6000546001600160a01b031633146100c35760405162461bcd60e51b815260206004820152601360248201527221b0b63632b91034b9903737ba1037bbb732b960691b604482015260640160405180910390fd5b600080546040516001600160a01b03808516939216917f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73591a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006020828403121561013057600080fd5b81356001600160a01b038116811461014757600080fd5b939250505056fea26469706673582212206570365ac95ba46c8d0928763befe51fb6fc8a93499f7dabfda28d18ee673a3e64736f6c63430008110033" input = - "0x6402540be4006007556305f5e10060085560126002556006610140527f4b6f6f6f706100000000000000000000000000000000000000000000000000006101605261014080600060c052602060c020602082510161012060006002818352015b8261012051602002111561007257610094565b61012051602002850151610120518501555b815160010180835281141561005f575b5050505050506003610140527f4b4f4f00000000000000000000000000000000000000000000000000000000006101605261014080600160c052602060c020602082510161012060006002818352015b826101205160200211156100f757610119565b61012051602002850151610120518501555b81516001018083528114156100e4575b505050505050600754604e6002541061013157600080fd5b600254600a0a808202821582848304141761014b57600080fd5b80905090509050610140526101405160033360e05260c052604060c02055610140516005553360065561014051610160523360007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6020610160a361099c56600436101561000d57610572565b600035601c52600051341561002157600080fd5b63a9059cbb8114156100785760043560a01c1561003d57600080fd5b3361014052600435610160526024356101805261018051610160516101405160065801610578565b6101e0526101e050600160005260206000f35b6323b872dd8114156101195760043560a01c1561009457600080fd5b60243560a01c156100a457600080fd5b60043561014052602435610160526044356101805261018051610160516101405160065801610578565b6101e0526101e050600460043560e05260c052604060c0203360e05260c052604060c02080546044358082101561010457600080fd5b80820390509050815550600160005260206000f35b63095ea7b381141561020a5760043560a01c1561013557600080fd5b600854602435111515156101ad576308c379a061014052602061016052603a610180527f43616e7420417070726f7665206d6f7265207468616e20312528313030204d696101a0527f6c6c696f6e2920546f6b656e7320666f72207472616e736665720000000000006101c05261018050608461015cfd5b60243560043360e05260c052604060c02060043560e05260c052604060c0205560243561014052600435337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9256020610140a3600160005260206000f35b6340c10f198114156102c65760043560a01c1561022657600080fd5b600654331461023457600080fd5b60006004351861024357600080fd5b6005805460243581818301101561025957600080fd5b80820190509050815550600360043560e05260c052604060c020805460243581818301101561028757600080fd5b808201905090508155506024356101405260043560007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6020610140a3005b6342966c688114156102f45733610140526004356101605261016051610140516006580161074d565b600050005b6379cc679081141561036c5760043560a01c1561031057600080fd5b600460043560e05260c052604060c0203360e05260c052604060c02080546024358082101561033e57600080fd5b80820390509050815550600435610140526024356101605261016051610140516006580161074d565b600050005b6306fdde038114156104115760008060c052602060c020610180602082540161012060006003818352015b826101205160200211156103aa576103cc565b61012051850154610120516020028501525b8151600101808352811415610397575b50505050505061018051806101a001818260206001820306601f82010390500336823750506020610160526040610180510160206001820306601f8201039050610160f35b6395d89b418114156104b65760018060c052602060c020610180602082540161012060006002818352015b8261012051602002111561044f57610471565b61012051850154610120516020028501525b815160010180835281141561043c575b50505050505061018051806101a001818260206001820306601f82010390500336823750506020610160526040610180510160206001820306601f8201039050610160f35b63313ce5678114156104ce5760025460005260206000f35b6370a082318114156105045760043560a01c156104ea57600080fd5b600360043560e05260c052604060c0205460005260206000f35b63dd62ed3e8114156105585760043560a01c1561052057600080fd5b60243560a01c1561053057600080fd5b600460043560e05260c052604060c02060243560e05260c052604060c0205460005260206000f35b6318160ddd8114156105705760055460005260206000f35b505b60006000fd5b6101a0526101405261016052610180526008546101805111151515610601576308c379a06101c05260206101e0526028610200527f5472616e73666572206c696d6974206f6620312528313030204d696c6c696f6e610220527f2920546f6b656e73000000000000000000000000000000000000000000000000610240526102005060846101dcfd5b60036101605160e05260c052604060c020546101805181818301101561062657600080fd5b808201905090506101c0526008546101c051111515156106aa576308c379a06101e052602061020052603a610220527f53696e676c652077616c6c65742063616e6e6f7420686f6c64206d6f72652074610240527f68616e20312528313030204d696c6c696f6e2920546f6b656e73000000000000610260526102205060846101fcfd5b60036101405160e05260c052604060c020805461018051808210156106ce57600080fd5b8082039050905081555060036101605160e05260c052604060c0208054610180518181830110156106fe57600080fd5b80820190509050815550610180516101e05261016051610140517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206101e0a360016000526000516101a051565b6101805261014052610160526000610140511861076957600080fd5b60058054610160518082101561077e57600080fd5b8082039050905081555060036101405160e05260c052604060c020805461016051808210156107ac57600080fd5b80820390509050815550610160516101a0526000610140517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206101a0a361018051565b6101ab61099c036101ab6000396101ab61099c036000f3" + "0x608060405234801561001057600080fd5b5060405161026438038061026483398101604081905261002f91610076565b600080546001600160a01b0319163390811782556040519091907f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a735908290a35050506100d1565b60008060006060848603121561008b57600080fd5b83516001600160701b03811681146100a257600080fd5b60208501519093506001600160a01b03811681146100bf57600080fd5b80925050604084015190509250925092565b610184806100e06000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063893d20e81461003b578063a6f9dae11461005a575b600080fd5b600054604080516001600160a01b039092168252519081900360200190f35b61006d61006836600461011e565b61006f565b005b6000546001600160a01b031633146100c35760405162461bcd60e51b815260206004820152601360248201527221b0b63632b91034b9903737ba1037bbb732b960691b604482015260640160405180910390fd5b600080546040516001600160a01b03808516939216917f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73591a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60006020828403121561013057600080fd5b81356001600160a01b038116811461014757600080fd5b939250505056fea26469706673582212206570365ac95ba46c8d0928763befe51fb6fc8a93499f7dabfda28d18ee673a3e64736f6c6343000811003300000000000000000000000000000000000000000000000000000002d2982db3000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e666f6f0000000000000000000000000000000000000000000000000000000000" contract_address = insert(:contract_address, contract_code: bytecode) @@ -333,77 +402,32 @@ defmodule BlockScoutWeb.API.V2.VerificationControllerTest do |> socket("no_id", %{}) |> subscribe_and_join(topic) - params = %{ - "source_code" => contract, - "compiler_version" => "v0.2.12", - "contract_name" => "abc" - } - - request = post(conn, "/api/v2/smart-contracts/#{contract_address.hash}/verification/via/vyper-code", params) - - assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) - - assert_receive %Phoenix.Socket.Message{ - payload: %{status: "success"}, - event: "verification_result", - topic: ^topic - }, - :timer.seconds(300) - - Application.put_env(:explorer, :solc_bin_api_url, before) - end - - test "blueprint contract verification", %{conn: conn} do - bypass = Bypass.open() - - sc_verifier_response = - File.read!( - "./test/support/fixture/smart_contract/smart_contract_verifier_vyper_multi_part_blueprint_response.json" + multipart = + Multipart.new() + |> Multipart.add_field("compiler_version", "v0.8.17+commit.8df45f5f") + |> Multipart.add_file_content(json, "name.json", + name: "files[0]", + headers: [{"content-type", "application/json"}] ) - old_env = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) - - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, - service_url: "http://localhost:#{bypass.port}", - enabled: true, - type: "sc_verifier", - eth_bytecode_db?: true - ) - - Bypass.expect_once(bypass, "POST", "/api/v2//verifier/vyper/sources%3Averify-multi-part", fn conn -> - Conn.resp(conn, 200, sc_verifier_response) - end) - - bytecode = - "0xfe7100346100235760206100995f395f516001555f5f5561005f61002760003961005f6000f35b5f80fd5f3560e01c60026001821660011b61005b01601e395f51565b63158ef93e81186100535734610057575f5460405260206040f3610053565b633fa4f245811861005357346100575760015460405260206040f35b5f5ffd5b5f80fd0018003784185f810400a16576797065728300030a0013" - - input = - "0x61009c3d81600a3d39f3fe7100346100235760206100995f395f516001555f5f5561005f61002760003961005f6000f35b5f80fd5f3560e01c60026001821660011b61005b01601e395f51565b63158ef93e81186100535734610057575f5460405260206040f3610053565b633fa4f245811861005357346100575760015460405260206040f35b5f5ffd5b5f80fd0018003784185f810400a16576797065728300030a0013" - - contract_address = insert(:contract_address, contract_code: bytecode) - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: input - ) - |> with_block(status: :ok) - - topic = "addresses:#{contract_address.hash}" - - {:ok, _reply, _socket} = - BlockScoutWeb.UserSocketV2 - |> socket("no_id", %{}) - |> subscribe_and_join(topic) + body = + multipart + |> Multipart.body() + |> Enum.to_list() + |> to_str() - # We can actually use any params here, as verification service response is defined in `sc_verifier_response` - params = %{ - "source_code" => "some_valid_source_code", - "compiler_version" => "v0.3.10", - "contract_name" => "abc" - } + [{name, value}] = Multipart.headers(multipart) - request = post(conn, "/api/v2/smart-contracts/#{contract_address.hash}/verification/via/vyper-code", params) + request = + post( + conn + |> Plug.Conn.put_req_header( + name, + value + ), + "/api/v2/smart-contracts/#{contract_address.hash}/verification/via/standard-input", + body + ) assert %{"message" => "Smart-contract verification started"} = json_response(request, 200) @@ -414,27 +438,7 @@ defmodule BlockScoutWeb.API.V2.VerificationControllerTest do }, :timer.seconds(300) - # Assert that the `is_blueprint=true` is stored in the database after verification - TestHelper.get_eip1967_implementation_zero_addresses() - - request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(contract_address.hash)}") - response = json_response(request, 200) - - assert response["is_blueprint"] == true - - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, old_env) - Bypass.down(bypass) - end - end - - describe "/api/v2/smart-contracts/{address_hash}/verification/via/vyper-multi-part" do - test "get 404", %{conn: conn} do - contract = insert(:smart_contract) - - params = %{"compiler_version" => "", "files" => ""} - request = post(conn, "/api/v2/smart-contracts/#{contract.address_hash}/verification/via/vyper-multi-part", params) - - assert %{"message" => "Not found"} = json_response(request, 404) + Application.put_env(:explorer, :solc_bin_api_url, before) end end diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index 302ed4dda86e..d6a881176c6f 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -1,3 +1,85 @@ +defmodule Explorer.Chain.SmartContract.Schema do + @moduledoc """ + Models smart-contract. + """ + alias Explorer.Chain.SmartContract.ExternalLibrary + + alias Explorer.Chain.{ + Address, + DecompiledSmartContract, + Hash, + SmartContractAdditionalSource + } + + case Application.compile_env(:explorer, :chain_type) do + :zksync -> + @chain_type_fields quote( + do: [ + field(:optimization_runs, :string), + field(:zk_compiler_version, :string, null: true) + ] + ) + + _ -> + @chain_type_fields quote(do: [field(:optimization_runs, :integer)]) + end + + defmacro generate do + quote do + typed_schema "smart_contracts" do + field(:name, :string, null: false) + field(:compiler_version, :string, null: false) + field(:optimization, :boolean, null: false) + field(:contract_source_code, :string, null: false) + field(:constructor_arguments, :string) + field(:evm_version, :string) + embeds_many(:external_libraries, ExternalLibrary, on_replace: :delete) + field(:abi, {:array, :map}) + field(:verified_via_sourcify, :boolean) + field(:verified_via_eth_bytecode_db, :boolean) + field(:verified_via_verifier_alliance, :boolean) + field(:partially_verified, :boolean) + field(:file_path, :string) + field(:is_vyper_contract, :boolean) + field(:is_changed_bytecode, :boolean, default: false) + field(:bytecode_checked_at, :utc_datetime_usec, default: DateTime.add(DateTime.utc_now(), -86400, :second)) + field(:contract_code_md5, :string, null: false) + field(:compiler_settings, :map) + field(:autodetect_constructor_args, :boolean, virtual: true) + field(:is_yul, :boolean, virtual: true) + field(:metadata_from_verified_bytecode_twin, :boolean, virtual: true) + field(:license_type, Ecto.Enum, values: @license_enum, default: :none) + field(:certified, :boolean) + field(:is_blueprint, :boolean) + + has_many( + :decompiled_smart_contracts, + DecompiledSmartContract, + foreign_key: :address_hash + ) + + belongs_to( + :address, + Address, + foreign_key: :address_hash, + references: :hash, + type: Hash.Address, + null: false + ) + + has_many(:smart_contract_additional_sources, SmartContractAdditionalSource, + references: :address_hash, + foreign_key: :address_hash + ) + + timestamps() + + unquote_splicing(@chain_type_fields) + end + end + end +end + defmodule Explorer.Chain.SmartContract do @moduledoc """ The representation of a verified Smart Contract. @@ -9,6 +91,7 @@ defmodule Explorer.Chain.SmartContract do """ require Logger + require Explorer.Chain.SmartContract.Schema use Explorer.Schema @@ -19,7 +102,6 @@ defmodule Explorer.Chain.SmartContract do Address, ContractMethod, Data, - DecompiledSmartContract, Hash, InternalTransaction, SmartContract, @@ -29,7 +111,7 @@ defmodule Explorer.Chain.SmartContract do alias Explorer.Chain.Address.Name, as: AddressName - alias Explorer.Chain.SmartContract.{ExternalLibrary, Proxy} + alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.SmartContract.Helper alias Explorer.SmartContract.Solidity.Verifier @@ -39,6 +121,21 @@ defmodule Explorer.Chain.SmartContract do @burn_address_hash_string "0x0000000000000000000000000000000000000000" @dead_address_hash_string "0x000000000000000000000000000000000000dEaD" + @required_attrs ~w(compiler_version optimization address_hash contract_code_md5)a + + @optional_common_attrs ~w(name contract_source_code evm_version optimization_runs constructor_arguments verified_via_sourcify verified_via_eth_bytecode_db verified_via_verifier_alliance partially_verified file_path is_vyper_contract is_changed_bytecode bytecode_checked_at autodetect_constructor_args license_type certified is_blueprint)a + + @optional_changeset_attrs ~w(abi compiler_settings)a + @optional_invalid_contract_changeset_attrs ~w(autodetect_constructor_args)a + + @chain_type_optional_attrs (case Application.compile_env(:explorer, :chain_type) do + :zksync -> + ~w(zk_compiler_version)a + + _ -> + ~w()a + end) + @doc """ Returns burn address hash """ @@ -260,6 +357,12 @@ defmodule Explorer.Chain.SmartContract do * `name` - the human-readable name of the smart contract. * `compiler_version` - the version of the Solidity compiler used to compile `contract_source_code` with `optimization` into `address` `t:Explorer.Chain.Address.t/0` `contract_code`. + #{case Application.compile_env(:explorer, :chain_type) do + :zksync -> """ + * `zk_compiler_version` - the version of ZkSolc or ZkVyper compilers. + """ + _ -> "" + end} * `optimization` - whether optimizations were turned on when compiling `contract_source_code` into `address` `t:Explorer.Chain.Address.t/0` `contract_code`. * `contract_source_code` - the Solidity source code that was compiled by `compiler_version` with `optimization` to @@ -281,94 +384,24 @@ defmodule Explorer.Chain.SmartContract do * `certified` - boolean flag, which can be set for set of smart-contracts via runtime env variable to prioritize those smart-contracts in the search. * `is_blueprint` - boolean flag, determines if contract is ERC-5202 compatible blueprint contract or not. """ - typed_schema "smart_contracts" do - field(:name, :string, null: false) - field(:compiler_version, :string, null: false) - field(:optimization, :boolean, null: false) - field(:contract_source_code, :string, null: false) - field(:constructor_arguments, :string) - field(:evm_version, :string) - field(:optimization_runs, :integer) - embeds_many(:external_libraries, ExternalLibrary, on_replace: :delete) - field(:abi, {:array, :map}) - field(:verified_via_sourcify, :boolean) - field(:verified_via_eth_bytecode_db, :boolean) - field(:verified_via_verifier_alliance, :boolean) - field(:partially_verified, :boolean) - field(:file_path, :string) - field(:is_vyper_contract, :boolean) - field(:is_changed_bytecode, :boolean, default: false) - field(:bytecode_checked_at, :utc_datetime_usec, default: DateTime.add(DateTime.utc_now(), -86400, :second)) - field(:contract_code_md5, :string, null: false) - field(:compiler_settings, :map) - field(:autodetect_constructor_args, :boolean, virtual: true) - field(:is_yul, :boolean, virtual: true) - field(:metadata_from_verified_bytecode_twin, :boolean, virtual: true) - field(:license_type, Ecto.Enum, values: @license_enum, default: :none) - field(:certified, :boolean) - field(:is_blueprint, :boolean) - - has_many( - :decompiled_smart_contracts, - DecompiledSmartContract, - foreign_key: :address_hash - ) - - belongs_to( - :address, - Address, - foreign_key: :address_hash, - references: :hash, - type: Hash.Address, - null: false - ) - - has_many(:smart_contract_additional_sources, SmartContractAdditionalSource, - references: :address_hash, - foreign_key: :address_hash - ) - - timestamps() - end + Explorer.Chain.SmartContract.Schema.generate() def preload_decompiled_smart_contract(contract) do Repo.preload(contract, :decompiled_smart_contracts) end def changeset(%__MODULE__{} = smart_contract, attrs) do + attrs_to_cast = + @required_attrs ++ + @optional_common_attrs ++ + @optional_changeset_attrs ++ + @chain_type_optional_attrs + + required_for_validation = [:name, :contract_source_code] ++ @required_attrs + smart_contract - |> cast(attrs, [ - :name, - :compiler_version, - :optimization, - :contract_source_code, - :address_hash, - :abi, - :constructor_arguments, - :evm_version, - :optimization_runs, - :verified_via_sourcify, - :verified_via_eth_bytecode_db, - :verified_via_verifier_alliance, - :partially_verified, - :file_path, - :is_vyper_contract, - :is_changed_bytecode, - :bytecode_checked_at, - :contract_code_md5, - :compiler_settings, - :license_type, - :certified, - :is_blueprint - ]) - |> validate_required([ - :name, - :compiler_version, - :optimization, - :contract_source_code, - :address_hash, - :contract_code_md5 - ]) + |> cast(attrs, attrs_to_cast) + |> validate_required(required_for_validation) |> unique_constraint(:address_hash) |> prepare_changes(&upsert_contract_methods/1) end @@ -380,34 +413,18 @@ defmodule Explorer.Chain.SmartContract do error_message, verification_with_files? \\ false ) do + attrs_to_cast = + @required_attrs ++ + @optional_common_attrs ++ + @optional_invalid_contract_changeset_attrs ++ + @chain_type_optional_attrs + validated = smart_contract - |> cast(attrs, [ - :name, - :compiler_version, - :optimization, - :contract_source_code, - :address_hash, - :evm_version, - :optimization_runs, - :constructor_arguments, - :verified_via_sourcify, - :verified_via_eth_bytecode_db, - :verified_via_verifier_alliance, - :partially_verified, - :file_path, - :is_vyper_contract, - :is_changed_bytecode, - :bytecode_checked_at, - :contract_code_md5, - :autodetect_constructor_args, - :license_type, - :certified, - :is_blueprint - ]) + |> cast(attrs, attrs_to_cast) |> (&if(verification_with_files?, do: &1, - else: validate_required(&1, [:compiler_version, :optimization, :address_hash, :contract_code_md5]) + else: validate_required(&1, @required_attrs) )).() field_to_put_message = if verification_with_files?, do: :files, else: select_error_field(error) @@ -467,15 +484,24 @@ defmodule Explorer.Chain.SmartContract do end def merge_twin_contract_with_changeset(nil, %Changeset{} = changeset) do + optimization_runs = + if Application.get_env(:explorer, :chain_type) == :zksync, + do: "0", + else: "200" + changeset |> Changeset.put_change(:name, "") - |> Changeset.put_change(:optimization_runs, "200") + |> Changeset.put_change(:optimization_runs, optimization_runs) |> Changeset.put_change(:optimization, true) |> Changeset.put_change(:evm_version, "default") |> Changeset.put_change(:compiler_version, "latest") |> Changeset.put_change(:contract_source_code, "") |> Changeset.put_change(:autodetect_constructor_args, true) |> Changeset.put_change(:is_yul, false) + |> (&if(Application.get_env(:explorer, :chain_type) == :zksync, + do: Changeset.put_change(&1, :zk_compiler_version, "latest"), + else: &1 + )).() end def merge_twin_vyper_contract_with_changeset( @@ -500,6 +526,10 @@ defmodule Explorer.Chain.SmartContract do |> Changeset.put_change(:name, "Vyper_contract") |> Changeset.put_change(:compiler_version, "latest") |> Changeset.put_change(:contract_source_code, "") + |> (&if(Application.get_env(:explorer, :chain_type) == :zksync, + do: Changeset.put_change(&1, :zk_compiler_version, "latest"), + else: &1 + )).() end def license_types_enum, do: @license_enum diff --git a/apps/explorer/lib/explorer/chain_spec/genesis_data.ex b/apps/explorer/lib/explorer/chain_spec/genesis_data.ex index 556819872d2a..c9f32938dd87 100644 --- a/apps/explorer/lib/explorer/chain_spec/genesis_data.ex +++ b/apps/explorer/lib/explorer/chain_spec/genesis_data.ex @@ -296,6 +296,7 @@ defmodule Explorer.ChainSpec.GenesisData do address_hash: contract["address"], name: contract["name"], file_path: nil, + # todo: process zksync zk_compiler compiler_version: contract["compiler"], evm_version: nil, optimization_runs: nil, diff --git a/apps/explorer/lib/explorer/smart_contract/compiler_version.ex b/apps/explorer/lib/explorer/smart_contract/compiler_version.ex index fccfcc3f5901..0151ee24bd4f 100644 --- a/apps/explorer/lib/explorer/smart_contract/compiler_version.ex +++ b/apps/explorer/lib/explorer/smart_contract/compiler_version.ex @@ -12,11 +12,12 @@ defmodule Explorer.SmartContract.CompilerVersion do @doc """ Fetches a list of compilers from the Ethereum Solidity API. """ - @spec fetch_versions(:solc | :vyper) :: {atom, [map]} + @spec fetch_versions(:solc | :vyper | :zk) :: {atom, [map]} def fetch_versions(compiler) do case compiler do :solc -> fetch_solc_versions() :vyper -> fetch_vyper_versions() + :zk -> fetch_zk_versions() end end @@ -34,26 +35,58 @@ defmodule Explorer.SmartContract.CompilerVersion do fetch_compiler_versions(&RustVerifierInterface.get_versions_list/0, :solc) end + defp fetch_zk_versions do + fetch_compiler_versions(&RustVerifierInterface.get_versions_list/0, :zk) + end + defp fetch_vyper_versions do fetch_compiler_versions(&RustVerifierInterface.vyper_get_versions_list/0, :vyper) end defp fetch_compiler_versions(compiler_list_fn, compiler_type) do if RustVerifierInterface.enabled?() do - compiler_list_fn.() + fetch_compiler_versions_sc_verified_enabled(compiler_list_fn, compiler_type) else - headers = [{"Content-Type", "application/json"}] + if compiler_type == :zk do + {:ok, []} + else + headers = [{"Content-Type", "application/json"}] - case HTTPoison.get(source_url(compiler_type), headers) do - {:ok, %{status_code: 200, body: body}} -> - {:ok, format_data(body, compiler_type)} + # credo:disable-for-next-line + case HTTPoison.get(source_url(compiler_type), headers) do + {:ok, %{status_code: 200, body: body}} -> + {:ok, format_data(body, compiler_type)} + + {:ok, %{status_code: _status_code, body: body}} -> + {:error, Helper.decode_json(body)["error"]} + + {:error, %{reason: reason}} -> + {:error, reason} + end + end + end + end - {:ok, %{status_code: _status_code, body: body}} -> - {:error, Helper.decode_json(body)["error"]} + defp fetch_compiler_versions_sc_verified_enabled(compiler_list_fn, compiler_type) do + if Application.get_env(:explorer, :chain_type) == :zksync do + # todo: refactor opportunity, currently, Blockscout 2 identical requests to microservice in order to get + # Solc and Zk compiler versions + case compiler_list_fn.() do + {:ok, {solc_compilers, zk_compilers}} -> + choose_compiler(compiler_type, %{:solc_compilers => solc_compilers, :zk_compilers => zk_compilers}) - {:error, %{reason: reason}} -> - {:error, reason} + _ -> + {:error, "Verifier microservice is unavailable"} end + else + compiler_list_fn.() + end + end + + defp choose_compiler(compiler_type, compilers) do + case compiler_type do + :solc -> {:ok, compilers.solc_compilers} + :zk -> {:ok, compilers.zk_compilers} end end diff --git a/apps/explorer/lib/explorer/smart_contract/helper.ex b/apps/explorer/lib/explorer/smart_contract/helper.ex index 90530ee756fa..51e7d86817bb 100644 --- a/apps/explorer/lib/explorer/smart_contract/helper.ex +++ b/apps/explorer/lib/explorer/smart_contract/helper.ex @@ -115,9 +115,14 @@ defmodule Explorer.SmartContract.Helper do def prepare_bytecode_for_microservice(body, creation_input, deployed_bytecode) def prepare_bytecode_for_microservice(body, empty, deployed_bytecode) when is_nil(empty) do - body - |> Map.put("bytecodeType", "DEPLOYED_BYTECODE") - |> Map.put("bytecode", deployed_bytecode) + if Application.get_env(:explorer, :chain_type) == :zksync do + body + |> Map.put("code", deployed_bytecode) + else + body + |> Map.put("bytecodeType", "DEPLOYED_BYTECODE") + |> Map.put("bytecode", deployed_bytecode) + end end def prepare_bytecode_for_microservice(body, creation_bytecode, _deployed_bytecode) do @@ -173,15 +178,19 @@ defmodule Explorer.SmartContract.Helper do "chainId" => Application.get_env(:block_scout_web, :chain_id) } - case SmartContract.creation_tx_with_bytecode(address_hash) do - %{init: init, tx: tx} -> - {init, deployed_bytecode, tx |> tx_to_metadata(init) |> Map.merge(metadata)} + if Application.get_env(:explorer, :chain_type) == :zksync do + {nil, deployed_bytecode, metadata} + else + case SmartContract.creation_tx_with_bytecode(address_hash) do + %{init: init, tx: tx} -> + {init, deployed_bytecode, tx |> tx_to_metadata(init) |> Map.merge(metadata)} - %{init: init, internal_tx: internal_tx} -> - {init, deployed_bytecode, internal_tx |> internal_tx_to_metadata(init) |> Map.merge(metadata)} + %{init: init, internal_tx: internal_tx} -> + {init, deployed_bytecode, internal_tx |> internal_tx_to_metadata(init) |> Map.merge(metadata)} - _ -> - {nil, deployed_bytecode, metadata} + _ -> + {nil, deployed_bytecode, metadata} + end end end diff --git a/apps/explorer/lib/explorer/smart_contract/rust_verifier_interface_behaviour.ex b/apps/explorer/lib/explorer/smart_contract/rust_verifier_interface_behaviour.ex index 97e7cc83be22..0de3d6639fd7 100644 --- a/apps/explorer/lib/explorer/smart_contract/rust_verifier_interface_behaviour.ex +++ b/apps/explorer/lib/explorer/smart_contract/rust_verifier_interface_behaviour.ex @@ -39,6 +39,18 @@ defmodule Explorer.SmartContract.RustVerifierInterfaceBehaviour do http_post_request(solidity_standard_json_verification_url(), append_metadata(body, metadata), true) end + def zksync_verify_standard_json_input( + %{ + "code" => _, + "solcCompiler" => _, + "zkCompiler" => _, + "input" => _ + } = body, + metadata + ) do + http_post_request(solidity_standard_json_verification_url(), append_metadata(body, metadata), true) + end + def vyper_verify_multipart( %{ "bytecode" => _, @@ -146,32 +158,64 @@ defmodule Explorer.SmartContract.RustVerifierInterfaceBehaviour do {:ok, source} end + # zksync + def process_verifier_response(%{"verificationSuccess" => success}, _) do + {:ok, success} + end + + # zksync + def process_verifier_response(%{"verificationFailure" => %{"message" => error}}, _) do + {:error, error} + end + + # zksync + def process_verifier_response(%{"compilationFailure" => %{"message" => error}}, _) do + {:error, error} + end + def process_verifier_response(%{"status" => "FAILURE", "message" => error}, _) do {:error, error} end def process_verifier_response(%{"compilerVersions" => versions}, _), do: {:ok, versions} - def process_verifier_response(other, _), do: {:error, other} + # zksync + def process_verifier_response(%{"solcCompilers" => solc_compilers, "zkCompilers" => zk_compilers}, _), + do: {:ok, {solc_compilers, zk_compilers}} + + def process_verifier_response(other, res) do + {:error, other} + end # Uses url encoded ("%3A") version of ':', as ':' symbol breaks `Bypass` library during tests. # https://github.com/PSPDFKit-labs/bypass/issues/122 def solidity_multiple_files_verification_url, - do: "#{base_api_url()}" <> "/verifier/solidity/sources%3Averify-multi-part" + do: base_api_url() <> "/verifier/solidity/sources%3Averify-multi-part" def vyper_multiple_files_verification_url, - do: "#{base_api_url()}" <> "/verifier/vyper/sources%3Averify-multi-part" + do: base_api_url() <> "/verifier/vyper/sources%3Averify-multi-part" def vyper_standard_json_verification_url, - do: "#{base_api_url()}" <> "/verifier/vyper/sources%3Averify-standard-json" + do: base_api_url() <> "/verifier/vyper/sources%3Averify-standard-json" - def solidity_standard_json_verification_url, - do: "#{base_api_url()}" <> "/verifier/solidity/sources%3Averify-standard-json" + def solidity_standard_json_verification_url do + base_api_url() <> verifier_path() <> "/solidity/sources%3Averify-standard-json" + end + + def versions_list_url do + base_api_url() <> verifier_path() <> "/solidity/versions" + end - def versions_list_url, do: "#{base_api_url()}" <> "/verifier/solidity/versions" + defp verifier_path do + if Application.get_env(:explorer, :chain_type) == :zksync do + "/zksync-verifier" + else + "/verifier" + end + end - def vyper_versions_list_url, do: "#{base_api_url()}" <> "/verifier/vyper/versions" + def vyper_versions_list_url, do: base_api_url() <> "/verifier/vyper/versions" def base_api_url, do: "#{base_url()}" <> "/api/v2" diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex b/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex index 1dae69055e40..c59f46bb6de5 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex @@ -86,6 +86,26 @@ defmodule Explorer.SmartContract.Solidity.Publisher do } = result_params} -> process_rust_verifier_response(result_params, address_hash, params, true, true) + # zksync + {:ok, + %{ + "compilationArtifacts" => compilation_artifacts_string, + "evmCompiler" => _, + "zkCompiler" => _, + "contractName" => _, + "fileName" => _, + "sources" => _, + "compilerSettings" => _, + "runtimeMatch" => _ + } = result_params} -> + compilation_artifacts = Jason.decode!(compilation_artifacts_string) + + transformed_result_params = + result_params + |> Map.put("abi", Map.get(compilation_artifacts, "abi")) + + process_rust_verifier_response(transformed_result_params, address_hash, params, true, true) + {:ok, %{abi: abi, constructor_arguments: constructor_arguments}, additional_params} -> params_with_constructor_arguments = params @@ -134,6 +154,73 @@ defmodule Explorer.SmartContract.Solidity.Publisher do end end + def process_rust_verifier_response( + source, + address_hash, + initial_params, + is_standard_json?, + save_file_path?, + automatically_verified? \\ false + ) + + # zksync + def process_rust_verifier_response( + %{ + "abi" => abi, + "evmCompiler" => %{"version" => compiler_version}, + "contractName" => contract_name, + "fileName" => file_name, + "sources" => sources, + "compilerSettings" => compiler_settings_string, + "runtimeMatch" => %{"type" => match_type}, + "zkCompiler" => %{"version" => zk_compiler_version} + }, + address_hash, + initial_params, + is_standard_json?, + save_file_path?, + _automatically_verified? + ) do + secondary_sources = + for {file, source} <- sources, + file != file_name, + do: %{"file_name" => file, "contract_source_code" => source, "address_hash" => address_hash} + + %{^file_name => contract_source_code} = sources + + compiler_settings = Jason.decode!(compiler_settings_string) + + optimization = extract_optimization(compiler_settings) + + optimization_runs = zksync_parse_optimization_runs(compiler_settings, optimization) + + constructor_arguments = + if initial_params["constructor_arguments"] !== "0x", do: initial_params["constructor_arguments"], else: nil + + prepared_params = + %{} + |> Map.put("optimization", optimization) + |> Map.put("optimization_runs", optimization_runs) + |> Map.put("evm_version", compiler_settings["evmVersion"] || "default") + |> Map.put("compiler_version", compiler_version) + |> Map.put("zk_compiler_version", zk_compiler_version) + |> Map.put("constructor_arguments", constructor_arguments) + |> Map.put("contract_source_code", contract_source_code) + |> Map.put("external_libraries", cast_libraries(compiler_settings["libraries"] || %{})) + |> Map.put("name", contract_name) + |> Map.put("file_path", if(save_file_path?, do: file_name)) + |> Map.put("secondary_sources", secondary_sources) + |> Map.put("compiler_settings", if(is_standard_json?, do: compiler_settings)) + |> Map.put("partially_verified", match_type == "PARTIAL") + |> Map.put("verified_via_sourcify", false) + |> Map.put("verified_via_eth_bytecode_db", false) + |> Map.put("verified_via_verifier_alliance", false) + |> Map.put("license_type", initial_params["license_type"]) + |> Map.put("is_blueprint", false) + + publish_smart_contract(address_hash, prepared_params, abi) + end + def process_rust_verifier_response( %{ "abi" => abi_string, @@ -149,7 +236,7 @@ defmodule Explorer.SmartContract.Solidity.Publisher do initial_params, is_standard_json?, save_file_path?, - automatically_verified? \\ false + automatically_verified? ) do secondary_sources = for {file, source} <- sources, @@ -162,10 +249,12 @@ defmodule Explorer.SmartContract.Solidity.Publisher do optimization = extract_optimization(compiler_settings) + optimization_runs = parse_optimization_runs(compiler_settings, optimization) + prepared_params = %{} |> Map.put("optimization", optimization) - |> Map.put("optimization_runs", if(optimization, do: compiler_settings["optimizer"]["runs"])) + |> Map.put("optimization_runs", optimization_runs) |> Map.put("evm_version", compiler_settings["evmVersion"] || "default") |> Map.put("compiler_version", compiler_version) |> Map.put("constructor_arguments", constructor_arguments) @@ -185,6 +274,18 @@ defmodule Explorer.SmartContract.Solidity.Publisher do publish_smart_contract(address_hash, prepared_params, Jason.decode!(abi_string || "null")) end + defp parse_optimization_runs(compiler_settings, optimization) do + if(optimization, do: compiler_settings["optimizer"]["runs"]) + end + + defp zksync_parse_optimization_runs(compiler_settings, optimization) do + optimizer = Map.get(compiler_settings, "optimizer") + + if optimization do + if optimizer && Map.has_key?(optimizer, "mode"), do: Map.get(optimizer, "mode"), else: "3" + end + end + def extract_optimization(compiler_settings), do: (compiler_settings["optimizer"] && compiler_settings["optimizer"]["enabled"]) || false @@ -262,6 +363,7 @@ defmodule Explorer.SmartContract.Solidity.Publisher do Map.put(attributes(address_hash, params, abi), :file_path, file_path) end + # credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity defp attributes(address_hash, params, abi \\ %{}) do constructor_arguments = params["constructor_arguments"] compiler_settings = params["compiler_settings"] @@ -279,30 +381,37 @@ defmodule Explorer.SmartContract.Solidity.Publisher do compiler_version = CompilerVersion.get_strict_compiler_version(:solc, params["compiler_version"]) - %{ - address_hash: address_hash, - name: params["name"], - file_path: params["file_path"], - compiler_version: compiler_version, - evm_version: params["evm_version"], - optimization_runs: params["optimization_runs"], - optimization: params["optimization"], - contract_source_code: params["contract_source_code"], - constructor_arguments: clean_constructor_arguments, - external_libraries: prepared_external_libraries, - secondary_sources: params["secondary_sources"], - abi: abi, - verified_via_sourcify: params["verified_via_sourcify"] || false, - verified_via_eth_bytecode_db: params["verified_via_eth_bytecode_db"] || false, - verified_via_verifier_alliance: params["verified_via_verifier_alliance"] || false, - partially_verified: params["partially_verified"] || false, - is_vyper_contract: false, - autodetect_constructor_args: params["autodetect_constructor_args"], - is_yul: params["is_yul"] || false, - compiler_settings: clean_compiler_settings, - license_type: prepare_license_type(params["license_type"]) || :none, - is_blueprint: params["is_blueprint"] || false - } + base_attributes = + %{ + address_hash: address_hash, + name: params["name"], + file_path: params["file_path"], + compiler_version: compiler_version, + evm_version: params["evm_version"], + optimization_runs: params["optimization_runs"], + optimization: params["optimization"], + contract_source_code: params["contract_source_code"], + constructor_arguments: clean_constructor_arguments, + external_libraries: prepared_external_libraries, + secondary_sources: params["secondary_sources"], + abi: abi, + verified_via_sourcify: params["verified_via_sourcify"] || false, + verified_via_eth_bytecode_db: params["verified_via_eth_bytecode_db"] || false, + verified_via_verifier_alliance: params["verified_via_verifier_alliance"] || false, + partially_verified: params["partially_verified"] || false, + is_vyper_contract: false, + autodetect_constructor_args: params["autodetect_constructor_args"], + is_yul: params["is_yul"] || false, + compiler_settings: clean_compiler_settings, + license_type: prepare_license_type(params["license_type"]) || :none, + is_blueprint: params["is_blueprint"] || false + } + + base_attributes + |> (&if(Application.get_env(:explorer, :chain_type) == :zksync, + do: Map.put(&1, :zk_compiler_version, params["zk_compiler_version"]), + else: &1 + )).() end defp clear_constructor_arguments(constructor_arguments) do diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex b/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex index d5e61dc1964b..79b4f34d7c2f 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex @@ -25,6 +25,8 @@ defmodule Explorer.SmartContract.Solidity.Verifier do @bytecode_hash_options ["default", "none", "bzzr1"] + @optimization_runs 200 + def evaluate_authenticity(_, %{"contract_source_code" => ""}), do: {:error, :contract_source_code} @@ -127,10 +129,23 @@ defmodule Explorer.SmartContract.Solidity.Verifier do def evaluate_authenticity_via_standard_json_input_inner(true, address_hash, params, json_input) do {creation_tx_input, deployed_bytecode, verifier_metadata} = fetch_data_for_verification(address_hash) - %{"compilerVersion" => params["compiler_version"]} + compiler_version_map = + if Application.get_env(:explorer, :chain_type) == :zksync do + %{ + "solcCompiler" => params["compiler_version"], + "zkCompiler" => params["zk_compiler_version"] + } + else + %{"compilerVersion" => params["compiler_version"]} + end + + compiler_version_map |> prepare_bytecode_for_microservice(creation_tx_input, deployed_bytecode) |> Map.put("input", json_input) - |> RustVerifierInterface.verify_standard_json_input(verifier_metadata) + |> (&if(Application.get_env(:explorer, :chain_type) == :zksync, + do: RustVerifierInterface.zksync_verify_standard_json_input(&1, verifier_metadata), + else: RustVerifierInterface.verify_standard_json_input(&1, verifier_metadata) + )).() end def evaluate_authenticity_via_standard_json_input_inner(false, address_hash, params, json_input) do @@ -215,6 +230,11 @@ defmodule Explorer.SmartContract.Solidity.Verifier do defp extract_settings_from_json(json_input) when is_map(json_input) do %{"enabled" => optimization, "runs" => optimization_runs} = json_input["settings"]["optimizer"] + optimization_runs = + if Application.get_env(:explorer, :chain_type) == :zksync, + do: to_string(optimization_runs), + else: optimization_runs + %{"optimization" => optimization} |> (&if(parse_boolean(optimization), do: Map.put(&1, "optimization_runs", optimization_runs), else: &1)).() end @@ -227,7 +247,7 @@ defmodule Explorer.SmartContract.Solidity.Verifier do external_libraries = Map.get(params, "external_libraries", %{}) constructor_arguments = Map.get(params, "constructor_arguments", "") evm_version = Map.get(params, "evm_version") - optimization_runs = Map.get(params, "optimization_runs", 200) + optimization_runs = Map.get(params, "optimization_runs", @optimization_runs) autodetect_constructor_arguments = params |> Map.get("autodetect_constructor_args", "true") |> parse_boolean() if is_compiler_version_at_least_0_6_0?(compiler_version) do diff --git a/apps/explorer/lib/explorer/third_party_integrations/sourcify.ex b/apps/explorer/lib/explorer/third_party_integrations/sourcify.ex index a7cbb9df8553..5604324d07db 100644 --- a/apps/explorer/lib/explorer/third_party_integrations/sourcify.ex +++ b/apps/explorer/lib/explorer/third_party_integrations/sourcify.ex @@ -367,13 +367,21 @@ defmodule Explorer.ThirdPartyIntegrations.Sourcify do contract_name = settings |> Map.get("compilationTarget") |> Map.get("#{compilation_target_file_path}") optimizer = Map.get(settings, "optimizer") + runs = + optimizer + |> Map.get("runs") + |> (&if(Application.get_env(:explorer, :chain_type) == :zksync, + do: to_string(&1), + else: &1 + )).() + params = %{} |> Map.put("name", contract_name) |> Map.put("compiler_version", compiler_version) |> Map.put("evm_version", Map.get(settings, "evmVersion")) |> Map.put("optimization", Map.get(optimizer, "enabled")) - |> Map.put("optimization_runs", Map.get(optimizer, "runs")) + |> Map.put("optimization_runs", runs) |> Map.put("external_libraries", Map.get(settings, "libraries")) |> Map.put("verified_via_sourcify", true) |> Map.put("compiler_settings", settings) diff --git a/apps/explorer/priv/zk_sync/migrations/20240716095237_add_zk_compiler_version_to_smart_contracts.exs b/apps/explorer/priv/zk_sync/migrations/20240716095237_add_zk_compiler_version_to_smart_contracts.exs new file mode 100644 index 000000000000..dd1202656f33 --- /dev/null +++ b/apps/explorer/priv/zk_sync/migrations/20240716095237_add_zk_compiler_version_to_smart_contracts.exs @@ -0,0 +1,10 @@ +defmodule Explorer.Repo.ZkSync.Migrations.AddZkCompilerVersionToSmartContracts do + use Ecto.Migration + + def change do + alter table(:smart_contracts) do + add(:zk_compiler_version, :string, null: true) + modify(:optimization_runs, :string, null: true) + end + end +end diff --git a/apps/explorer/test/explorer/smart_contract/solidity/publisher_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/publisher_test.exs index 3719efb3ccfc..2fcdec3d453d 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/publisher_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/publisher_test.exs @@ -1,237 +1,239 @@ -defmodule Explorer.SmartContract.Solidity.PublisherTest do - use ExUnit.Case, async: true +if Application.compile_env(:explorer, :chain_type) !== :zksync do + defmodule Explorer.SmartContract.Solidity.PublisherTest do + use ExUnit.Case, async: true - use Explorer.DataCase + use Explorer.DataCase - doctest Explorer.SmartContract.Solidity.Publisher + doctest Explorer.SmartContract.Solidity.Publisher - @moduletag timeout: :infinity + @moduletag timeout: :infinity - alias Explorer.Chain.{ContractMethod, SmartContract} - alias Explorer.{Factory, Repo} - alias Explorer.SmartContract.Solidity.Publisher + alias Explorer.Chain.{ContractMethod, SmartContract} + alias Explorer.{Factory, Repo} + alias Explorer.SmartContract.Solidity.Publisher - setup do - configuration = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, enabled: false) + setup do + configuration = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, enabled: false) - on_exit(fn -> - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, configuration) - end) - end - - describe "publish/2" do - test "with valid data creates a smart_contract" do - contract_code_info = Factory.contract_code_info_modern_compiler() - - contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) - - :transaction - |> insert(created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) - |> with_block(status: :ok) - - valid_attrs = %{ - "contract_source_code" => contract_code_info.source_code, - "compiler_version" => contract_code_info.version, - "name" => contract_code_info.name, - "optimization" => contract_code_info.optimized - } - - response = Publisher.publish(contract_address.hash, valid_attrs) - assert {:ok, %SmartContract{} = smart_contract} = response - - assert smart_contract.address_hash == contract_address.hash - assert smart_contract.name == valid_attrs["name"] - assert smart_contract.compiler_version == valid_attrs["compiler_version"] - assert smart_contract.optimization == valid_attrs["optimization"] - assert smart_contract.contract_source_code == valid_attrs["contract_source_code"] - assert is_nil(smart_contract.constructor_arguments) - assert smart_contract.abi == contract_code_info.abi + on_exit(fn -> + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, configuration) + end) end - test "detects and adds constructor arguments if autodetection is checked" do - path = File.cwd!() <> "/test/support/fixture/smart_contract/solidity_0.5.9_smart_contract.sol" - contract = File.read!(path) - - expected_constructor_arguments = - "00000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000a54657374546f6b656e32000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006546f6b656e320000000000000000000000000000000000000000000000000000" + describe "publish/2" do + test "with valid data creates a smart_contract" do + contract_code_info = Factory.contract_code_info_modern_compiler() - bytecode = - "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a723058205538c6fbf4f1885c37cee088edf3832ae2abb038a1a141daaa8b0ce7e9df42d964736f6c63430005090032" + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) - input = - "0x60806040526040518060400160405280600381526020017f302e3100000000000000000000000000000000000000000000000000000000008152506006908051906020019062000051929190620001e2565b503480156200005f57600080fd5b506040516200105b3803806200105b833981810160405260808110156200008557600080fd5b81019080805190602001909291908051640100000000811115620000a857600080fd5b82810190506020810184811115620000bf57600080fd5b8151856001820283011164010000000082111715620000dd57600080fd5b50509291906020018051906020019092919080516401000000008111156200010457600080fd5b828101905060208101848111156200011b57600080fd5b81518560018202830111640100000000821117156200013957600080fd5b5050929190505050836000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550836002819055508260039080519060200190620001a3929190620001e2565b5081600460006101000a81548160ff021916908360ff1602179055508060059080519060200190620001d7929190620001e2565b505050505062000291565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200022557805160ff191683800117855562000256565b8280016001018555821562000256579182015b828111156200025557825182559160200191906001019062000238565b5b50905062000265919062000269565b5090565b6200028e91905b808211156200028a57600081600090555060010162000270565b5090565b90565b610dba80620002a16000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a723058205538c6fbf4f1885c37cee088edf3832ae2abb038a1a141daaa8b0ce7e9df42d964736f6c63430005090032" + :transaction + |> insert(created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) + |> with_block(status: :ok) - contract_address = insert(:contract_address, contract_code: bytecode) + valid_attrs = %{ + "contract_source_code" => contract_code_info.source_code, + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized + } - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: input <> expected_constructor_arguments - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => contract, - "compiler_version" => "v0.5.9+commit.e560f70d", - "evm_version" => "petersburg", - "name" => "TestToken", - "optimization" => false, - "autodetect_constructor_args" => "true" - } - - assert {:ok, result} = Publisher.publish(contract_address.hash, params) - assert result.constructor_arguments == expected_constructor_arguments - end + response = Publisher.publish(contract_address.hash, valid_attrs) + assert {:ok, %SmartContract{} = smart_contract} = response - test "corresponding contract_methods are created for the abi" do - contract_code_info = Factory.contract_code_info_modern_compiler() + assert smart_contract.address_hash == contract_address.hash + assert smart_contract.name == valid_attrs["name"] + assert smart_contract.compiler_version == valid_attrs["compiler_version"] + assert smart_contract.optimization == valid_attrs["optimization"] + assert smart_contract.contract_source_code == valid_attrs["contract_source_code"] + assert is_nil(smart_contract.constructor_arguments) + assert smart_contract.abi == contract_code_info.abi + end - contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + test "detects and adds constructor arguments if autodetection is checked" do + path = File.cwd!() <> "/test/support/fixture/smart_contract/solidity_0.5.9_smart_contract.sol" + contract = File.read!(path) - :transaction - |> insert(created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) - |> with_block(status: :ok) + expected_constructor_arguments = + "00000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000a54657374546f6b656e32000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006546f6b656e320000000000000000000000000000000000000000000000000000" - valid_attrs = %{ - "contract_source_code" => contract_code_info.source_code, - "compiler_version" => contract_code_info.version, - "name" => contract_code_info.name, - "optimization" => contract_code_info.optimized - } + bytecode = + "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a723058205538c6fbf4f1885c37cee088edf3832ae2abb038a1a141daaa8b0ce7e9df42d964736f6c63430005090032" - response = Publisher.publish(contract_address.hash, valid_attrs) - assert {:ok, %SmartContract{} = _smart_contract} = response + input = + "0x60806040526040518060400160405280600381526020017f302e3100000000000000000000000000000000000000000000000000000000008152506006908051906020019062000051929190620001e2565b503480156200005f57600080fd5b506040516200105b3803806200105b833981810160405260808110156200008557600080fd5b81019080805190602001909291908051640100000000811115620000a857600080fd5b82810190506020810184811115620000bf57600080fd5b8151856001820283011164010000000082111715620000dd57600080fd5b50509291906020018051906020019092919080516401000000008111156200010457600080fd5b828101905060208101848111156200011b57600080fd5b81518560018202830111640100000000821117156200013957600080fd5b5050929190505050836000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550836002819055508260039080519060200190620001a3929190620001e2565b5081600460006101000a81548160ff021916908360ff1602179055508060059080519060200190620001d7929190620001e2565b505050505062000291565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200022557805160ff191683800117855562000256565b8280016001018555821562000256579182015b828111156200025557825182559160200191906001019062000238565b5b50905062000265919062000269565b5090565b6200028e91905b808211156200028a57600081600090555060010162000270565b5090565b90565b610dba80620002a16000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a723058205538c6fbf4f1885c37cee088edf3832ae2abb038a1a141daaa8b0ce7e9df42d964736f6c63430005090032" - Enum.each(contract_code_info.abi, fn selector -> - [parsed] = ABI.parse_specification([selector]) + contract_address = insert(:contract_address, contract_code: bytecode) - assert Repo.get_by(ContractMethod, abi: selector, identifier: parsed.method_id) - end) - end - - test "creates a smart contract with constructor arguments" do - contract_code_info = Factory.contract_code_info_with_constructor_arguments() + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: input <> expected_constructor_arguments + ) + |> with_block(status: :ok) - contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + params = %{ + "contract_source_code" => contract, + "compiler_version" => "v0.5.9+commit.e560f70d", + "evm_version" => "petersburg", + "name" => "TestToken", + "optimization" => false, + "autodetect_constructor_args" => "true" + } - params = %{ - "contract_source_code" => contract_code_info.source_code, - "compiler_version" => contract_code_info.version, - "name" => contract_code_info.name, - "optimization" => contract_code_info.optimized, - "optimization_runs" => contract_code_info.optimization_runs, - "constructor_arguments" => contract_code_info.constructor_args - } + assert {:ok, result} = Publisher.publish(contract_address.hash, params) + assert result.constructor_arguments == expected_constructor_arguments + end - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: contract_code_info.tx_input - ) - |> with_block(status: :ok) + test "corresponding contract_methods are created for the abi" do + contract_code_info = Factory.contract_code_info_modern_compiler() - response = Publisher.publish(contract_address.hash, params) - assert {:ok, %SmartContract{} = smart_contract} = response + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) - assert smart_contract.constructor_arguments == contract_code_info.constructor_args - end + :transaction + |> insert(created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) + |> with_block(status: :ok) - test "with invalid data returns error changeset" do - address_hash = "" + valid_attrs = %{ + "contract_source_code" => contract_code_info.source_code, + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized + } - invalid_attrs = %{ - "contract_source_code" => "", - "compiler_version" => "", - "name" => "", - "optimization" => "" - } + response = Publisher.publish(contract_address.hash, valid_attrs) + assert {:ok, %SmartContract{} = _smart_contract} = response - assert {:error, %Ecto.Changeset{}} = Publisher.publish(address_hash, invalid_attrs) - end + Enum.each(contract_code_info.abi, fn selector -> + [parsed] = ABI.parse_specification([selector]) - test "validates and creates smart contract with external libraries" do - contract_data = - "#{File.cwd!()}/test/support/fixture/smart_contract/contract_with_lib.json" - |> File.read!() - |> Jason.decode!() - |> List.first() - - compiler_version = contract_data["compiler_version"] - external_libraries = contract_data["external_libraries"] - name = contract_data["name"] - optimize = contract_data["optimize"] - contract = contract_data["contract"] - expected_bytecode = contract_data["expected_bytecode"] - tx_input = contract_data["tx_input"] - - contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode) - - :transaction - |> insert(created_contract_address_hash: contract_address.hash, input: "0x" <> tx_input) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => contract, - "compiler_version" => compiler_version, - "name" => name, - "optimization" => optimize - } - - external_libraries_form_params = - external_libraries - |> Enum.with_index() - |> Enum.reduce(%{}, fn {{name, address}, index}, acc -> - name_key = "library#{index + 1}_name" - address_key = "library#{index + 1}_address" - - acc - |> Map.put(name_key, name) - |> Map.put(address_key, address) + assert Repo.get_by(ContractMethod, abi: selector, identifier: parsed.method_id) end) - - response = Publisher.publish(contract_address.hash, params, external_libraries_form_params) - assert {:ok, %SmartContract{} = _smart_contract} = response - end - - test "allows to re-verify solidity contracts" do - contract_code_info = Factory.contract_code_info_modern_compiler() - - contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) - - :transaction - |> insert(created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) - |> with_block(status: :ok) - - valid_attrs = %{ - "contract_source_code" => contract_code_info.source_code, - "compiler_version" => contract_code_info.version, - "name" => contract_code_info.name, - "optimization" => contract_code_info.optimized - } - - response = Publisher.publish(contract_address.hash, valid_attrs) - assert {:ok, %SmartContract{}} = response - - updated_name = "AnotherContractName" - - updated_contract_source_code = - String.replace( - valid_attrs["contract_source_code"], - "contract #{valid_attrs["name"]}", - "contract #{updated_name}" + end + + test "creates a smart contract with constructor arguments" do + contract_code_info = Factory.contract_code_info_with_constructor_arguments() + + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + + params = %{ + "contract_source_code" => contract_code_info.source_code, + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized, + "optimization_runs" => contract_code_info.optimization_runs, + "constructor_arguments" => contract_code_info.constructor_args + } + + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: contract_code_info.tx_input ) - - valid_attrs = - valid_attrs - |> Map.put("name", updated_name) - |> Map.put("contract_source_code", updated_contract_source_code) - - response = Publisher.publish(contract_address.hash, valid_attrs) - assert {:ok, %SmartContract{} = smart_contract} = response - - assert smart_contract.name == valid_attrs["name"] - assert smart_contract.contract_source_code == valid_attrs["contract_source_code"] + |> with_block(status: :ok) + + response = Publisher.publish(contract_address.hash, params) + assert {:ok, %SmartContract{} = smart_contract} = response + + assert smart_contract.constructor_arguments == contract_code_info.constructor_args + end + + test "with invalid data returns error changeset" do + address_hash = "" + + invalid_attrs = %{ + "contract_source_code" => "", + "compiler_version" => "", + "name" => "", + "optimization" => "" + } + + assert {:error, %Ecto.Changeset{}} = Publisher.publish(address_hash, invalid_attrs) + end + + test "validates and creates smart contract with external libraries" do + contract_data = + "#{File.cwd!()}/test/support/fixture/smart_contract/contract_with_lib.json" + |> File.read!() + |> Jason.decode!() + |> List.first() + + compiler_version = contract_data["compiler_version"] + external_libraries = contract_data["external_libraries"] + name = contract_data["name"] + optimize = contract_data["optimize"] + contract = contract_data["contract"] + expected_bytecode = contract_data["expected_bytecode"] + tx_input = contract_data["tx_input"] + + contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode) + + :transaction + |> insert(created_contract_address_hash: contract_address.hash, input: "0x" <> tx_input) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => contract, + "compiler_version" => compiler_version, + "name" => name, + "optimization" => optimize + } + + external_libraries_form_params = + external_libraries + |> Enum.with_index() + |> Enum.reduce(%{}, fn {{name, address}, index}, acc -> + name_key = "library#{index + 1}_name" + address_key = "library#{index + 1}_address" + + acc + |> Map.put(name_key, name) + |> Map.put(address_key, address) + end) + + response = Publisher.publish(contract_address.hash, params, external_libraries_form_params) + assert {:ok, %SmartContract{} = _smart_contract} = response + end + + test "allows to re-verify solidity contracts" do + contract_code_info = Factory.contract_code_info_modern_compiler() + + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + + :transaction + |> insert(created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) + |> with_block(status: :ok) + + valid_attrs = %{ + "contract_source_code" => contract_code_info.source_code, + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized + } + + response = Publisher.publish(contract_address.hash, valid_attrs) + assert {:ok, %SmartContract{}} = response + + updated_name = "AnotherContractName" + + updated_contract_source_code = + String.replace( + valid_attrs["contract_source_code"], + "contract #{valid_attrs["name"]}", + "contract #{updated_name}" + ) + + valid_attrs = + valid_attrs + |> Map.put("name", updated_name) + |> Map.put("contract_source_code", updated_contract_source_code) + + response = Publisher.publish(contract_address.hash, valid_attrs) + assert {:ok, %SmartContract{} = smart_contract} = response + + assert smart_contract.name == valid_attrs["name"] + assert smart_contract.contract_source_code == valid_attrs["contract_source_code"] + end end end end diff --git a/apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs index 25eb23296275..a5500ecb84da 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs @@ -1,1282 +1,1286 @@ -defmodule Explorer.SmartContract.Solidity.VerifierTest do - use ExUnit.Case, async: true - use Explorer.DataCase - - @moduletag timeout: :infinity - - doctest Explorer.SmartContract.Solidity.Verifier - - alias Explorer.Chain.Data - alias Explorer.SmartContract.Solidity.Verifier - alias Explorer.Factory - - @code_0_4 """ - pragma solidity ^0.4.0; - contract Incrementer { - event Incremented(address indexed sender, uint256 newValue); - uint256 public value; - address public lastSender; - constructor(uint256 initialValue) public { - value = initialValue; - lastSender = msg.sender; - } - function inc(uint256 delta) public { - value = value + delta; - lastSender = msg.sender; - } - } - """ - - @code_0_5 """ - pragma solidity ^0.5.0; - contract Incrementer { - event Incremented(address indexed sender, uint256 newValue); - uint256 public value; - address public lastSender; - constructor(uint256 initialValue) public { - value = initialValue; - lastSender = msg.sender; - } - function inc(uint256 delta) public { - value = value + delta; - lastSender = msg.sender; - } - } - """ - - @code_0_6 """ - pragma solidity ^0.6.0; - contract Incrementer { - event Incremented(address indexed sender, uint256 newValue); - uint256 public value; - address public lastSender; - constructor(uint256 initialValue) public { - value = initialValue; - lastSender = msg.sender; - } - function inc(uint256 delta) public { - value = value + delta; - lastSender = msg.sender; - } - } - """ - - setup do - configuration = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, enabled: false) - - on_exit(fn -> - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, configuration) - end) - end +if Application.compile_env(:explorer, :chain_type) !== :zksync do + defmodule Explorer.SmartContract.Solidity.VerifierTest do + use ExUnit.Case, async: true + use Explorer.DataCase + + @moduletag timeout: :infinity + + doctest Explorer.SmartContract.Solidity.Verifier + + alias Explorer.Chain.Data + alias Explorer.SmartContract.Solidity.Verifier + alias Explorer.Factory + + @optimization_runs 200 + + @code_0_4 """ + pragma solidity ^0.4.0; + contract Incrementer { + event Incremented(address indexed sender, uint256 newValue); + uint256 public value; + address public lastSender; + constructor(uint256 initialValue) public { + value = initialValue; + lastSender = msg.sender; + } + function inc(uint256 delta) public { + value = value + delta; + lastSender = msg.sender; + } + } + """ + + @code_0_5 """ + pragma solidity ^0.5.0; + contract Incrementer { + event Incremented(address indexed sender, uint256 newValue); + uint256 public value; + address public lastSender; + constructor(uint256 initialValue) public { + value = initialValue; + lastSender = msg.sender; + } + function inc(uint256 delta) public { + value = value + delta; + lastSender = msg.sender; + } + } + """ + + @code_0_6 """ + pragma solidity ^0.6.0; + contract Incrementer { + event Incremented(address indexed sender, uint256 newValue); + uint256 public value; + address public lastSender; + constructor(uint256 initialValue) public { + value = initialValue; + lastSender = msg.sender; + } + function inc(uint256 delta) public { + value = value + delta; + lastSender = msg.sender; + } + } + """ - describe "evaluate_authenticity/2" do setup do - {:ok, contract_code_info: Factory.contract_code_info()} - end - - test "verifies the generated bytecode against bytecode retrieved from the blockchain", %{ - contract_code_info: contract_code_info - } do - contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) - - :transaction - |> insert(created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => contract_code_info.source_code, - "compiler_version" => contract_code_info.version, - "name" => contract_code_info.name, - "optimization" => contract_code_info.optimized - } + configuration = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, enabled: false) - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil + on_exit(fn -> + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, configuration) + end) end - test "verifies the generated bytecode with external libraries" do - contract_data = - "#{File.cwd!()}/test/support/fixture/smart_contract/contract_with_lib.json" - |> File.read!() - |> Jason.decode!() - |> List.first() - - compiler_version = contract_data["compiler_version"] - external_libraries = contract_data["external_libraries"] - name = contract_data["name"] - optimize = contract_data["optimize"] - contract = contract_data["contract"] - expected_bytecode = contract_data["expected_bytecode"] - tx_input = contract_data["tx_input"] - - contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode) - - :transaction - |> insert(created_contract_address_hash: contract_address.hash, input: "0x" <> tx_input) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => contract, - "compiler_version" => compiler_version, - "name" => name, - "optimization" => optimize, - "external_libraries" => external_libraries - } - - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil - end + describe "evaluate_authenticity/2" do + setup do + {:ok, contract_code_info: Factory.contract_code_info()} + end - test "verifies smart contract with new `whisper` metadata (bzz0 => bzz1) in solidity 0.5.11" do - contract_data = - "#{File.cwd!()}/test/support/fixture/smart_contract/solidity_5.11_new_whisper_metadata.json" - |> File.read!() - |> Jason.decode!() - - compiler_version = contract_data["compiler_version"] - name = contract_data["name"] - optimize = false - contract = contract_data["contract"] - expected_bytecode = contract_data["bytecode"] - evm_version = contract_data["evm_version"] - input = contract_data["input"] - - contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode) - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: "0x" <> input - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => contract, - "compiler_version" => compiler_version, - "evm_version" => evm_version, - "name" => name, - "optimization" => optimize - } - - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil - end + test "verifies the generated bytecode against bytecode retrieved from the blockchain", %{ + contract_code_info: contract_code_info + } do + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) - test "verifies smart contract and ignores not needed constructor arguments", %{ - contract_code_info: contract_code_info - } do - contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) - - constructor_arguments = "0102030405" - - params = %{ - "contract_source_code" => contract_code_info.source_code, - "compiler_version" => contract_code_info.version, - "name" => contract_code_info.name, - "optimization" => contract_code_info.optimized, - "constructor_arguments" => constructor_arguments - } - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: contract_code_info.tx_input - ) - |> with_block(status: :ok) - - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil - end + :transaction + |> insert(created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => contract_code_info.source_code, + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verifies the generated bytecode with external libraries" do + contract_data = + "#{File.cwd!()}/test/support/fixture/smart_contract/contract_with_lib.json" + |> File.read!() + |> Jason.decode!() + |> List.first() + + compiler_version = contract_data["compiler_version"] + external_libraries = contract_data["external_libraries"] + name = contract_data["name"] + optimize = contract_data["optimize"] + contract = contract_data["contract"] + expected_bytecode = contract_data["expected_bytecode"] + tx_input = contract_data["tx_input"] + + contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode) - test "tries to compile with the latest evm version if wrong evm version was provided" do - bytecode = - "0x6060604052341561000f57600080fd5b604051602080610223833981016040528080519060200190919050505b8060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b505b61019d806100866000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063256fec88146100545780633fa4f245146100a9578063812600df146100d2575b600080fd5b341561005f57600080fd5b6100676100f5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100b457600080fd5b6100bc61011b565b6040518082815260200191505060405180910390f35b34156100dd57600080fd5b6100f36004808035906020019091905050610121565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b505600a165627a7a723058201de7017582ff17d45730bc9aedeac5b399399b71b188f42a164609c1b6f7f4760029" - - constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" - - contract_address = insert(:contract_address, contract_code: bytecode) - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: bytecode <> constructor_arguments - ) - |> with_block(status: :ok) - - code = """ - pragma solidity ^0.4.15; - contract Incrementer { - event Incremented(address indexed sender, uint256 newValue); - uint256 public value; - address public lastSender; - function Incrementer(uint256 initialValue) { - value = initialValue; - lastSender = msg.sender; - } - function inc(uint256 delta) { - value = value + delta; - lastSender = msg.sender; - } - } - """ - - params = %{ - "contract_source_code" => code, - "compiler_version" => "v0.4.15+commit.bbb8e64f", - "evm_version" => "homestead", - "name" => "Incrementer", - "optimization" => false, - "constructor_arguments" => constructor_arguments - } - - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil - end + :transaction + |> insert(created_contract_address_hash: contract_address.hash, input: "0x" <> tx_input) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => contract, + "compiler_version" => compiler_version, + "name" => name, + "optimization" => optimize, + "external_libraries" => external_libraries + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verifies smart contract with new `whisper` metadata (bzz0 => bzz1) in solidity 0.5.11" do + contract_data = + "#{File.cwd!()}/test/support/fixture/smart_contract/solidity_5.11_new_whisper_metadata.json" + |> File.read!() + |> Jason.decode!() + + compiler_version = contract_data["compiler_version"] + name = contract_data["name"] + optimize = false + contract = contract_data["contract"] + expected_bytecode = contract_data["bytecode"] + evm_version = contract_data["evm_version"] + input = contract_data["input"] + + contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode) - test "verifies a library" do - bytecode = - "0x610102610026600b82828239805160001a60731461001957fe5b30600052607381538281f3fe730000000000000000000000000000000000000000301460806040526004361060335760003560e01c8063c2985578146038575b600080fd5b603e60b0565b6040805160208082528351818301528351919283929083019185019080838360005b8381101560765781810151838201526020016060565b50505050905090810190601f16801560a25780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080518082019091526003815262666f6f60e81b60208201529056fea265627a7a7231582079c18e1f9cf2812147d15e5d44f16ff748f8b7349d32dc9db50300a3ffbd3a9664736f6c634300050b0032" + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: "0x" <> input + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => contract, + "compiler_version" => compiler_version, + "evm_version" => evm_version, + "name" => name, + "optimization" => optimize + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verifies smart contract and ignores not needed constructor arguments", %{ + contract_code_info: contract_code_info + } do + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + + constructor_arguments = "0102030405" + + params = %{ + "contract_source_code" => contract_code_info.source_code, + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized, + "constructor_arguments" => constructor_arguments + } - contract_address = insert(:contract_address, contract_code: bytecode) + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: contract_code_info.tx_input + ) + |> with_block(status: :ok) - :transaction - |> insert(created_contract_address_hash: contract_address.hash, input: bytecode) - |> with_block(status: :ok) + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end - code = """ - pragma solidity 0.5.11; + test "tries to compile with the latest evm version if wrong evm version was provided" do + bytecode = + "0x6060604052341561000f57600080fd5b604051602080610223833981016040528080519060200190919050505b8060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b505b61019d806100866000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063256fec88146100545780633fa4f245146100a9578063812600df146100d2575b600080fd5b341561005f57600080fd5b6100676100f5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100b457600080fd5b6100bc61011b565b6040518082815260200191505060405180910390f35b34156100dd57600080fd5b6100f36004808035906020019091905050610121565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b505600a165627a7a723058201de7017582ff17d45730bc9aedeac5b399399b71b188f42a164609c1b6f7f4760029" - library Foo { - function foo() external pure returns (string memory) { - return "foo"; - } - } - """ + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" - params = %{ - "contract_source_code" => code, - "compiler_version" => "v0.5.11+commit.c082d0b4", - "evm_version" => "default", - "name" => "Foo", - "optimization" => true - } + contract_address = insert(:contract_address, contract_code: bytecode) - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil - end + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode <> constructor_arguments + ) + |> with_block(status: :ok) + + code = """ + pragma solidity ^0.4.15; + contract Incrementer { + event Incremented(address indexed sender, uint256 newValue); + uint256 public value; + address public lastSender; + function Incrementer(uint256 initialValue) { + value = initialValue; + lastSender = msg.sender; + } + function inc(uint256 delta) { + value = value + delta; + lastSender = msg.sender; + } + } + """ + + params = %{ + "contract_source_code" => code, + "compiler_version" => "v0.4.15+commit.bbb8e64f", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verifies a library" do + bytecode = + "0x610102610026600b82828239805160001a60731461001957fe5b30600052607381538281f3fe730000000000000000000000000000000000000000301460806040526004361060335760003560e01c8063c2985578146038575b600080fd5b603e60b0565b6040805160208082528351818301528351919283929083019185019080838360005b8381101560765781810151838201526020016060565b50505050905090810190601f16801560a25780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080518082019091526003815262666f6f60e81b60208201529056fea265627a7a7231582079c18e1f9cf2812147d15e5d44f16ff748f8b7349d32dc9db50300a3ffbd3a9664736f6c634300050b0032" + + contract_address = insert(:contract_address, contract_code: bytecode) - test "verifies smart contract compiled with Solidity 0.5.9 (includes new metadata in bytecode) with constructor args and does not verify with wrong args" do - path = File.cwd!() <> "/test/support/fixture/smart_contract/solidity_0.5.9_smart_contract.sol" - contract = File.read!(path) - - wrong_constructor_arguments = - "00000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000a54657374546f6b656e32000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006546f6b656e320000000000000000000000000000000000000000000000000000" - - constructor_arguments = - "0000000000000000000000000000000000000000000000000003635c9adc5dea0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000954657374546f6b656e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005546f6b656e000000000000000000000000000000000000000000000000000000" - - bytecode = - "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a723058202bede3d06720cdf63e8e43fa1d96f228a476cc899ae007bf684e802f2484ce7664736f6c63430005090032" - - input = - "0x60806040526040518060400160405280600381526020017f302e3100000000000000000000000000000000000000000000000000000000008152506006908051906020019062000051929190620001e2565b503480156200005f57600080fd5b506040516200105b3803806200105b833981810160405260808110156200008557600080fd5b81019080805190602001909291908051640100000000811115620000a857600080fd5b82810190506020810184811115620000bf57600080fd5b8151856001820283011164010000000082111715620000dd57600080fd5b50509291906020018051906020019092919080516401000000008111156200010457600080fd5b828101905060208101848111156200011b57600080fd5b81518560018202830111640100000000821117156200013957600080fd5b5050929190505050836000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550836002819055508260039080519060200190620001a3929190620001e2565b5081600460006101000a81548160ff021916908360ff1602179055508060059080519060200190620001d7929190620001e2565b505050505062000291565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200022557805160ff191683800117855562000256565b8280016001018555821562000256579182015b828111156200025557825182559160200191906001019062000238565b5b50905062000265919062000269565b5090565b6200028e91905b808211156200028a57600081600090555060010162000270565b5090565b90565b610dba80620002a16000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a723058202bede3d06720cdf63e8e43fa1d96f228a476cc899ae007bf684e802f2484ce7664736f6c63430005090032" - - contract_address = insert(:contract_address, contract_code: bytecode) - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: input <> constructor_arguments - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => contract, - "compiler_version" => "v0.5.9+commit.e560f70d", - "evm_version" => "petersburg", - "name" => "TestToken", - "optimization" => false, - "constructor_arguments" => constructor_arguments - } - - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil - - contract_address_1 = insert(:contract_address, contract_code: bytecode) - - :transaction - |> insert( - created_contract_address_hash: contract_address_1.hash, - input: input <> constructor_arguments - ) - |> with_block(status: :ok) - - params_1 = %{ - "contract_source_code" => contract, - "compiler_version" => "v0.5.9+commit.e560f70d", - "evm_version" => "petersburg", - "name" => "TestToken", - "optimization" => false, - "constructor_arguments" => wrong_constructor_arguments, - "autodetect_constructor_args" => false - } - - assert {:error, :constructor_arguments} = Verifier.evaluate_authenticity(contract_address_1.hash, params_1) - end + :transaction + |> insert(created_contract_address_hash: contract_address.hash, input: bytecode) + |> with_block(status: :ok) - # flaky test - test "returns error when bytecode doesn't match", %{contract_code_info: contract_code_info} do - contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + code = """ + pragma solidity 0.5.11; - :transaction - |> insert(created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) - |> with_block(status: :ok) + library Foo { + function foo() external pure returns (string memory) { + return "foo"; + } + } + """ - different_code = "pragma solidity ^0.4.24; contract SimpleStorage {}" + params = %{ + "contract_source_code" => code, + "compiler_version" => "v0.5.11+commit.c082d0b4", + "evm_version" => "default", + "name" => "Foo", + "optimization" => true + } - params = %{ - "contract_source_code" => different_code, - "compiler_version" => contract_code_info.version, - "evm_version" => "default", - "name" => contract_code_info.name, - "optimization" => contract_code_info.optimized - } + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end - response = Verifier.evaluate_authenticity(contract_address.hash, params) + test "verifies smart contract compiled with Solidity 0.5.9 (includes new metadata in bytecode) with constructor args and does not verify with wrong args" do + path = File.cwd!() <> "/test/support/fixture/smart_contract/solidity_0.5.9_smart_contract.sol" + contract = File.read!(path) - assert {:error, :generated_bytecode} = response - end + wrong_constructor_arguments = + "00000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000a54657374546f6b656e32000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006546f6b656e320000000000000000000000000000000000000000000000000000" - # flaky test - test "returns error when contract has constructor arguments and they were not provided" do - path = File.cwd!() <> "/test/support/fixture/smart_contract/solidity_0.5.9_smart_contract.sol" - contract = File.read!(path) + constructor_arguments = + "0000000000000000000000000000000000000000000000000003635c9adc5dea0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000954657374546f6b656e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005546f6b656e000000000000000000000000000000000000000000000000000000" - constructor_arguments = "" + bytecode = + "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a723058202bede3d06720cdf63e8e43fa1d96f228a476cc899ae007bf684e802f2484ce7664736f6c63430005090032" - bytecode = - "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a72305820fe0ba5210ac95870683c2cb054304b04565703bd16c7d7e956df694c9643c6d264736f6c63430005090032" + input = + "0x60806040526040518060400160405280600381526020017f302e3100000000000000000000000000000000000000000000000000000000008152506006908051906020019062000051929190620001e2565b503480156200005f57600080fd5b506040516200105b3803806200105b833981810160405260808110156200008557600080fd5b81019080805190602001909291908051640100000000811115620000a857600080fd5b82810190506020810184811115620000bf57600080fd5b8151856001820283011164010000000082111715620000dd57600080fd5b50509291906020018051906020019092919080516401000000008111156200010457600080fd5b828101905060208101848111156200011b57600080fd5b81518560018202830111640100000000821117156200013957600080fd5b5050929190505050836000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550836002819055508260039080519060200190620001a3929190620001e2565b5081600460006101000a81548160ff021916908360ff1602179055508060059080519060200190620001d7929190620001e2565b505050505062000291565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200022557805160ff191683800117855562000256565b8280016001018555821562000256579182015b828111156200025557825182559160200191906001019062000238565b5b50905062000265919062000269565b5090565b6200028e91905b808211156200028a57600081600090555060010162000270565b5090565b90565b610dba80620002a16000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a723058202bede3d06720cdf63e8e43fa1d96f228a476cc899ae007bf684e802f2484ce7664736f6c63430005090032" - contract_address = insert(:contract_address, contract_code: bytecode) + contract_address = insert(:contract_address, contract_code: bytecode) - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: bytecode <> constructor_arguments - ) - |> with_block(status: :ok) + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: input <> constructor_arguments + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => contract, + "compiler_version" => "v0.5.9+commit.e560f70d", + "evm_version" => "petersburg", + "name" => "TestToken", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + + contract_address_1 = insert(:contract_address, contract_code: bytecode) - params = %{ - "contract_source_code" => contract, - "compiler_version" => "v0.5.9+commit.e560f70d", - "evm_version" => "petersburg", - "name" => "TestToken", - "optimization" => false, - "constructor_arguments" => constructor_arguments - } + :transaction + |> insert( + created_contract_address_hash: contract_address_1.hash, + input: input <> constructor_arguments + ) + |> with_block(status: :ok) + + params_1 = %{ + "contract_source_code" => contract, + "compiler_version" => "v0.5.9+commit.e560f70d", + "evm_version" => "petersburg", + "name" => "TestToken", + "optimization" => false, + "constructor_arguments" => wrong_constructor_arguments, + "autodetect_constructor_args" => false + } + + assert {:error, :constructor_arguments} = Verifier.evaluate_authenticity(contract_address_1.hash, params_1) + end + + # flaky test + test "returns error when bytecode doesn't match", %{contract_code_info: contract_code_info} do + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) - assert {:error, :generated_bytecode} = Verifier.evaluate_authenticity(contract_address.hash, params) - end + :transaction + |> insert(created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) + |> with_block(status: :ok) - test "returns error when there is a compilation problem", %{contract_code_info: contract_code_info} do - contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + different_code = "pragma solidity ^0.4.24; contract SimpleStorage {}" - params = %{ - "contract_source_code" => "pragma solidity ^0.4.24; contract SimpleStorage { ", - "compiler_version" => contract_code_info.version, - "name" => contract_code_info.name, - "optimization" => contract_code_info.optimized - } + params = %{ + "contract_source_code" => different_code, + "compiler_version" => contract_code_info.version, + "evm_version" => "default", + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized + } - assert {:error, :compilation, "Function, variable, struct or modifier declaration expected."} = - Verifier.evaluate_authenticity(contract_address.hash, params) - end - end + response = Verifier.evaluate_authenticity(contract_address.hash, params) - describe "extract_bytecode_and_metadata_hash/1" do - test "extracts the bytecode from the hash" do - code = - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a723058203c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b0029" - - assert %{ - "metadata_hash_with_length" => - "a165627a7a723058203c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b0029", - "trimmed_bytecode" => - "608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600", - "compiler_version" => nil - } = Verifier.extract_bytecode_and_metadata_hash(code, code) - end + assert {:error, :generated_bytecode} = response + end - test "extracts everything to the left of the swarm hash" do - contract_creation_code = - "0x60806040526315c8dd0d60005534801561001857600080fd5b5060405161053e38038061053e8339810160408190526100379161039b565b855161004a906001906020890190610056565b505050505050506104f1565b828054828255906000526020600020908101928215610091579160200282015b82811115610091578251825591602001919060010190610076565b5061009d9291506100a1565b5090565b5b8082111561009d57600081556001016100a2565b60006001600160401b038311156100cf576100cf6104db565b60206100e3601f8501601f19168201610488565b91508382528484840111156100f757600080fd5b60005b84811015610113578381015183820183015281016100fa565b848111156101245760008286850101525b50509392505050565b600082601f83011261013d578081fd5b8151602061015261014d836104b8565b610488565b80838252828201915082860187848660051b8901011115610171578586fd5b855b858110156101a35781516001600160a01b0381168114610191578788fd5b84529284019290840190600101610173565b5090979650505050505050565b600082601f8301126101c0578081fd5b815160206101d061014d836104b8565b80838252828201915082860187848660051b89010111156101ef578586fd5b855b858110156101a35781518015158114610208578788fd5b845292840192908401906001016101f1565b600082601f83011261022a578081fd5b8151602061023a61014d836104b8565b80838252828201915082860187848660051b8901011115610259578586fd5b855b858110156101a35781516001600160401b03811115610278578788fd5b8801603f81018a13610288578788fd5b6102998a87830151604084016100b6565b855250928401929084019060010161025b565b600082601f8301126102bc578081fd5b815160206102cc61014d836104b8565b80838252828201915082860187848660051b89010111156102eb578586fd5b855b858110156101a3578151845292840192908401906001016102ed565b600082601f830112610319578081fd5b8151602061032961014d836104b8565b80838252828201915082860187848660051b8901011115610348578586fd5b855b858110156101a35781516001600160401b03811115610367578788fd5b8801603f81018a13610377578788fd5b6103888a87830151604084016100b6565b855250928401929084019060010161034a565b60008060008060008060c087890312156103b3578182fd5b86516001600160401b03808211156103c9578384fd5b6103d58a838b016102ac565b975060208901519150808211156103ea578384fd5b6103f68a838b0161012d565b9650604089015191508082111561040b578384fd5b6104178a838b016102ac565b9550606089015191508082111561042c578384fd5b6104388a838b016101b0565b9450608089015191508082111561044d578384fd5b6104598a838b0161021a565b935060a089015191508082111561046e578283fd5b5061047b89828a01610309565b9150509295509295509295565b604051601f8201601f191681016001600160401b03811182821017156104b0576104b06104db565b604052919050565b60006001600160401b038211156104d1576104d16104db565b5060051b60200190565b634e487b7160e01b600052604160045260246000fd5b603f806104ff6000396000f3fe6080604052600080fdfea26469706673582212209864ab97aa6a0d2c5cc0828f7fbe63df8fb5e90c758d49371edb3184bcc8239664736f6c6343000804003300000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000756b5b30000000000000000000000000000000000000000000000000000000000000002000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000006ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8f0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000371776500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003657771000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097177657177657177650000000000000000000000000000000000000000000000" + # flaky test + test "returns error when contract has constructor arguments and they were not provided" do + path = File.cwd!() <> "/test/support/fixture/smart_contract/solidity_0.5.9_smart_contract.sol" + contract = File.read!(path) - deployed_bytecode = - "0x6080604052600080fdfea26469706673582212209864ab97aa6a0d2c5cc0828f7fbe63df8fb5e90c758d49371edb3184bcc8239664736f6c63430008040033" + constructor_arguments = "" - assert %{ - "metadata_hash_with_length" => - "a26469706673582212209864ab97aa6a0d2c5cc0828f7fbe63df8fb5e90c758d49371edb3184bcc8239664736f6c63430008040033", - "trimmed_bytecode" => - "60806040526315c8dd0d60005534801561001857600080fd5b5060405161053e38038061053e8339810160408190526100379161039b565b855161004a906001906020890190610056565b505050505050506104f1565b828054828255906000526020600020908101928215610091579160200282015b82811115610091578251825591602001919060010190610076565b5061009d9291506100a1565b5090565b5b8082111561009d57600081556001016100a2565b60006001600160401b038311156100cf576100cf6104db565b60206100e3601f8501601f19168201610488565b91508382528484840111156100f757600080fd5b60005b84811015610113578381015183820183015281016100fa565b848111156101245760008286850101525b50509392505050565b600082601f83011261013d578081fd5b8151602061015261014d836104b8565b610488565b80838252828201915082860187848660051b8901011115610171578586fd5b855b858110156101a35781516001600160a01b0381168114610191578788fd5b84529284019290840190600101610173565b5090979650505050505050565b600082601f8301126101c0578081fd5b815160206101d061014d836104b8565b80838252828201915082860187848660051b89010111156101ef578586fd5b855b858110156101a35781518015158114610208578788fd5b845292840192908401906001016101f1565b600082601f83011261022a578081fd5b8151602061023a61014d836104b8565b80838252828201915082860187848660051b8901011115610259578586fd5b855b858110156101a35781516001600160401b03811115610278578788fd5b8801603f81018a13610288578788fd5b6102998a87830151604084016100b6565b855250928401929084019060010161025b565b600082601f8301126102bc578081fd5b815160206102cc61014d836104b8565b80838252828201915082860187848660051b89010111156102eb578586fd5b855b858110156101a3578151845292840192908401906001016102ed565b600082601f830112610319578081fd5b8151602061032961014d836104b8565b80838252828201915082860187848660051b8901011115610348578586fd5b855b858110156101a35781516001600160401b03811115610367578788fd5b8801603f81018a13610377578788fd5b6103888a87830151604084016100b6565b855250928401929084019060010161034a565b60008060008060008060c087890312156103b3578182fd5b86516001600160401b03808211156103c9578384fd5b6103d58a838b016102ac565b975060208901519150808211156103ea578384fd5b6103f68a838b0161012d565b9650604089015191508082111561040b578384fd5b6104178a838b016102ac565b9550606089015191508082111561042c578384fd5b6104388a838b016101b0565b9450608089015191508082111561044d578384fd5b6104598a838b0161021a565b935060a089015191508082111561046e578283fd5b5061047b89828a01610309565b9150509295509295509295565b604051601f8201601f191681016001600160401b03811182821017156104b0576104b06104db565b604052919050565b60006001600160401b038211156104d1576104d16104db565b5060051b60200190565b634e487b7160e01b600052604160045260246000fd5b603f806104ff6000396000f3fe6080604052600080fdfe00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000756b5b30000000000000000000000000000000000000000000000000000000000000002000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000006ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8f0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000371776500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003657771000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097177657177657177650000000000000000000000000000000000000000000000", - "compiler_version" => %CBOR.Tag{tag: :bytes, value: <<0, 8, 4>>} - } = Verifier.extract_bytecode_and_metadata_hash(contract_creation_code, deployed_bytecode) - end + bytecode = + "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80633177029f116100715780633177029f1461025f57806354fd4d50146102c557806370a082311461034857806395d89b41146103a0578063a9059cbb14610423578063dd62ed3e14610489576100a9565b806306fdde03146100ae578063095ea7b31461013157806318160ddd1461019757806323b872dd146101b5578063313ce5671461023b575b600080fd5b6100b6610501565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f65780820151818401526020810190506100db565b50505050905090810190601f1680156101235780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61017d6004803603604081101561014757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061059f565b604051808215151515815260200191505060405180910390f35b61019f610691565b6040518082815260200191505060405180910390f35b610221600480360360608110156101cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610696565b604051808215151515815260200191505060405180910390f35b61024361090f565b604051808260ff1660ff16815260200191505060405180910390f35b6102ab6004803603604081101561027557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610922565b604051808215151515815260200191505060405180910390f35b6102cd610a14565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561030d5780820151818401526020810190506102f2565b50505050905090810190601f16801561033a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a6004803603602081101561035e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab2565b6040518082815260200191505060405180910390f35b6103a8610afa565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103e85780820151818401526020810190506103cd565b50505050905090810190601f1680156104155780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61046f6004803603604081101561043957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610b98565b604051808215151515815260200191505060405180910390f35b6104eb6004803603604081101561049f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cfe565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105975780601f1061056c57610100808354040283529160200191610597565b820191906000526020600020905b81548152906001019060200180831161057a57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b600090565b6000816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610762575081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b801561076e5750600082115b1561090357816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610908565b600090505b9392505050565b600460009054906101000a900460ff1681565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60068054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610aaa5780601f10610a7f57610100808354040283529160200191610aaa565b820191906000526020600020905b815481529060010190602001808311610a8d57829003601f168201915b505050505081565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b905780601f10610b6557610100808354040283529160200191610b90565b820191906000526020600020905b815481529060010190602001808311610b7357829003601f168201915b505050505081565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410158015610be85750600082115b15610cf357816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610cf8565b600090505b92915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea265627a7a72305820fe0ba5210ac95870683c2cb054304b04565703bd16c7d7e956df694c9643c6d264736f6c63430005090032" - # https://solidity.readthedocs.io/en/v0.6.6/contracts.html?highlight=immutable#constant-and-immutable-state-variables - test "verifies smart-contract with `immutable` assignment" do - bytecode = - "0x6080604052348015600f57600080fd5b506004361060285760003560e01c8063fb49908514602d575b600080fd5b605060048036036020811015604157600080fd5b50356001600160a01b03166064565b604080519115158252519081900360200190f35b7f0000000000000000000000000000000000000000000000056b3977a93ae7c2006001600160a01b038216311191905056fea2646970667358221220b4fbf35809f2d1b85699a897ebb75d00c8c26b29b72decc53db18ddbd853352164736f6c63430006070033" - - tx_input = - "0x60e06040523360601b60c05234801561001757600080fd5b5060405161013f38038061013f8339818101604052604081101561003a57600080fd5b50805160209091015160808290526001600160a01b03163160a081905260c05160601c60cc6100736000395080606652505060cc6000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063fb49908514602d575b600080fd5b605060048036036020811015604157600080fd5b50356001600160a01b03166064565b604080519115158252519081900360200190f35b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038216311191905056fea2646970667358221220b4fbf35809f2d1b85699a897ebb75d00c8c26b29b72decc53db18ddbd853352164736f6c63430006070033000000000000000000000000000000000000000000000000000000000000000100000000000000000000000023602745048d3b8d0a7f953ad444da4cd237ac83" - - contract_address = insert(:contract_address, contract_code: bytecode) - - :transaction - |> insert(created_contract_address_hash: contract_address.hash, input: tx_input) - |> with_block(status: :ok) - - code = """ - pragma solidity >0.6.4 <0.7.0; - - contract C { - uint constant X = 32**22 + 8; - string constant TEXT = "abc"; - bytes32 constant MY_HASH = keccak256("abc"); - uint immutable decimals; - uint immutable maxBalance; - address immutable owner = msg.sender; - - constructor(uint _decimals, address _reference) public { - decimals = _decimals; - // Assignments to immutables can even access the environment. - maxBalance = _reference.balance; - } - - function isBalanceTooHigh(address _other) public view returns (bool) { - return _other.balance > maxBalance; - } - } - """ - - constructor_arguments = - "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000023602745048d3b8d0a7f953ad444da4cd237ac83" - - params = %{ - "contract_source_code" => code, - "compiler_version" => "v0.6.7+commit.b8d736ae", - "evm_version" => "default", - "name" => "C", - "optimization" => true, - "constructor_arguments" => constructor_arguments - } - - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil - end + contract_address = insert(:contract_address, contract_code: bytecode) - test "verifies smart-contract created from another contract" do - path = File.cwd!() <> "/test/support/fixture/smart_contract/contract_from_factory.sol" - contract = File.read!(path) + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode <> constructor_arguments + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => contract, + "compiler_version" => "v0.5.9+commit.e560f70d", + "evm_version" => "petersburg", + "name" => "TestToken", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + assert {:error, :generated_bytecode} = Verifier.evaluate_authenticity(contract_address.hash, params) + end + + test "returns error when there is a compilation problem", %{contract_code_info: contract_code_info} do + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + + params = %{ + "contract_source_code" => "pragma solidity ^0.4.24; contract SimpleStorage { ", + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized + } + + assert {:error, :compilation, "Function, variable, struct or modifier declaration expected."} = + Verifier.evaluate_authenticity(contract_address.hash, params) + end + end - constructor_arguments = "4e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37" + describe "extract_bytecode_and_metadata_hash/1" do + test "extracts the bytecode from the hash" do + code = + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a723058203c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b0029" + + assert %{ + "metadata_hash_with_length" => + "a165627a7a723058203c381c1b48b38d050c54d7ef296ecd411040e19420dfec94772b9c49ae106a0b0029", + "trimmed_bytecode" => + "608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600", + "compiler_version" => nil + } = Verifier.extract_bytecode_and_metadata_hash(code, code) + end + + test "extracts everything to the left of the swarm hash" do + contract_creation_code = + "0x60806040526315c8dd0d60005534801561001857600080fd5b5060405161053e38038061053e8339810160408190526100379161039b565b855161004a906001906020890190610056565b505050505050506104f1565b828054828255906000526020600020908101928215610091579160200282015b82811115610091578251825591602001919060010190610076565b5061009d9291506100a1565b5090565b5b8082111561009d57600081556001016100a2565b60006001600160401b038311156100cf576100cf6104db565b60206100e3601f8501601f19168201610488565b91508382528484840111156100f757600080fd5b60005b84811015610113578381015183820183015281016100fa565b848111156101245760008286850101525b50509392505050565b600082601f83011261013d578081fd5b8151602061015261014d836104b8565b610488565b80838252828201915082860187848660051b8901011115610171578586fd5b855b858110156101a35781516001600160a01b0381168114610191578788fd5b84529284019290840190600101610173565b5090979650505050505050565b600082601f8301126101c0578081fd5b815160206101d061014d836104b8565b80838252828201915082860187848660051b89010111156101ef578586fd5b855b858110156101a35781518015158114610208578788fd5b845292840192908401906001016101f1565b600082601f83011261022a578081fd5b8151602061023a61014d836104b8565b80838252828201915082860187848660051b8901011115610259578586fd5b855b858110156101a35781516001600160401b03811115610278578788fd5b8801603f81018a13610288578788fd5b6102998a87830151604084016100b6565b855250928401929084019060010161025b565b600082601f8301126102bc578081fd5b815160206102cc61014d836104b8565b80838252828201915082860187848660051b89010111156102eb578586fd5b855b858110156101a3578151845292840192908401906001016102ed565b600082601f830112610319578081fd5b8151602061032961014d836104b8565b80838252828201915082860187848660051b8901011115610348578586fd5b855b858110156101a35781516001600160401b03811115610367578788fd5b8801603f81018a13610377578788fd5b6103888a87830151604084016100b6565b855250928401929084019060010161034a565b60008060008060008060c087890312156103b3578182fd5b86516001600160401b03808211156103c9578384fd5b6103d58a838b016102ac565b975060208901519150808211156103ea578384fd5b6103f68a838b0161012d565b9650604089015191508082111561040b578384fd5b6104178a838b016102ac565b9550606089015191508082111561042c578384fd5b6104388a838b016101b0565b9450608089015191508082111561044d578384fd5b6104598a838b0161021a565b935060a089015191508082111561046e578283fd5b5061047b89828a01610309565b9150509295509295509295565b604051601f8201601f191681016001600160401b03811182821017156104b0576104b06104db565b604052919050565b60006001600160401b038211156104d1576104d16104db565b5060051b60200190565b634e487b7160e01b600052604160045260246000fd5b603f806104ff6000396000f3fe6080604052600080fdfea26469706673582212209864ab97aa6a0d2c5cc0828f7fbe63df8fb5e90c758d49371edb3184bcc8239664736f6c6343000804003300000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000756b5b30000000000000000000000000000000000000000000000000000000000000002000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000006ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8f0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000371776500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003657771000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097177657177657177650000000000000000000000000000000000000000000000" + + deployed_bytecode = + "0x6080604052600080fdfea26469706673582212209864ab97aa6a0d2c5cc0828f7fbe63df8fb5e90c758d49371edb3184bcc8239664736f6c63430008040033" + + assert %{ + "metadata_hash_with_length" => + "a26469706673582212209864ab97aa6a0d2c5cc0828f7fbe63df8fb5e90c758d49371edb3184bcc8239664736f6c63430008040033", + "trimmed_bytecode" => + "60806040526315c8dd0d60005534801561001857600080fd5b5060405161053e38038061053e8339810160408190526100379161039b565b855161004a906001906020890190610056565b505050505050506104f1565b828054828255906000526020600020908101928215610091579160200282015b82811115610091578251825591602001919060010190610076565b5061009d9291506100a1565b5090565b5b8082111561009d57600081556001016100a2565b60006001600160401b038311156100cf576100cf6104db565b60206100e3601f8501601f19168201610488565b91508382528484840111156100f757600080fd5b60005b84811015610113578381015183820183015281016100fa565b848111156101245760008286850101525b50509392505050565b600082601f83011261013d578081fd5b8151602061015261014d836104b8565b610488565b80838252828201915082860187848660051b8901011115610171578586fd5b855b858110156101a35781516001600160a01b0381168114610191578788fd5b84529284019290840190600101610173565b5090979650505050505050565b600082601f8301126101c0578081fd5b815160206101d061014d836104b8565b80838252828201915082860187848660051b89010111156101ef578586fd5b855b858110156101a35781518015158114610208578788fd5b845292840192908401906001016101f1565b600082601f83011261022a578081fd5b8151602061023a61014d836104b8565b80838252828201915082860187848660051b8901011115610259578586fd5b855b858110156101a35781516001600160401b03811115610278578788fd5b8801603f81018a13610288578788fd5b6102998a87830151604084016100b6565b855250928401929084019060010161025b565b600082601f8301126102bc578081fd5b815160206102cc61014d836104b8565b80838252828201915082860187848660051b89010111156102eb578586fd5b855b858110156101a3578151845292840192908401906001016102ed565b600082601f830112610319578081fd5b8151602061032961014d836104b8565b80838252828201915082860187848660051b8901011115610348578586fd5b855b858110156101a35781516001600160401b03811115610367578788fd5b8801603f81018a13610377578788fd5b6103888a87830151604084016100b6565b855250928401929084019060010161034a565b60008060008060008060c087890312156103b3578182fd5b86516001600160401b03808211156103c9578384fd5b6103d58a838b016102ac565b975060208901519150808211156103ea578384fd5b6103f68a838b0161012d565b9650604089015191508082111561040b578384fd5b6104178a838b016102ac565b9550606089015191508082111561042c578384fd5b6104388a838b016101b0565b9450608089015191508082111561044d578384fd5b6104598a838b0161021a565b935060a089015191508082111561046e578283fd5b5061047b89828a01610309565b9150509295509295509295565b604051601f8201601f191681016001600160401b03811182821017156104b0576104b06104db565b604052919050565b60006001600160401b038211156104d1576104d16104db565b5060051b60200190565b634e487b7160e01b600052604160045260246000fd5b603f806104ff6000396000f3fe6080604052600080fdfe00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000756b5b30000000000000000000000000000000000000000000000000000000000000002000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000006ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8f0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000371776500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003657771000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097177657177657177650000000000000000000000000000000000000000000000", + "compiler_version" => %CBOR.Tag{tag: :bytes, value: <<0, 8, 4>>} + } = Verifier.extract_bytecode_and_metadata_hash(contract_creation_code, deployed_bytecode) + end + + # https://solidity.readthedocs.io/en/v0.6.6/contracts.html?highlight=immutable#constant-and-immutable-state-variables + test "verifies smart-contract with `immutable` assignment" do + bytecode = + "0x6080604052348015600f57600080fd5b506004361060285760003560e01c8063fb49908514602d575b600080fd5b605060048036036020811015604157600080fd5b50356001600160a01b03166064565b604080519115158252519081900360200190f35b7f0000000000000000000000000000000000000000000000056b3977a93ae7c2006001600160a01b038216311191905056fea2646970667358221220b4fbf35809f2d1b85699a897ebb75d00c8c26b29b72decc53db18ddbd853352164736f6c63430006070033" + + tx_input = + "0x60e06040523360601b60c05234801561001757600080fd5b5060405161013f38038061013f8339818101604052604081101561003a57600080fd5b50805160209091015160808290526001600160a01b03163160a081905260c05160601c60cc6100736000395080606652505060cc6000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063fb49908514602d575b600080fd5b605060048036036020811015604157600080fd5b50356001600160a01b03166064565b604080519115158252519081900360200190f35b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038216311191905056fea2646970667358221220b4fbf35809f2d1b85699a897ebb75d00c8c26b29b72decc53db18ddbd853352164736f6c63430006070033000000000000000000000000000000000000000000000000000000000000000100000000000000000000000023602745048d3b8d0a7f953ad444da4cd237ac83" + + contract_address = insert(:contract_address, contract_code: bytecode) - bytecode = - "0x608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f60029" + :transaction + |> insert(created_contract_address_hash: contract_address.hash, input: tx_input) + |> with_block(status: :ok) + + code = """ + pragma solidity >0.6.4 <0.7.0; + + contract C { + uint constant X = 32**22 + 8; + string constant TEXT = "abc"; + bytes32 constant MY_HASH = keccak256("abc"); + uint immutable decimals; + uint immutable maxBalance; + address immutable owner = msg.sender; + + constructor(uint _decimals, address _reference) public { + decimals = _decimals; + // Assignments to immutables can even access the environment. + maxBalance = _reference.balance; + } + + function isBalanceTooHigh(address _other) public view returns (bool) { + return _other.balance > maxBalance; + } + } + """ + + constructor_arguments = + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000023602745048d3b8d0a7f953ad444da4cd237ac83" + + params = %{ + "contract_source_code" => code, + "compiler_version" => "v0.6.7+commit.b8d736ae", + "evm_version" => "default", + "name" => "C", + "optimization" => true, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verifies smart-contract created from another contract" do + path = File.cwd!() <> "/test/support/fixture/smart_contract/contract_from_factory.sol" + contract = File.read!(path) + + constructor_arguments = "4e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37" + + bytecode = + "0x608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f60029" + + init = + "0x608060405234801561001057600080fd5b506040516020806100cc83398101604052516000556099806100336000396000f300608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f600294e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37" + + contract_address = insert(:contract_address, contract_code: bytecode) + + transaction_success_details = [ + status: :ok + ] + + transaction = + :transaction + |> insert() + |> with_block(transaction_success_details) + + :internal_transaction + |> insert( + created_contract_address_hash: contract_address.hash, + init: init, + type: "create", + created_contract_code: bytecode, + input: nil, + transaction_hash: transaction.hash, + index: 0, + block_hash: transaction.block_hash, + block_index: 0 + ) + + params = %{ + "contract_source_code" => contract, + "compiler_version" => "v0.4.26+commit.4563c3fc", + "evm_version" => "default", + "name" => "ContractFromFactory", + "optimization" => true, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verifies smart-contract created from another contract using successful tx" do + path = File.cwd!() <> "/test/support/fixture/smart_contract/contract_from_factory.sol" + contract = File.read!(path) + + constructor_arguments = "4e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37" + + bytecode = + "0x608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f60029" + + init = + "0x608060405234801561001057600080fd5b506040516020806100cc83398101604052516000556099806100336000396000f300608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f600294e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37" + + contract_address = insert(:contract_address, contract_code: bytecode) + + transaction_success_details = [ + status: :ok + ] + + transaction_success = + :transaction + |> insert() + |> with_block(transaction_success_details) + + transaction_failure_details = [ + status: :error + ] + + transaction_failure = + :transaction + |> insert() + |> with_block(transaction_failure_details) + + :internal_transaction + |> insert( + created_contract_address_hash: contract_address.hash, + init: init, + type: "create", + created_contract_code: bytecode, + input: nil, + transaction_hash: transaction_success.hash, + index: 0, + block_hash: transaction_success.block_hash, + block_index: 0 + ) + + :internal_transaction + |> insert( + created_contract_address_hash: contract_address.hash, + init: init, + type: "create", + created_contract_code: bytecode, + input: nil, + transaction_hash: transaction_failure.hash, + index: 0, + block_hash: transaction_failure.block_hash, + block_index: 0 + ) + + params = %{ + "contract_source_code" => contract, + "compiler_version" => "v0.4.26+commit.4563c3fc", + "evm_version" => "default", + "name" => "ContractFromFactory", + "optimization" => true, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + end - init = - "0x608060405234801561001057600080fd5b506040516020806100cc83398101604052516000556099806100336000396000f300608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f600294e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37" + describe "compiler version tests" do + # flaky test + test "verification is failed if wrong version of compiler" do + bytecode_0_5_10 = + "0x608060405234801561001057600080fd5b506040516102453803806102458339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101a98061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820fb47165501c50aae8ccb0394b15f4302606e0ba55eb6d59fe12eca19ba494d5e64736f6c634300050a0032" - contract_address = insert(:contract_address, contract_code: bytecode) + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_0_5_10) + bytecode_constructor_arguments = "#{bytecode_0_5_10}#{constructor_arguments}" - transaction_success_details = [ - status: :ok - ] + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_constructor_arguments + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => @code_0_5, + "compiler_version" => "v0.5.11+commit.c082d0b4", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + response = Verifier.evaluate_authenticity(contract_address.hash, params) + assert {:error, :compiler_version} = response + end + + test "verification is successful if proper version of compiler" do + bytecode_0_5_10 = + "0x608060405234801561001057600080fd5b506040516102453803806102458339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101a98061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820fb47165501c50aae8ccb0394b15f4302606e0ba55eb6d59fe12eca19ba494d5e64736f6c634300050a0032" + + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_0_5_10) + bytecode_constructor_arguments = "#{bytecode_0_5_10}#{constructor_arguments}" - transaction = :transaction - |> insert() - |> with_block(transaction_success_details) - - :internal_transaction - |> insert( - created_contract_address_hash: contract_address.hash, - init: init, - type: "create", - created_contract_code: bytecode, - input: nil, - transaction_hash: transaction.hash, - index: 0, - block_hash: transaction.block_hash, - block_index: 0 - ) - - params = %{ - "contract_source_code" => contract, - "compiler_version" => "v0.4.26+commit.4563c3fc", - "evm_version" => "default", - "name" => "ContractFromFactory", - "optimization" => true, - "constructor_arguments" => constructor_arguments - } - - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_constructor_arguments + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => @code_0_5, + "compiler_version" => "v0.5.10+commit.5a6ea5b1", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end end - test "verifies smart-contract created from another contract using successful tx" do - path = File.cwd!() <> "/test/support/fixture/smart_contract/contract_from_factory.sol" - contract = File.read!(path) - - constructor_arguments = "4e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37" + describe "verification with nightly builds" do + test "verification is successful if proper nightly version of compiler ~0.4" do + bytecode_v0_4_24_nightly_2018_4_26_commit_ef2111a2 = + "0x608060405234801561001057600080fd5b5060405160208061023d833981018060405281019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101b28061008b6000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063256fec881461005c5780633fa4f245146100b3578063812600df146100de575b600080fd5b34801561006857600080fd5b5061007161010b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156100bf57600080fd5b506100c8610131565b6040518082815260200191505060405180910390f35b3480156100ea57600080fd5b5061010960048036038101908080359060200190929190505050610137565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505600a165627a7a723058202d622d653be0a507f7ac0bc89d8934ccdbaf5e127abd603c3864a462149885070029" - bytecode = - "0x608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f60029" + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_v0_4_24_nightly_2018_4_26_commit_ef2111a2) + bytecode_constructor_arguments = "#{bytecode_v0_4_24_nightly_2018_4_26_commit_ef2111a2}#{constructor_arguments}" - init = - "0x608060405234801561001057600080fd5b506040516020806100cc83398101604052516000556099806100336000396000f300608060405260043610603e5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416638052474d81146043575b600080fd5b348015604e57600080fd5b5060556067565b60408051918252519081900360200190f35b600054815600a165627a7a72305820a1a0ec90e133c3064fab0ae82aa02a020224ea39d2b5421b6788f800bdde02f600294e1477bdc40fc2458bf646f96f269502658277779fdf0f4080fe798a2d45bc37" + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_constructor_arguments + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => @code_0_4, + "compiler_version" => "v0.4.24-nightly.2018.4.26+commit.ef2111a2", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verification is successful if proper nightly version of compiler ~0.5.10" do + bytecode_0_5_10_nightly_2019_6_4_commit_95e6b2e4 = + "0x608060405234801561001057600080fd5b5060405161026a38038061026a8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101ce8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a723058208d4e3fa9b2179a8384e617e388dde334be1b44e7b11b42ab964ab1050e7cedca64736f6c637827302e352e31302d6e696768746c792e323031392e362e342b636f6d6d69742e39356536623265340057" + + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_0_5_10_nightly_2019_6_4_commit_95e6b2e4) + bytecode_constructor_arguments = "#{bytecode_0_5_10_nightly_2019_6_4_commit_95e6b2e4}#{constructor_arguments}" - contract_address = insert(:contract_address, contract_code: bytecode) + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_constructor_arguments + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => @code_0_5, + "compiler_version" => "v0.5.10-nightly.2019.6.4+commit.95e6b2e4", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verification is successful if proper nightly version of compiler ~0.5.11" do + bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753 = + "0x608060405234801561001057600080fd5b5060405161026b38038061026b8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101cf8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820f7420b8c3b16d83ce728d8c279f0f887c4dcd7bfcd38c484acc9cdb82fde785764736f6c637828302e352e31312d6e696768746c792e323031392e362e32352b636f6d6d69742e31636338343735330058" + + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753) + bytecode_constructor_arguments = "#{bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753}#{constructor_arguments}" - transaction_success_details = [ - status: :ok - ] + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_constructor_arguments + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => @code_0_5, + "compiler_version" => "v0.5.11-nightly.2019.6.25+commit.1cc84753", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verification is successful if proper nightly version of compiler ~0.5.14" do + bytecode_0_5_14_nightly_2019_12_10_commit_45aa7a88 = + "0x608060405234801561001057600080fd5b5060405161026c38038061026c8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101d08061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72315820ec5a7ce04b1c2f97a3d3e61ae1b5cb06585e81c504542fd9668a8ead654da72764736f6c637829302e352e31342d6e696768746c792e323031392e31322e31302b636f6d6d69742e34356161376138380059" + + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_0_5_14_nightly_2019_12_10_commit_45aa7a88) + bytecode_constructor_arguments = "#{bytecode_0_5_14_nightly_2019_12_10_commit_45aa7a88}#{constructor_arguments}" - transaction_success = :transaction - |> insert() - |> with_block(transaction_success_details) + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_constructor_arguments + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => @code_0_5, + "compiler_version" => "v0.5.14-nightly.2019.12.10+commit.45aa7a88", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verification is successful if proper nightly version of compiler ~0.6.0" do + bytecode_0_6_1_nightly_2020_1_2_commit_d082b9b8 = + "0x608060405234801561001057600080fd5b5060405161026a38038061026a8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101ce8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea264697066735822122029b5dde5889a195ed02cebb1a638ae3754be34464b9a2bc8b48b6286636031fb64736f6c637826302e362e312d6e696768746c792e323032302e312e322b636f6d6d69742e64303832623962380057" + + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_0_6_1_nightly_2020_1_2_commit_d082b9b8) + bytecode_constructor_arguments = "#{bytecode_0_6_1_nightly_2020_1_2_commit_d082b9b8}#{constructor_arguments}" - transaction_failure_details = [ - status: :error - ] + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_constructor_arguments + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => @code_0_6, + "compiler_version" => "v0.6.1-nightly.2020.1.2+commit.d082b9b8", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "verification is failed if wrong nightly version of compiler ~0.5.11" do + bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753 = + "0x608060405234801561001057600080fd5b5060405161026b38038061026b8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101cf8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820f5be0e6763c440be759726643bdd4b03370e9f1b58fd803ab18b0b4f2aa58b7664736f6c637828302e352e31312d6e696768746c792e323031392e362e32352b636f6d6d69742e31636338343735330058" + + constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" + contract_address = insert(:contract_address, contract_code: bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753) + bytecode_constructor_arguments = "#{bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753}#{constructor_arguments}" - transaction_failure = :transaction - |> insert() - |> with_block(transaction_failure_details) - - :internal_transaction - |> insert( - created_contract_address_hash: contract_address.hash, - init: init, - type: "create", - created_contract_code: bytecode, - input: nil, - transaction_hash: transaction_success.hash, - index: 0, - block_hash: transaction_success.block_hash, - block_index: 0 - ) - - :internal_transaction - |> insert( - created_contract_address_hash: contract_address.hash, - init: init, - type: "create", - created_contract_code: bytecode, - input: nil, - transaction_hash: transaction_failure.hash, - index: 0, - block_hash: transaction_failure.block_hash, - block_index: 0 - ) - - params = %{ - "contract_source_code" => contract, - "compiler_version" => "v0.4.26+commit.4563c3fc", - "evm_version" => "default", - "name" => "ContractFromFactory", - "optimization" => true, - "constructor_arguments" => constructor_arguments - } - - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil + |> insert( + created_contract_address_hash: contract_address.hash, + input: bytecode_constructor_arguments + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => @code_0_5, + "compiler_version" => "v0.5.11-nightly.2019.8.10+commit.f5f2bbb2", + "evm_version" => "homestead", + "name" => "Incrementer", + "optimization" => false, + "constructor_arguments" => constructor_arguments + } + + response = Verifier.evaluate_authenticity(contract_address.hash, params) + assert {:error, :compiler_version} = response + end end - end - describe "compiler version tests" do - # flaky test - test "verification is failed if wrong version of compiler" do - bytecode_0_5_10 = - "0x608060405234801561001057600080fd5b506040516102453803806102458339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101a98061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820fb47165501c50aae8ccb0394b15f4302606e0ba55eb6d59fe12eca19ba494d5e64736f6c634300050a0032" - - constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" - contract_address = insert(:contract_address, contract_code: bytecode_0_5_10) - bytecode_constructor_arguments = "#{bytecode_0_5_10}#{constructor_arguments}" - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: bytecode_constructor_arguments - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => @code_0_5, - "compiler_version" => "v0.5.11+commit.c082d0b4", - "evm_version" => "homestead", - "name" => "Incrementer", - "optimization" => false, - "constructor_arguments" => constructor_arguments - } - - response = Verifier.evaluate_authenticity(contract_address.hash, params) - assert {:error, :compiler_version} = response - end + describe "regression tests for https://github.com/blockscout/blockscout/pull/5166" do + test "issue 5114" do + contract_source_code = + "#{File.cwd!()}/test/support/fixture/smart_contract/issue_5114.sol" + |> File.read!() - test "verification is successful if proper version of compiler" do - bytecode_0_5_10 = - "0x608060405234801561001057600080fd5b506040516102453803806102458339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101a98061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820fb47165501c50aae8ccb0394b15f4302606e0ba55eb6d59fe12eca19ba494d5e64736f6c634300050a0032" - - constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" - contract_address = insert(:contract_address, contract_code: bytecode_0_5_10) - bytecode_constructor_arguments = "#{bytecode_0_5_10}#{constructor_arguments}" - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: bytecode_constructor_arguments - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => @code_0_5, - "compiler_version" => "v0.5.10+commit.5a6ea5b1", - "evm_version" => "homestead", - "name" => "Incrementer", - "optimization" => false, - "constructor_arguments" => constructor_arguments - } - - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil - end - end + bytecode = + "0x60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106ed565b610118565b61005b610093366004610707565b610164565b3480156100a457600080fd5b506100ad6101da565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106ed565b610217565b3480156100f557600080fd5b506100ad610241565b6101066102a2565b610116610111610346565b610355565b565b610120610379565b6001600160a01b0316336001600160a01b0316141561015957610154816040518060200160405280600081525060006103ac565b610161565b6101616100fe565b50565b61016c610379565b6001600160a01b0316336001600160a01b031614156101cd576101c88383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506103ac915050565b6101d5565b6101d56100fe565b505050565b60006101e4610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610346565b9050610214565b6102146100fe565b90565b61021f610379565b6001600160a01b0316336001600160a01b03161415610159576101548161040b565b600061024b610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610379565b606061029183836040518060600160405280602781526020016108016027913961045f565b9392505050565b803b15155b919050565b6102aa610379565b6001600160a01b0316336001600160a01b031614156103415760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b610116565b600061035061053a565b905090565b3660008037600080366000845af43d6000803e808015610374573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316905090565b6103b583610562565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806103f65750805b156101d557610405838361026c565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610434610379565b604080516001600160a01b03928316815291841660208301520160405180910390a161016181610611565b606061046a84610298565b6104c55760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610338565b600080856001600160a01b0316856040516104e09190610785565b600060405180830381855af49150503d806000811461051b576040519150601f19603f3d011682016040523d82523d6000602084013e610520565b606091505b509150915061053082828661069d565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61039d565b61056b81610298565b6105cd5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610338565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b6001600160a01b0381166106765760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b6064820152608401610338565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036105f0565b606083156106ac575081610291565b8251156106bc5782518084602001fd5b8160405162461bcd60e51b815260040161033891906107a1565b80356001600160a01b038116811461029d57600080fd5b6000602082840312156106fe578081fd5b610291826106d6565b60008060006040848603121561071b578182fd5b610724846106d6565b9250602084013567ffffffffffffffff80821115610740578384fd5b818601915086601f830112610753578384fd5b813581811115610761578485fd5b876020828501011115610772578485fd5b6020830194508093505050509250925092565b600082516107978184602087016107d4565b9190910192915050565b60006020825282518060208401526107c08160408501602087016107d4565b601f01601f19169190910160400192915050565b60005b838110156107ef5781810151838201526020016107d7565b83811115610405575050600091015256fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122016ea36e15be10f9560025e0ec9401e2e9110cb5ec41d110b4a0e391838c1f19b64736f6c63430008020033" - describe "verification with nightly builds" do - test "verification is successful if proper nightly version of compiler ~0.4" do - bytecode_v0_4_24_nightly_2018_4_26_commit_ef2111a2 = - "0x608060405234801561001057600080fd5b5060405160208061023d833981018060405281019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101b28061008b6000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063256fec881461005c5780633fa4f245146100b3578063812600df146100de575b600080fd5b34801561006857600080fd5b5061007161010b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156100bf57600080fd5b506100c8610131565b6040518082815260200191505060405180910390f35b3480156100ea57600080fd5b5061010960048036038101908080359060200190929190505050610137565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505600a165627a7a723058202d622d653be0a507f7ac0bc89d8934ccdbaf5e127abd603c3864a462149885070029" - - constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" - contract_address = insert(:contract_address, contract_code: bytecode_v0_4_24_nightly_2018_4_26_commit_ef2111a2) - bytecode_constructor_arguments = "#{bytecode_v0_4_24_nightly_2018_4_26_commit_ef2111a2}#{constructor_arguments}" - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: bytecode_constructor_arguments - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => @code_0_4, - "compiler_version" => "v0.4.24-nightly.2018.4.26+commit.ef2111a2", - "evm_version" => "homestead", - "name" => "Incrementer", - "optimization" => false, - "constructor_arguments" => constructor_arguments - } - - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil - end + contract_creation_code = + "0x608060405260405162000f4038038062000f408339810160408190526200002691620004d4565b82816200005560017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd62000603565b60008051602062000ef9833981519152146200008157634e487b7160e01b600052600160045260246000fd5b6200008f82826000620000ff565b50620000bf905060017fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610462000603565b60008051602062000ed983398151915214620000eb57634e487b7160e01b600052600160045260246000fd5b620000f68262000170565b5050506200066c565b6200010a83620001cb565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806200014c5750805b156200016b576200016983836200029360201b6200026c1760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6200019b620002c2565b604080516001600160a01b03928316815291841660208301520160405180910390a1620001c881620002fb565b50565b620001e1816200038b60201b620002981760201c565b620002495760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b806200027260008051602062000ef983398151915260001b6200039560201b620002141760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6060620002bb838360405180606001604052806027815260200162000f196027913962000398565b9392505050565b6000620002ec60008051602062000ed983398151915260001b6200039560201b620002141760201c565b546001600160a01b0316905090565b6001600160a01b038116620003625760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840162000240565b806200027260008051602062000ed983398151915260001b6200039560201b620002141760201c565b803b15155b919050565b90565b6060620003a5846200038b565b620004025760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b606482015260840162000240565b600080856001600160a01b0316856040516200041f9190620005b0565b600060405180830381855af49150503d80600081146200045c576040519150601f19603f3d011682016040523d82523d6000602084013e62000461565b606091505b509092509050620004748282866200047e565b9695505050505050565b606083156200048f575081620002bb565b825115620004a05782518084602001fd5b8160405162461bcd60e51b8152600401620002409190620005ce565b80516001600160a01b03811681146200039057600080fd5b600080600060608486031215620004e9578283fd5b620004f484620004bc565b92506200050460208501620004bc565b60408501519092506001600160401b038082111562000521578283fd5b818601915086601f83011262000535578283fd5b8151818111156200054a576200054a62000656565b604051601f8201601f19908116603f0116810190838211818310171562000575576200057562000656565b816040528281528960208487010111156200058e578586fd5b620005a183602083016020880162000627565b80955050505050509250925092565b60008251620005c481846020870162000627565b9190910192915050565b6000602082528251806020840152620005ef81604085016020870162000627565b601f01601f19169190910160400192915050565b6000828210156200062257634e487b7160e01b81526011600452602481fd5b500390565b60005b83811015620006445781810151838201526020016200062a565b83811115620001695750506000910152565b634e487b7160e01b600052604160045260246000fd5b61085d806200067c6000396000f3fe60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106ed565b610118565b61005b610093366004610707565b610164565b3480156100a457600080fd5b506100ad6101da565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106ed565b610217565b3480156100f557600080fd5b506100ad610241565b6101066102a2565b610116610111610346565b610355565b565b610120610379565b6001600160a01b0316336001600160a01b0316141561015957610154816040518060200160405280600081525060006103ac565b610161565b6101616100fe565b50565b61016c610379565b6001600160a01b0316336001600160a01b031614156101cd576101c88383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506103ac915050565b6101d5565b6101d56100fe565b505050565b60006101e4610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610346565b9050610214565b6102146100fe565b90565b61021f610379565b6001600160a01b0316336001600160a01b03161415610159576101548161040b565b600061024b610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610379565b606061029183836040518060600160405280602781526020016108016027913961045f565b9392505050565b803b15155b919050565b6102aa610379565b6001600160a01b0316336001600160a01b031614156103415760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b610116565b600061035061053a565b905090565b3660008037600080366000845af43d6000803e808015610374573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316905090565b6103b583610562565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806103f65750805b156101d557610405838361026c565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610434610379565b604080516001600160a01b03928316815291841660208301520160405180910390a161016181610611565b606061046a84610298565b6104c55760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610338565b600080856001600160a01b0316856040516104e09190610785565b600060405180830381855af49150503d806000811461051b576040519150601f19603f3d011682016040523d82523d6000602084013e610520565b606091505b509150915061053082828661069d565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61039d565b61056b81610298565b6105cd5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610338565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b6001600160a01b0381166106765760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b6064820152608401610338565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036105f0565b606083156106ac575081610291565b8251156106bc5782518084602001fd5b8160405162461bcd60e51b815260040161033891906107a1565b80356001600160a01b038116811461029d57600080fd5b6000602082840312156106fe578081fd5b610291826106d6565b60008060006040848603121561071b578182fd5b610724846106d6565b9250602084013567ffffffffffffffff80821115610740578384fd5b818601915086601f830112610753578384fd5b813581811115610761578485fd5b876020828501011115610772578485fd5b6020830194508093505050509250925092565b600082516107978184602087016107d4565b9190910192915050565b60006020825282518060208401526107c08160408501602087016107d4565b601f01601f19169190910160400192915050565b60005b838110156107ef5781810151838201526020016107d7565b83811115610405575050600091015256fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122016ea36e15be10f9560025e0ec9401e2e9110cb5ec41d110b4a0e391838c1f19b64736f6c63430008020033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65640000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f051200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - test "verification is successful if proper nightly version of compiler ~0.5.10" do - bytecode_0_5_10_nightly_2019_6_4_commit_95e6b2e4 = - "0x608060405234801561001057600080fd5b5060405161026a38038061026a8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101ce8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a723058208d4e3fa9b2179a8384e617e388dde334be1b44e7b11b42ab964ab1050e7cedca64736f6c637827302e352e31302d6e696768746c792e323031392e362e342b636f6d6d69742e39356536623265340057" - - constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" - contract_address = insert(:contract_address, contract_code: bytecode_0_5_10_nightly_2019_6_4_commit_95e6b2e4) - bytecode_constructor_arguments = "#{bytecode_0_5_10_nightly_2019_6_4_commit_95e6b2e4}#{constructor_arguments}" - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: bytecode_constructor_arguments - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => @code_0_5, - "compiler_version" => "v0.5.10-nightly.2019.6.4+commit.95e6b2e4", - "evm_version" => "homestead", - "name" => "Incrementer", - "optimization" => false, - "constructor_arguments" => constructor_arguments - } - - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil - end + contract_address = insert(:contract_address, contract_code: bytecode) - test "verification is successful if proper nightly version of compiler ~0.5.11" do - bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753 = - "0x608060405234801561001057600080fd5b5060405161026b38038061026b8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101cf8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820f7420b8c3b16d83ce728d8c279f0f887c4dcd7bfcd38c484acc9cdb82fde785764736f6c637828302e352e31312d6e696768746c792e323031392e362e32352b636f6d6d69742e31636338343735330058" - - constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" - contract_address = insert(:contract_address, contract_code: bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753) - bytecode_constructor_arguments = "#{bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753}#{constructor_arguments}" - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: bytecode_constructor_arguments - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => @code_0_5, - "compiler_version" => "v0.5.11-nightly.2019.6.25+commit.1cc84753", - "evm_version" => "homestead", - "name" => "Incrementer", - "optimization" => false, - "constructor_arguments" => constructor_arguments - } - - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil - end + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: contract_creation_code + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => contract_source_code, + "compiler_version" => "v0.8.2+commit.661d1103", + "evm_version" => "default", + "name" => "TransparentUpgradeableProxy", + "optimization" => true, + "optimization_runs" => @optimization_runs, + "autodetect_constructor_args" => true + } + + assert {:ok, + %{ + abi: abi, + constructor_arguments: + "0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f051200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + }} = Verifier.evaluate_authenticity(contract_address.hash, params) + + assert abi != nil + end + + test "issue 5127" do + contract_source_code = + "#{File.cwd!()}/test/support/fixture/smart_contract/issue_5127.sol" + |> File.read!() + + bytecode = + "0x60806040523480156200001157600080fd5b5060405162001bfc38038062001bfc8339810160408190526200003491620002d6565b6040805180820182526009808252682ca2a9902a37b5b2b760b91b6020808401918252845180860190955260038086526259455360e81b91860191909152600080546001600160a01b03808c16610100026001600160a81b03199092169190911790915560018054828b166001600160a01b03199182161790915560028054928a169290911691909117905585905582519293926012928992899289928992620000de9262000213565b508551620000f490600a90602089019062000213565b5050600b805460ff90951660ff199095169490941790935550620001209350339250889150506200012b565b505050505062000398565b6001600160a01b038216620001865760405162461bcd60e51b815260206004820152601f60248201527f4b415032303a206d696e7420746f20746865207a65726f206164647265737300604482015260640160405180910390fd5b80600860008282546200019a919062000334565b90915550506001600160a01b03821660009081526006602052604081208054839290620001c990849062000334565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b82805462000221906200035b565b90600052602060002090601f01602090048101928262000245576000855562000290565b82601f106200026057805160ff191683800117855562000290565b8280016001018555821562000290579182015b828111156200029057825182559160200191906001019062000273565b506200029e929150620002a2565b5090565b5b808211156200029e5760008155600101620002a3565b80516001600160a01b0381168114620002d157600080fd5b919050565b600080600080600060a08688031215620002ef57600080fd5b855194506200030160208701620002b9565b93506200031160408701620002b9565b92506200032160608701620002b9565b9150608086015190509295509295909350565b600082198211156200035657634e487b7160e01b600052601160045260246000fd5b500190565b600181811c908216806200037057607f821691505b602082108114156200039257634e487b7160e01b600052602260045260246000fd5b50919050565b61185480620003a86000396000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c80638456cb591161010f578063bddae40e116100a2578063dd62ed3e11610071578063dd62ed3e14610420578063de9c2a6b14610459578063f71c559c14610466578063f9f92be41461047957600080fd5b8063bddae40e146103cf578063c2f92192146103e2578063d864e740146103f5578063da72c1e81461040d57600080fd5b80639cfe42da116100de5780639cfe42da1461038e578063a1d5ec4b146103a1578063a457c2d7146103a9578063a9059cbb146103bc57600080fd5b80638456cb59146103405780638e39103c1461034857806390d6b45f1461035b57806395d89b411461038657600080fd5b8063394b652b1161018757806359e026f71161015657806359e026f7146102e65780635c975abb146102f9578063704b6c021461030457806370a082311461031757600080fd5b8063394b652b146102a357806339509351146102b85780633f4ba83a146102cb578063483a83df146102d357600080fd5b806318160ddd116101c357806318160ddd146102515780631ae878d31461026857806323b872dd14610271578063313ce5671461028457600080fd5b806306fdde03146101ea578063095ea7b3146102085780631714d7f31461022b575b600080fd5b6101f261049c565b6040516101ff9190611574565b60405180910390f35b61021b6102163660046115aa565b61052a565b60405190151581526020016101ff565b6101f2604051806040016040528060078152602001667975656d6d616960c81b81525081565b61025a60085481565b6040519081526020016101ff565b61025a60035481565b61021b61027f3660046115d4565b610540565b600b546102919060ff1681565b60405160ff90911681526020016101ff565b6102b66102b1366004611610565b610651565b005b61021b6102c63660046115aa565b61068c565b6102b66106c8565b6102b66102e1366004611629565b610701565b61021b6102f43660046115d4565b610739565b60005460ff1661021b565b6102b6610312366004611629565b61094f565b61025a610325366004611629565b6001600160a01b031660009081526006602052604090205490565b6102b6610a1b565b6102b6610356366004611629565b610a52565b60025461036e906001600160a01b031681565b6040516001600160a01b0390911681526020016101ff565b6101f2610a8a565b6102b661039c366004611629565b610a97565b6102b6610acf565b61021b6103b73660046115aa565b610b06565b61021b6103ca3660046115aa565b610b95565b6102b66103dd366004611629565b610bf4565b60015461036e906001600160a01b031681565b60005461036e9061010090046001600160a01b031681565b61021b61041b3660046115d4565b610c2c565b61025a61042e366004611644565b6001600160a01b03918216600090815260076020908152604080832093909416825291909152205490565b60045461021b9060ff1681565b61021b6104743660046115d4565b610ddb565b61021b610487366004611629565b60056020526000908152604090205460ff1681565b600980546104a990611677565b80601f01602080910402602001604051908101604052809291908181526020018280546104d590611677565b80156105225780601f106104f757610100808354040283529160200191610522565b820191906000526020600020905b81548152906001019060200180831161050557829003601f168201915b505050505081565b6000610537338484610ede565b50600192915050565b6000805460ff161561056d5760405162461bcd60e51b8152600401610564906116b2565b60405180910390fd5b6001600160a01b038416600090815260056020526040902054849060ff16156105a85760405162461bcd60e51b8152600401610564906116cd565b6105b3858585611002565b6001600160a01b0385166000908152600760209081526040808320338452909152902054838110156106385760405162461bcd60e51b815260206004820152602860248201527f4b415032303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b6064820152608401610564565b6106458633868403610ede565b50600195945050505050565b60005461010090046001600160a01b031633146106805760405162461bcd60e51b815260040161056490611704565b610689816111d1565b50565b3360008181526007602090815260408083206001600160a01b038716845290915281205490916105379185906106c3908690611751565b610ede565b60005461010090046001600160a01b031633146106f75760405162461bcd60e51b815260040161056490611704565b6106ff611217565b565b60005461010090046001600160a01b031633146107305760405162461bcd60e51b815260040161056490611704565b61068981611298565b6000805460ff161561075d5760405162461bcd60e51b8152600401610564906116b2565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf0916107aa91339190600401611769565b602060405180830381865afa1580156107c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107eb9190611795565b6108075760405162461bcd60e51b8152600401610564906117b7565b6003546002546040516306f19a8d60e21b81526001600160a01b03878116600483015290911690631bc66a3490602401602060405180830381865afa158015610854573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061087891906117ee565b101580156108f657506003546002546040516306f19a8d60e21b81526001600160a01b03868116600483015290911690631bc66a34906024015b602060405180830381865afa1580156108cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f391906117ee565b10155b61093a5760405162461bcd60e51b81526020600482015260156024820152744f6e6c7920696e7465726e616c20707572706f736560581b6044820152606401610564565b610945848484611002565b5060019392505050565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf09161099c91339190600401611769565b602060405180830381865afa1580156109b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109dd9190611795565b6109f95760405162461bcd60e51b8152600401610564906117b7565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60005461010090046001600160a01b03163314610a4a5760405162461bcd60e51b815260040161056490611704565b6106ff6112f2565b60005461010090046001600160a01b03163314610a815760405162461bcd60e51b815260040161056490611704565b6106898161134a565b600a80546104a990611677565b60005461010090046001600160a01b03163314610ac65760405162461bcd60e51b815260040161056490611704565b61068981611400565b60005461010090046001600160a01b03163314610afe5760405162461bcd60e51b815260040161056490611704565b6106ff61148a565b3360009081526007602090815260408083206001600160a01b038616845290915281205482811015610b885760405162461bcd60e51b815260206004820152602560248201527f4b415032303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610564565b6109453385858403610ede565b6000805460ff1615610bb95760405162461bcd60e51b8152600401610564906116b2565b3360008181526005602052604090205460ff1615610be95760405162461bcd60e51b8152600401610564906116cd565b610945338585611002565b60005461010090046001600160a01b03163314610c235760405162461bcd60e51b815260040161056490611704565b610689816114c2565b6000805461010090046001600160a01b03163314610c5c5760405162461bcd60e51b815260040161056490611704565b6001600160a01b038416600090815260066020526040902054821115610cd25760405162461bcd60e51b815260206004820152602560248201527f4b415032303a207472616e7366657220616d6f756e74206578636565642062616044820152646c616e636560d81b6064820152608401610564565b6001600160a01b038316610d285760405162461bcd60e51b815260206004820152601f60248201527f4b415032303a207472616e7366657220746f207a65726f2061646472657373006044820152606401610564565b6001600160a01b03841660009081526006602052604081208054849290610d50908490611807565b90915550506001600160a01b03831660009081526006602052604081208054849290610d7d908490611751565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610dc991815260200190565b60405180910390a35060019392505050565b6000805460ff1615610dff5760405162461bcd60e51b8152600401610564906116b2565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf091610e4c91339190600401611769565b602060405180830381865afa158015610e69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8d9190611795565b610ea95760405162461bcd60e51b8152600401610564906117b7565b6003546002546040516306f19a8d60e21b81526001600160a01b03878116600483015290911690631bc66a34906024016108b2565b6001600160a01b038316610f405760405162461bcd60e51b8152602060048201526024808201527f4b415032303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610564565b6001600160a01b038216610fa15760405162461bcd60e51b815260206004820152602260248201527f4b415032303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610564565b6001600160a01b0383811660008181526007602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6001600160a01b0383166110665760405162461bcd60e51b815260206004820152602560248201527f4b415032303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610564565b6001600160a01b0382166110c85760405162461bcd60e51b815260206004820152602360248201527f4b415032303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610564565b6001600160a01b038316600090815260066020526040902054818110156111405760405162461bcd60e51b815260206004820152602660248201527f4b415032303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610564565b6001600160a01b03808516600090815260066020526040808220858503905591851681529081208054849290611177908490611751565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516111c391815260200190565b60405180910390a350505050565b600380549082905560408051828152602081018490527f4bb7c3aa2e207c70c9f2b8b0d81e076d62b704e041cdedb61959edd1814912f491015b60405180910390a15050565b60005460ff1661124e5760405162461bcd60e51b815260206004820152600260248201526104e560f41b6044820152606401610564565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b600280546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f192570e0851c3af9ff6a477c94534e97444b3893085cf6ac37fb7e7ec335f01e910161120b565b60005460ff16156113155760405162461bcd60e51b8152600401610564906116b2565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861127b3390565b6001600160a01b038116600090815260056020526040902054819060ff166113b45760405162461bcd60e51b815260206004820152601b60248201527f41646472657373206973206e6f7420696e20626c61636b6c69737400000000006044820152606401610564565b6001600160a01b038216600081815260056020526040808220805460ff19169055513392917f6690dc53a3b1d37db94233f7c004408862ea909761dc5760b0e925276754f87591a35050565b6001600160a01b038116600090815260056020526040902054819060ff161561143b5760405162461bcd60e51b8152600401610564906116cd565b6001600160a01b038216600081815260056020526040808220805460ff19166001179055513392917fef674dcdab521405fef2bf4b5d2c6a6434e3ab02bc5a94fb89dd035704b83b0991a35050565b6004805460ff191660011790556040517fa5881517cf4ae3e7f6bcd00c68314e59f3ce78b5606d1b08253addc3c957e43b90600090a1565b60008054610100600160a81b0319166101006001600160a01b0384811682810293909317938490556040805193845291909304909216602082015282917f129aa2e3e7b369511a5c100a66e80b6c6231b4e60460799e7c1ed36e14121568910161120b565b6000815180845260005b8181101561154d57602081850181015186830182015201611531565b8181111561155f576000602083870101525b50601f01601f19169290920160200192915050565b6020815260006115876020830184611527565b9392505050565b80356001600160a01b03811681146115a557600080fd5b919050565b600080604083850312156115bd57600080fd5b6115c68361158e565b946020939093013593505050565b6000806000606084860312156115e957600080fd5b6115f28461158e565b92506116006020850161158e565b9150604084013590509250925092565b60006020828403121561162257600080fd5b5035919050565b60006020828403121561163b57600080fd5b6115878261158e565b6000806040838503121561165757600080fd5b6116608361158e565b915061166e6020840161158e565b90509250929050565b600181811c9082168061168b57607f821691505b602082108114156116ac57634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252600190820152600560fc1b604082015260600190565b60208082526017908201527f4164647265737320697320696e20626c61636b6c697374000000000000000000604082015260600190565b60208082526019908201527f52657374726963746564206f6e6c7920636f6d6d697474656500000000000000604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600082198211156117645761176461173b565b500190565b6001600160a01b038316815260406020820181905260009061178d90830184611527565b949350505050565b6000602082840312156117a757600080fd5b8151801515811461158757600080fd5b6020808252601b908201527f52657374726963746564206f6e6c792073757065722061646d696e0000000000604082015260600190565b60006020828403121561180057600080fd5b5051919050565b6000828210156118195761181961173b565b50039056fea2646970667358221220f9aa231f0e2c136b376cc26c179eea8f7aae62c23797e6e17b4ca4462f9ad1af64736f6c634300080b0033" + + contract_creation_code = + "0x60806040523480156200001157600080fd5b5060405162001bfc38038062001bfc8339810160408190526200003491620002d6565b6040805180820182526009808252682ca2a9902a37b5b2b760b91b6020808401918252845180860190955260038086526259455360e81b91860191909152600080546001600160a01b03808c16610100026001600160a81b03199092169190911790915560018054828b166001600160a01b03199182161790915560028054928a169290911691909117905585905582519293926012928992899289928992620000de9262000213565b508551620000f490600a90602089019062000213565b5050600b805460ff90951660ff199095169490941790935550620001209350339250889150506200012b565b505050505062000398565b6001600160a01b038216620001865760405162461bcd60e51b815260206004820152601f60248201527f4b415032303a206d696e7420746f20746865207a65726f206164647265737300604482015260640160405180910390fd5b80600860008282546200019a919062000334565b90915550506001600160a01b03821660009081526006602052604081208054839290620001c990849062000334565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b82805462000221906200035b565b90600052602060002090601f01602090048101928262000245576000855562000290565b82601f106200026057805160ff191683800117855562000290565b8280016001018555821562000290579182015b828111156200029057825182559160200191906001019062000273565b506200029e929150620002a2565b5090565b5b808211156200029e5760008155600101620002a3565b80516001600160a01b0381168114620002d157600080fd5b919050565b600080600080600060a08688031215620002ef57600080fd5b855194506200030160208701620002b9565b93506200031160408701620002b9565b92506200032160608701620002b9565b9150608086015190509295509295909350565b600082198211156200035657634e487b7160e01b600052601160045260246000fd5b500190565b600181811c908216806200037057607f821691505b602082108114156200039257634e487b7160e01b600052602260045260246000fd5b50919050565b61185480620003a86000396000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c80638456cb591161010f578063bddae40e116100a2578063dd62ed3e11610071578063dd62ed3e14610420578063de9c2a6b14610459578063f71c559c14610466578063f9f92be41461047957600080fd5b8063bddae40e146103cf578063c2f92192146103e2578063d864e740146103f5578063da72c1e81461040d57600080fd5b80639cfe42da116100de5780639cfe42da1461038e578063a1d5ec4b146103a1578063a457c2d7146103a9578063a9059cbb146103bc57600080fd5b80638456cb59146103405780638e39103c1461034857806390d6b45f1461035b57806395d89b411461038657600080fd5b8063394b652b1161018757806359e026f71161015657806359e026f7146102e65780635c975abb146102f9578063704b6c021461030457806370a082311461031757600080fd5b8063394b652b146102a357806339509351146102b85780633f4ba83a146102cb578063483a83df146102d357600080fd5b806318160ddd116101c357806318160ddd146102515780631ae878d31461026857806323b872dd14610271578063313ce5671461028457600080fd5b806306fdde03146101ea578063095ea7b3146102085780631714d7f31461022b575b600080fd5b6101f261049c565b6040516101ff9190611574565b60405180910390f35b61021b6102163660046115aa565b61052a565b60405190151581526020016101ff565b6101f2604051806040016040528060078152602001667975656d6d616960c81b81525081565b61025a60085481565b6040519081526020016101ff565b61025a60035481565b61021b61027f3660046115d4565b610540565b600b546102919060ff1681565b60405160ff90911681526020016101ff565b6102b66102b1366004611610565b610651565b005b61021b6102c63660046115aa565b61068c565b6102b66106c8565b6102b66102e1366004611629565b610701565b61021b6102f43660046115d4565b610739565b60005460ff1661021b565b6102b6610312366004611629565b61094f565b61025a610325366004611629565b6001600160a01b031660009081526006602052604090205490565b6102b6610a1b565b6102b6610356366004611629565b610a52565b60025461036e906001600160a01b031681565b6040516001600160a01b0390911681526020016101ff565b6101f2610a8a565b6102b661039c366004611629565b610a97565b6102b6610acf565b61021b6103b73660046115aa565b610b06565b61021b6103ca3660046115aa565b610b95565b6102b66103dd366004611629565b610bf4565b60015461036e906001600160a01b031681565b60005461036e9061010090046001600160a01b031681565b61021b61041b3660046115d4565b610c2c565b61025a61042e366004611644565b6001600160a01b03918216600090815260076020908152604080832093909416825291909152205490565b60045461021b9060ff1681565b61021b6104743660046115d4565b610ddb565b61021b610487366004611629565b60056020526000908152604090205460ff1681565b600980546104a990611677565b80601f01602080910402602001604051908101604052809291908181526020018280546104d590611677565b80156105225780601f106104f757610100808354040283529160200191610522565b820191906000526020600020905b81548152906001019060200180831161050557829003601f168201915b505050505081565b6000610537338484610ede565b50600192915050565b6000805460ff161561056d5760405162461bcd60e51b8152600401610564906116b2565b60405180910390fd5b6001600160a01b038416600090815260056020526040902054849060ff16156105a85760405162461bcd60e51b8152600401610564906116cd565b6105b3858585611002565b6001600160a01b0385166000908152600760209081526040808320338452909152902054838110156106385760405162461bcd60e51b815260206004820152602860248201527f4b415032303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b6064820152608401610564565b6106458633868403610ede565b50600195945050505050565b60005461010090046001600160a01b031633146106805760405162461bcd60e51b815260040161056490611704565b610689816111d1565b50565b3360008181526007602090815260408083206001600160a01b038716845290915281205490916105379185906106c3908690611751565b610ede565b60005461010090046001600160a01b031633146106f75760405162461bcd60e51b815260040161056490611704565b6106ff611217565b565b60005461010090046001600160a01b031633146107305760405162461bcd60e51b815260040161056490611704565b61068981611298565b6000805460ff161561075d5760405162461bcd60e51b8152600401610564906116b2565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf0916107aa91339190600401611769565b602060405180830381865afa1580156107c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107eb9190611795565b6108075760405162461bcd60e51b8152600401610564906117b7565b6003546002546040516306f19a8d60e21b81526001600160a01b03878116600483015290911690631bc66a3490602401602060405180830381865afa158015610854573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061087891906117ee565b101580156108f657506003546002546040516306f19a8d60e21b81526001600160a01b03868116600483015290911690631bc66a34906024015b602060405180830381865afa1580156108cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f391906117ee565b10155b61093a5760405162461bcd60e51b81526020600482015260156024820152744f6e6c7920696e7465726e616c20707572706f736560581b6044820152606401610564565b610945848484611002565b5060019392505050565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf09161099c91339190600401611769565b602060405180830381865afa1580156109b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109dd9190611795565b6109f95760405162461bcd60e51b8152600401610564906117b7565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60005461010090046001600160a01b03163314610a4a5760405162461bcd60e51b815260040161056490611704565b6106ff6112f2565b60005461010090046001600160a01b03163314610a815760405162461bcd60e51b815260040161056490611704565b6106898161134a565b600a80546104a990611677565b60005461010090046001600160a01b03163314610ac65760405162461bcd60e51b815260040161056490611704565b61068981611400565b60005461010090046001600160a01b03163314610afe5760405162461bcd60e51b815260040161056490611704565b6106ff61148a565b3360009081526007602090815260408083206001600160a01b038616845290915281205482811015610b885760405162461bcd60e51b815260206004820152602560248201527f4b415032303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610564565b6109453385858403610ede565b6000805460ff1615610bb95760405162461bcd60e51b8152600401610564906116b2565b3360008181526005602052604090205460ff1615610be95760405162461bcd60e51b8152600401610564906116cd565b610945338585611002565b60005461010090046001600160a01b03163314610c235760405162461bcd60e51b815260040161056490611704565b610689816114c2565b6000805461010090046001600160a01b03163314610c5c5760405162461bcd60e51b815260040161056490611704565b6001600160a01b038416600090815260066020526040902054821115610cd25760405162461bcd60e51b815260206004820152602560248201527f4b415032303a207472616e7366657220616d6f756e74206578636565642062616044820152646c616e636560d81b6064820152608401610564565b6001600160a01b038316610d285760405162461bcd60e51b815260206004820152601f60248201527f4b415032303a207472616e7366657220746f207a65726f2061646472657373006044820152606401610564565b6001600160a01b03841660009081526006602052604081208054849290610d50908490611807565b90915550506001600160a01b03831660009081526006602052604081208054849290610d7d908490611751565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610dc991815260200190565b60405180910390a35060019392505050565b6000805460ff1615610dff5760405162461bcd60e51b8152600401610564906116b2565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf091610e4c91339190600401611769565b602060405180830381865afa158015610e69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8d9190611795565b610ea95760405162461bcd60e51b8152600401610564906117b7565b6003546002546040516306f19a8d60e21b81526001600160a01b03878116600483015290911690631bc66a34906024016108b2565b6001600160a01b038316610f405760405162461bcd60e51b8152602060048201526024808201527f4b415032303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610564565b6001600160a01b038216610fa15760405162461bcd60e51b815260206004820152602260248201527f4b415032303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610564565b6001600160a01b0383811660008181526007602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6001600160a01b0383166110665760405162461bcd60e51b815260206004820152602560248201527f4b415032303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610564565b6001600160a01b0382166110c85760405162461bcd60e51b815260206004820152602360248201527f4b415032303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610564565b6001600160a01b038316600090815260066020526040902054818110156111405760405162461bcd60e51b815260206004820152602660248201527f4b415032303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610564565b6001600160a01b03808516600090815260066020526040808220858503905591851681529081208054849290611177908490611751565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516111c391815260200190565b60405180910390a350505050565b600380549082905560408051828152602081018490527f4bb7c3aa2e207c70c9f2b8b0d81e076d62b704e041cdedb61959edd1814912f491015b60405180910390a15050565b60005460ff1661124e5760405162461bcd60e51b815260206004820152600260248201526104e560f41b6044820152606401610564565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b600280546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f192570e0851c3af9ff6a477c94534e97444b3893085cf6ac37fb7e7ec335f01e910161120b565b60005460ff16156113155760405162461bcd60e51b8152600401610564906116b2565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861127b3390565b6001600160a01b038116600090815260056020526040902054819060ff166113b45760405162461bcd60e51b815260206004820152601b60248201527f41646472657373206973206e6f7420696e20626c61636b6c69737400000000006044820152606401610564565b6001600160a01b038216600081815260056020526040808220805460ff19169055513392917f6690dc53a3b1d37db94233f7c004408862ea909761dc5760b0e925276754f87591a35050565b6001600160a01b038116600090815260056020526040902054819060ff161561143b5760405162461bcd60e51b8152600401610564906116cd565b6001600160a01b038216600081815260056020526040808220805460ff19166001179055513392917fef674dcdab521405fef2bf4b5d2c6a6434e3ab02bc5a94fb89dd035704b83b0991a35050565b6004805460ff191660011790556040517fa5881517cf4ae3e7f6bcd00c68314e59f3ce78b5606d1b08253addc3c957e43b90600090a1565b60008054610100600160a81b0319166101006001600160a01b0384811682810293909317938490556040805193845291909304909216602082015282917f129aa2e3e7b369511a5c100a66e80b6c6231b4e60460799e7c1ed36e14121568910161120b565b6000815180845260005b8181101561154d57602081850181015186830182015201611531565b8181111561155f576000602083870101525b50601f01601f19169290920160200192915050565b6020815260006115876020830184611527565b9392505050565b80356001600160a01b03811681146115a557600080fd5b919050565b600080604083850312156115bd57600080fd5b6115c68361158e565b946020939093013593505050565b6000806000606084860312156115e957600080fd5b6115f28461158e565b92506116006020850161158e565b9150604084013590509250925092565b60006020828403121561162257600080fd5b5035919050565b60006020828403121561163b57600080fd5b6115878261158e565b6000806040838503121561165757600080fd5b6116608361158e565b915061166e6020840161158e565b90509250929050565b600181811c9082168061168b57607f821691505b602082108114156116ac57634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252600190820152600560fc1b604082015260600190565b60208082526017908201527f4164647265737320697320696e20626c61636b6c697374000000000000000000604082015260600190565b60208082526019908201527f52657374726963746564206f6e6c7920636f6d6d697474656500000000000000604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600082198211156117645761176461173b565b500190565b6001600160a01b038316815260406020820181905260009061178d90830184611527565b949350505050565b6000602082840312156117a757600080fd5b8151801515811461158757600080fd5b6020808252601b908201527f52657374726963746564206f6e6c792073757065722061646d696e0000000000604082015260600190565b60006020828403121561180057600080fd5b5051919050565b6000828210156118195761181961173b565b50039056fea2646970667358221220f9aa231f0e2c136b376cc26c179eea8f7aae62c23797e6e17b4ca4462f9ad1af64736f6c634300080b0033000000000000000000000000000000000000000000084595161401484a00000000000000000000000000000073d8f731ec0d3945d807a904bf93954b82b0d594000000000000000000000000c5333c0d3cf6fc8f84f3ccb0d5a73dbda2eceb500000000000000000000000002c8abd9c61d4e973ca8db5545c54c90e44a2445c0000000000000000000000000000000000000000000000000000000000000004" + + contract_address = insert(:contract_address, contract_code: bytecode) - test "verification is successful if proper nightly version of compiler ~0.5.14" do - bytecode_0_5_14_nightly_2019_12_10_commit_45aa7a88 = - "0x608060405234801561001057600080fd5b5060405161026c38038061026c8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101d08061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72315820ec5a7ce04b1c2f97a3d3e61ae1b5cb06585e81c504542fd9668a8ead654da72764736f6c637829302e352e31342d6e696768746c792e323031392e31322e31302b636f6d6d69742e34356161376138380059" - - constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" - contract_address = insert(:contract_address, contract_code: bytecode_0_5_14_nightly_2019_12_10_commit_45aa7a88) - bytecode_constructor_arguments = "#{bytecode_0_5_14_nightly_2019_12_10_commit_45aa7a88}#{constructor_arguments}" - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: bytecode_constructor_arguments - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => @code_0_5, - "compiler_version" => "v0.5.14-nightly.2019.12.10+commit.45aa7a88", - "evm_version" => "homestead", - "name" => "Incrementer", - "optimization" => false, - "constructor_arguments" => constructor_arguments - } - - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil - end + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: contract_creation_code + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => contract_source_code, + "compiler_version" => "v0.8.11+commit.d7f03943", + "evm_version" => "default", + "name" => "YESToken", + "optimization" => true, + "optimization_runs" => @optimization_runs, + "autodetect_constructor_args" => true + } + + assert {:ok, + %{ + abi: abi, + constructor_arguments: + "000000000000000000000000000000000000000000084595161401484a00000000000000000000000000000073d8f731ec0d3945d807a904bf93954b82b0d594000000000000000000000000c5333c0d3cf6fc8f84f3ccb0d5a73dbda2eceb500000000000000000000000002c8abd9c61d4e973ca8db5545c54c90e44a2445c0000000000000000000000000000000000000000000000000000000000000004" + }} = Verifier.evaluate_authenticity(contract_address.hash, params) + + assert abi != nil + end + + test "issue 3082" do + contract_source_code = + "#{File.cwd!()}/test/support/fixture/smart_contract/issue_3082.sol" + |> File.read!() + + bytecode = + "0x608060405234801561001057600080fd5b50600436106101e55760003560e01c80638d3ce69d1161010f578063b2cec8ea116100a2578063cbecb28811610071578063cbecb2881461050a578063f049f33414610512578063f2fde38b14610532578063fc0c546a14610558576101e5565b8063b2cec8ea146104a2578063b99ef521146104c2578063bac883ef146104e2578063bd4b2be914610502576101e5565b8063983c44d6116100de578063983c44d6146103b75780639972444f146103bf578063a3df582a146103df578063a4c0ed36146103e7576101e5565b80638d3ce69d146103635780638d3df3461461036b5780638da5cb5b1461038b5780638f32d59b146103af576101e5565b8063392e53cd11610187578063622c5e4511610156578063622c5e451461032b578063715018a6146103335780637af7c0401461033b5780638129fc1c1461035b576101e5565b8063392e53cd146102b757806359791d6d146102bf5780635f26622f146102df578063604f21771461030b576101e5565b80630f10e06f116101c35780630f10e06f146102695780632755731e14610289578063375a4cab14610291578063381a4113146102af576101e5565b8063014a969a146101ea578063047fc9aa1461021e57806304a3922014610238575b600080fd5b61020a6004803603602081101561020057600080fd5b503560ff16610560565b604080519115158252519081900360200190f35b610226610575565b60408051918252519081900360200190f35b6102676004803603604081101561024e57600080fd5b50803560ff1690602001356001600160a01b0316610584565b005b6102266004803603602081101561027f57600080fd5b503560ff166106d0565b6102266106e2565b6102996106e8565b6040805160ff9092168252519081900360200190f35b61020a6106ed565b61020a6106f6565b610226600480360360208110156102d557600080fd5b503560ff16610704565b610267600480360360408110156102f557600080fd5b506001600160a01b038135169060200135610716565b6102266004803603602081101561032157600080fd5b503560ff16610fc4565b610299610fd6565b610267610fdb565b61020a6004803603602081101561035157600080fd5b503560ff16611073565b610267611088565b6102996111de565b6102266004803603602081101561038157600080fd5b503560ff166111e3565b6103936111f5565b604080516001600160a01b039092168252519081900360200190f35b61020a611204565b610299611215565b610393600480360360208110156103d557600080fd5b503560ff1661121a565b610226611235565b61020a600480360360608110156103fd57600080fd5b6001600160a01b038235169160208101359181019060608101604082013564010000000081111561042d57600080fd5b82018360208201111561043f57600080fd5b8035906020019184600183028401116401000000008311171561046157600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955061123b945050505050565b610226600480360360208110156104b857600080fd5b503560ff16611274565b610226600480360360208110156104d857600080fd5b503560ff16611286565b610267600480360360208110156104f857600080fd5b503560ff16611298565b610299611652565b610299611657565b6102266004803603602081101561052857600080fd5b503560ff1661165c565b6102676004803603602081101561054857600080fd5b50356001600160a01b031661166e565b6103936116d3565b600b6020526000908152604090205460ff1681565b6a070fe2cd68c25ff4f0000081565b60ff821660011480610599575060ff82166005145b6105d7576040805162461bcd60e51b815260206004820152600a6024820152691ddc9bdb99c81c1bdbdb60b21b604482015290519081900360640190fd5b60ff82166000908152600260205260409020546001600160a01b03163314610637576040805162461bcd60e51b815260206004820152600e60248201526d1b9bdd08185d5d1a1bdc9a5e995960921b604482015290519081900360640190fd5b610640816116e2565b60ff82166000818152600260209081526040918290205482516001600160a01b0391821681529085169181019190915281517f09a67390cbf7986a6bb0fcb44307b4844ba258bca3f38599d2b344824df4ba1d929181900390910190a260ff91909116600090815260026020526040902080546001600160a01b0319166001600160a01b03909216919091179055565b60056020526000908152604090205481565b600d5481565b600581565b600e5460ff1681565b600e54610100900460ff1681565b60086020526000908152604090205481565b61071e611204565b61076f576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600e5460ff16156107c7576040805162461bcd60e51b815260206004820152601760248201527f616c7265616479207072652d696e697469616c697a6564000000000000000000604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b038481169190911791829055604080516370a0823160e01b81523060048201529051600093909216916370a0823191602480820192602092909190829003018186803b15801561082b57600080fd5b505afa15801561083f573d6000803e3d6000fd5b505050506040513d602081101561085557600080fd5b505190506a070fe2cd68c25ff4f0000081146108b1576040805162461bcd60e51b815260206004820152601660248201527577726f6e6720636f6e74726163742062616c616e636560501b604482015290519081900360640190fd5b6108b961172f565b600d55600e805460ff191660011790556003600090815260026020527f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c3546040805163189acdbd60e31b81526001600160a01b0387811660048301529151919092169263c4d66de8926024808201939182900301818387803b15801561093e57600080fd5b505af1158015610952573d6000803e3d6000fd5b50506004600081815260026020527fee60d0579bcffd98e668647d59fec1ff86a7fb340ce572e844f234ae73a6918f546040805163189acdbd60e31b81526001600160a01b038a811695820195909552905193909116945063c4d66de893506024808201939182900301818387803b1580156109cd57600080fd5b505af11580156109e1573d6000803e3d6000fd5b5050600360008181526020919091527fcbc4e5fb02c3d1de23a9f1e014b4d2ee5aeaea9505df5e855c9210bf472495af54909250610a3a9150606490610a2e90601963ffffffff61173316565b9063ffffffff61179516565b600154600260009081527f679795a0195a1b76cdebb7c51d74e058aee92919b8c3389af86ef24535e8a28c54600360209081527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d546040805163238a3fe160e01b81526001600160a01b039485166004820152602481019290925251959650919093169363238a3fe19360448084019491938390030190829087803b158015610ae257600080fd5b505af1158015610af6573d6000803e3d6000fd5b505050506040513d6020811015610b0c57600080fd5b505060015460036000908152600260209081527f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c3546040805163a9059cbb60e01b81526001600160a01b039283166004820152602481018790529051919094169363a9059cbb9360448083019493928390030190829087803b158015610b9157600080fd5b505af1158015610ba5573d6000803e3d6000fd5b505050506040513d6020811015610bbb57600080fd5b50506006600090815260036020527fc69056f16cbaa3c616b828e333ab7d3a32310765507f8f58359e99ebb7a885f354610bfb908563ffffffff6117ff16565b60015460066000908152600260209081527f59dd4b18488d12f51eda69757a0ed42a2010c14b564330cc74a06895e60c077b546040805163238a3fe160e01b81526001600160a01b03928316600482015260248101879052905195965093169363238a3fe193604480820194918390030190829087803b158015610c7e57600080fd5b505af1158015610c92573d6000803e3d6000fd5b505050506040513d6020811015610ca857600080fd5b50508315610d35576001546040805163238a3fe160e01b81526000600482018190526024820188905291516001600160a01b039093169263238a3fe192604480840193602093929083900390910190829087803b158015610d0857600080fd5b505af1158015610d1c573d6000803e3d6000fd5b505050506040513d6020811015610d3257600080fd5b50505b60026000527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d5460046020527f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a754610d929163ffffffff6117ff16565b60046020527f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a75560036000527f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa054610df0908363ffffffff6117ff16565b7f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa05560066000527fc69056f16cbaa3c616b828e333ab7d3a32310765507f8f58359e99ebb7a885f35460046020527fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f54610e6f9163ffffffff6117ff16565b6006600052600460209081527fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f91909155604080516001600160a01b0388168152339281019290925280517f20e0b9d27e138a83ff1b3f687932144f5e913aa93855ac36c2611ec1dfae704e9281900390910190a160026000819052600360209081527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d546040805191825233928201929092528151600080516020611b49833981519152929181900390910190a2604080518381523360208201528151600392600080516020611b49833981519152928290030190a2604080518281523360208201528151600692600080516020611b49833981519152928290030190a28315610fbd57604080518581523360208201528151600092600080516020611b49833981519152928290030190a25b5050505050565b60036020526000908152604090205481565b600481565b610fe3611204565b611034576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b5c1b195b595b9d1959608a1b604482015290519081900360640190fd5b600a6020526000908152604090205460ff1681565b600e5460ff166110d5576040805162461bcd60e51b81526020600482015260136024820152721b9bdd081c1c994b5a5b9a5d1a585b1a5e9959606a1b604482015290519081900360640190fd5b600e54610100900460ff1615611128576040805162461bcd60e51b8152602060048201526013602482015272185b1c9958591e481a5b9a5d1a585b1a5e9959606a1b604482015290519081900360640190fd5b6276a700611146600d5461113a61172f565b9063ffffffff6117ff16565b101561118f57611154611204565b61118f5760405162461bcd60e51b8152600401808060200182810382526027815260200180611b226027913960400191505060405180910390fd5b61119761172f565b600c55600e805461ff0019166101001790556040805133815290517f908408e307fc569b417f6cbec5d5a06f44a0a505ac0479b47d421a4b2fd6a1e69181900360200190a1565b600381565b60066020526000908152604090205481565b6000546001600160a01b031690565b6000546001600160a01b0316331490565b600181565b6002602052600090815260409020546001600160a01b031681565b600c5481565b600060405162461bcd60e51b815260040180806020018281038252602e815260200180611bb0602e913960400191505060405180910390fd5b60096020526000908152604090205481565b60046020526000908152604090205481565b600e54610100900460ff166112e6576040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b9a5d1a585b1a5e9959608a1b604482015290519081900360640190fd5b60ff8116600090815260056020526040902054600c54829161130e919063ffffffff61185c16565b61131661172f565b10158015611336575060ff8082166000908152600b602052604090205416155b6113715760405162461bcd60e51b8152600401808060200182810382526029815260200180611bde6029913960400191505060405180910390fd5b60ff821660031480611386575060ff82166004145b80611394575060ff82166001145b806113a2575060ff82166005145b6113e0576040805162461bcd60e51b815260206004820152600a6024820152691ddc9bdb99c81c1bdbdb60b21b604482015290519081900360640190fd5b60ff8083166000908152600a6020526040812054909116611426575060ff8216600090815260096020908152604080832054600a909252909120805460ff191660011790555b6000611431846118b6565b60ff85166000908152600860205260409020549091506114689061145b908363ffffffff61173316565b839063ffffffff61185c16565b9150600082116114bf576040805162461bcd60e51b815260206004820152601960248201527f6e6f20696e7374616c6c6d656e747320617661696c61626c6500000000000000604482015290519081900360640190fd5b60006114cc8584846119a0565b90506114de838263ffffffff61185c16565b925060ff8516600314806114f5575060ff85166004145b156115905760015460ff8616600090815260026020908152604080832054815163a9059cbb60e01b81526001600160a01b03918216600482015260248101899052915194169363a9059cbb93604480840194938390030190829087803b15801561155e57600080fd5b505af1158015611572573d6000803e3d6000fd5b505050506040513d602081101561158857600080fd5b506116219050565b60015460ff8616600090815260026020908152604080832054815163238a3fe160e01b81526001600160a01b03918216600482015260248101899052915194169363238a3fe193604480840194938390030190829087803b1580156115f457600080fd5b505af1158015611608573d6000803e3d6000fd5b505050506040513d602081101561161e57600080fd5b50505b60408051848152336020820152815160ff881692600080516020611b49833981519152928290030190a25050505050565b600681565b600281565b60076020526000908152604090205481565b611676611204565b6116c7576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6116d081611a5d565b50565b6001546001600160a01b031681565b6001600160a01b0381166116d0576040805162461bcd60e51b815260206004820152600f60248201526e696e76616c6964206164647265737360881b604482015290519081900360640190fd5b4290565b6000826117425750600061178f565b8282028284828161174f57fe5b041461178c5760405162461bcd60e51b8152600401808060200182810382526021815260200180611b8f6021913960400191505060405180910390fd5b90505b92915050565b60008082116117eb576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b60008284816117f657fe5b04949350505050565b600082821115611856576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60008282018381101561178c576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60ff811660009081526007602052604081205481906118de906201518063ffffffff61173316565b60ff8416600090815260056020526040812054600c54929350909161191a91849161190e9163ffffffff61185c16565b9063ffffffff61185c16565b905061192f62015180610a2e8361113a61172f565b60ff851660009081526006602090815260408083205460079092529091205491945090611962908563ffffffff61185c16565b11156119995760ff84166000908152600760209081526040808320546006909252909120546119969163ffffffff6117ff16565b92505b5050919050565b60ff831660009081526004602052604081205481906119c5908563ffffffff6117ff16565b60ff86166000908152600460209081526040808320939093556007905220546119f4908463ffffffff61185c16565b60ff861660009081526007602081815260408084208590556006825290922054915211611a555760ff851660009081526004602052604090205415611a4c575060ff8416600090815260046020526040812080549190555b611a5585611afd565b949350505050565b6001600160a01b038116611aa25760405162461bcd60e51b8152600401808060200182810382526026815260200180611b696026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60ff166000908152600b60205260409020805460ff19166001179055565b3b15159056fe666f72206e6f77206f6e6c79206f776e65722063616e2063616c6c2074686973206d6574686f64a8b65b82b2ff2a955e75c1bfa6a0e92aafb764156295da77a0a4c714f3895c724f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7773656e64696e6720746f6b656e7320746f207468697320636f6e7472616374206973206e6f7420616c6c6f776564696e7374616c6c6d656e747320617265206e6f742061637469766520666f72207468697320706f6f6ca265627a7a723058208fc7fe0d6f40178495c4e655627ac8a6bc837eb89686042a07161f078a2ea04464736f6c634300050a0032" + + contract_creation_code = + "0x6080604052600e805461ffff191690553480156200001c57600080fd5b5060405162002ac338038062002ac3833981810160405260c08110156200004257600080fd5b50805160208201516040808401516060850151608086015160a090960151600080546001600160a01b031916331780825594519697959693959294929391926001600160a01b0316917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3620000cf846001600160a01b031662000aa160201b62001b1b1760201c565b8015620000f65750620000f6836001600160a01b031662000aa160201b62001b1b1760201c565b6200016257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f6e6f74206120636f6e7472616374206164647265737300000000000000000000604482015290519081900360640190fd5b62000176866001600160e01b0362000aa716565b6200018a856001600160e01b0362000aa716565b6200019e826001600160e01b0362000aa716565b620001b2816001600160e01b0362000aa716565b7fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e080546001600160a01b03199081166001600160a01b03898116919091179092557f679795a0195a1b76cdebb7c51d74e058aee92919b8c3389af86ef24535e8a28c805482168884161790557f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c380548216878416179081905560008051602062002a42833981519152805483168785161790557fb98b78633099fa36ed8b8680c4f8092689e1e04080eb9cbb077ca38a14d7e384805483168685161790557f59dd4b18488d12f51eda69757a0ed42a2010c14b564330cc74a06895e60c077b8054909216848416179091556a034f086f3b33b684000000600080516020620029e2833981519152556954b40b1f852bda000000600080516020620029c283398151915255600360005260026020908152604080517f5fd6632000000000000000000000000000000000000000000000000000000000815290519290931692635fd663209260048083019392829003018186803b1580156200035257600080fd5b505afa15801562000367573d6000803e3d6000fd5b505050506040513d60208110156200037e57600080fd5b505160008051602062002aa383398151915255600460008190526002602090815260008051602062002a4283398151915254604080517f5fd6632000000000000000000000000000000000000000000000000000000000815290516001600160a01b0390921693635fd6632093828201939092909190829003018186803b1580156200040957600080fd5b505afa1580156200041e573d6000803e3d6000fd5b505050506040513d60208110156200043557600080fd5b50516003602090815260008051602062002a2283398151915282905569940785b073a9e904000060008051602062002a8383398151915281905569ace68dbebd988d50000060008051602062002a0283398151915281905560008051602062002aa383398151915254600080516020620029c2833981519152546001600052600080516020620029e2833981519152546a070fe2cd68c25ff4f00000966200050e969495620004fa95909486949293859391928492906200185c62000b20821b17901c565b62000b2060201b6200185c1790919060201c565b146200057b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f77726f6e672073756d206f6620706f6f6c73207374616b657300000000000000604482015290519081900360640190fd5b600080516020620029e2833981519152547fabd6e7cb50984ff9c2f3e18a2660c3353dadf4e3291deeb275dae2cd1e44fe05819055600080516020620029c2833981519152547f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a75560008051602062002aa3833981519152547f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa05560008051602062002a22833981519152547f1a1e6821cde7d0159c0d293177871e09677b4e42307c7db3ba94f8648a5a050f5560008051602062002a83833981519152547f04cde762ef08b6b6c5ded8e8c4c0b3f4e5c9ad7342c88fcc93681b4588b73f055560008051602062002a02833981519152547fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f55600160005260036020908152620006ef91606491620006db91906014906200173362000b9e821b17901c565b62000c1660201b620017951790919060201c565b7f92e85d02570a8092d09a6e3a57665bc3815a2699a4074001bf1ccabf660f5a365560036000819052602090815260008051602062002aa3833981519152546200074d91606491620006db91600a906200173362000b9e821b17901c565b7fc575c31fea594a6eb97c8e9d3f9caee4c16218c6ef37e923234c0fe9014a61e75560046000526003602090815260008051602062002a2283398151915254620007ab91606491620006db916014906200173362000b9e821b17901c565b7f8dc18c4ccfd75f5c815b63770fa542fd953e8fef7e0e44bbdd4913470ce7e9cb5560056000526003602090815260008051602062002a83833981519152546200080991606491620006db916014906200173362000b9e821b17901c565b7f74b05292d1d4b2b48b65261b07099d24244bcb069f138d9a6bfdcf776becac4c556301baf8007f1471eb6eb2c5e789fc3de43f8ce62938c7d1836ec861730447e2ada8fd81017b556224ea007fa9bc9a3a348c357ba16b37005d7e6b3236198c0e939f4af8c5f19b8deeb8ebc055626ebe007f3eec716f11ba9e820c81ca75eb978ffb45831ef8b7a53e5e422c26008e1ca6d58190557f458b30c2d72bfd2c6317304a4594ecbafe5f729d3111b65fdc3a33bd48e5432d5560066020526101507f3e5fec24aa4dc4e5aee2e025e51e1392c72a2500577559fae9665c6d52bd6a315560e07f75f96ab15d697e93042dc45b5c896c4b27e89bb6eaf39475c5c371cb2513f7d25560fc7fc5069e24aaadb2addc3e52e868fcf3f4f8acf5a87e24300992fd4540c2a87eed81905560056000527fbfd358e93f18da3ed276c3afdbdba00b8f0b6008a03476a6a86bd6320ee6938b556200097260016001600160e01b0362000c9c16565b7fad67d757c34507f157cacfa2e3153e9f260a2244f30428821be7be64587ac55f5560036000819052602081815260008051602062002aa383398151915254620009e69291620009d791606491620006db91906023906200173362000b9e821b17901c565b6001600160e01b0362000cc516565b600360005260086020527f625b35f5e76f098dd7c3a05b10e2e5e78a4a01228d60c3b143426cdf36d264555562000a2760046001600160e01b0362000c9c16565b600460005260086020527f9321edea6e3be4df59a344b401fab4f888b556fda1f954244cff9204bad624b85562000a6860056001600160e01b0362000c9c16565b600560005260086020527f91238f30f286c9a1c6e901c4eda3b214c381c846e3dbe48df95c21488e8e1fdb555062000d77945050505050565b3b151590565b6001600160a01b03811662000b1d57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f696e76616c696420616464726573730000000000000000000000000000000000604482015290519081900360640190fd5b50565b60008282018381101562000b9557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60008262000baf5750600062000b98565b8282028284828162000bbd57fe5b041462000b95576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018062002a626021913960400191505060405180910390fd5b600080821162000c8757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b600082848162000c9357fe5b04949350505050565b60ff811660009081526009602052604081205462000b989083906001600160e01b0362000cc516565b60ff8216600090815260066020908152604080832054600383529083205462000b9592620006db91908690620017ff62000cff821b17901c565b60008282111562000d7157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b611c3b8062000d876000396000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c80638d3ce69d1161010f578063b2cec8ea116100a2578063cbecb28811610071578063cbecb2881461050a578063f049f33414610512578063f2fde38b14610532578063fc0c546a14610558576101e5565b8063b2cec8ea146104a2578063b99ef521146104c2578063bac883ef146104e2578063bd4b2be914610502576101e5565b8063983c44d6116100de578063983c44d6146103b75780639972444f146103bf578063a3df582a146103df578063a4c0ed36146103e7576101e5565b80638d3ce69d146103635780638d3df3461461036b5780638da5cb5b1461038b5780638f32d59b146103af576101e5565b8063392e53cd11610187578063622c5e4511610156578063622c5e451461032b578063715018a6146103335780637af7c0401461033b5780638129fc1c1461035b576101e5565b8063392e53cd146102b757806359791d6d146102bf5780635f26622f146102df578063604f21771461030b576101e5565b80630f10e06f116101c35780630f10e06f146102695780632755731e14610289578063375a4cab14610291578063381a4113146102af576101e5565b8063014a969a146101ea578063047fc9aa1461021e57806304a3922014610238575b600080fd5b61020a6004803603602081101561020057600080fd5b503560ff16610560565b604080519115158252519081900360200190f35b610226610575565b60408051918252519081900360200190f35b6102676004803603604081101561024e57600080fd5b50803560ff1690602001356001600160a01b0316610584565b005b6102266004803603602081101561027f57600080fd5b503560ff166106d0565b6102266106e2565b6102996106e8565b6040805160ff9092168252519081900360200190f35b61020a6106ed565b61020a6106f6565b610226600480360360208110156102d557600080fd5b503560ff16610704565b610267600480360360408110156102f557600080fd5b506001600160a01b038135169060200135610716565b6102266004803603602081101561032157600080fd5b503560ff16610fc4565b610299610fd6565b610267610fdb565b61020a6004803603602081101561035157600080fd5b503560ff16611073565b610267611088565b6102996111de565b6102266004803603602081101561038157600080fd5b503560ff166111e3565b6103936111f5565b604080516001600160a01b039092168252519081900360200190f35b61020a611204565b610299611215565b610393600480360360208110156103d557600080fd5b503560ff1661121a565b610226611235565b61020a600480360360608110156103fd57600080fd5b6001600160a01b038235169160208101359181019060608101604082013564010000000081111561042d57600080fd5b82018360208201111561043f57600080fd5b8035906020019184600183028401116401000000008311171561046157600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955061123b945050505050565b610226600480360360208110156104b857600080fd5b503560ff16611274565b610226600480360360208110156104d857600080fd5b503560ff16611286565b610267600480360360208110156104f857600080fd5b503560ff16611298565b610299611652565b610299611657565b6102266004803603602081101561052857600080fd5b503560ff1661165c565b6102676004803603602081101561054857600080fd5b50356001600160a01b031661166e565b6103936116d3565b600b6020526000908152604090205460ff1681565b6a070fe2cd68c25ff4f0000081565b60ff821660011480610599575060ff82166005145b6105d7576040805162461bcd60e51b815260206004820152600a6024820152691ddc9bdb99c81c1bdbdb60b21b604482015290519081900360640190fd5b60ff82166000908152600260205260409020546001600160a01b03163314610637576040805162461bcd60e51b815260206004820152600e60248201526d1b9bdd08185d5d1a1bdc9a5e995960921b604482015290519081900360640190fd5b610640816116e2565b60ff82166000818152600260209081526040918290205482516001600160a01b0391821681529085169181019190915281517f09a67390cbf7986a6bb0fcb44307b4844ba258bca3f38599d2b344824df4ba1d929181900390910190a260ff91909116600090815260026020526040902080546001600160a01b0319166001600160a01b03909216919091179055565b60056020526000908152604090205481565b600d5481565b600581565b600e5460ff1681565b600e54610100900460ff1681565b60086020526000908152604090205481565b61071e611204565b61076f576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600e5460ff16156107c7576040805162461bcd60e51b815260206004820152601760248201527f616c7265616479207072652d696e697469616c697a6564000000000000000000604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b038481169190911791829055604080516370a0823160e01b81523060048201529051600093909216916370a0823191602480820192602092909190829003018186803b15801561082b57600080fd5b505afa15801561083f573d6000803e3d6000fd5b505050506040513d602081101561085557600080fd5b505190506a070fe2cd68c25ff4f0000081146108b1576040805162461bcd60e51b815260206004820152601660248201527577726f6e6720636f6e74726163742062616c616e636560501b604482015290519081900360640190fd5b6108b961172f565b600d55600e805460ff191660011790556003600090815260026020527f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c3546040805163189acdbd60e31b81526001600160a01b0387811660048301529151919092169263c4d66de8926024808201939182900301818387803b15801561093e57600080fd5b505af1158015610952573d6000803e3d6000fd5b50506004600081815260026020527fee60d0579bcffd98e668647d59fec1ff86a7fb340ce572e844f234ae73a6918f546040805163189acdbd60e31b81526001600160a01b038a811695820195909552905193909116945063c4d66de893506024808201939182900301818387803b1580156109cd57600080fd5b505af11580156109e1573d6000803e3d6000fd5b5050600360008181526020919091527fcbc4e5fb02c3d1de23a9f1e014b4d2ee5aeaea9505df5e855c9210bf472495af54909250610a3a9150606490610a2e90601963ffffffff61173316565b9063ffffffff61179516565b600154600260009081527f679795a0195a1b76cdebb7c51d74e058aee92919b8c3389af86ef24535e8a28c54600360209081527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d546040805163238a3fe160e01b81526001600160a01b039485166004820152602481019290925251959650919093169363238a3fe19360448084019491938390030190829087803b158015610ae257600080fd5b505af1158015610af6573d6000803e3d6000fd5b505050506040513d6020811015610b0c57600080fd5b505060015460036000908152600260209081527f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c3546040805163a9059cbb60e01b81526001600160a01b039283166004820152602481018790529051919094169363a9059cbb9360448083019493928390030190829087803b158015610b9157600080fd5b505af1158015610ba5573d6000803e3d6000fd5b505050506040513d6020811015610bbb57600080fd5b50506006600090815260036020527fc69056f16cbaa3c616b828e333ab7d3a32310765507f8f58359e99ebb7a885f354610bfb908563ffffffff6117ff16565b60015460066000908152600260209081527f59dd4b18488d12f51eda69757a0ed42a2010c14b564330cc74a06895e60c077b546040805163238a3fe160e01b81526001600160a01b03928316600482015260248101879052905195965093169363238a3fe193604480820194918390030190829087803b158015610c7e57600080fd5b505af1158015610c92573d6000803e3d6000fd5b505050506040513d6020811015610ca857600080fd5b50508315610d35576001546040805163238a3fe160e01b81526000600482018190526024820188905291516001600160a01b039093169263238a3fe192604480840193602093929083900390910190829087803b158015610d0857600080fd5b505af1158015610d1c573d6000803e3d6000fd5b505050506040513d6020811015610d3257600080fd5b50505b60026000527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d5460046020527f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a754610d929163ffffffff6117ff16565b60046020527f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a75560036000527f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa054610df0908363ffffffff6117ff16565b7f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa05560066000527fc69056f16cbaa3c616b828e333ab7d3a32310765507f8f58359e99ebb7a885f35460046020527fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f54610e6f9163ffffffff6117ff16565b6006600052600460209081527fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f91909155604080516001600160a01b0388168152339281019290925280517f20e0b9d27e138a83ff1b3f687932144f5e913aa93855ac36c2611ec1dfae704e9281900390910190a160026000819052600360209081527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d546040805191825233928201929092528151600080516020611b49833981519152929181900390910190a2604080518381523360208201528151600392600080516020611b49833981519152928290030190a2604080518281523360208201528151600692600080516020611b49833981519152928290030190a28315610fbd57604080518581523360208201528151600092600080516020611b49833981519152928290030190a25b5050505050565b60036020526000908152604090205481565b600481565b610fe3611204565b611034576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b5c1b195b595b9d1959608a1b604482015290519081900360640190fd5b600a6020526000908152604090205460ff1681565b600e5460ff166110d5576040805162461bcd60e51b81526020600482015260136024820152721b9bdd081c1c994b5a5b9a5d1a585b1a5e9959606a1b604482015290519081900360640190fd5b600e54610100900460ff1615611128576040805162461bcd60e51b8152602060048201526013602482015272185b1c9958591e481a5b9a5d1a585b1a5e9959606a1b604482015290519081900360640190fd5b6276a700611146600d5461113a61172f565b9063ffffffff6117ff16565b101561118f57611154611204565b61118f5760405162461bcd60e51b8152600401808060200182810382526027815260200180611b226027913960400191505060405180910390fd5b61119761172f565b600c55600e805461ff0019166101001790556040805133815290517f908408e307fc569b417f6cbec5d5a06f44a0a505ac0479b47d421a4b2fd6a1e69181900360200190a1565b600381565b60066020526000908152604090205481565b6000546001600160a01b031690565b6000546001600160a01b0316331490565b600181565b6002602052600090815260409020546001600160a01b031681565b600c5481565b600060405162461bcd60e51b815260040180806020018281038252602e815260200180611bb0602e913960400191505060405180910390fd5b60096020526000908152604090205481565b60046020526000908152604090205481565b600e54610100900460ff166112e6576040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b9a5d1a585b1a5e9959608a1b604482015290519081900360640190fd5b60ff8116600090815260056020526040902054600c54829161130e919063ffffffff61185c16565b61131661172f565b10158015611336575060ff8082166000908152600b602052604090205416155b6113715760405162461bcd60e51b8152600401808060200182810382526029815260200180611bde6029913960400191505060405180910390fd5b60ff821660031480611386575060ff82166004145b80611394575060ff82166001145b806113a2575060ff82166005145b6113e0576040805162461bcd60e51b815260206004820152600a6024820152691ddc9bdb99c81c1bdbdb60b21b604482015290519081900360640190fd5b60ff8083166000908152600a6020526040812054909116611426575060ff8216600090815260096020908152604080832054600a909252909120805460ff191660011790555b6000611431846118b6565b60ff85166000908152600860205260409020549091506114689061145b908363ffffffff61173316565b839063ffffffff61185c16565b9150600082116114bf576040805162461bcd60e51b815260206004820152601960248201527f6e6f20696e7374616c6c6d656e747320617661696c61626c6500000000000000604482015290519081900360640190fd5b60006114cc8584846119a0565b90506114de838263ffffffff61185c16565b925060ff8516600314806114f5575060ff85166004145b156115905760015460ff8616600090815260026020908152604080832054815163a9059cbb60e01b81526001600160a01b03918216600482015260248101899052915194169363a9059cbb93604480840194938390030190829087803b15801561155e57600080fd5b505af1158015611572573d6000803e3d6000fd5b505050506040513d602081101561158857600080fd5b506116219050565b60015460ff8616600090815260026020908152604080832054815163238a3fe160e01b81526001600160a01b03918216600482015260248101899052915194169363238a3fe193604480840194938390030190829087803b1580156115f457600080fd5b505af1158015611608573d6000803e3d6000fd5b505050506040513d602081101561161e57600080fd5b50505b60408051848152336020820152815160ff881692600080516020611b49833981519152928290030190a25050505050565b600681565b600281565b60076020526000908152604090205481565b611676611204565b6116c7576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6116d081611a5d565b50565b6001546001600160a01b031681565b6001600160a01b0381166116d0576040805162461bcd60e51b815260206004820152600f60248201526e696e76616c6964206164647265737360881b604482015290519081900360640190fd5b4290565b6000826117425750600061178f565b8282028284828161174f57fe5b041461178c5760405162461bcd60e51b8152600401808060200182810382526021815260200180611b8f6021913960400191505060405180910390fd5b90505b92915050565b60008082116117eb576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b60008284816117f657fe5b04949350505050565b600082821115611856576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60008282018381101561178c576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60ff811660009081526007602052604081205481906118de906201518063ffffffff61173316565b60ff8416600090815260056020526040812054600c54929350909161191a91849161190e9163ffffffff61185c16565b9063ffffffff61185c16565b905061192f62015180610a2e8361113a61172f565b60ff851660009081526006602090815260408083205460079092529091205491945090611962908563ffffffff61185c16565b11156119995760ff84166000908152600760209081526040808320546006909252909120546119969163ffffffff6117ff16565b92505b5050919050565b60ff831660009081526004602052604081205481906119c5908563ffffffff6117ff16565b60ff86166000908152600460209081526040808320939093556007905220546119f4908463ffffffff61185c16565b60ff861660009081526007602081815260408084208590556006825290922054915211611a555760ff851660009081526004602052604090205415611a4c575060ff8416600090815260046020526040812080549190555b611a5585611afd565b949350505050565b6001600160a01b038116611aa25760405162461bcd60e51b8152600401808060200182810382526026815260200180611b696026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60ff166000908152600b60205260409020805460ff19166001179055565b3b15159056fe666f72206e6f77206f6e6c79206f776e65722063616e2063616c6c2074686973206d6574686f64a8b65b82b2ff2a955e75c1bfa6a0e92aafb764156295da77a0a4c714f3895c724f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7773656e64696e6720746f6b656e7320746f207468697320636f6e7472616374206973206e6f7420616c6c6f776564696e7374616c6c6d656e747320617265206e6f742061637469766520666f72207468697320706f6f6ca265627a7a723058208fc7fe0d6f40178495c4e655627ac8a6bc837eb89686042a07161f078a2ea04464736f6c634300050a0032c3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4da15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054cc69056f16cbaa3c616b828e333ab7d3a32310765507f8f58359e99ebb7a885f383ec6a1f0257b830b5e016457c9cf1435391bf56cc98f369a58a54fe93772465ee60d0579bcffd98e668647d59fec1ff86a7fb340ce572e844f234ae73a6918f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77405aad32e1adbac89bb7f176e338b8fc6e994ca210c9bb7bdca249b465942250cbc4e5fb02c3d1de23a9f1e014b4d2ee5aeaea9505df5e855c9210bf472495af00000000000000000000000086edd0c110d1fc7f8a5e1108623b3b1b4e3740f90000000000000000000000000df05adac0159e215111696339ad4998e5871b3d0000000000000000000000003cfe51b61e25750ab1426b0072e5d0cc5c30aafa0000000000000000000000000218b706898d234b85d2494df21eb0677eaea91800000000000000000000000086edd0c110d1fc7f8a5e1108623b3b1b4e3740f90000000000000000000000000df05adac0159e215111696339ad4998e5871b3d" + + contract_address = insert(:contract_address, contract_code: bytecode) - test "verification is successful if proper nightly version of compiler ~0.6.0" do - bytecode_0_6_1_nightly_2020_1_2_commit_d082b9b8 = - "0x608060405234801561001057600080fd5b5060405161026a38038061026a8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101ce8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea264697066735822122029b5dde5889a195ed02cebb1a638ae3754be34464b9a2bc8b48b6286636031fb64736f6c637826302e362e312d6e696768746c792e323032302e312e322b636f6d6d69742e64303832623962380057" - - constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" - contract_address = insert(:contract_address, contract_code: bytecode_0_6_1_nightly_2020_1_2_commit_d082b9b8) - bytecode_constructor_arguments = "#{bytecode_0_6_1_nightly_2020_1_2_commit_d082b9b8}#{constructor_arguments}" - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: bytecode_constructor_arguments - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => @code_0_6, - "compiler_version" => "v0.6.1-nightly.2020.1.2+commit.d082b9b8", - "evm_version" => "homestead", - "name" => "Incrementer", - "optimization" => false, - "constructor_arguments" => constructor_arguments - } - - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil - end + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: contract_creation_code + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => contract_source_code, + "compiler_version" => "v0.5.10+commit.5a6ea5b1", + "evm_version" => "default", + "name" => "Distribution", + "optimization" => true, + "optimization_runs" => @optimization_runs, + "autodetect_constructor_args" => true + } + + assert {:ok, + %{ + abi: abi, + constructor_arguments: + "00000000000000000000000086edd0c110d1fc7f8a5e1108623b3b1b4e3740f90000000000000000000000000df05adac0159e215111696339ad4998e5871b3d0000000000000000000000003cfe51b61e25750ab1426b0072e5d0cc5c30aafa0000000000000000000000000218b706898d234b85d2494df21eb0677eaea91800000000000000000000000086edd0c110d1fc7f8a5e1108623b3b1b4e3740f90000000000000000000000000df05adac0159e215111696339ad4998e5871b3d" + }} = Verifier.evaluate_authenticity(contract_address.hash, params) + + assert abi != nil + end + + test "another failed constructor args verification" do + contract_source_code = + "#{File.cwd!()}/test/support/fixture/smart_contract/issue_with_constructor_args.sol" + |> File.read!() + + bytecode = + "0x60806040523661001357610011610017565b005b6100115b61002761002261005e565b610096565b565b606061004e838360405180606001604052806027815260200161024c602791396100ba565b9392505050565b3b151590565b90565b60006100917f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b3660008037600080366000845af43d6000803e8080156100b5573d6000f35b3d6000fd5b6060833b61011e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b60648201526084015b60405180910390fd5b600080856001600160a01b03168560405161013991906101cc565b600060405180830381855af49150503d8060008114610174576040519150601f19603f3d011682016040523d82523d6000602084013e610179565b606091505b5091509150610189828286610193565b9695505050505050565b606083156101a257508161004e565b8251156101b25782518084602001fd5b8160405162461bcd60e51b815260040161011591906101e8565b600082516101de81846020870161021b565b9190910192915050565b600060208252825180602084015261020781604085016020870161021b565b601f01601f19169190910160400192915050565b60005b8381101561023657818101518382015260200161021e565b83811115610245576000848401525b5050505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212209b8470f06e8a3960c912103fc2be177edaad69584ee3c7d2809ee737e79408e764736f6c63430008020033" + + contract_creation_code = + "0x6080604052604051610772380380610772833981016040819052610022916102f7565b61004d60017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd61040f565b60008051602061072b8339815191521461007757634e487b7160e01b600052600160045260246000fd5b6100838282600061008a565b5050610474565b610093836100f4565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806100d45750805b156100ef576100ed83836101b460201b6100291760201c565b505b505050565b610107816101e060201b6100551760201c565b61016e5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b8061019360008051602061072b83398151915260001b6101e660201b61005b1760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b60606101d9838360405180606001604052806027815260200161074b602791396101e9565b9392505050565b3b151590565b90565b6060833b6102485760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610165565b600080856001600160a01b03168560405161026391906103c0565b600060405180830381855af49150503d806000811461029e576040519150601f19603f3d011682016040523d82523d6000602084013e6102a3565b606091505b5090925090506102b48282866102be565b9695505050505050565b606083156102cd5750816101d9565b8251156102dd5782518084602001fd5b8160405162461bcd60e51b815260040161016591906103dc565b60008060408385031215610309578182fd5b82516001600160a01b038116811461031f578283fd5b60208401519092506001600160401b038082111561033b578283fd5b818501915085601f83011261034e578283fd5b8151818111156103605761036061045e565b604051601f8201601f19908116603f011681019083821181831017156103885761038861045e565b816040528281528860208487010111156103a0578586fd5b6103b1836020830160208801610432565b80955050505050509250929050565b600082516103d2818460208701610432565b9190910192915050565b60006020825282518060208401526103fb816040850160208701610432565b601f01601f19169190910160400192915050565b60008282101561042d57634e487b7160e01b81526011600452602481fd5b500390565b60005b8381101561044d578181015183820152602001610435565b838111156100ed5750506000910152565b634e487b7160e01b600052604160045260246000fd5b6102a8806104836000396000f3fe60806040523661001357610011610017565b005b6100115b61002761002261005e565b610096565b565b606061004e838360405180606001604052806027815260200161024c602791396100ba565b9392505050565b3b151590565b90565b60006100917f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b3660008037600080366000845af43d6000803e8080156100b5573d6000f35b3d6000fd5b6060833b61011e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b60648201526084015b60405180910390fd5b600080856001600160a01b03168560405161013991906101cc565b600060405180830381855af49150503d8060008114610174576040519150601f19603f3d011682016040523d82523d6000602084013e610179565b606091505b5091509150610189828286610193565b9695505050505050565b606083156101a257508161004e565b8251156101b25782518084602001fd5b8160405162461bcd60e51b815260040161011591906101e8565b600082516101de81846020870161021b565b9190910192915050565b600060208252825180602084015261020781604085016020870161021b565b601f01601f19169190910160400192915050565b60005b8381101561023657818101518382015260200161021e565b83811115610245576000848401525b5050505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212209b8470f06e8a3960c912103fc2be177edaad69584ee3c7d2809ee737e79408e764736f6c63430008020033360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564000000000000000000000000569e7b559a3af9b2350b8db2afd8977b8bd0517200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000024c4d66de8000000000000000000000000217373ab5e0082b2ce622169672eca6f4462319c00000000000000000000000000000000000000000000000000000000" + + contract_address = insert(:contract_address, contract_code: bytecode) - test "verification is failed if wrong nightly version of compiler ~0.5.11" do - bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753 = - "0x608060405234801561001057600080fd5b5060405161026b38038061026b8339818101604052602081101561003357600080fd5b81019080805190602001909291905050508060008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101cf8061009c6000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c010000000000000000000000000000000000000000000000000000000090048063256fec88146100635780633fa4f245146100ad578063812600df146100cb575b600080fd5b61006b6100f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100b561011f565b6040518082815260200191505060405180910390f35b6100f7600480360360208110156100e157600080fd5b8101908080359060200190929190505050610125565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005481565b806000540160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72305820f5be0e6763c440be759726643bdd4b03370e9f1b58fd803ab18b0b4f2aa58b7664736f6c637828302e352e31312d6e696768746c792e323031392e362e32352b636f6d6d69742e31636338343735330058" - - constructor_arguments = "000000000000000000000000000000000000000000000000000000000000000a" - contract_address = insert(:contract_address, contract_code: bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753) - bytecode_constructor_arguments = "#{bytecode_0_5_11_nightly_2019_6_25_commit_1cc84753}#{constructor_arguments}" - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: bytecode_constructor_arguments - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => @code_0_5, - "compiler_version" => "v0.5.11-nightly.2019.8.10+commit.f5f2bbb2", - "evm_version" => "homestead", - "name" => "Incrementer", - "optimization" => false, - "constructor_arguments" => constructor_arguments - } - - response = Verifier.evaluate_authenticity(contract_address.hash, params) - assert {:error, :compiler_version} = response + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: contract_creation_code + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => contract_source_code, + "compiler_version" => "v0.8.2+commit.661d1103", + "evm_version" => "default", + "name" => "ERC1967Proxy", + "optimization" => true, + "optimization_runs" => @optimization_runs, + "autodetect_constructor_args" => true + } + + assert {:ok, + %{ + abi: abi, + constructor_arguments: + "000000000000000000000000569e7b559a3af9b2350b8db2afd8977b8bd0517200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000024c4d66de8000000000000000000000000217373ab5e0082b2ce622169672eca6f4462319c00000000000000000000000000000000000000000000000000000000" + }} = Verifier.evaluate_authenticity(contract_address.hash, params) + + assert abi != nil + end end - end - describe "regression tests for https://github.com/blockscout/blockscout/pull/5166" do - test "issue 5114" do - contract_source_code = - "#{File.cwd!()}/test/support/fixture/smart_contract/issue_5114.sol" - |> File.read!() - - bytecode = - "0x60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106ed565b610118565b61005b610093366004610707565b610164565b3480156100a457600080fd5b506100ad6101da565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106ed565b610217565b3480156100f557600080fd5b506100ad610241565b6101066102a2565b610116610111610346565b610355565b565b610120610379565b6001600160a01b0316336001600160a01b0316141561015957610154816040518060200160405280600081525060006103ac565b610161565b6101616100fe565b50565b61016c610379565b6001600160a01b0316336001600160a01b031614156101cd576101c88383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506103ac915050565b6101d5565b6101d56100fe565b505050565b60006101e4610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610346565b9050610214565b6102146100fe565b90565b61021f610379565b6001600160a01b0316336001600160a01b03161415610159576101548161040b565b600061024b610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610379565b606061029183836040518060600160405280602781526020016108016027913961045f565b9392505050565b803b15155b919050565b6102aa610379565b6001600160a01b0316336001600160a01b031614156103415760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b610116565b600061035061053a565b905090565b3660008037600080366000845af43d6000803e808015610374573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316905090565b6103b583610562565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806103f65750805b156101d557610405838361026c565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610434610379565b604080516001600160a01b03928316815291841660208301520160405180910390a161016181610611565b606061046a84610298565b6104c55760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610338565b600080856001600160a01b0316856040516104e09190610785565b600060405180830381855af49150503d806000811461051b576040519150601f19603f3d011682016040523d82523d6000602084013e610520565b606091505b509150915061053082828661069d565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61039d565b61056b81610298565b6105cd5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610338565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b6001600160a01b0381166106765760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b6064820152608401610338565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036105f0565b606083156106ac575081610291565b8251156106bc5782518084602001fd5b8160405162461bcd60e51b815260040161033891906107a1565b80356001600160a01b038116811461029d57600080fd5b6000602082840312156106fe578081fd5b610291826106d6565b60008060006040848603121561071b578182fd5b610724846106d6565b9250602084013567ffffffffffffffff80821115610740578384fd5b818601915086601f830112610753578384fd5b813581811115610761578485fd5b876020828501011115610772578485fd5b6020830194508093505050509250925092565b600082516107978184602087016107d4565b9190910192915050565b60006020825282518060208401526107c08160408501602087016107d4565b601f01601f19169190910160400192915050565b60005b838110156107ef5781810151838201526020016107d7565b83811115610405575050600091015256fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122016ea36e15be10f9560025e0ec9401e2e9110cb5ec41d110b4a0e391838c1f19b64736f6c63430008020033" - - contract_creation_code = - "0x608060405260405162000f4038038062000f408339810160408190526200002691620004d4565b82816200005560017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd62000603565b60008051602062000ef9833981519152146200008157634e487b7160e01b600052600160045260246000fd5b6200008f82826000620000ff565b50620000bf905060017fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610462000603565b60008051602062000ed983398151915214620000eb57634e487b7160e01b600052600160045260246000fd5b620000f68262000170565b5050506200066c565b6200010a83620001cb565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806200014c5750805b156200016b576200016983836200029360201b6200026c1760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6200019b620002c2565b604080516001600160a01b03928316815291841660208301520160405180910390a1620001c881620002fb565b50565b620001e1816200038b60201b620002981760201c565b620002495760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b806200027260008051602062000ef983398151915260001b6200039560201b620002141760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6060620002bb838360405180606001604052806027815260200162000f196027913962000398565b9392505050565b6000620002ec60008051602062000ed983398151915260001b6200039560201b620002141760201c565b546001600160a01b0316905090565b6001600160a01b038116620003625760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840162000240565b806200027260008051602062000ed983398151915260001b6200039560201b620002141760201c565b803b15155b919050565b90565b6060620003a5846200038b565b620004025760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b606482015260840162000240565b600080856001600160a01b0316856040516200041f9190620005b0565b600060405180830381855af49150503d80600081146200045c576040519150601f19603f3d011682016040523d82523d6000602084013e62000461565b606091505b509092509050620004748282866200047e565b9695505050505050565b606083156200048f575081620002bb565b825115620004a05782518084602001fd5b8160405162461bcd60e51b8152600401620002409190620005ce565b80516001600160a01b03811681146200039057600080fd5b600080600060608486031215620004e9578283fd5b620004f484620004bc565b92506200050460208501620004bc565b60408501519092506001600160401b038082111562000521578283fd5b818601915086601f83011262000535578283fd5b8151818111156200054a576200054a62000656565b604051601f8201601f19908116603f0116810190838211818310171562000575576200057562000656565b816040528281528960208487010111156200058e578586fd5b620005a183602083016020880162000627565b80955050505050509250925092565b60008251620005c481846020870162000627565b9190910192915050565b6000602082528251806020840152620005ef81604085016020870162000627565b601f01601f19169190910160400192915050565b6000828210156200062257634e487b7160e01b81526011600452602481fd5b500390565b60005b83811015620006445781810151838201526020016200062a565b83811115620001695750506000910152565b634e487b7160e01b600052604160045260246000fd5b61085d806200067c6000396000f3fe60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106ed565b610118565b61005b610093366004610707565b610164565b3480156100a457600080fd5b506100ad6101da565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106ed565b610217565b3480156100f557600080fd5b506100ad610241565b6101066102a2565b610116610111610346565b610355565b565b610120610379565b6001600160a01b0316336001600160a01b0316141561015957610154816040518060200160405280600081525060006103ac565b610161565b6101616100fe565b50565b61016c610379565b6001600160a01b0316336001600160a01b031614156101cd576101c88383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506103ac915050565b6101d5565b6101d56100fe565b505050565b60006101e4610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610346565b9050610214565b6102146100fe565b90565b61021f610379565b6001600160a01b0316336001600160a01b03161415610159576101548161040b565b600061024b610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610379565b606061029183836040518060600160405280602781526020016108016027913961045f565b9392505050565b803b15155b919050565b6102aa610379565b6001600160a01b0316336001600160a01b031614156103415760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b610116565b600061035061053a565b905090565b3660008037600080366000845af43d6000803e808015610374573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316905090565b6103b583610562565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806103f65750805b156101d557610405838361026c565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610434610379565b604080516001600160a01b03928316815291841660208301520160405180910390a161016181610611565b606061046a84610298565b6104c55760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610338565b600080856001600160a01b0316856040516104e09190610785565b600060405180830381855af49150503d806000811461051b576040519150601f19603f3d011682016040523d82523d6000602084013e610520565b606091505b509150915061053082828661069d565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61039d565b61056b81610298565b6105cd5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610338565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b6001600160a01b0381166106765760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b6064820152608401610338565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036105f0565b606083156106ac575081610291565b8251156106bc5782518084602001fd5b8160405162461bcd60e51b815260040161033891906107a1565b80356001600160a01b038116811461029d57600080fd5b6000602082840312156106fe578081fd5b610291826106d6565b60008060006040848603121561071b578182fd5b610724846106d6565b9250602084013567ffffffffffffffff80821115610740578384fd5b818601915086601f830112610753578384fd5b813581811115610761578485fd5b876020828501011115610772578485fd5b6020830194508093505050509250925092565b600082516107978184602087016107d4565b9190910192915050565b60006020825282518060208401526107c08160408501602087016107d4565b601f01601f19169190910160400192915050565b60005b838110156107ef5781810151838201526020016107d7565b83811115610405575050600091015256fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122016ea36e15be10f9560025e0ec9401e2e9110cb5ec41d110b4a0e391838c1f19b64736f6c63430008020033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65640000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f051200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - - contract_address = insert(:contract_address, contract_code: bytecode) - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: contract_creation_code - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => contract_source_code, - "compiler_version" => "v0.8.2+commit.661d1103", - "evm_version" => "default", - "name" => "TransparentUpgradeableProxy", - "optimization" => true, - "optimization_runs" => 200, - "autodetect_constructor_args" => true - } - - assert {:ok, - %{ - abi: abi, - constructor_arguments: - "0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f051200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }} = Verifier.evaluate_authenticity(contract_address.hash, params) - - assert abi != nil - end + describe "another regression tests" do + test "accepting correct constructor args" do + contract_source_code = + "#{File.cwd!()}/test/support/fixture/smart_contract/issue_5114.sol" + |> File.read!() - test "issue 5127" do - contract_source_code = - "#{File.cwd!()}/test/support/fixture/smart_contract/issue_5127.sol" - |> File.read!() - - bytecode = - "0x60806040523480156200001157600080fd5b5060405162001bfc38038062001bfc8339810160408190526200003491620002d6565b6040805180820182526009808252682ca2a9902a37b5b2b760b91b6020808401918252845180860190955260038086526259455360e81b91860191909152600080546001600160a01b03808c16610100026001600160a81b03199092169190911790915560018054828b166001600160a01b03199182161790915560028054928a169290911691909117905585905582519293926012928992899289928992620000de9262000213565b508551620000f490600a90602089019062000213565b5050600b805460ff90951660ff199095169490941790935550620001209350339250889150506200012b565b505050505062000398565b6001600160a01b038216620001865760405162461bcd60e51b815260206004820152601f60248201527f4b415032303a206d696e7420746f20746865207a65726f206164647265737300604482015260640160405180910390fd5b80600860008282546200019a919062000334565b90915550506001600160a01b03821660009081526006602052604081208054839290620001c990849062000334565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b82805462000221906200035b565b90600052602060002090601f01602090048101928262000245576000855562000290565b82601f106200026057805160ff191683800117855562000290565b8280016001018555821562000290579182015b828111156200029057825182559160200191906001019062000273565b506200029e929150620002a2565b5090565b5b808211156200029e5760008155600101620002a3565b80516001600160a01b0381168114620002d157600080fd5b919050565b600080600080600060a08688031215620002ef57600080fd5b855194506200030160208701620002b9565b93506200031160408701620002b9565b92506200032160608701620002b9565b9150608086015190509295509295909350565b600082198211156200035657634e487b7160e01b600052601160045260246000fd5b500190565b600181811c908216806200037057607f821691505b602082108114156200039257634e487b7160e01b600052602260045260246000fd5b50919050565b61185480620003a86000396000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c80638456cb591161010f578063bddae40e116100a2578063dd62ed3e11610071578063dd62ed3e14610420578063de9c2a6b14610459578063f71c559c14610466578063f9f92be41461047957600080fd5b8063bddae40e146103cf578063c2f92192146103e2578063d864e740146103f5578063da72c1e81461040d57600080fd5b80639cfe42da116100de5780639cfe42da1461038e578063a1d5ec4b146103a1578063a457c2d7146103a9578063a9059cbb146103bc57600080fd5b80638456cb59146103405780638e39103c1461034857806390d6b45f1461035b57806395d89b411461038657600080fd5b8063394b652b1161018757806359e026f71161015657806359e026f7146102e65780635c975abb146102f9578063704b6c021461030457806370a082311461031757600080fd5b8063394b652b146102a357806339509351146102b85780633f4ba83a146102cb578063483a83df146102d357600080fd5b806318160ddd116101c357806318160ddd146102515780631ae878d31461026857806323b872dd14610271578063313ce5671461028457600080fd5b806306fdde03146101ea578063095ea7b3146102085780631714d7f31461022b575b600080fd5b6101f261049c565b6040516101ff9190611574565b60405180910390f35b61021b6102163660046115aa565b61052a565b60405190151581526020016101ff565b6101f2604051806040016040528060078152602001667975656d6d616960c81b81525081565b61025a60085481565b6040519081526020016101ff565b61025a60035481565b61021b61027f3660046115d4565b610540565b600b546102919060ff1681565b60405160ff90911681526020016101ff565b6102b66102b1366004611610565b610651565b005b61021b6102c63660046115aa565b61068c565b6102b66106c8565b6102b66102e1366004611629565b610701565b61021b6102f43660046115d4565b610739565b60005460ff1661021b565b6102b6610312366004611629565b61094f565b61025a610325366004611629565b6001600160a01b031660009081526006602052604090205490565b6102b6610a1b565b6102b6610356366004611629565b610a52565b60025461036e906001600160a01b031681565b6040516001600160a01b0390911681526020016101ff565b6101f2610a8a565b6102b661039c366004611629565b610a97565b6102b6610acf565b61021b6103b73660046115aa565b610b06565b61021b6103ca3660046115aa565b610b95565b6102b66103dd366004611629565b610bf4565b60015461036e906001600160a01b031681565b60005461036e9061010090046001600160a01b031681565b61021b61041b3660046115d4565b610c2c565b61025a61042e366004611644565b6001600160a01b03918216600090815260076020908152604080832093909416825291909152205490565b60045461021b9060ff1681565b61021b6104743660046115d4565b610ddb565b61021b610487366004611629565b60056020526000908152604090205460ff1681565b600980546104a990611677565b80601f01602080910402602001604051908101604052809291908181526020018280546104d590611677565b80156105225780601f106104f757610100808354040283529160200191610522565b820191906000526020600020905b81548152906001019060200180831161050557829003601f168201915b505050505081565b6000610537338484610ede565b50600192915050565b6000805460ff161561056d5760405162461bcd60e51b8152600401610564906116b2565b60405180910390fd5b6001600160a01b038416600090815260056020526040902054849060ff16156105a85760405162461bcd60e51b8152600401610564906116cd565b6105b3858585611002565b6001600160a01b0385166000908152600760209081526040808320338452909152902054838110156106385760405162461bcd60e51b815260206004820152602860248201527f4b415032303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b6064820152608401610564565b6106458633868403610ede565b50600195945050505050565b60005461010090046001600160a01b031633146106805760405162461bcd60e51b815260040161056490611704565b610689816111d1565b50565b3360008181526007602090815260408083206001600160a01b038716845290915281205490916105379185906106c3908690611751565b610ede565b60005461010090046001600160a01b031633146106f75760405162461bcd60e51b815260040161056490611704565b6106ff611217565b565b60005461010090046001600160a01b031633146107305760405162461bcd60e51b815260040161056490611704565b61068981611298565b6000805460ff161561075d5760405162461bcd60e51b8152600401610564906116b2565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf0916107aa91339190600401611769565b602060405180830381865afa1580156107c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107eb9190611795565b6108075760405162461bcd60e51b8152600401610564906117b7565b6003546002546040516306f19a8d60e21b81526001600160a01b03878116600483015290911690631bc66a3490602401602060405180830381865afa158015610854573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061087891906117ee565b101580156108f657506003546002546040516306f19a8d60e21b81526001600160a01b03868116600483015290911690631bc66a34906024015b602060405180830381865afa1580156108cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f391906117ee565b10155b61093a5760405162461bcd60e51b81526020600482015260156024820152744f6e6c7920696e7465726e616c20707572706f736560581b6044820152606401610564565b610945848484611002565b5060019392505050565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf09161099c91339190600401611769565b602060405180830381865afa1580156109b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109dd9190611795565b6109f95760405162461bcd60e51b8152600401610564906117b7565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60005461010090046001600160a01b03163314610a4a5760405162461bcd60e51b815260040161056490611704565b6106ff6112f2565b60005461010090046001600160a01b03163314610a815760405162461bcd60e51b815260040161056490611704565b6106898161134a565b600a80546104a990611677565b60005461010090046001600160a01b03163314610ac65760405162461bcd60e51b815260040161056490611704565b61068981611400565b60005461010090046001600160a01b03163314610afe5760405162461bcd60e51b815260040161056490611704565b6106ff61148a565b3360009081526007602090815260408083206001600160a01b038616845290915281205482811015610b885760405162461bcd60e51b815260206004820152602560248201527f4b415032303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610564565b6109453385858403610ede565b6000805460ff1615610bb95760405162461bcd60e51b8152600401610564906116b2565b3360008181526005602052604090205460ff1615610be95760405162461bcd60e51b8152600401610564906116cd565b610945338585611002565b60005461010090046001600160a01b03163314610c235760405162461bcd60e51b815260040161056490611704565b610689816114c2565b6000805461010090046001600160a01b03163314610c5c5760405162461bcd60e51b815260040161056490611704565b6001600160a01b038416600090815260066020526040902054821115610cd25760405162461bcd60e51b815260206004820152602560248201527f4b415032303a207472616e7366657220616d6f756e74206578636565642062616044820152646c616e636560d81b6064820152608401610564565b6001600160a01b038316610d285760405162461bcd60e51b815260206004820152601f60248201527f4b415032303a207472616e7366657220746f207a65726f2061646472657373006044820152606401610564565b6001600160a01b03841660009081526006602052604081208054849290610d50908490611807565b90915550506001600160a01b03831660009081526006602052604081208054849290610d7d908490611751565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610dc991815260200190565b60405180910390a35060019392505050565b6000805460ff1615610dff5760405162461bcd60e51b8152600401610564906116b2565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf091610e4c91339190600401611769565b602060405180830381865afa158015610e69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8d9190611795565b610ea95760405162461bcd60e51b8152600401610564906117b7565b6003546002546040516306f19a8d60e21b81526001600160a01b03878116600483015290911690631bc66a34906024016108b2565b6001600160a01b038316610f405760405162461bcd60e51b8152602060048201526024808201527f4b415032303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610564565b6001600160a01b038216610fa15760405162461bcd60e51b815260206004820152602260248201527f4b415032303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610564565b6001600160a01b0383811660008181526007602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6001600160a01b0383166110665760405162461bcd60e51b815260206004820152602560248201527f4b415032303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610564565b6001600160a01b0382166110c85760405162461bcd60e51b815260206004820152602360248201527f4b415032303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610564565b6001600160a01b038316600090815260066020526040902054818110156111405760405162461bcd60e51b815260206004820152602660248201527f4b415032303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610564565b6001600160a01b03808516600090815260066020526040808220858503905591851681529081208054849290611177908490611751565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516111c391815260200190565b60405180910390a350505050565b600380549082905560408051828152602081018490527f4bb7c3aa2e207c70c9f2b8b0d81e076d62b704e041cdedb61959edd1814912f491015b60405180910390a15050565b60005460ff1661124e5760405162461bcd60e51b815260206004820152600260248201526104e560f41b6044820152606401610564565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b600280546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f192570e0851c3af9ff6a477c94534e97444b3893085cf6ac37fb7e7ec335f01e910161120b565b60005460ff16156113155760405162461bcd60e51b8152600401610564906116b2565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861127b3390565b6001600160a01b038116600090815260056020526040902054819060ff166113b45760405162461bcd60e51b815260206004820152601b60248201527f41646472657373206973206e6f7420696e20626c61636b6c69737400000000006044820152606401610564565b6001600160a01b038216600081815260056020526040808220805460ff19169055513392917f6690dc53a3b1d37db94233f7c004408862ea909761dc5760b0e925276754f87591a35050565b6001600160a01b038116600090815260056020526040902054819060ff161561143b5760405162461bcd60e51b8152600401610564906116cd565b6001600160a01b038216600081815260056020526040808220805460ff19166001179055513392917fef674dcdab521405fef2bf4b5d2c6a6434e3ab02bc5a94fb89dd035704b83b0991a35050565b6004805460ff191660011790556040517fa5881517cf4ae3e7f6bcd00c68314e59f3ce78b5606d1b08253addc3c957e43b90600090a1565b60008054610100600160a81b0319166101006001600160a01b0384811682810293909317938490556040805193845291909304909216602082015282917f129aa2e3e7b369511a5c100a66e80b6c6231b4e60460799e7c1ed36e14121568910161120b565b6000815180845260005b8181101561154d57602081850181015186830182015201611531565b8181111561155f576000602083870101525b50601f01601f19169290920160200192915050565b6020815260006115876020830184611527565b9392505050565b80356001600160a01b03811681146115a557600080fd5b919050565b600080604083850312156115bd57600080fd5b6115c68361158e565b946020939093013593505050565b6000806000606084860312156115e957600080fd5b6115f28461158e565b92506116006020850161158e565b9150604084013590509250925092565b60006020828403121561162257600080fd5b5035919050565b60006020828403121561163b57600080fd5b6115878261158e565b6000806040838503121561165757600080fd5b6116608361158e565b915061166e6020840161158e565b90509250929050565b600181811c9082168061168b57607f821691505b602082108114156116ac57634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252600190820152600560fc1b604082015260600190565b60208082526017908201527f4164647265737320697320696e20626c61636b6c697374000000000000000000604082015260600190565b60208082526019908201527f52657374726963746564206f6e6c7920636f6d6d697474656500000000000000604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600082198211156117645761176461173b565b500190565b6001600160a01b038316815260406020820181905260009061178d90830184611527565b949350505050565b6000602082840312156117a757600080fd5b8151801515811461158757600080fd5b6020808252601b908201527f52657374726963746564206f6e6c792073757065722061646d696e0000000000604082015260600190565b60006020828403121561180057600080fd5b5051919050565b6000828210156118195761181961173b565b50039056fea2646970667358221220f9aa231f0e2c136b376cc26c179eea8f7aae62c23797e6e17b4ca4462f9ad1af64736f6c634300080b0033" - - contract_creation_code = - "0x60806040523480156200001157600080fd5b5060405162001bfc38038062001bfc8339810160408190526200003491620002d6565b6040805180820182526009808252682ca2a9902a37b5b2b760b91b6020808401918252845180860190955260038086526259455360e81b91860191909152600080546001600160a01b03808c16610100026001600160a81b03199092169190911790915560018054828b166001600160a01b03199182161790915560028054928a169290911691909117905585905582519293926012928992899289928992620000de9262000213565b508551620000f490600a90602089019062000213565b5050600b805460ff90951660ff199095169490941790935550620001209350339250889150506200012b565b505050505062000398565b6001600160a01b038216620001865760405162461bcd60e51b815260206004820152601f60248201527f4b415032303a206d696e7420746f20746865207a65726f206164647265737300604482015260640160405180910390fd5b80600860008282546200019a919062000334565b90915550506001600160a01b03821660009081526006602052604081208054839290620001c990849062000334565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b82805462000221906200035b565b90600052602060002090601f01602090048101928262000245576000855562000290565b82601f106200026057805160ff191683800117855562000290565b8280016001018555821562000290579182015b828111156200029057825182559160200191906001019062000273565b506200029e929150620002a2565b5090565b5b808211156200029e5760008155600101620002a3565b80516001600160a01b0381168114620002d157600080fd5b919050565b600080600080600060a08688031215620002ef57600080fd5b855194506200030160208701620002b9565b93506200031160408701620002b9565b92506200032160608701620002b9565b9150608086015190509295509295909350565b600082198211156200035657634e487b7160e01b600052601160045260246000fd5b500190565b600181811c908216806200037057607f821691505b602082108114156200039257634e487b7160e01b600052602260045260246000fd5b50919050565b61185480620003a86000396000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c80638456cb591161010f578063bddae40e116100a2578063dd62ed3e11610071578063dd62ed3e14610420578063de9c2a6b14610459578063f71c559c14610466578063f9f92be41461047957600080fd5b8063bddae40e146103cf578063c2f92192146103e2578063d864e740146103f5578063da72c1e81461040d57600080fd5b80639cfe42da116100de5780639cfe42da1461038e578063a1d5ec4b146103a1578063a457c2d7146103a9578063a9059cbb146103bc57600080fd5b80638456cb59146103405780638e39103c1461034857806390d6b45f1461035b57806395d89b411461038657600080fd5b8063394b652b1161018757806359e026f71161015657806359e026f7146102e65780635c975abb146102f9578063704b6c021461030457806370a082311461031757600080fd5b8063394b652b146102a357806339509351146102b85780633f4ba83a146102cb578063483a83df146102d357600080fd5b806318160ddd116101c357806318160ddd146102515780631ae878d31461026857806323b872dd14610271578063313ce5671461028457600080fd5b806306fdde03146101ea578063095ea7b3146102085780631714d7f31461022b575b600080fd5b6101f261049c565b6040516101ff9190611574565b60405180910390f35b61021b6102163660046115aa565b61052a565b60405190151581526020016101ff565b6101f2604051806040016040528060078152602001667975656d6d616960c81b81525081565b61025a60085481565b6040519081526020016101ff565b61025a60035481565b61021b61027f3660046115d4565b610540565b600b546102919060ff1681565b60405160ff90911681526020016101ff565b6102b66102b1366004611610565b610651565b005b61021b6102c63660046115aa565b61068c565b6102b66106c8565b6102b66102e1366004611629565b610701565b61021b6102f43660046115d4565b610739565b60005460ff1661021b565b6102b6610312366004611629565b61094f565b61025a610325366004611629565b6001600160a01b031660009081526006602052604090205490565b6102b6610a1b565b6102b6610356366004611629565b610a52565b60025461036e906001600160a01b031681565b6040516001600160a01b0390911681526020016101ff565b6101f2610a8a565b6102b661039c366004611629565b610a97565b6102b6610acf565b61021b6103b73660046115aa565b610b06565b61021b6103ca3660046115aa565b610b95565b6102b66103dd366004611629565b610bf4565b60015461036e906001600160a01b031681565b60005461036e9061010090046001600160a01b031681565b61021b61041b3660046115d4565b610c2c565b61025a61042e366004611644565b6001600160a01b03918216600090815260076020908152604080832093909416825291909152205490565b60045461021b9060ff1681565b61021b6104743660046115d4565b610ddb565b61021b610487366004611629565b60056020526000908152604090205460ff1681565b600980546104a990611677565b80601f01602080910402602001604051908101604052809291908181526020018280546104d590611677565b80156105225780601f106104f757610100808354040283529160200191610522565b820191906000526020600020905b81548152906001019060200180831161050557829003601f168201915b505050505081565b6000610537338484610ede565b50600192915050565b6000805460ff161561056d5760405162461bcd60e51b8152600401610564906116b2565b60405180910390fd5b6001600160a01b038416600090815260056020526040902054849060ff16156105a85760405162461bcd60e51b8152600401610564906116cd565b6105b3858585611002565b6001600160a01b0385166000908152600760209081526040808320338452909152902054838110156106385760405162461bcd60e51b815260206004820152602860248201527f4b415032303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b6064820152608401610564565b6106458633868403610ede565b50600195945050505050565b60005461010090046001600160a01b031633146106805760405162461bcd60e51b815260040161056490611704565b610689816111d1565b50565b3360008181526007602090815260408083206001600160a01b038716845290915281205490916105379185906106c3908690611751565b610ede565b60005461010090046001600160a01b031633146106f75760405162461bcd60e51b815260040161056490611704565b6106ff611217565b565b60005461010090046001600160a01b031633146107305760405162461bcd60e51b815260040161056490611704565b61068981611298565b6000805460ff161561075d5760405162461bcd60e51b8152600401610564906116b2565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf0916107aa91339190600401611769565b602060405180830381865afa1580156107c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107eb9190611795565b6108075760405162461bcd60e51b8152600401610564906117b7565b6003546002546040516306f19a8d60e21b81526001600160a01b03878116600483015290911690631bc66a3490602401602060405180830381865afa158015610854573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061087891906117ee565b101580156108f657506003546002546040516306f19a8d60e21b81526001600160a01b03868116600483015290911690631bc66a34906024015b602060405180830381865afa1580156108cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f391906117ee565b10155b61093a5760405162461bcd60e51b81526020600482015260156024820152744f6e6c7920696e7465726e616c20707572706f736560581b6044820152606401610564565b610945848484611002565b5060019392505050565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf09161099c91339190600401611769565b602060405180830381865afa1580156109b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109dd9190611795565b6109f95760405162461bcd60e51b8152600401610564906117b7565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60005461010090046001600160a01b03163314610a4a5760405162461bcd60e51b815260040161056490611704565b6106ff6112f2565b60005461010090046001600160a01b03163314610a815760405162461bcd60e51b815260040161056490611704565b6106898161134a565b600a80546104a990611677565b60005461010090046001600160a01b03163314610ac65760405162461bcd60e51b815260040161056490611704565b61068981611400565b60005461010090046001600160a01b03163314610afe5760405162461bcd60e51b815260040161056490611704565b6106ff61148a565b3360009081526007602090815260408083206001600160a01b038616845290915281205482811015610b885760405162461bcd60e51b815260206004820152602560248201527f4b415032303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610564565b6109453385858403610ede565b6000805460ff1615610bb95760405162461bcd60e51b8152600401610564906116b2565b3360008181526005602052604090205460ff1615610be95760405162461bcd60e51b8152600401610564906116cd565b610945338585611002565b60005461010090046001600160a01b03163314610c235760405162461bcd60e51b815260040161056490611704565b610689816114c2565b6000805461010090046001600160a01b03163314610c5c5760405162461bcd60e51b815260040161056490611704565b6001600160a01b038416600090815260066020526040902054821115610cd25760405162461bcd60e51b815260206004820152602560248201527f4b415032303a207472616e7366657220616d6f756e74206578636565642062616044820152646c616e636560d81b6064820152608401610564565b6001600160a01b038316610d285760405162461bcd60e51b815260206004820152601f60248201527f4b415032303a207472616e7366657220746f207a65726f2061646472657373006044820152606401610564565b6001600160a01b03841660009081526006602052604081208054849290610d50908490611807565b90915550506001600160a01b03831660009081526006602052604081208054849290610d7d908490611751565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610dc991815260200190565b60405180910390a35060019392505050565b6000805460ff1615610dff5760405162461bcd60e51b8152600401610564906116b2565b60015460408051808201825260078152667975656d6d616960c81b602082015290516302b98ccf60e41b81526001600160a01b0390921691632b98ccf091610e4c91339190600401611769565b602060405180830381865afa158015610e69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e8d9190611795565b610ea95760405162461bcd60e51b8152600401610564906117b7565b6003546002546040516306f19a8d60e21b81526001600160a01b03878116600483015290911690631bc66a34906024016108b2565b6001600160a01b038316610f405760405162461bcd60e51b8152602060048201526024808201527f4b415032303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610564565b6001600160a01b038216610fa15760405162461bcd60e51b815260206004820152602260248201527f4b415032303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610564565b6001600160a01b0383811660008181526007602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6001600160a01b0383166110665760405162461bcd60e51b815260206004820152602560248201527f4b415032303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610564565b6001600160a01b0382166110c85760405162461bcd60e51b815260206004820152602360248201527f4b415032303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610564565b6001600160a01b038316600090815260066020526040902054818110156111405760405162461bcd60e51b815260206004820152602660248201527f4b415032303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610564565b6001600160a01b03808516600090815260066020526040808220858503905591851681529081208054849290611177908490611751565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516111c391815260200190565b60405180910390a350505050565b600380549082905560408051828152602081018490527f4bb7c3aa2e207c70c9f2b8b0d81e076d62b704e041cdedb61959edd1814912f491015b60405180910390a15050565b60005460ff1661124e5760405162461bcd60e51b815260206004820152600260248201526104e560f41b6044820152606401610564565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b600280546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f192570e0851c3af9ff6a477c94534e97444b3893085cf6ac37fb7e7ec335f01e910161120b565b60005460ff16156113155760405162461bcd60e51b8152600401610564906116b2565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861127b3390565b6001600160a01b038116600090815260056020526040902054819060ff166113b45760405162461bcd60e51b815260206004820152601b60248201527f41646472657373206973206e6f7420696e20626c61636b6c69737400000000006044820152606401610564565b6001600160a01b038216600081815260056020526040808220805460ff19169055513392917f6690dc53a3b1d37db94233f7c004408862ea909761dc5760b0e925276754f87591a35050565b6001600160a01b038116600090815260056020526040902054819060ff161561143b5760405162461bcd60e51b8152600401610564906116cd565b6001600160a01b038216600081815260056020526040808220805460ff19166001179055513392917fef674dcdab521405fef2bf4b5d2c6a6434e3ab02bc5a94fb89dd035704b83b0991a35050565b6004805460ff191660011790556040517fa5881517cf4ae3e7f6bcd00c68314e59f3ce78b5606d1b08253addc3c957e43b90600090a1565b60008054610100600160a81b0319166101006001600160a01b0384811682810293909317938490556040805193845291909304909216602082015282917f129aa2e3e7b369511a5c100a66e80b6c6231b4e60460799e7c1ed36e14121568910161120b565b6000815180845260005b8181101561154d57602081850181015186830182015201611531565b8181111561155f576000602083870101525b50601f01601f19169290920160200192915050565b6020815260006115876020830184611527565b9392505050565b80356001600160a01b03811681146115a557600080fd5b919050565b600080604083850312156115bd57600080fd5b6115c68361158e565b946020939093013593505050565b6000806000606084860312156115e957600080fd5b6115f28461158e565b92506116006020850161158e565b9150604084013590509250925092565b60006020828403121561162257600080fd5b5035919050565b60006020828403121561163b57600080fd5b6115878261158e565b6000806040838503121561165757600080fd5b6116608361158e565b915061166e6020840161158e565b90509250929050565b600181811c9082168061168b57607f821691505b602082108114156116ac57634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252600190820152600560fc1b604082015260600190565b60208082526017908201527f4164647265737320697320696e20626c61636b6c697374000000000000000000604082015260600190565b60208082526019908201527f52657374726963746564206f6e6c7920636f6d6d697474656500000000000000604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600082198211156117645761176461173b565b500190565b6001600160a01b038316815260406020820181905260009061178d90830184611527565b949350505050565b6000602082840312156117a757600080fd5b8151801515811461158757600080fd5b6020808252601b908201527f52657374726963746564206f6e6c792073757065722061646d696e0000000000604082015260600190565b60006020828403121561180057600080fd5b5051919050565b6000828210156118195761181961173b565b50039056fea2646970667358221220f9aa231f0e2c136b376cc26c179eea8f7aae62c23797e6e17b4ca4462f9ad1af64736f6c634300080b0033000000000000000000000000000000000000000000084595161401484a00000000000000000000000000000073d8f731ec0d3945d807a904bf93954b82b0d594000000000000000000000000c5333c0d3cf6fc8f84f3ccb0d5a73dbda2eceb500000000000000000000000002c8abd9c61d4e973ca8db5545c54c90e44a2445c0000000000000000000000000000000000000000000000000000000000000004" - - contract_address = insert(:contract_address, contract_code: bytecode) - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: contract_creation_code - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => contract_source_code, - "compiler_version" => "v0.8.11+commit.d7f03943", - "evm_version" => "default", - "name" => "YESToken", - "optimization" => true, - "optimization_runs" => 200, - "autodetect_constructor_args" => true - } - - assert {:ok, - %{ - abi: abi, - constructor_arguments: - "000000000000000000000000000000000000000000084595161401484a00000000000000000000000000000073d8f731ec0d3945d807a904bf93954b82b0d594000000000000000000000000c5333c0d3cf6fc8f84f3ccb0d5a73dbda2eceb500000000000000000000000002c8abd9c61d4e973ca8db5545c54c90e44a2445c0000000000000000000000000000000000000000000000000000000000000004" - }} = Verifier.evaluate_authenticity(contract_address.hash, params) - - assert abi != nil - end + bytecode = + "0x60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106ed565b610118565b61005b610093366004610707565b610164565b3480156100a457600080fd5b506100ad6101da565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106ed565b610217565b3480156100f557600080fd5b506100ad610241565b6101066102a2565b610116610111610346565b610355565b565b610120610379565b6001600160a01b0316336001600160a01b0316141561015957610154816040518060200160405280600081525060006103ac565b610161565b6101616100fe565b50565b61016c610379565b6001600160a01b0316336001600160a01b031614156101cd576101c88383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506103ac915050565b6101d5565b6101d56100fe565b505050565b60006101e4610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610346565b9050610214565b6102146100fe565b90565b61021f610379565b6001600160a01b0316336001600160a01b03161415610159576101548161040b565b600061024b610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610379565b606061029183836040518060600160405280602781526020016108016027913961045f565b9392505050565b803b15155b919050565b6102aa610379565b6001600160a01b0316336001600160a01b031614156103415760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b610116565b600061035061053a565b905090565b3660008037600080366000845af43d6000803e808015610374573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316905090565b6103b583610562565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806103f65750805b156101d557610405838361026c565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610434610379565b604080516001600160a01b03928316815291841660208301520160405180910390a161016181610611565b606061046a84610298565b6104c55760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610338565b600080856001600160a01b0316856040516104e09190610785565b600060405180830381855af49150503d806000811461051b576040519150601f19603f3d011682016040523d82523d6000602084013e610520565b606091505b509150915061053082828661069d565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61039d565b61056b81610298565b6105cd5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610338565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b6001600160a01b0381166106765760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b6064820152608401610338565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036105f0565b606083156106ac575081610291565b8251156106bc5782518084602001fd5b8160405162461bcd60e51b815260040161033891906107a1565b80356001600160a01b038116811461029d57600080fd5b6000602082840312156106fe578081fd5b610291826106d6565b60008060006040848603121561071b578182fd5b610724846106d6565b9250602084013567ffffffffffffffff80821115610740578384fd5b818601915086601f830112610753578384fd5b813581811115610761578485fd5b876020828501011115610772578485fd5b6020830194508093505050509250925092565b600082516107978184602087016107d4565b9190910192915050565b60006020825282518060208401526107c08160408501602087016107d4565b601f01601f19169190910160400192915050565b60005b838110156107ef5781810151838201526020016107d7565b83811115610405575050600091015256fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122016ea36e15be10f9560025e0ec9401e2e9110cb5ec41d110b4a0e391838c1f19b64736f6c63430008020033" - test "issue 3082" do - contract_source_code = - "#{File.cwd!()}/test/support/fixture/smart_contract/issue_3082.sol" - |> File.read!() - - bytecode = - "0x608060405234801561001057600080fd5b50600436106101e55760003560e01c80638d3ce69d1161010f578063b2cec8ea116100a2578063cbecb28811610071578063cbecb2881461050a578063f049f33414610512578063f2fde38b14610532578063fc0c546a14610558576101e5565b8063b2cec8ea146104a2578063b99ef521146104c2578063bac883ef146104e2578063bd4b2be914610502576101e5565b8063983c44d6116100de578063983c44d6146103b75780639972444f146103bf578063a3df582a146103df578063a4c0ed36146103e7576101e5565b80638d3ce69d146103635780638d3df3461461036b5780638da5cb5b1461038b5780638f32d59b146103af576101e5565b8063392e53cd11610187578063622c5e4511610156578063622c5e451461032b578063715018a6146103335780637af7c0401461033b5780638129fc1c1461035b576101e5565b8063392e53cd146102b757806359791d6d146102bf5780635f26622f146102df578063604f21771461030b576101e5565b80630f10e06f116101c35780630f10e06f146102695780632755731e14610289578063375a4cab14610291578063381a4113146102af576101e5565b8063014a969a146101ea578063047fc9aa1461021e57806304a3922014610238575b600080fd5b61020a6004803603602081101561020057600080fd5b503560ff16610560565b604080519115158252519081900360200190f35b610226610575565b60408051918252519081900360200190f35b6102676004803603604081101561024e57600080fd5b50803560ff1690602001356001600160a01b0316610584565b005b6102266004803603602081101561027f57600080fd5b503560ff166106d0565b6102266106e2565b6102996106e8565b6040805160ff9092168252519081900360200190f35b61020a6106ed565b61020a6106f6565b610226600480360360208110156102d557600080fd5b503560ff16610704565b610267600480360360408110156102f557600080fd5b506001600160a01b038135169060200135610716565b6102266004803603602081101561032157600080fd5b503560ff16610fc4565b610299610fd6565b610267610fdb565b61020a6004803603602081101561035157600080fd5b503560ff16611073565b610267611088565b6102996111de565b6102266004803603602081101561038157600080fd5b503560ff166111e3565b6103936111f5565b604080516001600160a01b039092168252519081900360200190f35b61020a611204565b610299611215565b610393600480360360208110156103d557600080fd5b503560ff1661121a565b610226611235565b61020a600480360360608110156103fd57600080fd5b6001600160a01b038235169160208101359181019060608101604082013564010000000081111561042d57600080fd5b82018360208201111561043f57600080fd5b8035906020019184600183028401116401000000008311171561046157600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955061123b945050505050565b610226600480360360208110156104b857600080fd5b503560ff16611274565b610226600480360360208110156104d857600080fd5b503560ff16611286565b610267600480360360208110156104f857600080fd5b503560ff16611298565b610299611652565b610299611657565b6102266004803603602081101561052857600080fd5b503560ff1661165c565b6102676004803603602081101561054857600080fd5b50356001600160a01b031661166e565b6103936116d3565b600b6020526000908152604090205460ff1681565b6a070fe2cd68c25ff4f0000081565b60ff821660011480610599575060ff82166005145b6105d7576040805162461bcd60e51b815260206004820152600a6024820152691ddc9bdb99c81c1bdbdb60b21b604482015290519081900360640190fd5b60ff82166000908152600260205260409020546001600160a01b03163314610637576040805162461bcd60e51b815260206004820152600e60248201526d1b9bdd08185d5d1a1bdc9a5e995960921b604482015290519081900360640190fd5b610640816116e2565b60ff82166000818152600260209081526040918290205482516001600160a01b0391821681529085169181019190915281517f09a67390cbf7986a6bb0fcb44307b4844ba258bca3f38599d2b344824df4ba1d929181900390910190a260ff91909116600090815260026020526040902080546001600160a01b0319166001600160a01b03909216919091179055565b60056020526000908152604090205481565b600d5481565b600581565b600e5460ff1681565b600e54610100900460ff1681565b60086020526000908152604090205481565b61071e611204565b61076f576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600e5460ff16156107c7576040805162461bcd60e51b815260206004820152601760248201527f616c7265616479207072652d696e697469616c697a6564000000000000000000604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b038481169190911791829055604080516370a0823160e01b81523060048201529051600093909216916370a0823191602480820192602092909190829003018186803b15801561082b57600080fd5b505afa15801561083f573d6000803e3d6000fd5b505050506040513d602081101561085557600080fd5b505190506a070fe2cd68c25ff4f0000081146108b1576040805162461bcd60e51b815260206004820152601660248201527577726f6e6720636f6e74726163742062616c616e636560501b604482015290519081900360640190fd5b6108b961172f565b600d55600e805460ff191660011790556003600090815260026020527f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c3546040805163189acdbd60e31b81526001600160a01b0387811660048301529151919092169263c4d66de8926024808201939182900301818387803b15801561093e57600080fd5b505af1158015610952573d6000803e3d6000fd5b50506004600081815260026020527fee60d0579bcffd98e668647d59fec1ff86a7fb340ce572e844f234ae73a6918f546040805163189acdbd60e31b81526001600160a01b038a811695820195909552905193909116945063c4d66de893506024808201939182900301818387803b1580156109cd57600080fd5b505af11580156109e1573d6000803e3d6000fd5b5050600360008181526020919091527fcbc4e5fb02c3d1de23a9f1e014b4d2ee5aeaea9505df5e855c9210bf472495af54909250610a3a9150606490610a2e90601963ffffffff61173316565b9063ffffffff61179516565b600154600260009081527f679795a0195a1b76cdebb7c51d74e058aee92919b8c3389af86ef24535e8a28c54600360209081527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d546040805163238a3fe160e01b81526001600160a01b039485166004820152602481019290925251959650919093169363238a3fe19360448084019491938390030190829087803b158015610ae257600080fd5b505af1158015610af6573d6000803e3d6000fd5b505050506040513d6020811015610b0c57600080fd5b505060015460036000908152600260209081527f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c3546040805163a9059cbb60e01b81526001600160a01b039283166004820152602481018790529051919094169363a9059cbb9360448083019493928390030190829087803b158015610b9157600080fd5b505af1158015610ba5573d6000803e3d6000fd5b505050506040513d6020811015610bbb57600080fd5b50506006600090815260036020527fc69056f16cbaa3c616b828e333ab7d3a32310765507f8f58359e99ebb7a885f354610bfb908563ffffffff6117ff16565b60015460066000908152600260209081527f59dd4b18488d12f51eda69757a0ed42a2010c14b564330cc74a06895e60c077b546040805163238a3fe160e01b81526001600160a01b03928316600482015260248101879052905195965093169363238a3fe193604480820194918390030190829087803b158015610c7e57600080fd5b505af1158015610c92573d6000803e3d6000fd5b505050506040513d6020811015610ca857600080fd5b50508315610d35576001546040805163238a3fe160e01b81526000600482018190526024820188905291516001600160a01b039093169263238a3fe192604480840193602093929083900390910190829087803b158015610d0857600080fd5b505af1158015610d1c573d6000803e3d6000fd5b505050506040513d6020811015610d3257600080fd5b50505b60026000527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d5460046020527f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a754610d929163ffffffff6117ff16565b60046020527f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a75560036000527f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa054610df0908363ffffffff6117ff16565b7f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa05560066000527fc69056f16cbaa3c616b828e333ab7d3a32310765507f8f58359e99ebb7a885f35460046020527fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f54610e6f9163ffffffff6117ff16565b6006600052600460209081527fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f91909155604080516001600160a01b0388168152339281019290925280517f20e0b9d27e138a83ff1b3f687932144f5e913aa93855ac36c2611ec1dfae704e9281900390910190a160026000819052600360209081527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d546040805191825233928201929092528151600080516020611b49833981519152929181900390910190a2604080518381523360208201528151600392600080516020611b49833981519152928290030190a2604080518281523360208201528151600692600080516020611b49833981519152928290030190a28315610fbd57604080518581523360208201528151600092600080516020611b49833981519152928290030190a25b5050505050565b60036020526000908152604090205481565b600481565b610fe3611204565b611034576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b5c1b195b595b9d1959608a1b604482015290519081900360640190fd5b600a6020526000908152604090205460ff1681565b600e5460ff166110d5576040805162461bcd60e51b81526020600482015260136024820152721b9bdd081c1c994b5a5b9a5d1a585b1a5e9959606a1b604482015290519081900360640190fd5b600e54610100900460ff1615611128576040805162461bcd60e51b8152602060048201526013602482015272185b1c9958591e481a5b9a5d1a585b1a5e9959606a1b604482015290519081900360640190fd5b6276a700611146600d5461113a61172f565b9063ffffffff6117ff16565b101561118f57611154611204565b61118f5760405162461bcd60e51b8152600401808060200182810382526027815260200180611b226027913960400191505060405180910390fd5b61119761172f565b600c55600e805461ff0019166101001790556040805133815290517f908408e307fc569b417f6cbec5d5a06f44a0a505ac0479b47d421a4b2fd6a1e69181900360200190a1565b600381565b60066020526000908152604090205481565b6000546001600160a01b031690565b6000546001600160a01b0316331490565b600181565b6002602052600090815260409020546001600160a01b031681565b600c5481565b600060405162461bcd60e51b815260040180806020018281038252602e815260200180611bb0602e913960400191505060405180910390fd5b60096020526000908152604090205481565b60046020526000908152604090205481565b600e54610100900460ff166112e6576040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b9a5d1a585b1a5e9959608a1b604482015290519081900360640190fd5b60ff8116600090815260056020526040902054600c54829161130e919063ffffffff61185c16565b61131661172f565b10158015611336575060ff8082166000908152600b602052604090205416155b6113715760405162461bcd60e51b8152600401808060200182810382526029815260200180611bde6029913960400191505060405180910390fd5b60ff821660031480611386575060ff82166004145b80611394575060ff82166001145b806113a2575060ff82166005145b6113e0576040805162461bcd60e51b815260206004820152600a6024820152691ddc9bdb99c81c1bdbdb60b21b604482015290519081900360640190fd5b60ff8083166000908152600a6020526040812054909116611426575060ff8216600090815260096020908152604080832054600a909252909120805460ff191660011790555b6000611431846118b6565b60ff85166000908152600860205260409020549091506114689061145b908363ffffffff61173316565b839063ffffffff61185c16565b9150600082116114bf576040805162461bcd60e51b815260206004820152601960248201527f6e6f20696e7374616c6c6d656e747320617661696c61626c6500000000000000604482015290519081900360640190fd5b60006114cc8584846119a0565b90506114de838263ffffffff61185c16565b925060ff8516600314806114f5575060ff85166004145b156115905760015460ff8616600090815260026020908152604080832054815163a9059cbb60e01b81526001600160a01b03918216600482015260248101899052915194169363a9059cbb93604480840194938390030190829087803b15801561155e57600080fd5b505af1158015611572573d6000803e3d6000fd5b505050506040513d602081101561158857600080fd5b506116219050565b60015460ff8616600090815260026020908152604080832054815163238a3fe160e01b81526001600160a01b03918216600482015260248101899052915194169363238a3fe193604480840194938390030190829087803b1580156115f457600080fd5b505af1158015611608573d6000803e3d6000fd5b505050506040513d602081101561161e57600080fd5b50505b60408051848152336020820152815160ff881692600080516020611b49833981519152928290030190a25050505050565b600681565b600281565b60076020526000908152604090205481565b611676611204565b6116c7576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6116d081611a5d565b50565b6001546001600160a01b031681565b6001600160a01b0381166116d0576040805162461bcd60e51b815260206004820152600f60248201526e696e76616c6964206164647265737360881b604482015290519081900360640190fd5b4290565b6000826117425750600061178f565b8282028284828161174f57fe5b041461178c5760405162461bcd60e51b8152600401808060200182810382526021815260200180611b8f6021913960400191505060405180910390fd5b90505b92915050565b60008082116117eb576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b60008284816117f657fe5b04949350505050565b600082821115611856576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60008282018381101561178c576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60ff811660009081526007602052604081205481906118de906201518063ffffffff61173316565b60ff8416600090815260056020526040812054600c54929350909161191a91849161190e9163ffffffff61185c16565b9063ffffffff61185c16565b905061192f62015180610a2e8361113a61172f565b60ff851660009081526006602090815260408083205460079092529091205491945090611962908563ffffffff61185c16565b11156119995760ff84166000908152600760209081526040808320546006909252909120546119969163ffffffff6117ff16565b92505b5050919050565b60ff831660009081526004602052604081205481906119c5908563ffffffff6117ff16565b60ff86166000908152600460209081526040808320939093556007905220546119f4908463ffffffff61185c16565b60ff861660009081526007602081815260408084208590556006825290922054915211611a555760ff851660009081526004602052604090205415611a4c575060ff8416600090815260046020526040812080549190555b611a5585611afd565b949350505050565b6001600160a01b038116611aa25760405162461bcd60e51b8152600401808060200182810382526026815260200180611b696026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60ff166000908152600b60205260409020805460ff19166001179055565b3b15159056fe666f72206e6f77206f6e6c79206f776e65722063616e2063616c6c2074686973206d6574686f64a8b65b82b2ff2a955e75c1bfa6a0e92aafb764156295da77a0a4c714f3895c724f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7773656e64696e6720746f6b656e7320746f207468697320636f6e7472616374206973206e6f7420616c6c6f776564696e7374616c6c6d656e747320617265206e6f742061637469766520666f72207468697320706f6f6ca265627a7a723058208fc7fe0d6f40178495c4e655627ac8a6bc837eb89686042a07161f078a2ea04464736f6c634300050a0032" - - contract_creation_code = - "0x6080604052600e805461ffff191690553480156200001c57600080fd5b5060405162002ac338038062002ac3833981810160405260c08110156200004257600080fd5b50805160208201516040808401516060850151608086015160a090960151600080546001600160a01b031916331780825594519697959693959294929391926001600160a01b0316917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3620000cf846001600160a01b031662000aa160201b62001b1b1760201c565b8015620000f65750620000f6836001600160a01b031662000aa160201b62001b1b1760201c565b6200016257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f6e6f74206120636f6e7472616374206164647265737300000000000000000000604482015290519081900360640190fd5b62000176866001600160e01b0362000aa716565b6200018a856001600160e01b0362000aa716565b6200019e826001600160e01b0362000aa716565b620001b2816001600160e01b0362000aa716565b7fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e080546001600160a01b03199081166001600160a01b03898116919091179092557f679795a0195a1b76cdebb7c51d74e058aee92919b8c3389af86ef24535e8a28c805482168884161790557f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c380548216878416179081905560008051602062002a42833981519152805483168785161790557fb98b78633099fa36ed8b8680c4f8092689e1e04080eb9cbb077ca38a14d7e384805483168685161790557f59dd4b18488d12f51eda69757a0ed42a2010c14b564330cc74a06895e60c077b8054909216848416179091556a034f086f3b33b684000000600080516020620029e2833981519152556954b40b1f852bda000000600080516020620029c283398151915255600360005260026020908152604080517f5fd6632000000000000000000000000000000000000000000000000000000000815290519290931692635fd663209260048083019392829003018186803b1580156200035257600080fd5b505afa15801562000367573d6000803e3d6000fd5b505050506040513d60208110156200037e57600080fd5b505160008051602062002aa383398151915255600460008190526002602090815260008051602062002a4283398151915254604080517f5fd6632000000000000000000000000000000000000000000000000000000000815290516001600160a01b0390921693635fd6632093828201939092909190829003018186803b1580156200040957600080fd5b505afa1580156200041e573d6000803e3d6000fd5b505050506040513d60208110156200043557600080fd5b50516003602090815260008051602062002a2283398151915282905569940785b073a9e904000060008051602062002a8383398151915281905569ace68dbebd988d50000060008051602062002a0283398151915281905560008051602062002aa383398151915254600080516020620029c2833981519152546001600052600080516020620029e2833981519152546a070fe2cd68c25ff4f00000966200050e969495620004fa95909486949293859391928492906200185c62000b20821b17901c565b62000b2060201b6200185c1790919060201c565b146200057b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f77726f6e672073756d206f6620706f6f6c73207374616b657300000000000000604482015290519081900360640190fd5b600080516020620029e2833981519152547fabd6e7cb50984ff9c2f3e18a2660c3353dadf4e3291deeb275dae2cd1e44fe05819055600080516020620029c2833981519152547f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a75560008051602062002aa3833981519152547f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa05560008051602062002a22833981519152547f1a1e6821cde7d0159c0d293177871e09677b4e42307c7db3ba94f8648a5a050f5560008051602062002a83833981519152547f04cde762ef08b6b6c5ded8e8c4c0b3f4e5c9ad7342c88fcc93681b4588b73f055560008051602062002a02833981519152547fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f55600160005260036020908152620006ef91606491620006db91906014906200173362000b9e821b17901c565b62000c1660201b620017951790919060201c565b7f92e85d02570a8092d09a6e3a57665bc3815a2699a4074001bf1ccabf660f5a365560036000819052602090815260008051602062002aa3833981519152546200074d91606491620006db91600a906200173362000b9e821b17901c565b7fc575c31fea594a6eb97c8e9d3f9caee4c16218c6ef37e923234c0fe9014a61e75560046000526003602090815260008051602062002a2283398151915254620007ab91606491620006db916014906200173362000b9e821b17901c565b7f8dc18c4ccfd75f5c815b63770fa542fd953e8fef7e0e44bbdd4913470ce7e9cb5560056000526003602090815260008051602062002a83833981519152546200080991606491620006db916014906200173362000b9e821b17901c565b7f74b05292d1d4b2b48b65261b07099d24244bcb069f138d9a6bfdcf776becac4c556301baf8007f1471eb6eb2c5e789fc3de43f8ce62938c7d1836ec861730447e2ada8fd81017b556224ea007fa9bc9a3a348c357ba16b37005d7e6b3236198c0e939f4af8c5f19b8deeb8ebc055626ebe007f3eec716f11ba9e820c81ca75eb978ffb45831ef8b7a53e5e422c26008e1ca6d58190557f458b30c2d72bfd2c6317304a4594ecbafe5f729d3111b65fdc3a33bd48e5432d5560066020526101507f3e5fec24aa4dc4e5aee2e025e51e1392c72a2500577559fae9665c6d52bd6a315560e07f75f96ab15d697e93042dc45b5c896c4b27e89bb6eaf39475c5c371cb2513f7d25560fc7fc5069e24aaadb2addc3e52e868fcf3f4f8acf5a87e24300992fd4540c2a87eed81905560056000527fbfd358e93f18da3ed276c3afdbdba00b8f0b6008a03476a6a86bd6320ee6938b556200097260016001600160e01b0362000c9c16565b7fad67d757c34507f157cacfa2e3153e9f260a2244f30428821be7be64587ac55f5560036000819052602081815260008051602062002aa383398151915254620009e69291620009d791606491620006db91906023906200173362000b9e821b17901c565b6001600160e01b0362000cc516565b600360005260086020527f625b35f5e76f098dd7c3a05b10e2e5e78a4a01228d60c3b143426cdf36d264555562000a2760046001600160e01b0362000c9c16565b600460005260086020527f9321edea6e3be4df59a344b401fab4f888b556fda1f954244cff9204bad624b85562000a6860056001600160e01b0362000c9c16565b600560005260086020527f91238f30f286c9a1c6e901c4eda3b214c381c846e3dbe48df95c21488e8e1fdb555062000d77945050505050565b3b151590565b6001600160a01b03811662000b1d57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f696e76616c696420616464726573730000000000000000000000000000000000604482015290519081900360640190fd5b50565b60008282018381101562000b9557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60008262000baf5750600062000b98565b8282028284828162000bbd57fe5b041462000b95576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018062002a626021913960400191505060405180910390fd5b600080821162000c8757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b600082848162000c9357fe5b04949350505050565b60ff811660009081526009602052604081205462000b989083906001600160e01b0362000cc516565b60ff8216600090815260066020908152604080832054600383529083205462000b9592620006db91908690620017ff62000cff821b17901c565b60008282111562000d7157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b611c3b8062000d876000396000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c80638d3ce69d1161010f578063b2cec8ea116100a2578063cbecb28811610071578063cbecb2881461050a578063f049f33414610512578063f2fde38b14610532578063fc0c546a14610558576101e5565b8063b2cec8ea146104a2578063b99ef521146104c2578063bac883ef146104e2578063bd4b2be914610502576101e5565b8063983c44d6116100de578063983c44d6146103b75780639972444f146103bf578063a3df582a146103df578063a4c0ed36146103e7576101e5565b80638d3ce69d146103635780638d3df3461461036b5780638da5cb5b1461038b5780638f32d59b146103af576101e5565b8063392e53cd11610187578063622c5e4511610156578063622c5e451461032b578063715018a6146103335780637af7c0401461033b5780638129fc1c1461035b576101e5565b8063392e53cd146102b757806359791d6d146102bf5780635f26622f146102df578063604f21771461030b576101e5565b80630f10e06f116101c35780630f10e06f146102695780632755731e14610289578063375a4cab14610291578063381a4113146102af576101e5565b8063014a969a146101ea578063047fc9aa1461021e57806304a3922014610238575b600080fd5b61020a6004803603602081101561020057600080fd5b503560ff16610560565b604080519115158252519081900360200190f35b610226610575565b60408051918252519081900360200190f35b6102676004803603604081101561024e57600080fd5b50803560ff1690602001356001600160a01b0316610584565b005b6102266004803603602081101561027f57600080fd5b503560ff166106d0565b6102266106e2565b6102996106e8565b6040805160ff9092168252519081900360200190f35b61020a6106ed565b61020a6106f6565b610226600480360360208110156102d557600080fd5b503560ff16610704565b610267600480360360408110156102f557600080fd5b506001600160a01b038135169060200135610716565b6102266004803603602081101561032157600080fd5b503560ff16610fc4565b610299610fd6565b610267610fdb565b61020a6004803603602081101561035157600080fd5b503560ff16611073565b610267611088565b6102996111de565b6102266004803603602081101561038157600080fd5b503560ff166111e3565b6103936111f5565b604080516001600160a01b039092168252519081900360200190f35b61020a611204565b610299611215565b610393600480360360208110156103d557600080fd5b503560ff1661121a565b610226611235565b61020a600480360360608110156103fd57600080fd5b6001600160a01b038235169160208101359181019060608101604082013564010000000081111561042d57600080fd5b82018360208201111561043f57600080fd5b8035906020019184600183028401116401000000008311171561046157600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955061123b945050505050565b610226600480360360208110156104b857600080fd5b503560ff16611274565b610226600480360360208110156104d857600080fd5b503560ff16611286565b610267600480360360208110156104f857600080fd5b503560ff16611298565b610299611652565b610299611657565b6102266004803603602081101561052857600080fd5b503560ff1661165c565b6102676004803603602081101561054857600080fd5b50356001600160a01b031661166e565b6103936116d3565b600b6020526000908152604090205460ff1681565b6a070fe2cd68c25ff4f0000081565b60ff821660011480610599575060ff82166005145b6105d7576040805162461bcd60e51b815260206004820152600a6024820152691ddc9bdb99c81c1bdbdb60b21b604482015290519081900360640190fd5b60ff82166000908152600260205260409020546001600160a01b03163314610637576040805162461bcd60e51b815260206004820152600e60248201526d1b9bdd08185d5d1a1bdc9a5e995960921b604482015290519081900360640190fd5b610640816116e2565b60ff82166000818152600260209081526040918290205482516001600160a01b0391821681529085169181019190915281517f09a67390cbf7986a6bb0fcb44307b4844ba258bca3f38599d2b344824df4ba1d929181900390910190a260ff91909116600090815260026020526040902080546001600160a01b0319166001600160a01b03909216919091179055565b60056020526000908152604090205481565b600d5481565b600581565b600e5460ff1681565b600e54610100900460ff1681565b60086020526000908152604090205481565b61071e611204565b61076f576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b600e5460ff16156107c7576040805162461bcd60e51b815260206004820152601760248201527f616c7265616479207072652d696e697469616c697a6564000000000000000000604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b038481169190911791829055604080516370a0823160e01b81523060048201529051600093909216916370a0823191602480820192602092909190829003018186803b15801561082b57600080fd5b505afa15801561083f573d6000803e3d6000fd5b505050506040513d602081101561085557600080fd5b505190506a070fe2cd68c25ff4f0000081146108b1576040805162461bcd60e51b815260206004820152601660248201527577726f6e6720636f6e74726163742062616c616e636560501b604482015290519081900360640190fd5b6108b961172f565b600d55600e805460ff191660011790556003600090815260026020527f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c3546040805163189acdbd60e31b81526001600160a01b0387811660048301529151919092169263c4d66de8926024808201939182900301818387803b15801561093e57600080fd5b505af1158015610952573d6000803e3d6000fd5b50506004600081815260026020527fee60d0579bcffd98e668647d59fec1ff86a7fb340ce572e844f234ae73a6918f546040805163189acdbd60e31b81526001600160a01b038a811695820195909552905193909116945063c4d66de893506024808201939182900301818387803b1580156109cd57600080fd5b505af11580156109e1573d6000803e3d6000fd5b5050600360008181526020919091527fcbc4e5fb02c3d1de23a9f1e014b4d2ee5aeaea9505df5e855c9210bf472495af54909250610a3a9150606490610a2e90601963ffffffff61173316565b9063ffffffff61179516565b600154600260009081527f679795a0195a1b76cdebb7c51d74e058aee92919b8c3389af86ef24535e8a28c54600360209081527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d546040805163238a3fe160e01b81526001600160a01b039485166004820152602481019290925251959650919093169363238a3fe19360448084019491938390030190829087803b158015610ae257600080fd5b505af1158015610af6573d6000803e3d6000fd5b505050506040513d6020811015610b0c57600080fd5b505060015460036000908152600260209081527f88601476d11616a71c5be67555bd1dff4b1cbf21533d2669b768b61518cfe1c3546040805163a9059cbb60e01b81526001600160a01b039283166004820152602481018790529051919094169363a9059cbb9360448083019493928390030190829087803b158015610b9157600080fd5b505af1158015610ba5573d6000803e3d6000fd5b505050506040513d6020811015610bbb57600080fd5b50506006600090815260036020527fc69056f16cbaa3c616b828e333ab7d3a32310765507f8f58359e99ebb7a885f354610bfb908563ffffffff6117ff16565b60015460066000908152600260209081527f59dd4b18488d12f51eda69757a0ed42a2010c14b564330cc74a06895e60c077b546040805163238a3fe160e01b81526001600160a01b03928316600482015260248101879052905195965093169363238a3fe193604480820194918390030190829087803b158015610c7e57600080fd5b505af1158015610c92573d6000803e3d6000fd5b505050506040513d6020811015610ca857600080fd5b50508315610d35576001546040805163238a3fe160e01b81526000600482018190526024820188905291516001600160a01b039093169263238a3fe192604480840193602093929083900390910190829087803b158015610d0857600080fd5b505af1158015610d1c573d6000803e3d6000fd5b505050506040513d6020811015610d3257600080fd5b50505b60026000527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d5460046020527f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a754610d929163ffffffff6117ff16565b60046020527f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a75560036000527f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa054610df0908363ffffffff6117ff16565b7f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa05560066000527fc69056f16cbaa3c616b828e333ab7d3a32310765507f8f58359e99ebb7a885f35460046020527fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f54610e6f9163ffffffff6117ff16565b6006600052600460209081527fc59312466997bb42aaaf719ece141047820e6b34531e1670dc1852a453648f0f91909155604080516001600160a01b0388168152339281019290925280517f20e0b9d27e138a83ff1b3f687932144f5e913aa93855ac36c2611ec1dfae704e9281900390910190a160026000819052600360209081527fc3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4d546040805191825233928201929092528151600080516020611b49833981519152929181900390910190a2604080518381523360208201528151600392600080516020611b49833981519152928290030190a2604080518281523360208201528151600692600080516020611b49833981519152928290030190a28315610fbd57604080518581523360208201528151600092600080516020611b49833981519152928290030190a25b5050505050565b60036020526000908152604090205481565b600481565b610fe3611204565b611034576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b5c1b195b595b9d1959608a1b604482015290519081900360640190fd5b600a6020526000908152604090205460ff1681565b600e5460ff166110d5576040805162461bcd60e51b81526020600482015260136024820152721b9bdd081c1c994b5a5b9a5d1a585b1a5e9959606a1b604482015290519081900360640190fd5b600e54610100900460ff1615611128576040805162461bcd60e51b8152602060048201526013602482015272185b1c9958591e481a5b9a5d1a585b1a5e9959606a1b604482015290519081900360640190fd5b6276a700611146600d5461113a61172f565b9063ffffffff6117ff16565b101561118f57611154611204565b61118f5760405162461bcd60e51b8152600401808060200182810382526027815260200180611b226027913960400191505060405180910390fd5b61119761172f565b600c55600e805461ff0019166101001790556040805133815290517f908408e307fc569b417f6cbec5d5a06f44a0a505ac0479b47d421a4b2fd6a1e69181900360200190a1565b600381565b60066020526000908152604090205481565b6000546001600160a01b031690565b6000546001600160a01b0316331490565b600181565b6002602052600090815260409020546001600160a01b031681565b600c5481565b600060405162461bcd60e51b815260040180806020018281038252602e815260200180611bb0602e913960400191505060405180910390fd5b60096020526000908152604090205481565b60046020526000908152604090205481565b600e54610100900460ff166112e6576040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b9a5d1a585b1a5e9959608a1b604482015290519081900360640190fd5b60ff8116600090815260056020526040902054600c54829161130e919063ffffffff61185c16565b61131661172f565b10158015611336575060ff8082166000908152600b602052604090205416155b6113715760405162461bcd60e51b8152600401808060200182810382526029815260200180611bde6029913960400191505060405180910390fd5b60ff821660031480611386575060ff82166004145b80611394575060ff82166001145b806113a2575060ff82166005145b6113e0576040805162461bcd60e51b815260206004820152600a6024820152691ddc9bdb99c81c1bdbdb60b21b604482015290519081900360640190fd5b60ff8083166000908152600a6020526040812054909116611426575060ff8216600090815260096020908152604080832054600a909252909120805460ff191660011790555b6000611431846118b6565b60ff85166000908152600860205260409020549091506114689061145b908363ffffffff61173316565b839063ffffffff61185c16565b9150600082116114bf576040805162461bcd60e51b815260206004820152601960248201527f6e6f20696e7374616c6c6d656e747320617661696c61626c6500000000000000604482015290519081900360640190fd5b60006114cc8584846119a0565b90506114de838263ffffffff61185c16565b925060ff8516600314806114f5575060ff85166004145b156115905760015460ff8616600090815260026020908152604080832054815163a9059cbb60e01b81526001600160a01b03918216600482015260248101899052915194169363a9059cbb93604480840194938390030190829087803b15801561155e57600080fd5b505af1158015611572573d6000803e3d6000fd5b505050506040513d602081101561158857600080fd5b506116219050565b60015460ff8616600090815260026020908152604080832054815163238a3fe160e01b81526001600160a01b03918216600482015260248101899052915194169363238a3fe193604480840194938390030190829087803b1580156115f457600080fd5b505af1158015611608573d6000803e3d6000fd5b505050506040513d602081101561161e57600080fd5b50505b60408051848152336020820152815160ff881692600080516020611b49833981519152928290030190a25050505050565b600681565b600281565b60076020526000908152604090205481565b611676611204565b6116c7576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6116d081611a5d565b50565b6001546001600160a01b031681565b6001600160a01b0381166116d0576040805162461bcd60e51b815260206004820152600f60248201526e696e76616c6964206164647265737360881b604482015290519081900360640190fd5b4290565b6000826117425750600061178f565b8282028284828161174f57fe5b041461178c5760405162461bcd60e51b8152600401808060200182810382526021815260200180611b8f6021913960400191505060405180910390fd5b90505b92915050565b60008082116117eb576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b60008284816117f657fe5b04949350505050565b600082821115611856576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60008282018381101561178c576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b60ff811660009081526007602052604081205481906118de906201518063ffffffff61173316565b60ff8416600090815260056020526040812054600c54929350909161191a91849161190e9163ffffffff61185c16565b9063ffffffff61185c16565b905061192f62015180610a2e8361113a61172f565b60ff851660009081526006602090815260408083205460079092529091205491945090611962908563ffffffff61185c16565b11156119995760ff84166000908152600760209081526040808320546006909252909120546119969163ffffffff6117ff16565b92505b5050919050565b60ff831660009081526004602052604081205481906119c5908563ffffffff6117ff16565b60ff86166000908152600460209081526040808320939093556007905220546119f4908463ffffffff61185c16565b60ff861660009081526007602081815260408084208590556006825290922054915211611a555760ff851660009081526004602052604090205415611a4c575060ff8416600090815260046020526040812080549190555b611a5585611afd565b949350505050565b6001600160a01b038116611aa25760405162461bcd60e51b8152600401808060200182810382526026815260200180611b696026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b60ff166000908152600b60205260409020805460ff19166001179055565b3b15159056fe666f72206e6f77206f6e6c79206f776e65722063616e2063616c6c2074686973206d6574686f64a8b65b82b2ff2a955e75c1bfa6a0e92aafb764156295da77a0a4c714f3895c724f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7773656e64696e6720746f6b656e7320746f207468697320636f6e7472616374206973206e6f7420616c6c6f776564696e7374616c6c6d656e747320617265206e6f742061637469766520666f72207468697320706f6f6ca265627a7a723058208fc7fe0d6f40178495c4e655627ac8a6bc837eb89686042a07161f078a2ea04464736f6c634300050a0032c3a24b0501bd2c13a7e57f2db4369ec4c223447539fc0724a9d55ac4a06ebd4da15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054cc69056f16cbaa3c616b828e333ab7d3a32310765507f8f58359e99ebb7a885f383ec6a1f0257b830b5e016457c9cf1435391bf56cc98f369a58a54fe93772465ee60d0579bcffd98e668647d59fec1ff86a7fb340ce572e844f234ae73a6918f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77405aad32e1adbac89bb7f176e338b8fc6e994ca210c9bb7bdca249b465942250cbc4e5fb02c3d1de23a9f1e014b4d2ee5aeaea9505df5e855c9210bf472495af00000000000000000000000086edd0c110d1fc7f8a5e1108623b3b1b4e3740f90000000000000000000000000df05adac0159e215111696339ad4998e5871b3d0000000000000000000000003cfe51b61e25750ab1426b0072e5d0cc5c30aafa0000000000000000000000000218b706898d234b85d2494df21eb0677eaea91800000000000000000000000086edd0c110d1fc7f8a5e1108623b3b1b4e3740f90000000000000000000000000df05adac0159e215111696339ad4998e5871b3d" - - contract_address = insert(:contract_address, contract_code: bytecode) - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: contract_creation_code - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => contract_source_code, - "compiler_version" => "v0.5.10+commit.5a6ea5b1", - "evm_version" => "default", - "name" => "Distribution", - "optimization" => true, - "optimization_runs" => 200, - "autodetect_constructor_args" => true - } - - assert {:ok, - %{ - abi: abi, - constructor_arguments: - "00000000000000000000000086edd0c110d1fc7f8a5e1108623b3b1b4e3740f90000000000000000000000000df05adac0159e215111696339ad4998e5871b3d0000000000000000000000003cfe51b61e25750ab1426b0072e5d0cc5c30aafa0000000000000000000000000218b706898d234b85d2494df21eb0677eaea91800000000000000000000000086edd0c110d1fc7f8a5e1108623b3b1b4e3740f90000000000000000000000000df05adac0159e215111696339ad4998e5871b3d" - }} = Verifier.evaluate_authenticity(contract_address.hash, params) - - assert abi != nil - end + contract_creation_code = + "0x608060405260405162000f4038038062000f408339810160408190526200002691620004d4565b82816200005560017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd62000603565b60008051602062000ef9833981519152146200008157634e487b7160e01b600052600160045260246000fd5b6200008f82826000620000ff565b50620000bf905060017fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610462000603565b60008051602062000ed983398151915214620000eb57634e487b7160e01b600052600160045260246000fd5b620000f68262000170565b5050506200066c565b6200010a83620001cb565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806200014c5750805b156200016b576200016983836200029360201b6200026c1760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6200019b620002c2565b604080516001600160a01b03928316815291841660208301520160405180910390a1620001c881620002fb565b50565b620001e1816200038b60201b620002981760201c565b620002495760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b806200027260008051602062000ef983398151915260001b6200039560201b620002141760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6060620002bb838360405180606001604052806027815260200162000f196027913962000398565b9392505050565b6000620002ec60008051602062000ed983398151915260001b6200039560201b620002141760201c565b546001600160a01b0316905090565b6001600160a01b038116620003625760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840162000240565b806200027260008051602062000ed983398151915260001b6200039560201b620002141760201c565b803b15155b919050565b90565b6060620003a5846200038b565b620004025760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b606482015260840162000240565b600080856001600160a01b0316856040516200041f9190620005b0565b600060405180830381855af49150503d80600081146200045c576040519150601f19603f3d011682016040523d82523d6000602084013e62000461565b606091505b509092509050620004748282866200047e565b9695505050505050565b606083156200048f575081620002bb565b825115620004a05782518084602001fd5b8160405162461bcd60e51b8152600401620002409190620005ce565b80516001600160a01b03811681146200039057600080fd5b600080600060608486031215620004e9578283fd5b620004f484620004bc565b92506200050460208501620004bc565b60408501519092506001600160401b038082111562000521578283fd5b818601915086601f83011262000535578283fd5b8151818111156200054a576200054a62000656565b604051601f8201601f19908116603f0116810190838211818310171562000575576200057562000656565b816040528281528960208487010111156200058e578586fd5b620005a183602083016020880162000627565b80955050505050509250925092565b60008251620005c481846020870162000627565b9190910192915050565b6000602082528251806020840152620005ef81604085016020870162000627565b601f01601f19169190910160400192915050565b6000828210156200062257634e487b7160e01b81526011600452602481fd5b500390565b60005b83811015620006445781810151838201526020016200062a565b83811115620001695750506000910152565b634e487b7160e01b600052604160045260246000fd5b61085d806200067c6000396000f3fe60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106ed565b610118565b61005b610093366004610707565b610164565b3480156100a457600080fd5b506100ad6101da565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106ed565b610217565b3480156100f557600080fd5b506100ad610241565b6101066102a2565b610116610111610346565b610355565b565b610120610379565b6001600160a01b0316336001600160a01b0316141561015957610154816040518060200160405280600081525060006103ac565b610161565b6101616100fe565b50565b61016c610379565b6001600160a01b0316336001600160a01b031614156101cd576101c88383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506103ac915050565b6101d5565b6101d56100fe565b505050565b60006101e4610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610346565b9050610214565b6102146100fe565b90565b61021f610379565b6001600160a01b0316336001600160a01b03161415610159576101548161040b565b600061024b610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610379565b606061029183836040518060600160405280602781526020016108016027913961045f565b9392505050565b803b15155b919050565b6102aa610379565b6001600160a01b0316336001600160a01b031614156103415760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b610116565b600061035061053a565b905090565b3660008037600080366000845af43d6000803e808015610374573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316905090565b6103b583610562565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806103f65750805b156101d557610405838361026c565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610434610379565b604080516001600160a01b03928316815291841660208301520160405180910390a161016181610611565b606061046a84610298565b6104c55760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610338565b600080856001600160a01b0316856040516104e09190610785565b600060405180830381855af49150503d806000811461051b576040519150601f19603f3d011682016040523d82523d6000602084013e610520565b606091505b509150915061053082828661069d565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61039d565b61056b81610298565b6105cd5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610338565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b6001600160a01b0381166106765760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b6064820152608401610338565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036105f0565b606083156106ac575081610291565b8251156106bc5782518084602001fd5b8160405162461bcd60e51b815260040161033891906107a1565b80356001600160a01b038116811461029d57600080fd5b6000602082840312156106fe578081fd5b610291826106d6565b60008060006040848603121561071b578182fd5b610724846106d6565b9250602084013567ffffffffffffffff80821115610740578384fd5b818601915086601f830112610753578384fd5b813581811115610761578485fd5b876020828501011115610772578485fd5b6020830194508093505050509250925092565b600082516107978184602087016107d4565b9190910192915050565b60006020825282518060208401526107c08160408501602087016107d4565b601f01601f19169190910160400192915050565b60005b838110156107ef5781810151838201526020016107d7565b83811115610405575050600091015256fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122016ea36e15be10f9560025e0ec9401e2e9110cb5ec41d110b4a0e391838c1f19b64736f6c63430008020033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65640000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f051200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - test "another failed constructor args verification" do - contract_source_code = - "#{File.cwd!()}/test/support/fixture/smart_contract/issue_with_constructor_args.sol" - |> File.read!() - - bytecode = - "0x60806040523661001357610011610017565b005b6100115b61002761002261005e565b610096565b565b606061004e838360405180606001604052806027815260200161024c602791396100ba565b9392505050565b3b151590565b90565b60006100917f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b3660008037600080366000845af43d6000803e8080156100b5573d6000f35b3d6000fd5b6060833b61011e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b60648201526084015b60405180910390fd5b600080856001600160a01b03168560405161013991906101cc565b600060405180830381855af49150503d8060008114610174576040519150601f19603f3d011682016040523d82523d6000602084013e610179565b606091505b5091509150610189828286610193565b9695505050505050565b606083156101a257508161004e565b8251156101b25782518084602001fd5b8160405162461bcd60e51b815260040161011591906101e8565b600082516101de81846020870161021b565b9190910192915050565b600060208252825180602084015261020781604085016020870161021b565b601f01601f19169190910160400192915050565b60005b8381101561023657818101518382015260200161021e565b83811115610245576000848401525b5050505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212209b8470f06e8a3960c912103fc2be177edaad69584ee3c7d2809ee737e79408e764736f6c63430008020033" - - contract_creation_code = - "0x6080604052604051610772380380610772833981016040819052610022916102f7565b61004d60017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd61040f565b60008051602061072b8339815191521461007757634e487b7160e01b600052600160045260246000fd5b6100838282600061008a565b5050610474565b610093836100f4565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806100d45750805b156100ef576100ed83836101b460201b6100291760201c565b505b505050565b610107816101e060201b6100551760201c565b61016e5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b8061019360008051602061072b83398151915260001b6101e660201b61005b1760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b60606101d9838360405180606001604052806027815260200161074b602791396101e9565b9392505050565b3b151590565b90565b6060833b6102485760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610165565b600080856001600160a01b03168560405161026391906103c0565b600060405180830381855af49150503d806000811461029e576040519150601f19603f3d011682016040523d82523d6000602084013e6102a3565b606091505b5090925090506102b48282866102be565b9695505050505050565b606083156102cd5750816101d9565b8251156102dd5782518084602001fd5b8160405162461bcd60e51b815260040161016591906103dc565b60008060408385031215610309578182fd5b82516001600160a01b038116811461031f578283fd5b60208401519092506001600160401b038082111561033b578283fd5b818501915085601f83011261034e578283fd5b8151818111156103605761036061045e565b604051601f8201601f19908116603f011681019083821181831017156103885761038861045e565b816040528281528860208487010111156103a0578586fd5b6103b1836020830160208801610432565b80955050505050509250929050565b600082516103d2818460208701610432565b9190910192915050565b60006020825282518060208401526103fb816040850160208701610432565b601f01601f19169190910160400192915050565b60008282101561042d57634e487b7160e01b81526011600452602481fd5b500390565b60005b8381101561044d578181015183820152602001610435565b838111156100ed5750506000910152565b634e487b7160e01b600052604160045260246000fd5b6102a8806104836000396000f3fe60806040523661001357610011610017565b005b6100115b61002761002261005e565b610096565b565b606061004e838360405180606001604052806027815260200161024c602791396100ba565b9392505050565b3b151590565b90565b60006100917f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b3660008037600080366000845af43d6000803e8080156100b5573d6000f35b3d6000fd5b6060833b61011e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b60648201526084015b60405180910390fd5b600080856001600160a01b03168560405161013991906101cc565b600060405180830381855af49150503d8060008114610174576040519150601f19603f3d011682016040523d82523d6000602084013e610179565b606091505b5091509150610189828286610193565b9695505050505050565b606083156101a257508161004e565b8251156101b25782518084602001fd5b8160405162461bcd60e51b815260040161011591906101e8565b600082516101de81846020870161021b565b9190910192915050565b600060208252825180602084015261020781604085016020870161021b565b601f01601f19169190910160400192915050565b60005b8381101561023657818101518382015260200161021e565b83811115610245576000848401525b5050505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212209b8470f06e8a3960c912103fc2be177edaad69584ee3c7d2809ee737e79408e764736f6c63430008020033360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564000000000000000000000000569e7b559a3af9b2350b8db2afd8977b8bd0517200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000024c4d66de8000000000000000000000000217373ab5e0082b2ce622169672eca6f4462319c00000000000000000000000000000000000000000000000000000000" - - contract_address = insert(:contract_address, contract_code: bytecode) - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: contract_creation_code - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => contract_source_code, - "compiler_version" => "v0.8.2+commit.661d1103", - "evm_version" => "default", - "name" => "ERC1967Proxy", - "optimization" => true, - "optimization_runs" => 200, - "autodetect_constructor_args" => true - } - - assert {:ok, - %{ - abi: abi, - constructor_arguments: - "000000000000000000000000569e7b559a3af9b2350b8db2afd8977b8bd0517200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000024c4d66de8000000000000000000000000217373ab5e0082b2ce622169672eca6f4462319c00000000000000000000000000000000000000000000000000000000" - }} = Verifier.evaluate_authenticity(contract_address.hash, params) - - assert abi != nil - end - end + contract_address = insert(:contract_address, contract_code: bytecode) - describe "another regression tests" do - test "accepting correct constructor args" do - contract_source_code = - "#{File.cwd!()}/test/support/fixture/smart_contract/issue_5114.sol" - |> File.read!() - - bytecode = - "0x60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106ed565b610118565b61005b610093366004610707565b610164565b3480156100a457600080fd5b506100ad6101da565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106ed565b610217565b3480156100f557600080fd5b506100ad610241565b6101066102a2565b610116610111610346565b610355565b565b610120610379565b6001600160a01b0316336001600160a01b0316141561015957610154816040518060200160405280600081525060006103ac565b610161565b6101616100fe565b50565b61016c610379565b6001600160a01b0316336001600160a01b031614156101cd576101c88383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506103ac915050565b6101d5565b6101d56100fe565b505050565b60006101e4610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610346565b9050610214565b6102146100fe565b90565b61021f610379565b6001600160a01b0316336001600160a01b03161415610159576101548161040b565b600061024b610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610379565b606061029183836040518060600160405280602781526020016108016027913961045f565b9392505050565b803b15155b919050565b6102aa610379565b6001600160a01b0316336001600160a01b031614156103415760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b610116565b600061035061053a565b905090565b3660008037600080366000845af43d6000803e808015610374573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316905090565b6103b583610562565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806103f65750805b156101d557610405838361026c565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610434610379565b604080516001600160a01b03928316815291841660208301520160405180910390a161016181610611565b606061046a84610298565b6104c55760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610338565b600080856001600160a01b0316856040516104e09190610785565b600060405180830381855af49150503d806000811461051b576040519150601f19603f3d011682016040523d82523d6000602084013e610520565b606091505b509150915061053082828661069d565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61039d565b61056b81610298565b6105cd5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610338565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b6001600160a01b0381166106765760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b6064820152608401610338565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036105f0565b606083156106ac575081610291565b8251156106bc5782518084602001fd5b8160405162461bcd60e51b815260040161033891906107a1565b80356001600160a01b038116811461029d57600080fd5b6000602082840312156106fe578081fd5b610291826106d6565b60008060006040848603121561071b578182fd5b610724846106d6565b9250602084013567ffffffffffffffff80821115610740578384fd5b818601915086601f830112610753578384fd5b813581811115610761578485fd5b876020828501011115610772578485fd5b6020830194508093505050509250925092565b600082516107978184602087016107d4565b9190910192915050565b60006020825282518060208401526107c08160408501602087016107d4565b601f01601f19169190910160400192915050565b60005b838110156107ef5781810151838201526020016107d7565b83811115610405575050600091015256fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122016ea36e15be10f9560025e0ec9401e2e9110cb5ec41d110b4a0e391838c1f19b64736f6c63430008020033" - - contract_creation_code = - "0x608060405260405162000f4038038062000f408339810160408190526200002691620004d4565b82816200005560017f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbd62000603565b60008051602062000ef9833981519152146200008157634e487b7160e01b600052600160045260246000fd5b6200008f82826000620000ff565b50620000bf905060017fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610462000603565b60008051602062000ed983398151915214620000eb57634e487b7160e01b600052600160045260246000fd5b620000f68262000170565b5050506200066c565b6200010a83620001cb565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806200014c5750805b156200016b576200016983836200029360201b6200026c1760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6200019b620002c2565b604080516001600160a01b03928316815291841660208301520160405180910390a1620001c881620002fb565b50565b620001e1816200038b60201b620002981760201c565b620002495760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b806200027260008051602062000ef983398151915260001b6200039560201b620002141760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6060620002bb838360405180606001604052806027815260200162000f196027913962000398565b9392505050565b6000620002ec60008051602062000ed983398151915260001b6200039560201b620002141760201c565b546001600160a01b0316905090565b6001600160a01b038116620003625760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840162000240565b806200027260008051602062000ed983398151915260001b6200039560201b620002141760201c565b803b15155b919050565b90565b6060620003a5846200038b565b620004025760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b606482015260840162000240565b600080856001600160a01b0316856040516200041f9190620005b0565b600060405180830381855af49150503d80600081146200045c576040519150601f19603f3d011682016040523d82523d6000602084013e62000461565b606091505b509092509050620004748282866200047e565b9695505050505050565b606083156200048f575081620002bb565b825115620004a05782518084602001fd5b8160405162461bcd60e51b8152600401620002409190620005ce565b80516001600160a01b03811681146200039057600080fd5b600080600060608486031215620004e9578283fd5b620004f484620004bc565b92506200050460208501620004bc565b60408501519092506001600160401b038082111562000521578283fd5b818601915086601f83011262000535578283fd5b8151818111156200054a576200054a62000656565b604051601f8201601f19908116603f0116810190838211818310171562000575576200057562000656565b816040528281528960208487010111156200058e578586fd5b620005a183602083016020880162000627565b80955050505050509250925092565b60008251620005c481846020870162000627565b9190910192915050565b6000602082528251806020840152620005ef81604085016020870162000627565b601f01601f19169190910160400192915050565b6000828210156200062257634e487b7160e01b81526011600452602481fd5b500390565b60005b83811015620006445781810151838201526020016200062a565b83811115620001695750506000910152565b634e487b7160e01b600052604160045260246000fd5b61085d806200067c6000396000f3fe60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106ed565b610118565b61005b610093366004610707565b610164565b3480156100a457600080fd5b506100ad6101da565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106ed565b610217565b3480156100f557600080fd5b506100ad610241565b6101066102a2565b610116610111610346565b610355565b565b610120610379565b6001600160a01b0316336001600160a01b0316141561015957610154816040518060200160405280600081525060006103ac565b610161565b6101616100fe565b50565b61016c610379565b6001600160a01b0316336001600160a01b031614156101cd576101c88383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506103ac915050565b6101d5565b6101d56100fe565b505050565b60006101e4610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610346565b9050610214565b6102146100fe565b90565b61021f610379565b6001600160a01b0316336001600160a01b03161415610159576101548161040b565b600061024b610379565b6001600160a01b0316336001600160a01b0316141561020c57610205610379565b606061029183836040518060600160405280602781526020016108016027913961045f565b9392505050565b803b15155b919050565b6102aa610379565b6001600160a01b0316336001600160a01b031614156103415760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b610116565b600061035061053a565b905090565b3660008037600080366000845af43d6000803e808015610374573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316905090565b6103b583610562565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806103f65750805b156101d557610405838361026c565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610434610379565b604080516001600160a01b03928316815291841660208301520160405180910390a161016181610611565b606061046a84610298565b6104c55760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610338565b600080856001600160a01b0316856040516104e09190610785565b600060405180830381855af49150503d806000811461051b576040519150601f19603f3d011682016040523d82523d6000602084013e610520565b606091505b509150915061053082828661069d565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61039d565b61056b81610298565b6105cd5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610338565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b6001600160a01b0381166106765760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b6064820152608401610338565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036105f0565b606083156106ac575081610291565b8251156106bc5782518084602001fd5b8160405162461bcd60e51b815260040161033891906107a1565b80356001600160a01b038116811461029d57600080fd5b6000602082840312156106fe578081fd5b610291826106d6565b60008060006040848603121561071b578182fd5b610724846106d6565b9250602084013567ffffffffffffffff80821115610740578384fd5b818601915086601f830112610753578384fd5b813581811115610761578485fd5b876020828501011115610772578485fd5b6020830194508093505050509250925092565b600082516107978184602087016107d4565b9190910192915050565b60006020825282518060208401526107c08160408501602087016107d4565b601f01601f19169190910160400192915050565b60005b838110156107ef5781810151838201526020016107d7565b83811115610405575050600091015256fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122016ea36e15be10f9560025e0ec9401e2e9110cb5ec41d110b4a0e391838c1f19b64736f6c63430008020033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65640000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f051200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - - contract_address = insert(:contract_address, contract_code: bytecode) - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: contract_creation_code - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => contract_source_code, - "compiler_version" => "v0.8.2+commit.661d1103", - "evm_version" => "default", - "name" => "TransparentUpgradeableProxy", - "optimization" => true, - "optimization_runs" => 200, - "autodetect_constructor_args" => false, - "constructor_arguments" => - "0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f051200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - } - - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) - assert abi != nil - end + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: contract_creation_code + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => contract_source_code, + "compiler_version" => "v0.8.2+commit.661d1103", + "evm_version" => "default", + "name" => "TransparentUpgradeableProxy", + "optimization" => true, + "optimization_runs" => @optimization_runs, + "autodetect_constructor_args" => false, + "constructor_arguments" => + "0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f051200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + } + + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) + assert abi != nil + end + + test "issue 4758" do + contract_source_code = + "#{File.cwd!()}/test/support/fixture/smart_contract/issue_4758.sol" + |> File.read!() + + bytecode = + "0x6080604052600436106101135760003560e01c8063759bd1fe116100a0578063a97eaba711610064578063a97eaba7146102db578063b7ec2086146102f1578063c7802ba214610306578063dd48f77414610326578063e2fa9ee01461034657600080fd5b8063759bd1fe146102535780638e8fa11f1461026657806390ffb8641461028657806391b7f5ed146102a65780639b6dbc8a146102c657600080fd5b80634707d000116100e75780634707d000146101c357806347535d7b146101e55780634a1bb6b41461020a57806353cdef02146102205780636aa7c1ab1461024057600080fd5b8062c0f916146101185780632481bb5c146101555780632ae3dc741461017557806339c4576214610195575b600080fd5b34801561012457600080fd5b50610138610133366004611be4565b610366565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561016157600080fd5b50600054610138906001600160a01b031681565b34801561018157600080fd5b50610138610190366004611a62565b610386565b3480156101a157600080fd5b506101b56101b0366004611a62565b6103f6565b60405190815260200161014c565b3480156101cf57600080fd5b506101e36101de366004611aca565b610492565b005b3480156101f157600080fd5b506101fa61064d565b604051901515815260200161014c565b34801561021657600080fd5b506101b560045481565b34801561022c57600080fd5b506101e361023b366004611aae565b610667565b6101e361024e36600461197b565b6107e2565b6101e3610261366004611b02565b610ad5565b34801561027257600080fd5b506101e36102813660046119f3565b610b11565b34801561029257600080fd5b506101fa6102a1366004611a62565b610c95565b3480156102b257600080fd5b506101e36102c1366004611be4565b610ca9565b3480156102d257600080fd5b506101e3610df6565b3480156102e757600080fd5b506101b560055481565b3480156102fd57600080fd5b506101b5610ed4565b34801561031257600080fd5b506101e3610321366004611b63565b610ff8565b34801561033257600080fd5b506101e3610341366004611aca565b6113d1565b34801561035257600080fd5b506101e3610361366004611be4565b6114e5565b6006816004811061037657600080fd5b01546001600160a01b0316905081565b600060068260018111156103aa57634e487b7160e01b600052602160045260246000fd5b846103b65760006103b9565b60025b60ff166103c69190611e6e565b600481106103e457634e487b7160e01b600052603260045260246000fd5b01546001600160a01b03169392505050565b6001546000906001600160a01b03166370a082316104148585610386565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b15801561045357600080fd5b505afa158015610467573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061048b9190611a96565b9392505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac1906104c090600401611ce8565b60206040518083038186803b1580156104d857600080fd5b505afa1580156104ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610510919061195f565b6001600160a01b0316336001600160a01b0316146105495760405162461bcd60e51b815260040161054090611da8565b60405180910390fd5b6040516370a0823160e01b81523060048201526001600160a01b0383169063a9059cbb90839083906370a082319060240160206040518083038186803b15801561059257600080fd5b505afa1580156105a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ca9190611a96565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381600087803b15801561061057600080fd5b505af1158015610624573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106489190611a46565b505050565b60008060055411801561066257506005544210155b905090565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac19061069590600401611dfe565b60206040518083038186803b1580156106ad57600080fd5b505afa1580156106c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e5919061195f565b6001600160a01b0316336001600160a01b0316146107155760405162461bcd60e51b815260040161054090611e23565b6001600160a01b0381166107875760405162461bcd60e51b815260206004820152603360248201527f596f75206e65656420746f2070726f7669646520616e2061637475616c20627260448201527234b233b2903230ba309031b7b73a3930b1ba1760691b6064820152608401610540565b600080546040516001600160a01b03808516939216917f6a6e057f21cc834cf349d8150e92867f52cb34d54375f174c09c431538c3dfb991a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6107ea61064d565b151560011461080b5760405162461bcd60e51b815260040161054090611d5d565b826108585760405162461bcd60e51b815260206004820152601d60248201527f596f75206e65656420746f2061636365707420746865207465726d732e0000006044820152606401610540565b806108755760405162461bcd60e51b815260040161054090611d18565b6000806108828287610386565b6001546040516370a0823160e01b81526001600160a01b038084166004830152929350600092909116906370a082319060240160206040518083038186803b1580156108cd57600080fd5b505afa1580156108e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109059190611a96565b9050600081116109575760405162461bcd60e51b815260206004820181905260248201527f5468652072657175657374656420617373657420697320736f6c64206f75742e6044820152606401610540565b6000610961610ed4565b9050803410156109ca5760405162461bcd60e51b815260206004820152602e60248201527f53656e6420656e6f7567682063757272656e637920746f20706179206174206c60448201526d32b0b9ba1037b7329034ba32b69760911b6064820152608401610540565b60006109d68234611e86565b9050828111156109e35750815b60006109ef8383611ea6565b9050610a008b8b84898989896115ce565b60005460405163bf40fac160e01b815260206004820152600b60248201526a62656e656669636961727960a81b6044820152610aac9183916001600160a01b039091169063bf40fac19060640160206040518083038186803b158015610a6557600080fd5b505afa158015610a79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a9d919061195f565b6001600160a01b0316906117eb565b80341115610ac857610ac8610ac18234611ec5565b33906117eb565b5050505050505050505050565b610add61064d565b1515600114610afe5760405162461bcd60e51b815260040161054090611d5d565b610b0b33858585856107e2565b50505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac190610b3f90600401611ce8565b60206040518083038186803b158015610b5757600080fd5b505afa158015610b6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b8f919061195f565b6001600160a01b0316336001600160a01b031614610bbf5760405162461bcd60e51b815260040161054090611da8565b6001600160a01b038316610c155760405162461bcd60e51b815260206004820152601e60248201527f6e65656420612076616c696420726576657273652072656769737472617200006044820152606401610540565b60405163c47f002760e01b81526001600160a01b0384169063c47f002790610c439085908590600401611cb9565b602060405180830381600087803b158015610c5d57600080fd5b505af1158015610c71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0b9190611a96565b6000610ca183836103f6565b159392505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac190610cd790600401611dfe565b60206040518083038186803b158015610cef57600080fd5b505afa158015610d03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d27919061195f565b6001600160a01b0316336001600160a01b031614610d575760405162461bcd60e51b815260040161054090611e23565b60008111610db55760405162461bcd60e51b815260206004820152602560248201527f596f75206e65656420746f2070726f766964652061206e6f6e2d7a65726f20706044820152643934b1b29760d91b6064820152608401610540565b60045460408051918252602082018390527f8aa4fa52648a6d15edce8a179c792c86f3719d0cc3c572cf90f91948f0f2cb68910160405180910390a1600455565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac190610e2490600401611dfe565b60206040518083038186803b158015610e3c57600080fd5b505afa158015610e50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e74919061195f565b6001600160a01b0316336001600160a01b031614610ea45760405162461bcd60e51b815260040161054090611e23565b600060058190556040517f589bfff7e7b59e33b97fa923dbc99256a6d2fdf9a631b431fa2dc06f4eea0ded9190a1565b6000805460405163bf40fac160e01b81526020600482015260066024820152654f7261636c6560d01b60448201526064916001600160a01b03169063bf40fac190830160206040518083038186803b158015610f2f57600080fd5b505afa158015610f43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f67919061195f565b60405163048cc1a760e51b8152600060048201526001600160a01b03919091169063919834e09060240160206040518083038186803b158015610fa957600080fd5b505afa158015610fbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe19190611a96565b600454610fee9190611ea6565b6106629190611e86565b61100061064d565b15156001146110215760405162461bcd60e51b815260040161054090611d5d565b8261106e5760405162461bcd60e51b815260206004820152601d60248201527f596f75206e65656420746f2061636365707420746865207465726d732e0000006044820152606401610540565b8061108b5760405162461bcd60e51b815260040161054090611d18565b6001600087828111156110ae57634e487b7160e01b600052602160045260246000fd5b905060006110bc838a610386565b6001546040516370a0823160e01b81526001600160a01b038084166004830152929350600092909116906370a082319060240160206040518083038186803b15801561110757600080fd5b505afa15801561111b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113f9190611a96565b9050808911156111a95760405162461bcd60e51b815260206004820152602f60248201527f4e6f7420656e6f7567682061737365747320617661696c61626c6520746f206260448201526e3abc903a3430ba1030b6b7bab73a1760891b6064820152608401610540565b600254604051627eeac760e11b8152336004820152602481018590528a916001600160a01b03169062fdd58e9060440160206040518083038186803b1580156111f157600080fd5b505afa158015611205573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112299190611a96565b10156112ad5760405162461bcd60e51b815260206004820152604760248201527f596f75206e65656420746f206f776e20656e6f7567682070726573616c65207660448201527f6f75636865727320746f2072656465656d20746865207370656369666965642060648201526630b6b7bab73a1760c91b608482015260a401610540565b604080516001808252818301909252600091602080830190803683375050604080516001808252818301909252929350600092915060208083019080368337019050509050848260008151811061131457634e487b7160e01b600052603260045260246000fd5b6020026020010181815250508a8160008151811061134257634e487b7160e01b600052603260045260246000fd5b6020908102919091010152600254604051637507c42960e11b81526001600160a01b039091169063ea0f88529061138190339086908690600401611c36565b600060405180830381600087803b15801561139b57600080fd5b505af11580156113af573d6000803e3d6000fd5b505050506113c38a8d8d89888860006115ce565b505050505050505050505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac1906113ff90600401611ce8565b60206040518083038186803b15801561141757600080fd5b505afa15801561142b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144f919061195f565b6001600160a01b0316336001600160a01b03161461147f5760405162461bcd60e51b815260040161054090611da8565b60405163a22cb46560e01b81526001600160a01b0382811660048301526001602483015283169063a22cb46590604401600060405180830381600087803b1580156114c957600080fd5b505af11580156114dd573d6000803e3d6000fd5b505050505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac19061151390600401611dfe565b60206040518083038186803b15801561152b57600080fd5b505afa15801561153f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611563919061195f565b6001600160a01b0316336001600160a01b0316146115935760405162461bcd60e51b815260040161054090611e23565b60058190556040518181527f397a093f9e29b7327f453f9672278c54e4daed6b689ee9bb458ae76800452ad59060200160405180910390a150565b60005b858110156117e157600180546000916001600160a01b0390911690632f745c599087906115fe8689611ec5565b6116089190611ec5565b6040516001600160e01b031960e085901b1681526001600160a01b039092166004830152602482015260440160206040518083038186803b15801561164c57600080fd5b505afa158015611660573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116849190611a96565b600154604051632142170760e11b81526001600160a01b0388811660048301528c81166024830152604482018490529293509116906342842e0e90606401600060405180830381600087803b1580156116dc57600080fd5b505af11580156116f0573d6000803e3d6000fd5b505050508088600181111561171557634e487b7160e01b600052602160045260246000fd5b604080516001600160a01b038d168152891515602082015290810186905233907f1682a2a055cfda159fc278195b31d5f7ab61f5128c423fa0ea2bf511565a9b6d9060600160405180910390a46003546001805460405163ebaf492d60e01b81526001600160a01b039384169363ebaf492d9361179b9390911691869190600401611c76565b600060405180830381600087803b1580156117b557600080fd5b505af11580156117c9573d6000803e3d6000fd5b505050505080806117d990611edc565b9150506115d1565b5050505050505050565b8047101561183b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610540565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114611888576040519150601f19603f3d011682016040523d82523d6000602084013e61188d565b606091505b50509050806106485760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610540565b80356002811061191357600080fd5b919050565b60008083601f840112611929578182fd5b50813567ffffffffffffffff811115611940578182fd5b60208301915083602082850101111561195857600080fd5b9250929050565b600060208284031215611970578081fd5b815161048b81611f0d565b600080600080600060808688031215611992578081fd5b853561199d81611f0d565b94506119ab60208701611904565b935060408601356119bb81611f25565b9250606086013567ffffffffffffffff8111156119d6578182fd5b6119e288828901611918565b969995985093965092949392505050565b600080600060408486031215611a07578283fd5b8335611a1281611f0d565b9250602084013567ffffffffffffffff811115611a2d578283fd5b611a3986828701611918565b9497909650939450505050565b600060208284031215611a57578081fd5b815161048b81611f25565b60008060408385031215611a74578182fd5b8235611a7f81611f25565b9150611a8d60208401611904565b90509250929050565b600060208284031215611aa7578081fd5b5051919050565b600060208284031215611abf578081fd5b813561048b81611f0d565b60008060408385031215611adc578182fd5b8235611ae781611f0d565b91506020830135611af781611f0d565b809150509250929050565b60008060008060608587031215611b17578384fd5b611b2085611904565b93506020850135611b3081611f25565b9250604085013567ffffffffffffffff811115611b4b578283fd5b611b5787828801611918565b95989497509550505050565b60008060008060008060a08789031215611b7b578081fd5b611b8487611904565b9550602087013594506040870135611b9b81611f0d565b93506060870135611bab81611f25565b9250608087013567ffffffffffffffff811115611bc6578182fd5b611bd289828a01611918565b979a9699509497509295939492505050565b600060208284031215611bf5578081fd5b5035919050565b6000815180845260208085019450808401835b83811015611c2b57815187529582019590820190600101611c0f565b509495945050505050565b6001600160a01b0384168152606060208201819052600090611c5a90830185611bfc565b8281036040840152611c6c8185611bfc565b9695505050505050565b6001600160a01b0384168152602081018390526060810160048310611cab57634e487b7160e01b600052602160045260246000fd5b826040830152949350505050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b6020808252601690820152751d1bdad95b905cdcda59db9b595b9d10dbdb9d1c9bdb60521b604082015260600190565b60208082526025908201527f596f75206e65656420746f2073656e642074686520616363657074616e6365206040820152643a32bc3a1760d91b606082015260800190565b6020808252602b908201527f546869732063616c6c206f6e6c7920776f726b73207768656e2074686520736860408201526a37b81034b99037b832b71760a91b606082015260800190565b60208082526036908201527f746f6b656e41737369676e6d656e74436f6e74726f6c206b65792072657175696040820152753932b2103337b9103a3434b990333ab731ba34b7b71760511b606082015260800190565b6020808252600b908201526a1cda1bdc10dbdb9d1c9bdb60aa1b604082015260600190565b6020808252602b908201527f73686f70436f6e74726f6c206b657920726571756972656420666f722074686960408201526a3990333ab731ba34b7b71760a91b606082015260800190565b60008219821115611e8157611e81611ef7565b500190565b600082611ea157634e487b7160e01b81526012600452602481fd5b500490565b6000816000190483118215151615611ec057611ec0611ef7565b500290565b600082821015611ed757611ed7611ef7565b500390565b6000600019821415611ef057611ef0611ef7565b5060010190565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b0381168114611f2257600080fd5b50565b8015158114611f2257600080fdfea2646970667358221220f2e9ba9e74b25591261868f1d84c2beef56c6930ea91cecb35779eaf5d20ba0b64736f6c63430008040033" + + contract_creation_code = + "0x608060405260006005553480156200001657600080fd5b50604051620024e6380380620024e68339810160408190526200003991620003e5565b600080546001600160a01b0319166001600160a01b038816908117909155620000cf5760405162461bcd60e51b815260206004820152603360248201527f596f75206e65656420746f2070726f7669646520616e2061637475616c20627260448201527f69646765206461746120636f6e74726163742e0000000000000000000000000060648201526084015b60405180910390fd5b600380546001600160a01b0319166001600160a01b038716908117909155620001615760405162461bcd60e51b815260206004820152603860248201527f596f75206e65656420746f2070726f7669646520616e2061637475616c20736860448201527f697070696e67206d616e6167657220636f6e74726163742e00000000000000006064820152608401620000c6565b600180546001600160a01b0319166001600160a01b038616908117909155620001e25760405162461bcd60e51b81526020600482015260376024820152600080516020620024c683398151915260448201527f7970746f7374616d7020332e3120636f6e74726163742e0000000000000000006064820152608401620000c6565b600280546001600160a01b0319166001600160a01b038516908117909155620002635760405162461bcd60e51b815260206004820152603f6024820152600080516020620024c683398151915260448201527f7970746f7374616d7020332e312050726573616c6520636f6e74726163742e006064820152608401620000c6565b600482905581620002c55760405162461bcd60e51b815260206004820152602560248201527f596f75206e65656420746f2070726f766964652061206e6f6e2d7a65726f20706044820152643934b1b29760d91b6064820152608401620000c6565b805160049081146200032f5760405162461bcd60e51b815260206004820152602c60248201527f4e65656420636f727265637420616d6f756e74206f6620746f6b656e20706f6f60448201526b361030b2323932b9b9b2b99760a11b6064820152608401620000c6565b60005b81811015620003ba578281815181106200035c57634e487b7160e01b600052603260045260246000fd5b6020026020010151600682600481106200038657634e487b7160e01b600052603260045260246000fd5b0180546001600160a01b0319166001600160a01b039290921691909117905580620003b1816200050f565b91505062000332565b50505050505050506200054d565b80516001600160a01b0381168114620003e057600080fd5b919050565b60008060008060008060c08789031215620003fe578182fd5b6200040987620003c8565b955060206200041a818901620003c8565b95506200042a60408901620003c8565b94506200043a60608901620003c8565b608089015160a08a015191955093506001600160401b03808211156200045e578384fd5b818a0191508a601f83011262000472578384fd5b81518181111562000487576200048762000537565b8060051b604051601f19603f83011681018181108582111715620004af57620004af62000537565b604052828152858101935084860182860187018f1015620004ce578788fd5b8795505b83861015620004fb57620004e681620003c8565b855260019590950194938601938601620004d2565b508096505050505050509295509295509295565b60006000198214156200053057634e487b7160e01b81526011600452602481fd5b5060010190565b634e487b7160e01b600052604160045260246000fd5b611f69806200055d6000396000f3fe6080604052600436106101135760003560e01c8063759bd1fe116100a0578063a97eaba711610064578063a97eaba7146102db578063b7ec2086146102f1578063c7802ba214610306578063dd48f77414610326578063e2fa9ee01461034657600080fd5b8063759bd1fe146102535780638e8fa11f1461026657806390ffb8641461028657806391b7f5ed146102a65780639b6dbc8a146102c657600080fd5b80634707d000116100e75780634707d000146101c357806347535d7b146101e55780634a1bb6b41461020a57806353cdef02146102205780636aa7c1ab1461024057600080fd5b8062c0f916146101185780632481bb5c146101555780632ae3dc741461017557806339c4576214610195575b600080fd5b34801561012457600080fd5b50610138610133366004611be4565b610366565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561016157600080fd5b50600054610138906001600160a01b031681565b34801561018157600080fd5b50610138610190366004611a62565b610386565b3480156101a157600080fd5b506101b56101b0366004611a62565b6103f6565b60405190815260200161014c565b3480156101cf57600080fd5b506101e36101de366004611aca565b610492565b005b3480156101f157600080fd5b506101fa61064d565b604051901515815260200161014c565b34801561021657600080fd5b506101b560045481565b34801561022c57600080fd5b506101e361023b366004611aae565b610667565b6101e361024e36600461197b565b6107e2565b6101e3610261366004611b02565b610ad5565b34801561027257600080fd5b506101e36102813660046119f3565b610b11565b34801561029257600080fd5b506101fa6102a1366004611a62565b610c95565b3480156102b257600080fd5b506101e36102c1366004611be4565b610ca9565b3480156102d257600080fd5b506101e3610df6565b3480156102e757600080fd5b506101b560055481565b3480156102fd57600080fd5b506101b5610ed4565b34801561031257600080fd5b506101e3610321366004611b63565b610ff8565b34801561033257600080fd5b506101e3610341366004611aca565b6113d1565b34801561035257600080fd5b506101e3610361366004611be4565b6114e5565b6006816004811061037657600080fd5b01546001600160a01b0316905081565b600060068260018111156103aa57634e487b7160e01b600052602160045260246000fd5b846103b65760006103b9565b60025b60ff166103c69190611e6e565b600481106103e457634e487b7160e01b600052603260045260246000fd5b01546001600160a01b03169392505050565b6001546000906001600160a01b03166370a082316104148585610386565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b15801561045357600080fd5b505afa158015610467573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061048b9190611a96565b9392505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac1906104c090600401611ce8565b60206040518083038186803b1580156104d857600080fd5b505afa1580156104ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610510919061195f565b6001600160a01b0316336001600160a01b0316146105495760405162461bcd60e51b815260040161054090611da8565b60405180910390fd5b6040516370a0823160e01b81523060048201526001600160a01b0383169063a9059cbb90839083906370a082319060240160206040518083038186803b15801561059257600080fd5b505afa1580156105a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ca9190611a96565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381600087803b15801561061057600080fd5b505af1158015610624573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106489190611a46565b505050565b60008060055411801561066257506005544210155b905090565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac19061069590600401611dfe565b60206040518083038186803b1580156106ad57600080fd5b505afa1580156106c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e5919061195f565b6001600160a01b0316336001600160a01b0316146107155760405162461bcd60e51b815260040161054090611e23565b6001600160a01b0381166107875760405162461bcd60e51b815260206004820152603360248201527f596f75206e65656420746f2070726f7669646520616e2061637475616c20627260448201527234b233b2903230ba309031b7b73a3930b1ba1760691b6064820152608401610540565b600080546040516001600160a01b03808516939216917f6a6e057f21cc834cf349d8150e92867f52cb34d54375f174c09c431538c3dfb991a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6107ea61064d565b151560011461080b5760405162461bcd60e51b815260040161054090611d5d565b826108585760405162461bcd60e51b815260206004820152601d60248201527f596f75206e65656420746f2061636365707420746865207465726d732e0000006044820152606401610540565b806108755760405162461bcd60e51b815260040161054090611d18565b6000806108828287610386565b6001546040516370a0823160e01b81526001600160a01b038084166004830152929350600092909116906370a082319060240160206040518083038186803b1580156108cd57600080fd5b505afa1580156108e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109059190611a96565b9050600081116109575760405162461bcd60e51b815260206004820181905260248201527f5468652072657175657374656420617373657420697320736f6c64206f75742e6044820152606401610540565b6000610961610ed4565b9050803410156109ca5760405162461bcd60e51b815260206004820152602e60248201527f53656e6420656e6f7567682063757272656e637920746f20706179206174206c60448201526d32b0b9ba1037b7329034ba32b69760911b6064820152608401610540565b60006109d68234611e86565b9050828111156109e35750815b60006109ef8383611ea6565b9050610a008b8b84898989896115ce565b60005460405163bf40fac160e01b815260206004820152600b60248201526a62656e656669636961727960a81b6044820152610aac9183916001600160a01b039091169063bf40fac19060640160206040518083038186803b158015610a6557600080fd5b505afa158015610a79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a9d919061195f565b6001600160a01b0316906117eb565b80341115610ac857610ac8610ac18234611ec5565b33906117eb565b5050505050505050505050565b610add61064d565b1515600114610afe5760405162461bcd60e51b815260040161054090611d5d565b610b0b33858585856107e2565b50505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac190610b3f90600401611ce8565b60206040518083038186803b158015610b5757600080fd5b505afa158015610b6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b8f919061195f565b6001600160a01b0316336001600160a01b031614610bbf5760405162461bcd60e51b815260040161054090611da8565b6001600160a01b038316610c155760405162461bcd60e51b815260206004820152601e60248201527f6e65656420612076616c696420726576657273652072656769737472617200006044820152606401610540565b60405163c47f002760e01b81526001600160a01b0384169063c47f002790610c439085908590600401611cb9565b602060405180830381600087803b158015610c5d57600080fd5b505af1158015610c71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0b9190611a96565b6000610ca183836103f6565b159392505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac190610cd790600401611dfe565b60206040518083038186803b158015610cef57600080fd5b505afa158015610d03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d27919061195f565b6001600160a01b0316336001600160a01b031614610d575760405162461bcd60e51b815260040161054090611e23565b60008111610db55760405162461bcd60e51b815260206004820152602560248201527f596f75206e65656420746f2070726f766964652061206e6f6e2d7a65726f20706044820152643934b1b29760d91b6064820152608401610540565b60045460408051918252602082018390527f8aa4fa52648a6d15edce8a179c792c86f3719d0cc3c572cf90f91948f0f2cb68910160405180910390a1600455565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac190610e2490600401611dfe565b60206040518083038186803b158015610e3c57600080fd5b505afa158015610e50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e74919061195f565b6001600160a01b0316336001600160a01b031614610ea45760405162461bcd60e51b815260040161054090611e23565b600060058190556040517f589bfff7e7b59e33b97fa923dbc99256a6d2fdf9a631b431fa2dc06f4eea0ded9190a1565b6000805460405163bf40fac160e01b81526020600482015260066024820152654f7261636c6560d01b60448201526064916001600160a01b03169063bf40fac190830160206040518083038186803b158015610f2f57600080fd5b505afa158015610f43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f67919061195f565b60405163048cc1a760e51b8152600060048201526001600160a01b03919091169063919834e09060240160206040518083038186803b158015610fa957600080fd5b505afa158015610fbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe19190611a96565b600454610fee9190611ea6565b6106629190611e86565b61100061064d565b15156001146110215760405162461bcd60e51b815260040161054090611d5d565b8261106e5760405162461bcd60e51b815260206004820152601d60248201527f596f75206e65656420746f2061636365707420746865207465726d732e0000006044820152606401610540565b8061108b5760405162461bcd60e51b815260040161054090611d18565b6001600087828111156110ae57634e487b7160e01b600052602160045260246000fd5b905060006110bc838a610386565b6001546040516370a0823160e01b81526001600160a01b038084166004830152929350600092909116906370a082319060240160206040518083038186803b15801561110757600080fd5b505afa15801561111b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113f9190611a96565b9050808911156111a95760405162461bcd60e51b815260206004820152602f60248201527f4e6f7420656e6f7567682061737365747320617661696c61626c6520746f206260448201526e3abc903a3430ba1030b6b7bab73a1760891b6064820152608401610540565b600254604051627eeac760e11b8152336004820152602481018590528a916001600160a01b03169062fdd58e9060440160206040518083038186803b1580156111f157600080fd5b505afa158015611205573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112299190611a96565b10156112ad5760405162461bcd60e51b815260206004820152604760248201527f596f75206e65656420746f206f776e20656e6f7567682070726573616c65207660448201527f6f75636865727320746f2072656465656d20746865207370656369666965642060648201526630b6b7bab73a1760c91b608482015260a401610540565b604080516001808252818301909252600091602080830190803683375050604080516001808252818301909252929350600092915060208083019080368337019050509050848260008151811061131457634e487b7160e01b600052603260045260246000fd5b6020026020010181815250508a8160008151811061134257634e487b7160e01b600052603260045260246000fd5b6020908102919091010152600254604051637507c42960e11b81526001600160a01b039091169063ea0f88529061138190339086908690600401611c36565b600060405180830381600087803b15801561139b57600080fd5b505af11580156113af573d6000803e3d6000fd5b505050506113c38a8d8d89888860006115ce565b505050505050505050505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac1906113ff90600401611ce8565b60206040518083038186803b15801561141757600080fd5b505afa15801561142b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144f919061195f565b6001600160a01b0316336001600160a01b03161461147f5760405162461bcd60e51b815260040161054090611da8565b60405163a22cb46560e01b81526001600160a01b0382811660048301526001602483015283169063a22cb46590604401600060405180830381600087803b1580156114c957600080fd5b505af11580156114dd573d6000803e3d6000fd5b505050505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac19061151390600401611dfe565b60206040518083038186803b15801561152b57600080fd5b505afa15801561153f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611563919061195f565b6001600160a01b0316336001600160a01b0316146115935760405162461bcd60e51b815260040161054090611e23565b60058190556040518181527f397a093f9e29b7327f453f9672278c54e4daed6b689ee9bb458ae76800452ad59060200160405180910390a150565b60005b858110156117e157600180546000916001600160a01b0390911690632f745c599087906115fe8689611ec5565b6116089190611ec5565b6040516001600160e01b031960e085901b1681526001600160a01b039092166004830152602482015260440160206040518083038186803b15801561164c57600080fd5b505afa158015611660573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116849190611a96565b600154604051632142170760e11b81526001600160a01b0388811660048301528c81166024830152604482018490529293509116906342842e0e90606401600060405180830381600087803b1580156116dc57600080fd5b505af11580156116f0573d6000803e3d6000fd5b505050508088600181111561171557634e487b7160e01b600052602160045260246000fd5b604080516001600160a01b038d168152891515602082015290810186905233907f1682a2a055cfda159fc278195b31d5f7ab61f5128c423fa0ea2bf511565a9b6d9060600160405180910390a46003546001805460405163ebaf492d60e01b81526001600160a01b039384169363ebaf492d9361179b9390911691869190600401611c76565b600060405180830381600087803b1580156117b557600080fd5b505af11580156117c9573d6000803e3d6000fd5b505050505080806117d990611edc565b9150506115d1565b5050505050505050565b8047101561183b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610540565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114611888576040519150601f19603f3d011682016040523d82523d6000602084013e61188d565b606091505b50509050806106485760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610540565b80356002811061191357600080fd5b919050565b60008083601f840112611929578182fd5b50813567ffffffffffffffff811115611940578182fd5b60208301915083602082850101111561195857600080fd5b9250929050565b600060208284031215611970578081fd5b815161048b81611f0d565b600080600080600060808688031215611992578081fd5b853561199d81611f0d565b94506119ab60208701611904565b935060408601356119bb81611f25565b9250606086013567ffffffffffffffff8111156119d6578182fd5b6119e288828901611918565b969995985093965092949392505050565b600080600060408486031215611a07578283fd5b8335611a1281611f0d565b9250602084013567ffffffffffffffff811115611a2d578283fd5b611a3986828701611918565b9497909650939450505050565b600060208284031215611a57578081fd5b815161048b81611f25565b60008060408385031215611a74578182fd5b8235611a7f81611f25565b9150611a8d60208401611904565b90509250929050565b600060208284031215611aa7578081fd5b5051919050565b600060208284031215611abf578081fd5b813561048b81611f0d565b60008060408385031215611adc578182fd5b8235611ae781611f0d565b91506020830135611af781611f0d565b809150509250929050565b60008060008060608587031215611b17578384fd5b611b2085611904565b93506020850135611b3081611f25565b9250604085013567ffffffffffffffff811115611b4b578283fd5b611b5787828801611918565b95989497509550505050565b60008060008060008060a08789031215611b7b578081fd5b611b8487611904565b9550602087013594506040870135611b9b81611f0d565b93506060870135611bab81611f25565b9250608087013567ffffffffffffffff811115611bc6578182fd5b611bd289828a01611918565b979a9699509497509295939492505050565b600060208284031215611bf5578081fd5b5035919050565b6000815180845260208085019450808401835b83811015611c2b57815187529582019590820190600101611c0f565b509495945050505050565b6001600160a01b0384168152606060208201819052600090611c5a90830185611bfc565b8281036040840152611c6c8185611bfc565b9695505050505050565b6001600160a01b0384168152602081018390526060810160048310611cab57634e487b7160e01b600052602160045260246000fd5b826040830152949350505050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b6020808252601690820152751d1bdad95b905cdcda59db9b595b9d10dbdb9d1c9bdb60521b604082015260600190565b60208082526025908201527f596f75206e65656420746f2073656e642074686520616363657074616e6365206040820152643a32bc3a1760d91b606082015260800190565b6020808252602b908201527f546869732063616c6c206f6e6c7920776f726b73207768656e2074686520736860408201526a37b81034b99037b832b71760a91b606082015260800190565b60208082526036908201527f746f6b656e41737369676e6d656e74436f6e74726f6c206b65792072657175696040820152753932b2103337b9103a3434b990333ab731ba34b7b71760511b606082015260800190565b6020808252600b908201526a1cda1bdc10dbdb9d1c9bdb60aa1b604082015260600190565b6020808252602b908201527f73686f70436f6e74726f6c206b657920726571756972656420666f722074686960408201526a3990333ab731ba34b7b71760a91b606082015260800190565b60008219821115611e8157611e81611ef7565b500190565b600082611ea157634e487b7160e01b81526012600452602481fd5b500490565b6000816000190483118215151615611ec057611ec0611ef7565b500290565b600082821015611ed757611ed7611ef7565b500390565b6000600019821415611ef057611ef0611ef7565b5060010190565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b0381168114611f2257600080fd5b50565b8015158114611f2257600080fdfea2646970667358221220f2e9ba9e74b25591261868f1d84c2beef56c6930ea91cecb35779eaf5d20ba0b64736f6c63430008040033596f75206e65656420746f2070726f7669646520616e2061637475616c2043720000000000000000000000000884fc15e31b1b634732e140cb3f94b3cbfdd1c500000000000000000000000065fa22ba3d2ae2fcace9aabd40ef4605fa6365680000000000000000000000003151b7dd9f6e806d2709153765925c373af470890000000000000000000000000e76fdd1c4dc5336c8a083f709fd415eaf32b9bf00000000000000000000000000000000000000000000000000000000000003de00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000040000000000000000000000008abc5ad351bf625b71b41872145a9f78df35bbd3000000000000000000000000f1044e94a41a4ce7a132755f041811aa11e2891f000000000000000000000000c52d7b6293d8f772f25db88fb44f9ecd1be5a7c8000000000000000000000000a5a1e60613312a15d327e6347389ceec4e173ba1" + + contract_address = insert(:contract_address, contract_code: bytecode) + + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: contract_creation_code + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => contract_source_code, + "compiler_version" => "v0.8.4+commit.c7e474f2", + "evm_version" => "default", + "name" => "CS3_1OnChainShop", + "optimization" => true, + "optimization_runs" => @optimization_runs, + "autodetect_constructor_args" => true + } + + assert {:ok, + %{ + abi: abi, + constructor_arguments: + "0000000000000000000000000884fc15e31b1b634732e140cb3f94b3cbfdd1c500000000000000000000000065fa22ba3d2ae2fcace9aabd40ef4605fa6365680000000000000000000000003151b7dd9f6e806d2709153765925c373af470890000000000000000000000000e76fdd1c4dc5336c8a083f709fd415eaf32b9bf00000000000000000000000000000000000000000000000000000000000003de00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000040000000000000000000000008abc5ad351bf625b71b41872145a9f78df35bbd3000000000000000000000000f1044e94a41a4ce7a132755f041811aa11e2891f000000000000000000000000c52d7b6293d8f772f25db88fb44f9ecd1be5a7c8000000000000000000000000a5a1e60613312a15d327e6347389ceec4e173ba1" + }} = Verifier.evaluate_authenticity(contract_address.hash, params) + + assert abi != nil + end + + test "issue 5430, 5434" do + contract_source_code = "contract C {function test() external pure returns (uint256) { return 1;} }" + + bytecode = + "0x6080604052348015600f57600080fd5b506004361060285760003560e01c8063f8a8fd6d14602d575b600080fd5b60336047565b604051603e91906067565b60405180910390f35b60006001905090565b6000819050919050565b6061816050565b82525050565b6000602082019050607a6000830184605a565b9291505056fea164736f6c637823302e382e31342d63692e323032322e342e31332b636f6d6d69742e3235393233633166002b" + + contract_creation_code = + "0x608060405234801561001057600080fd5b5060ae8061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063f8a8fd6d14602d575b600080fd5b60336047565b604051603e91906067565b60405180910390f35b60006001905090565b6000819050919050565b6061816050565b82525050565b6000602082019050607a6000830184605a565b9291505056fea164736f6c637823302e382e31342d63692e323032322e342e31332b636f6d6d69742e3235393233633166002b" + + contract_address = insert(:contract_address, contract_code: bytecode) - test "issue 4758" do - contract_source_code = - "#{File.cwd!()}/test/support/fixture/smart_contract/issue_4758.sol" - |> File.read!() - - bytecode = - "0x6080604052600436106101135760003560e01c8063759bd1fe116100a0578063a97eaba711610064578063a97eaba7146102db578063b7ec2086146102f1578063c7802ba214610306578063dd48f77414610326578063e2fa9ee01461034657600080fd5b8063759bd1fe146102535780638e8fa11f1461026657806390ffb8641461028657806391b7f5ed146102a65780639b6dbc8a146102c657600080fd5b80634707d000116100e75780634707d000146101c357806347535d7b146101e55780634a1bb6b41461020a57806353cdef02146102205780636aa7c1ab1461024057600080fd5b8062c0f916146101185780632481bb5c146101555780632ae3dc741461017557806339c4576214610195575b600080fd5b34801561012457600080fd5b50610138610133366004611be4565b610366565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561016157600080fd5b50600054610138906001600160a01b031681565b34801561018157600080fd5b50610138610190366004611a62565b610386565b3480156101a157600080fd5b506101b56101b0366004611a62565b6103f6565b60405190815260200161014c565b3480156101cf57600080fd5b506101e36101de366004611aca565b610492565b005b3480156101f157600080fd5b506101fa61064d565b604051901515815260200161014c565b34801561021657600080fd5b506101b560045481565b34801561022c57600080fd5b506101e361023b366004611aae565b610667565b6101e361024e36600461197b565b6107e2565b6101e3610261366004611b02565b610ad5565b34801561027257600080fd5b506101e36102813660046119f3565b610b11565b34801561029257600080fd5b506101fa6102a1366004611a62565b610c95565b3480156102b257600080fd5b506101e36102c1366004611be4565b610ca9565b3480156102d257600080fd5b506101e3610df6565b3480156102e757600080fd5b506101b560055481565b3480156102fd57600080fd5b506101b5610ed4565b34801561031257600080fd5b506101e3610321366004611b63565b610ff8565b34801561033257600080fd5b506101e3610341366004611aca565b6113d1565b34801561035257600080fd5b506101e3610361366004611be4565b6114e5565b6006816004811061037657600080fd5b01546001600160a01b0316905081565b600060068260018111156103aa57634e487b7160e01b600052602160045260246000fd5b846103b65760006103b9565b60025b60ff166103c69190611e6e565b600481106103e457634e487b7160e01b600052603260045260246000fd5b01546001600160a01b03169392505050565b6001546000906001600160a01b03166370a082316104148585610386565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b15801561045357600080fd5b505afa158015610467573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061048b9190611a96565b9392505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac1906104c090600401611ce8565b60206040518083038186803b1580156104d857600080fd5b505afa1580156104ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610510919061195f565b6001600160a01b0316336001600160a01b0316146105495760405162461bcd60e51b815260040161054090611da8565b60405180910390fd5b6040516370a0823160e01b81523060048201526001600160a01b0383169063a9059cbb90839083906370a082319060240160206040518083038186803b15801561059257600080fd5b505afa1580156105a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ca9190611a96565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381600087803b15801561061057600080fd5b505af1158015610624573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106489190611a46565b505050565b60008060055411801561066257506005544210155b905090565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac19061069590600401611dfe565b60206040518083038186803b1580156106ad57600080fd5b505afa1580156106c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e5919061195f565b6001600160a01b0316336001600160a01b0316146107155760405162461bcd60e51b815260040161054090611e23565b6001600160a01b0381166107875760405162461bcd60e51b815260206004820152603360248201527f596f75206e65656420746f2070726f7669646520616e2061637475616c20627260448201527234b233b2903230ba309031b7b73a3930b1ba1760691b6064820152608401610540565b600080546040516001600160a01b03808516939216917f6a6e057f21cc834cf349d8150e92867f52cb34d54375f174c09c431538c3dfb991a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6107ea61064d565b151560011461080b5760405162461bcd60e51b815260040161054090611d5d565b826108585760405162461bcd60e51b815260206004820152601d60248201527f596f75206e65656420746f2061636365707420746865207465726d732e0000006044820152606401610540565b806108755760405162461bcd60e51b815260040161054090611d18565b6000806108828287610386565b6001546040516370a0823160e01b81526001600160a01b038084166004830152929350600092909116906370a082319060240160206040518083038186803b1580156108cd57600080fd5b505afa1580156108e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109059190611a96565b9050600081116109575760405162461bcd60e51b815260206004820181905260248201527f5468652072657175657374656420617373657420697320736f6c64206f75742e6044820152606401610540565b6000610961610ed4565b9050803410156109ca5760405162461bcd60e51b815260206004820152602e60248201527f53656e6420656e6f7567682063757272656e637920746f20706179206174206c60448201526d32b0b9ba1037b7329034ba32b69760911b6064820152608401610540565b60006109d68234611e86565b9050828111156109e35750815b60006109ef8383611ea6565b9050610a008b8b84898989896115ce565b60005460405163bf40fac160e01b815260206004820152600b60248201526a62656e656669636961727960a81b6044820152610aac9183916001600160a01b039091169063bf40fac19060640160206040518083038186803b158015610a6557600080fd5b505afa158015610a79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a9d919061195f565b6001600160a01b0316906117eb565b80341115610ac857610ac8610ac18234611ec5565b33906117eb565b5050505050505050505050565b610add61064d565b1515600114610afe5760405162461bcd60e51b815260040161054090611d5d565b610b0b33858585856107e2565b50505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac190610b3f90600401611ce8565b60206040518083038186803b158015610b5757600080fd5b505afa158015610b6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b8f919061195f565b6001600160a01b0316336001600160a01b031614610bbf5760405162461bcd60e51b815260040161054090611da8565b6001600160a01b038316610c155760405162461bcd60e51b815260206004820152601e60248201527f6e65656420612076616c696420726576657273652072656769737472617200006044820152606401610540565b60405163c47f002760e01b81526001600160a01b0384169063c47f002790610c439085908590600401611cb9565b602060405180830381600087803b158015610c5d57600080fd5b505af1158015610c71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0b9190611a96565b6000610ca183836103f6565b159392505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac190610cd790600401611dfe565b60206040518083038186803b158015610cef57600080fd5b505afa158015610d03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d27919061195f565b6001600160a01b0316336001600160a01b031614610d575760405162461bcd60e51b815260040161054090611e23565b60008111610db55760405162461bcd60e51b815260206004820152602560248201527f596f75206e65656420746f2070726f766964652061206e6f6e2d7a65726f20706044820152643934b1b29760d91b6064820152608401610540565b60045460408051918252602082018390527f8aa4fa52648a6d15edce8a179c792c86f3719d0cc3c572cf90f91948f0f2cb68910160405180910390a1600455565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac190610e2490600401611dfe565b60206040518083038186803b158015610e3c57600080fd5b505afa158015610e50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e74919061195f565b6001600160a01b0316336001600160a01b031614610ea45760405162461bcd60e51b815260040161054090611e23565b600060058190556040517f589bfff7e7b59e33b97fa923dbc99256a6d2fdf9a631b431fa2dc06f4eea0ded9190a1565b6000805460405163bf40fac160e01b81526020600482015260066024820152654f7261636c6560d01b60448201526064916001600160a01b03169063bf40fac190830160206040518083038186803b158015610f2f57600080fd5b505afa158015610f43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f67919061195f565b60405163048cc1a760e51b8152600060048201526001600160a01b03919091169063919834e09060240160206040518083038186803b158015610fa957600080fd5b505afa158015610fbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe19190611a96565b600454610fee9190611ea6565b6106629190611e86565b61100061064d565b15156001146110215760405162461bcd60e51b815260040161054090611d5d565b8261106e5760405162461bcd60e51b815260206004820152601d60248201527f596f75206e65656420746f2061636365707420746865207465726d732e0000006044820152606401610540565b8061108b5760405162461bcd60e51b815260040161054090611d18565b6001600087828111156110ae57634e487b7160e01b600052602160045260246000fd5b905060006110bc838a610386565b6001546040516370a0823160e01b81526001600160a01b038084166004830152929350600092909116906370a082319060240160206040518083038186803b15801561110757600080fd5b505afa15801561111b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113f9190611a96565b9050808911156111a95760405162461bcd60e51b815260206004820152602f60248201527f4e6f7420656e6f7567682061737365747320617661696c61626c6520746f206260448201526e3abc903a3430ba1030b6b7bab73a1760891b6064820152608401610540565b600254604051627eeac760e11b8152336004820152602481018590528a916001600160a01b03169062fdd58e9060440160206040518083038186803b1580156111f157600080fd5b505afa158015611205573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112299190611a96565b10156112ad5760405162461bcd60e51b815260206004820152604760248201527f596f75206e65656420746f206f776e20656e6f7567682070726573616c65207660448201527f6f75636865727320746f2072656465656d20746865207370656369666965642060648201526630b6b7bab73a1760c91b608482015260a401610540565b604080516001808252818301909252600091602080830190803683375050604080516001808252818301909252929350600092915060208083019080368337019050509050848260008151811061131457634e487b7160e01b600052603260045260246000fd5b6020026020010181815250508a8160008151811061134257634e487b7160e01b600052603260045260246000fd5b6020908102919091010152600254604051637507c42960e11b81526001600160a01b039091169063ea0f88529061138190339086908690600401611c36565b600060405180830381600087803b15801561139b57600080fd5b505af11580156113af573d6000803e3d6000fd5b505050506113c38a8d8d89888860006115ce565b505050505050505050505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac1906113ff90600401611ce8565b60206040518083038186803b15801561141757600080fd5b505afa15801561142b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144f919061195f565b6001600160a01b0316336001600160a01b03161461147f5760405162461bcd60e51b815260040161054090611da8565b60405163a22cb46560e01b81526001600160a01b0382811660048301526001602483015283169063a22cb46590604401600060405180830381600087803b1580156114c957600080fd5b505af11580156114dd573d6000803e3d6000fd5b505050505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac19061151390600401611dfe565b60206040518083038186803b15801561152b57600080fd5b505afa15801561153f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611563919061195f565b6001600160a01b0316336001600160a01b0316146115935760405162461bcd60e51b815260040161054090611e23565b60058190556040518181527f397a093f9e29b7327f453f9672278c54e4daed6b689ee9bb458ae76800452ad59060200160405180910390a150565b60005b858110156117e157600180546000916001600160a01b0390911690632f745c599087906115fe8689611ec5565b6116089190611ec5565b6040516001600160e01b031960e085901b1681526001600160a01b039092166004830152602482015260440160206040518083038186803b15801561164c57600080fd5b505afa158015611660573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116849190611a96565b600154604051632142170760e11b81526001600160a01b0388811660048301528c81166024830152604482018490529293509116906342842e0e90606401600060405180830381600087803b1580156116dc57600080fd5b505af11580156116f0573d6000803e3d6000fd5b505050508088600181111561171557634e487b7160e01b600052602160045260246000fd5b604080516001600160a01b038d168152891515602082015290810186905233907f1682a2a055cfda159fc278195b31d5f7ab61f5128c423fa0ea2bf511565a9b6d9060600160405180910390a46003546001805460405163ebaf492d60e01b81526001600160a01b039384169363ebaf492d9361179b9390911691869190600401611c76565b600060405180830381600087803b1580156117b557600080fd5b505af11580156117c9573d6000803e3d6000fd5b505050505080806117d990611edc565b9150506115d1565b5050505050505050565b8047101561183b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610540565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114611888576040519150601f19603f3d011682016040523d82523d6000602084013e61188d565b606091505b50509050806106485760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610540565b80356002811061191357600080fd5b919050565b60008083601f840112611929578182fd5b50813567ffffffffffffffff811115611940578182fd5b60208301915083602082850101111561195857600080fd5b9250929050565b600060208284031215611970578081fd5b815161048b81611f0d565b600080600080600060808688031215611992578081fd5b853561199d81611f0d565b94506119ab60208701611904565b935060408601356119bb81611f25565b9250606086013567ffffffffffffffff8111156119d6578182fd5b6119e288828901611918565b969995985093965092949392505050565b600080600060408486031215611a07578283fd5b8335611a1281611f0d565b9250602084013567ffffffffffffffff811115611a2d578283fd5b611a3986828701611918565b9497909650939450505050565b600060208284031215611a57578081fd5b815161048b81611f25565b60008060408385031215611a74578182fd5b8235611a7f81611f25565b9150611a8d60208401611904565b90509250929050565b600060208284031215611aa7578081fd5b5051919050565b600060208284031215611abf578081fd5b813561048b81611f0d565b60008060408385031215611adc578182fd5b8235611ae781611f0d565b91506020830135611af781611f0d565b809150509250929050565b60008060008060608587031215611b17578384fd5b611b2085611904565b93506020850135611b3081611f25565b9250604085013567ffffffffffffffff811115611b4b578283fd5b611b5787828801611918565b95989497509550505050565b60008060008060008060a08789031215611b7b578081fd5b611b8487611904565b9550602087013594506040870135611b9b81611f0d565b93506060870135611bab81611f25565b9250608087013567ffffffffffffffff811115611bc6578182fd5b611bd289828a01611918565b979a9699509497509295939492505050565b600060208284031215611bf5578081fd5b5035919050565b6000815180845260208085019450808401835b83811015611c2b57815187529582019590820190600101611c0f565b509495945050505050565b6001600160a01b0384168152606060208201819052600090611c5a90830185611bfc565b8281036040840152611c6c8185611bfc565b9695505050505050565b6001600160a01b0384168152602081018390526060810160048310611cab57634e487b7160e01b600052602160045260246000fd5b826040830152949350505050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b6020808252601690820152751d1bdad95b905cdcda59db9b595b9d10dbdb9d1c9bdb60521b604082015260600190565b60208082526025908201527f596f75206e65656420746f2073656e642074686520616363657074616e6365206040820152643a32bc3a1760d91b606082015260800190565b6020808252602b908201527f546869732063616c6c206f6e6c7920776f726b73207768656e2074686520736860408201526a37b81034b99037b832b71760a91b606082015260800190565b60208082526036908201527f746f6b656e41737369676e6d656e74436f6e74726f6c206b65792072657175696040820152753932b2103337b9103a3434b990333ab731ba34b7b71760511b606082015260800190565b6020808252600b908201526a1cda1bdc10dbdb9d1c9bdb60aa1b604082015260600190565b6020808252602b908201527f73686f70436f6e74726f6c206b657920726571756972656420666f722074686960408201526a3990333ab731ba34b7b71760a91b606082015260800190565b60008219821115611e8157611e81611ef7565b500190565b600082611ea157634e487b7160e01b81526012600452602481fd5b500490565b6000816000190483118215151615611ec057611ec0611ef7565b500290565b600082821015611ed757611ed7611ef7565b500390565b6000600019821415611ef057611ef0611ef7565b5060010190565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b0381168114611f2257600080fd5b50565b8015158114611f2257600080fdfea2646970667358221220f2e9ba9e74b25591261868f1d84c2beef56c6930ea91cecb35779eaf5d20ba0b64736f6c63430008040033" - - contract_creation_code = - "0x608060405260006005553480156200001657600080fd5b50604051620024e6380380620024e68339810160408190526200003991620003e5565b600080546001600160a01b0319166001600160a01b038816908117909155620000cf5760405162461bcd60e51b815260206004820152603360248201527f596f75206e65656420746f2070726f7669646520616e2061637475616c20627260448201527f69646765206461746120636f6e74726163742e0000000000000000000000000060648201526084015b60405180910390fd5b600380546001600160a01b0319166001600160a01b038716908117909155620001615760405162461bcd60e51b815260206004820152603860248201527f596f75206e65656420746f2070726f7669646520616e2061637475616c20736860448201527f697070696e67206d616e6167657220636f6e74726163742e00000000000000006064820152608401620000c6565b600180546001600160a01b0319166001600160a01b038616908117909155620001e25760405162461bcd60e51b81526020600482015260376024820152600080516020620024c683398151915260448201527f7970746f7374616d7020332e3120636f6e74726163742e0000000000000000006064820152608401620000c6565b600280546001600160a01b0319166001600160a01b038516908117909155620002635760405162461bcd60e51b815260206004820152603f6024820152600080516020620024c683398151915260448201527f7970746f7374616d7020332e312050726573616c6520636f6e74726163742e006064820152608401620000c6565b600482905581620002c55760405162461bcd60e51b815260206004820152602560248201527f596f75206e65656420746f2070726f766964652061206e6f6e2d7a65726f20706044820152643934b1b29760d91b6064820152608401620000c6565b805160049081146200032f5760405162461bcd60e51b815260206004820152602c60248201527f4e65656420636f727265637420616d6f756e74206f6620746f6b656e20706f6f60448201526b361030b2323932b9b9b2b99760a11b6064820152608401620000c6565b60005b81811015620003ba578281815181106200035c57634e487b7160e01b600052603260045260246000fd5b6020026020010151600682600481106200038657634e487b7160e01b600052603260045260246000fd5b0180546001600160a01b0319166001600160a01b039290921691909117905580620003b1816200050f565b91505062000332565b50505050505050506200054d565b80516001600160a01b0381168114620003e057600080fd5b919050565b60008060008060008060c08789031215620003fe578182fd5b6200040987620003c8565b955060206200041a818901620003c8565b95506200042a60408901620003c8565b94506200043a60608901620003c8565b608089015160a08a015191955093506001600160401b03808211156200045e578384fd5b818a0191508a601f83011262000472578384fd5b81518181111562000487576200048762000537565b8060051b604051601f19603f83011681018181108582111715620004af57620004af62000537565b604052828152858101935084860182860187018f1015620004ce578788fd5b8795505b83861015620004fb57620004e681620003c8565b855260019590950194938601938601620004d2565b508096505050505050509295509295509295565b60006000198214156200053057634e487b7160e01b81526011600452602481fd5b5060010190565b634e487b7160e01b600052604160045260246000fd5b611f69806200055d6000396000f3fe6080604052600436106101135760003560e01c8063759bd1fe116100a0578063a97eaba711610064578063a97eaba7146102db578063b7ec2086146102f1578063c7802ba214610306578063dd48f77414610326578063e2fa9ee01461034657600080fd5b8063759bd1fe146102535780638e8fa11f1461026657806390ffb8641461028657806391b7f5ed146102a65780639b6dbc8a146102c657600080fd5b80634707d000116100e75780634707d000146101c357806347535d7b146101e55780634a1bb6b41461020a57806353cdef02146102205780636aa7c1ab1461024057600080fd5b8062c0f916146101185780632481bb5c146101555780632ae3dc741461017557806339c4576214610195575b600080fd5b34801561012457600080fd5b50610138610133366004611be4565b610366565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561016157600080fd5b50600054610138906001600160a01b031681565b34801561018157600080fd5b50610138610190366004611a62565b610386565b3480156101a157600080fd5b506101b56101b0366004611a62565b6103f6565b60405190815260200161014c565b3480156101cf57600080fd5b506101e36101de366004611aca565b610492565b005b3480156101f157600080fd5b506101fa61064d565b604051901515815260200161014c565b34801561021657600080fd5b506101b560045481565b34801561022c57600080fd5b506101e361023b366004611aae565b610667565b6101e361024e36600461197b565b6107e2565b6101e3610261366004611b02565b610ad5565b34801561027257600080fd5b506101e36102813660046119f3565b610b11565b34801561029257600080fd5b506101fa6102a1366004611a62565b610c95565b3480156102b257600080fd5b506101e36102c1366004611be4565b610ca9565b3480156102d257600080fd5b506101e3610df6565b3480156102e757600080fd5b506101b560055481565b3480156102fd57600080fd5b506101b5610ed4565b34801561031257600080fd5b506101e3610321366004611b63565b610ff8565b34801561033257600080fd5b506101e3610341366004611aca565b6113d1565b34801561035257600080fd5b506101e3610361366004611be4565b6114e5565b6006816004811061037657600080fd5b01546001600160a01b0316905081565b600060068260018111156103aa57634e487b7160e01b600052602160045260246000fd5b846103b65760006103b9565b60025b60ff166103c69190611e6e565b600481106103e457634e487b7160e01b600052603260045260246000fd5b01546001600160a01b03169392505050565b6001546000906001600160a01b03166370a082316104148585610386565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b15801561045357600080fd5b505afa158015610467573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061048b9190611a96565b9392505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac1906104c090600401611ce8565b60206040518083038186803b1580156104d857600080fd5b505afa1580156104ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610510919061195f565b6001600160a01b0316336001600160a01b0316146105495760405162461bcd60e51b815260040161054090611da8565b60405180910390fd5b6040516370a0823160e01b81523060048201526001600160a01b0383169063a9059cbb90839083906370a082319060240160206040518083038186803b15801561059257600080fd5b505afa1580156105a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ca9190611a96565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381600087803b15801561061057600080fd5b505af1158015610624573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106489190611a46565b505050565b60008060055411801561066257506005544210155b905090565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac19061069590600401611dfe565b60206040518083038186803b1580156106ad57600080fd5b505afa1580156106c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e5919061195f565b6001600160a01b0316336001600160a01b0316146107155760405162461bcd60e51b815260040161054090611e23565b6001600160a01b0381166107875760405162461bcd60e51b815260206004820152603360248201527f596f75206e65656420746f2070726f7669646520616e2061637475616c20627260448201527234b233b2903230ba309031b7b73a3930b1ba1760691b6064820152608401610540565b600080546040516001600160a01b03808516939216917f6a6e057f21cc834cf349d8150e92867f52cb34d54375f174c09c431538c3dfb991a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6107ea61064d565b151560011461080b5760405162461bcd60e51b815260040161054090611d5d565b826108585760405162461bcd60e51b815260206004820152601d60248201527f596f75206e65656420746f2061636365707420746865207465726d732e0000006044820152606401610540565b806108755760405162461bcd60e51b815260040161054090611d18565b6000806108828287610386565b6001546040516370a0823160e01b81526001600160a01b038084166004830152929350600092909116906370a082319060240160206040518083038186803b1580156108cd57600080fd5b505afa1580156108e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109059190611a96565b9050600081116109575760405162461bcd60e51b815260206004820181905260248201527f5468652072657175657374656420617373657420697320736f6c64206f75742e6044820152606401610540565b6000610961610ed4565b9050803410156109ca5760405162461bcd60e51b815260206004820152602e60248201527f53656e6420656e6f7567682063757272656e637920746f20706179206174206c60448201526d32b0b9ba1037b7329034ba32b69760911b6064820152608401610540565b60006109d68234611e86565b9050828111156109e35750815b60006109ef8383611ea6565b9050610a008b8b84898989896115ce565b60005460405163bf40fac160e01b815260206004820152600b60248201526a62656e656669636961727960a81b6044820152610aac9183916001600160a01b039091169063bf40fac19060640160206040518083038186803b158015610a6557600080fd5b505afa158015610a79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a9d919061195f565b6001600160a01b0316906117eb565b80341115610ac857610ac8610ac18234611ec5565b33906117eb565b5050505050505050505050565b610add61064d565b1515600114610afe5760405162461bcd60e51b815260040161054090611d5d565b610b0b33858585856107e2565b50505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac190610b3f90600401611ce8565b60206040518083038186803b158015610b5757600080fd5b505afa158015610b6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b8f919061195f565b6001600160a01b0316336001600160a01b031614610bbf5760405162461bcd60e51b815260040161054090611da8565b6001600160a01b038316610c155760405162461bcd60e51b815260206004820152601e60248201527f6e65656420612076616c696420726576657273652072656769737472617200006044820152606401610540565b60405163c47f002760e01b81526001600160a01b0384169063c47f002790610c439085908590600401611cb9565b602060405180830381600087803b158015610c5d57600080fd5b505af1158015610c71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0b9190611a96565b6000610ca183836103f6565b159392505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac190610cd790600401611dfe565b60206040518083038186803b158015610cef57600080fd5b505afa158015610d03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d27919061195f565b6001600160a01b0316336001600160a01b031614610d575760405162461bcd60e51b815260040161054090611e23565b60008111610db55760405162461bcd60e51b815260206004820152602560248201527f596f75206e65656420746f2070726f766964652061206e6f6e2d7a65726f20706044820152643934b1b29760d91b6064820152608401610540565b60045460408051918252602082018390527f8aa4fa52648a6d15edce8a179c792c86f3719d0cc3c572cf90f91948f0f2cb68910160405180910390a1600455565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac190610e2490600401611dfe565b60206040518083038186803b158015610e3c57600080fd5b505afa158015610e50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e74919061195f565b6001600160a01b0316336001600160a01b031614610ea45760405162461bcd60e51b815260040161054090611e23565b600060058190556040517f589bfff7e7b59e33b97fa923dbc99256a6d2fdf9a631b431fa2dc06f4eea0ded9190a1565b6000805460405163bf40fac160e01b81526020600482015260066024820152654f7261636c6560d01b60448201526064916001600160a01b03169063bf40fac190830160206040518083038186803b158015610f2f57600080fd5b505afa158015610f43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f67919061195f565b60405163048cc1a760e51b8152600060048201526001600160a01b03919091169063919834e09060240160206040518083038186803b158015610fa957600080fd5b505afa158015610fbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe19190611a96565b600454610fee9190611ea6565b6106629190611e86565b61100061064d565b15156001146110215760405162461bcd60e51b815260040161054090611d5d565b8261106e5760405162461bcd60e51b815260206004820152601d60248201527f596f75206e65656420746f2061636365707420746865207465726d732e0000006044820152606401610540565b8061108b5760405162461bcd60e51b815260040161054090611d18565b6001600087828111156110ae57634e487b7160e01b600052602160045260246000fd5b905060006110bc838a610386565b6001546040516370a0823160e01b81526001600160a01b038084166004830152929350600092909116906370a082319060240160206040518083038186803b15801561110757600080fd5b505afa15801561111b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113f9190611a96565b9050808911156111a95760405162461bcd60e51b815260206004820152602f60248201527f4e6f7420656e6f7567682061737365747320617661696c61626c6520746f206260448201526e3abc903a3430ba1030b6b7bab73a1760891b6064820152608401610540565b600254604051627eeac760e11b8152336004820152602481018590528a916001600160a01b03169062fdd58e9060440160206040518083038186803b1580156111f157600080fd5b505afa158015611205573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112299190611a96565b10156112ad5760405162461bcd60e51b815260206004820152604760248201527f596f75206e65656420746f206f776e20656e6f7567682070726573616c65207660448201527f6f75636865727320746f2072656465656d20746865207370656369666965642060648201526630b6b7bab73a1760c91b608482015260a401610540565b604080516001808252818301909252600091602080830190803683375050604080516001808252818301909252929350600092915060208083019080368337019050509050848260008151811061131457634e487b7160e01b600052603260045260246000fd5b6020026020010181815250508a8160008151811061134257634e487b7160e01b600052603260045260246000fd5b6020908102919091010152600254604051637507c42960e11b81526001600160a01b039091169063ea0f88529061138190339086908690600401611c36565b600060405180830381600087803b15801561139b57600080fd5b505af11580156113af573d6000803e3d6000fd5b505050506113c38a8d8d89888860006115ce565b505050505050505050505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac1906113ff90600401611ce8565b60206040518083038186803b15801561141757600080fd5b505afa15801561142b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144f919061195f565b6001600160a01b0316336001600160a01b03161461147f5760405162461bcd60e51b815260040161054090611da8565b60405163a22cb46560e01b81526001600160a01b0382811660048301526001602483015283169063a22cb46590604401600060405180830381600087803b1580156114c957600080fd5b505af11580156114dd573d6000803e3d6000fd5b505050505050565b60005460405163bf40fac160e01b81526001600160a01b039091169063bf40fac19061151390600401611dfe565b60206040518083038186803b15801561152b57600080fd5b505afa15801561153f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611563919061195f565b6001600160a01b0316336001600160a01b0316146115935760405162461bcd60e51b815260040161054090611e23565b60058190556040518181527f397a093f9e29b7327f453f9672278c54e4daed6b689ee9bb458ae76800452ad59060200160405180910390a150565b60005b858110156117e157600180546000916001600160a01b0390911690632f745c599087906115fe8689611ec5565b6116089190611ec5565b6040516001600160e01b031960e085901b1681526001600160a01b039092166004830152602482015260440160206040518083038186803b15801561164c57600080fd5b505afa158015611660573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116849190611a96565b600154604051632142170760e11b81526001600160a01b0388811660048301528c81166024830152604482018490529293509116906342842e0e90606401600060405180830381600087803b1580156116dc57600080fd5b505af11580156116f0573d6000803e3d6000fd5b505050508088600181111561171557634e487b7160e01b600052602160045260246000fd5b604080516001600160a01b038d168152891515602082015290810186905233907f1682a2a055cfda159fc278195b31d5f7ab61f5128c423fa0ea2bf511565a9b6d9060600160405180910390a46003546001805460405163ebaf492d60e01b81526001600160a01b039384169363ebaf492d9361179b9390911691869190600401611c76565b600060405180830381600087803b1580156117b557600080fd5b505af11580156117c9573d6000803e3d6000fd5b505050505080806117d990611edc565b9150506115d1565b5050505050505050565b8047101561183b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610540565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114611888576040519150601f19603f3d011682016040523d82523d6000602084013e61188d565b606091505b50509050806106485760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610540565b80356002811061191357600080fd5b919050565b60008083601f840112611929578182fd5b50813567ffffffffffffffff811115611940578182fd5b60208301915083602082850101111561195857600080fd5b9250929050565b600060208284031215611970578081fd5b815161048b81611f0d565b600080600080600060808688031215611992578081fd5b853561199d81611f0d565b94506119ab60208701611904565b935060408601356119bb81611f25565b9250606086013567ffffffffffffffff8111156119d6578182fd5b6119e288828901611918565b969995985093965092949392505050565b600080600060408486031215611a07578283fd5b8335611a1281611f0d565b9250602084013567ffffffffffffffff811115611a2d578283fd5b611a3986828701611918565b9497909650939450505050565b600060208284031215611a57578081fd5b815161048b81611f25565b60008060408385031215611a74578182fd5b8235611a7f81611f25565b9150611a8d60208401611904565b90509250929050565b600060208284031215611aa7578081fd5b5051919050565b600060208284031215611abf578081fd5b813561048b81611f0d565b60008060408385031215611adc578182fd5b8235611ae781611f0d565b91506020830135611af781611f0d565b809150509250929050565b60008060008060608587031215611b17578384fd5b611b2085611904565b93506020850135611b3081611f25565b9250604085013567ffffffffffffffff811115611b4b578283fd5b611b5787828801611918565b95989497509550505050565b60008060008060008060a08789031215611b7b578081fd5b611b8487611904565b9550602087013594506040870135611b9b81611f0d565b93506060870135611bab81611f25565b9250608087013567ffffffffffffffff811115611bc6578182fd5b611bd289828a01611918565b979a9699509497509295939492505050565b600060208284031215611bf5578081fd5b5035919050565b6000815180845260208085019450808401835b83811015611c2b57815187529582019590820190600101611c0f565b509495945050505050565b6001600160a01b0384168152606060208201819052600090611c5a90830185611bfc565b8281036040840152611c6c8185611bfc565b9695505050505050565b6001600160a01b0384168152602081018390526060810160048310611cab57634e487b7160e01b600052602160045260246000fd5b826040830152949350505050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b6020808252601690820152751d1bdad95b905cdcda59db9b595b9d10dbdb9d1c9bdb60521b604082015260600190565b60208082526025908201527f596f75206e65656420746f2073656e642074686520616363657074616e6365206040820152643a32bc3a1760d91b606082015260800190565b6020808252602b908201527f546869732063616c6c206f6e6c7920776f726b73207768656e2074686520736860408201526a37b81034b99037b832b71760a91b606082015260800190565b60208082526036908201527f746f6b656e41737369676e6d656e74436f6e74726f6c206b65792072657175696040820152753932b2103337b9103a3434b990333ab731ba34b7b71760511b606082015260800190565b6020808252600b908201526a1cda1bdc10dbdb9d1c9bdb60aa1b604082015260600190565b6020808252602b908201527f73686f70436f6e74726f6c206b657920726571756972656420666f722074686960408201526a3990333ab731ba34b7b71760a91b606082015260800190565b60008219821115611e8157611e81611ef7565b500190565b600082611ea157634e487b7160e01b81526012600452602481fd5b500490565b6000816000190483118215151615611ec057611ec0611ef7565b500290565b600082821015611ed757611ed7611ef7565b500390565b6000600019821415611ef057611ef0611ef7565b5060010190565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b0381168114611f2257600080fd5b50565b8015158114611f2257600080fdfea2646970667358221220f2e9ba9e74b25591261868f1d84c2beef56c6930ea91cecb35779eaf5d20ba0b64736f6c63430008040033596f75206e65656420746f2070726f7669646520616e2061637475616c2043720000000000000000000000000884fc15e31b1b634732e140cb3f94b3cbfdd1c500000000000000000000000065fa22ba3d2ae2fcace9aabd40ef4605fa6365680000000000000000000000003151b7dd9f6e806d2709153765925c373af470890000000000000000000000000e76fdd1c4dc5336c8a083f709fd415eaf32b9bf00000000000000000000000000000000000000000000000000000000000003de00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000040000000000000000000000008abc5ad351bf625b71b41872145a9f78df35bbd3000000000000000000000000f1044e94a41a4ce7a132755f041811aa11e2891f000000000000000000000000c52d7b6293d8f772f25db88fb44f9ecd1be5a7c8000000000000000000000000a5a1e60613312a15d327e6347389ceec4e173ba1" - - contract_address = insert(:contract_address, contract_code: bytecode) - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: contract_creation_code - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => contract_source_code, - "compiler_version" => "v0.8.4+commit.c7e474f2", - "evm_version" => "default", - "name" => "CS3_1OnChainShop", - "optimization" => true, - "optimization_runs" => 200, - "autodetect_constructor_args" => true - } - - assert {:ok, - %{ - abi: abi, - constructor_arguments: - "0000000000000000000000000884fc15e31b1b634732e140cb3f94b3cbfdd1c500000000000000000000000065fa22ba3d2ae2fcace9aabd40ef4605fa6365680000000000000000000000003151b7dd9f6e806d2709153765925c373af470890000000000000000000000000e76fdd1c4dc5336c8a083f709fd415eaf32b9bf00000000000000000000000000000000000000000000000000000000000003de00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000040000000000000000000000008abc5ad351bf625b71b41872145a9f78df35bbd3000000000000000000000000f1044e94a41a4ce7a132755f041811aa11e2891f000000000000000000000000c52d7b6293d8f772f25db88fb44f9ecd1be5a7c8000000000000000000000000a5a1e60613312a15d327e6347389ceec4e173ba1" - }} = Verifier.evaluate_authenticity(contract_address.hash, params) - - assert abi != nil + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: contract_creation_code + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => contract_source_code, + "compiler_version" => "v0.8.14-nightly.2022.4.13+commit.25923c1f", + "evm_version" => "default", + "name" => "C", + "optimization" => false, + "autodetect_constructor_args" => true + } + + assert {:ok, + %{ + abi: abi + }} = Verifier.evaluate_authenticity(contract_address.hash, params) + + assert abi != nil + end + + test "issue 5431 (smart contract was compiled with bytecodeHash=none; constructor with arguments)" do + contract_source_code = + "#{File.cwd!()}/test/support/fixture/smart_contract/issue_5431.sol" + |> File.read!() + + bytecode = + "0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063893d20e81461003b578063a6f9dae114610059575b600080fd5b610043610075565b604051610050919061022a565b60405180910390f35b610073600480360381019061006e9190610276565b61009e565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461012c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161012390610300565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610214826101e9565b9050919050565b61022481610209565b82525050565b600060208201905061023f600083018461021b565b92915050565b600080fd5b61025381610209565b811461025e57600080fd5b50565b6000813590506102708161024a565b92915050565b60006020828403121561028c5761028b610245565b5b600061029a84828501610261565b91505092915050565b600082825260208201905092915050565b7f43616c6c6572206973206e6f74206f776e657200000000000000000000000000600082015250565b60006102ea6013836102a3565b91506102f5826102b4565b602082019050919050565b60006020820190508181036000830152610319816102dd565b905091905056fea164736f6c6343000808000a" + + contract_creation_code = + "0x608060405234801561001057600080fd5b506040516104e13803806104e183398181016040528101906100329190610165565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a350506101a5565b600080fd5b6000819050919050565b61010c816100f9565b811461011757600080fd5b50565b60008151905061012981610103565b92915050565b6000819050919050565b6101428161012f565b811461014d57600080fd5b50565b60008151905061015f81610139565b92915050565b6000806040838503121561017c5761017b6100f4565b5b600061018a8582860161011a565b925050602061019b85828601610150565b9150509250929050565b61032d806101b46000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063893d20e81461003b578063a6f9dae114610059575b600080fd5b610043610075565b604051610050919061022a565b60405180910390f35b610073600480360381019061006e9190610276565b61009e565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461012c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161012390610300565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610214826101e9565b9050919050565b61022481610209565b82525050565b600060208201905061023f600083018461021b565b92915050565b600080fd5b61025381610209565b811461025e57600080fd5b50565b6000813590506102708161024a565b92915050565b60006020828403121561028c5761028b610245565b5b600061029a84828501610261565b91505092915050565b600082825260208201905092915050565b7f43616c6c6572206973206e6f74206f776e657200000000000000000000000000600082015250565b60006102ea6013836102a3565b91506102f5826102b4565b602082019050919050565b60006020820190508181036000830152610319816102dd565b905091905056fea164736f6c6343000808000afffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040000000000000000000000000000000000000000000000000000000000005886" + + contract_address = insert(:contract_address, contract_code: bytecode) + + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: contract_creation_code + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => contract_source_code, + "compiler_version" => "v0.8.8+commit.dddeac2f", + "evm_version" => "default", + "name" => "Owner", + "optimization" => false, + "autodetect_constructor_args" => true + } + + assert {:ok, + %{ + abi: abi, + constructor_arguments: + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040000000000000000000000000000000000000000000000000000000000005886" + }} = Verifier.evaluate_authenticity(contract_address.hash, params) + + assert abi != nil + end end - test "issue 5430, 5434" do - contract_source_code = "contract C {function test() external pure returns (uint256) { return 1;} }" + describe "cover new functionality from https://github.com/blockscout/blockscout/pull/5479" do + test "return {:error, :no_creation_data} when there is no contract creation code" do + contract_source_code = + "#{File.cwd!()}/test/support/fixture/smart_contract/issue_5431.sol" + |> File.read!() - bytecode = - "0x6080604052348015600f57600080fd5b506004361060285760003560e01c8063f8a8fd6d14602d575b600080fd5b60336047565b604051603e91906067565b60405180910390f35b60006001905090565b6000819050919050565b6061816050565b82525050565b6000602082019050607a6000830184605a565b9291505056fea164736f6c637823302e382e31342d63692e323032322e342e31332b636f6d6d69742e3235393233633166002b" + bytecode = + "0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063893d20e81461003b578063a6f9dae114610059575b600080fd5b610043610075565b604051610050919061022a565b60405180910390f35b610073600480360381019061006e9190610276565b61009e565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461012c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161012390610300565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610214826101e9565b9050919050565b61022481610209565b82525050565b600060208201905061023f600083018461021b565b92915050565b600080fd5b61025381610209565b811461025e57600080fd5b50565b6000813590506102708161024a565b92915050565b60006020828403121561028c5761028b610245565b5b600061029a84828501610261565b91505092915050565b600082825260208201905092915050565b7f43616c6c6572206973206e6f74206f776e657200000000000000000000000000600082015250565b60006102ea6013836102a3565b91506102f5826102b4565b602082019050919050565b60006020820190508181036000830152610319816102dd565b905091905056fea164736f6c6343000808000a" - contract_creation_code = - "0x608060405234801561001057600080fd5b5060ae8061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063f8a8fd6d14602d575b600080fd5b60336047565b604051603e91906067565b60405180910390f35b60006001905090565b6000819050919050565b6061816050565b82525050565b6000602082019050607a6000830184605a565b9291505056fea164736f6c637823302e382e31342d63692e323032322e342e31332b636f6d6d69742e3235393233633166002b" + contract_address = insert(:contract_address, contract_code: bytecode) - contract_address = insert(:contract_address, contract_code: bytecode) + params = %{ + "contract_source_code" => contract_source_code, + "compiler_version" => "v0.8.8+commit.dddeac2f", + "evm_version" => "default", + "name" => "Owner", + "optimization" => false, + "autodetect_constructor_args" => true + } - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: contract_creation_code - ) - |> with_block(status: :ok) + assert {:error, :no_creation_data} = Verifier.evaluate_authenticity(contract_address.hash, params) + end - params = %{ - "contract_source_code" => contract_source_code, - "compiler_version" => "v0.8.14-nightly.2022.4.13+commit.25923c1f", - "evm_version" => "default", - "name" => "C", - "optimization" => false, - "autodetect_constructor_args" => true - } + test "return {:error, :deployed_bytecode}" do + contract_source_code = + "#{File.cwd!()}/test/support/fixture/smart_contract/issue_5431.sol" + |> File.read!() - assert {:ok, - %{ - abi: abi - }} = Verifier.evaluate_authenticity(contract_address.hash, params) + contract_creation_code = + "0x608060405234801561001057600080fd5b506040516104e13803806104e183398181016040528101906100329190610165565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a350506101a5565b600080fd5b6000819050919050565b61010c816100f9565b811461011757600080fd5b50565b60008151905061012981610103565b92915050565b6000819050919050565b6101428161012f565b811461014d57600080fd5b50565b60008151905061015f81610139565b92915050565b6000806040838503121561017c5761017b6100f4565b5b600061018a8582860161011a565b925050602061019b85828601610150565b9150509250929050565b61032d806101b46000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063893d20e81461003b578063a6f9dae114610059575b600080fd5b610043610075565b604051610050919061022a565b60405180910390f35b610073600480360381019061006e9190610276565b61009e565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461012c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161012390610300565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610214826101e9565b9050919050565b61022481610209565b82525050565b600060208201905061023f600083018461021b565b92915050565b600080fd5b61025381610209565b811461025e57600080fd5b50565b6000813590506102708161024a565b92915050565b60006020828403121561028c5761028b610245565b5b600061029a84828501610261565b91505092915050565b600082825260208201905092915050565b7f43616c6c6572206973206e6f74206f776e657200000000000000000000000000600082015250565b60006102ea6013836102a3565b91506102f5826102b4565b602082019050919050565b60006020820190508181036000830152610319816102dd565b905091905056fea164736f6c6343000808000afffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040000000000000000000000000000000000000000000000000000000000005886" - assert abi != nil - end + contract_address = insert(:contract_address, contract_code: "0x") - test "issue 5431 (smart contract was compiled with bytecodeHash=none; constructor with arguments)" do - contract_source_code = - "#{File.cwd!()}/test/support/fixture/smart_contract/issue_5431.sol" - |> File.read!() - - bytecode = - "0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063893d20e81461003b578063a6f9dae114610059575b600080fd5b610043610075565b604051610050919061022a565b60405180910390f35b610073600480360381019061006e9190610276565b61009e565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461012c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161012390610300565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610214826101e9565b9050919050565b61022481610209565b82525050565b600060208201905061023f600083018461021b565b92915050565b600080fd5b61025381610209565b811461025e57600080fd5b50565b6000813590506102708161024a565b92915050565b60006020828403121561028c5761028b610245565b5b600061029a84828501610261565b91505092915050565b600082825260208201905092915050565b7f43616c6c6572206973206e6f74206f776e657200000000000000000000000000600082015250565b60006102ea6013836102a3565b91506102f5826102b4565b602082019050919050565b60006020820190508181036000830152610319816102dd565b905091905056fea164736f6c6343000808000a" - - contract_creation_code = - "0x608060405234801561001057600080fd5b506040516104e13803806104e183398181016040528101906100329190610165565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a350506101a5565b600080fd5b6000819050919050565b61010c816100f9565b811461011757600080fd5b50565b60008151905061012981610103565b92915050565b6000819050919050565b6101428161012f565b811461014d57600080fd5b50565b60008151905061015f81610139565b92915050565b6000806040838503121561017c5761017b6100f4565b5b600061018a8582860161011a565b925050602061019b85828601610150565b9150509250929050565b61032d806101b46000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063893d20e81461003b578063a6f9dae114610059575b600080fd5b610043610075565b604051610050919061022a565b60405180910390f35b610073600480360381019061006e9190610276565b61009e565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461012c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161012390610300565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610214826101e9565b9050919050565b61022481610209565b82525050565b600060208201905061023f600083018461021b565b92915050565b600080fd5b61025381610209565b811461025e57600080fd5b50565b6000813590506102708161024a565b92915050565b60006020828403121561028c5761028b610245565b5b600061029a84828501610261565b91505092915050565b600082825260208201905092915050565b7f43616c6c6572206973206e6f74206f776e657200000000000000000000000000600082015250565b60006102ea6013836102a3565b91506102f5826102b4565b602082019050919050565b60006020820190508181036000830152610319816102dd565b905091905056fea164736f6c6343000808000afffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040000000000000000000000000000000000000000000000000000000000005886" - - contract_address = insert(:contract_address, contract_code: bytecode) - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: contract_creation_code - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => contract_source_code, - "compiler_version" => "v0.8.8+commit.dddeac2f", - "evm_version" => "default", - "name" => "Owner", - "optimization" => false, - "autodetect_constructor_args" => true - } - - assert {:ok, - %{ - abi: abi, - constructor_arguments: - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040000000000000000000000000000000000000000000000000000000000005886" - }} = Verifier.evaluate_authenticity(contract_address.hash, params) - - assert abi != nil + :transaction + |> insert( + created_contract_address_hash: contract_address.hash, + input: contract_creation_code + ) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => contract_source_code, + "compiler_version" => "v0.8.8+commit.dddeac2f", + "evm_version" => "default", + "name" => "Owner", + "optimization" => false, + "autodetect_constructor_args" => true + } + + assert {:error, :deployed_bytecode} = Verifier.evaluate_authenticity(contract_address.hash, params) + end end - end - describe "cover new functionality from https://github.com/blockscout/blockscout/pull/5479" do - test "return {:error, :no_creation_data} when there is no contract creation code" do - contract_source_code = - "#{File.cwd!()}/test/support/fixture/smart_contract/issue_5431.sol" - |> File.read!() + describe "tests from constructor_arguments_test.exs" do + test "verifies with require messages" do + source_code = + "#{File.cwd!()}/test/support/fixture/smart_contract/home_bridge.sol" + |> File.read!() - bytecode = - "0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063893d20e81461003b578063a6f9dae114610059575b600080fd5b610043610075565b604051610050919061022a565b60405180910390f35b610073600480360381019061006e9190610276565b61009e565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461012c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161012390610300565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610214826101e9565b9050919050565b61022481610209565b82525050565b600060208201905061023f600083018461021b565b92915050565b600080fd5b61025381610209565b811461025e57600080fd5b50565b6000813590506102708161024a565b92915050565b60006020828403121561028c5761028b610245565b5b600061029a84828501610261565b91505092915050565b600082825260208201905092915050565b7f43616c6c6572206973206e6f74206f776e657200000000000000000000000000600082015250565b60006102ea6013836102a3565b91506102f5826102b4565b602082019050919050565b60006020820190508181036000830152610319816102dd565b905091905056fea164736f6c6343000808000a" + constructor_arguments = + "000000000000000000000000fb5a36f0e12cef9f88d95f0e02cad4ba183336dc0000000000000000000000000000000000000000000000000000000000000032" - contract_address = insert(:contract_address, contract_code: bytecode) + input = + "608060405234801561001057600080fd5b50604051604080610c2e8339810180604052604081101561003057600080fd5b5080516020909101516001600160a01b038216610098576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180610c0b6023913960400191505060405180910390fd5b60648111156100f2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526034815260200180610bd76034913960400191505060405180910390fd5b600180546001600160a01b0319166001600160a01b039390931692909217909155600255610ab2806101256000396000f3fe6080604052600436106100555760003560e01c806305dab2881461005a5780630b1ec76014610081578063236459c7146100b2578063397bc6411461010b578063b60d428814610135578063f176cde71461013f575b600080fd5b34801561006657600080fd5b5061006f610184565b60408051918252519081900360200190f35b34801561008d57600080fd5b5061009661018a565b604080516001600160a01b039092168252519081900360200190f35b3480156100be57600080fd5b506100f7600480360360808110156100d557600080fd5b50803590602081013590604081013590606001356001600160a01b0316610199565b604080519115158252519081900360200190f35b34801561011757600080fd5b506100f76004803603602081101561012e57600080fd5b50356103c7565b61013d6103df565b005b34801561014b57600080fd5b5061013d6004803603608081101561016257600080fd5b50803590602081013590604081013590606001356001600160a01b03166103e1565b60025481565b6001546001600160a01b031681565b60006001600160a01b0382166101e357604051600160e51b62461bcd028152600401808060200182810382526027815260200180610a3a6027913960400191505060405180910390fd5b6000831161023b5760408051600160e51b62461bcd02815260206004820152601760248201527f616d6f756e74206d757374206e6f74206265207a65726f000000000000000000604482015290519081900360640190fd5b60408051602080820188905281830187905260608083018790526001600160a01b038616901b608083015282516074818403018152609490920183528151918101919091206000818152918290529190206002015460ff16156102e85760408051600160e51b62461bcd02815260206004820152601a60248201527f7472616e7366657220616c726561647920636f6d706c65746564000000000000604482015290519081900360640190fd5b600081815260208190526040812060010190805b82548110156103b15760015483546001600160a01b039091169063facd743b9085908490811061032857fe5b600091825260209182902001546040805163ffffffff851660e01b81526001600160a01b0390921660048301525160248083019392829003018186803b15801561037157600080fd5b505afa158015610385573d6000803e3d6000fd5b505050506040513d602081101561039b57600080fd5b5051156103a9576001820191505b6001016102fc565b506103ba61070c565b1115979650505050505050565b60006020819052908152604090206002015460ff1681565b565b60408051602080820187905281830186905260608083018690526001600160a01b038516901b608083015282516074818403018152609490920183528151918101919091206000818152918290529190206002015460ff161561048e5760408051600160e51b62461bcd02815260206004820152601a60248201527f7472616e7366657220616c726561647920636f6d706c65746564000000000000604482015290519081900360640190fd5b60015460408051600160e01b63facd743b02815233600482015290516001600160a01b039092169163facd743b91602480820192602092909190829003018186803b1580156104dc57600080fd5b505afa1580156104f0573d6000803e3d6000fd5b505050506040513d602081101561050657600080fd5b505161054657604051600160e51b62461bcd028152600401808060200182810382526026815260200180610a616026913960400191505060405180910390fd5b6001600160a01b03821661058e57604051600160e51b62461bcd028152600401808060200182810382526027815260200180610a3a6027913960400191505060405180910390fd5b600083116105e65760408051600160e51b62461bcd02815260206004820152601760248201527f616d6f756e74206d757374206e6f74206265207a65726f000000000000000000604482015290519081900360640190fd5b6105f0813361079f565b156106485760408051868152602081018690528082018590526001600160a01b0384166060820152905133917fdee96a12459a8c17d4cf9571d9ab18de19fa1055adff514e2d25595382d218df919081900360800190a25b61065181610830565b1561070557600081815260208190526040812060028101805460ff19166001908117909155610682929101906109da565b6040516000906001600160a01b0384169085156108fc0290869084818181858888f1604080518c8152602081018c90528082018b90526001600160a01b038a166060820152821515608082015290519196507f546c8621785b0cc9f951c75b68621fbdfce93ba6df3943b1271813c3598852d1955081900360a0019350915050a1505b5050505050565b60006064600254600160009054906101000a90046001600160a01b03166001600160a01b031663d6832ea96040518163ffffffff1660e01b815260040160206040518083038186803b15801561076157600080fd5b505afa158015610775573d6000803e3d6000fd5b505050506040513d602081101561078b57600080fd5b5051026063018161079857fe5b0490505b90565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16156107d05750600061082a565b506000828152602081815260408083206001600160a01b038516808552818452918420805460ff19166001908117909155848452908101805480830182559085529290932090910180546001600160a01b03191690911790555b92915050565b60008061083b61070c565b600084815260208190526040902060010154909150811115610861576000915050610893565b61086a83610898565b60008381526020819052604090206001015481111561088d576000915050610893565b60019150505b919050565b6000818152602081905260408120600101905b81548110156109d55760015482546001600160a01b039091169063facd743b908490849081106108d757fe5b600091825260209182902001546040805163ffffffff851660e01b81526001600160a01b0390921660048301525160248083019392829003018186803b15801561092057600080fd5b505afa158015610934573d6000803e3d6000fd5b505050506040513d602081101561094a57600080fd5b505115610959576001016109d0565b81548290600019810190811061096b57fe5b9060005260206000200160009054906101000a90046001600160a01b031682828154811061099557fe5b600091825260209091200180546001600160a01b0319166001600160a01b039290921691909117905581546109ce8360001983016109fb565b505b6108ab565b505050565b50805460008255906000526020600020908101906109f89190610a1b565b50565b8154818355818111156109d5576000838152602090206109d59181019083015b61079c91905b80821115610a355760008155600101610a21565b509056fe726563697069656e74206d757374206e6f7420626520746865207a65726f2061646472657373216d7573742062652076616c696461746f7220746f20636f6e6669726d207472616e7366657273a165627a7a7230582034f1dd7d09db2ab8295f85e72b0543ef67ff431b2d624264cea6c7a9ce4e1f1900295f76616c696461746f7273526571756972656450657263656e74206d757374206265206265747765656e203020616e642031303070726f7879206d757374206e6f7420626520746865207a65726f206164647265737321000000000000000000000000fb5a36f0e12cef9f88d95f0e02cad4ba183336dc0000000000000000000000000000000000000000000000000000000000000032" - params = %{ - "contract_source_code" => contract_source_code, - "compiler_version" => "v0.8.8+commit.dddeac2f", - "evm_version" => "default", - "name" => "Owner", - "optimization" => false, - "autodetect_constructor_args" => true - } + deployed_bytecode = + "0x6080604052600436106100555760003560e01c806305dab2881461005a5780630b1ec76014610081578063236459c7146100b2578063397bc6411461010b578063b60d428814610135578063f176cde71461013f575b600080fd5b34801561006657600080fd5b5061006f610184565b60408051918252519081900360200190f35b34801561008d57600080fd5b5061009661018a565b604080516001600160a01b039092168252519081900360200190f35b3480156100be57600080fd5b506100f7600480360360808110156100d557600080fd5b50803590602081013590604081013590606001356001600160a01b0316610199565b604080519115158252519081900360200190f35b34801561011757600080fd5b506100f76004803603602081101561012e57600080fd5b50356103c7565b61013d6103df565b005b34801561014b57600080fd5b5061013d6004803603608081101561016257600080fd5b50803590602081013590604081013590606001356001600160a01b03166103e1565b60025481565b6001546001600160a01b031681565b60006001600160a01b0382166101e357604051600160e51b62461bcd028152600401808060200182810382526027815260200180610a3a6027913960400191505060405180910390fd5b6000831161023b5760408051600160e51b62461bcd02815260206004820152601760248201527f616d6f756e74206d757374206e6f74206265207a65726f000000000000000000604482015290519081900360640190fd5b60408051602080820188905281830187905260608083018790526001600160a01b038616901b608083015282516074818403018152609490920183528151918101919091206000818152918290529190206002015460ff16156102e85760408051600160e51b62461bcd02815260206004820152601a60248201527f7472616e7366657220616c726561647920636f6d706c65746564000000000000604482015290519081900360640190fd5b600081815260208190526040812060010190805b82548110156103b15760015483546001600160a01b039091169063facd743b9085908490811061032857fe5b600091825260209182902001546040805163ffffffff851660e01b81526001600160a01b0390921660048301525160248083019392829003018186803b15801561037157600080fd5b505afa158015610385573d6000803e3d6000fd5b505050506040513d602081101561039b57600080fd5b5051156103a9576001820191505b6001016102fc565b506103ba61070c565b1115979650505050505050565b60006020819052908152604090206002015460ff1681565b565b60408051602080820187905281830186905260608083018690526001600160a01b038516901b608083015282516074818403018152609490920183528151918101919091206000818152918290529190206002015460ff161561048e5760408051600160e51b62461bcd02815260206004820152601a60248201527f7472616e7366657220616c726561647920636f6d706c65746564000000000000604482015290519081900360640190fd5b60015460408051600160e01b63facd743b02815233600482015290516001600160a01b039092169163facd743b91602480820192602092909190829003018186803b1580156104dc57600080fd5b505afa1580156104f0573d6000803e3d6000fd5b505050506040513d602081101561050657600080fd5b505161054657604051600160e51b62461bcd028152600401808060200182810382526026815260200180610a616026913960400191505060405180910390fd5b6001600160a01b03821661058e57604051600160e51b62461bcd028152600401808060200182810382526027815260200180610a3a6027913960400191505060405180910390fd5b600083116105e65760408051600160e51b62461bcd02815260206004820152601760248201527f616d6f756e74206d757374206e6f74206265207a65726f000000000000000000604482015290519081900360640190fd5b6105f0813361079f565b156106485760408051868152602081018690528082018590526001600160a01b0384166060820152905133917fdee96a12459a8c17d4cf9571d9ab18de19fa1055adff514e2d25595382d218df919081900360800190a25b61065181610830565b1561070557600081815260208190526040812060028101805460ff19166001908117909155610682929101906109da565b6040516000906001600160a01b0384169085156108fc0290869084818181858888f1604080518c8152602081018c90528082018b90526001600160a01b038a166060820152821515608082015290519196507f546c8621785b0cc9f951c75b68621fbdfce93ba6df3943b1271813c3598852d1955081900360a0019350915050a1505b5050505050565b60006064600254600160009054906101000a90046001600160a01b03166001600160a01b031663d6832ea96040518163ffffffff1660e01b815260040160206040518083038186803b15801561076157600080fd5b505afa158015610775573d6000803e3d6000fd5b505050506040513d602081101561078b57600080fd5b5051026063018161079857fe5b0490505b90565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16156107d05750600061082a565b506000828152602081815260408083206001600160a01b038516808552818452918420805460ff19166001908117909155848452908101805480830182559085529290932090910180546001600160a01b03191690911790555b92915050565b60008061083b61070c565b600084815260208190526040902060010154909150811115610861576000915050610893565b61086a83610898565b60008381526020819052604090206001015481111561088d576000915050610893565b60019150505b919050565b6000818152602081905260408120600101905b81548110156109d55760015482546001600160a01b039091169063facd743b908490849081106108d757fe5b600091825260209182902001546040805163ffffffff851660e01b81526001600160a01b0390921660048301525160248083019392829003018186803b15801561092057600080fd5b505afa158015610934573d6000803e3d6000fd5b505050506040513d602081101561094a57600080fd5b505115610959576001016109d0565b81548290600019810190811061096b57fe5b9060005260206000200160009054906101000a90046001600160a01b031682828154811061099557fe5b600091825260209091200180546001600160a01b0319166001600160a01b039290921691909117905581546109ce8360001983016109fb565b505b6108ab565b505050565b50805460008255906000526020600020908101906109f89190610a1b565b50565b8154818355818111156109d5576000838152602090206109d59181019083015b61079c91905b80821115610a355760008155600101610a21565b509056fe726563697069656e74206d757374206e6f7420626520746865207a65726f2061646472657373216d7573742062652076616c696461746f7220746f20636f6e6669726d207472616e7366657273a165627a7a7230582034f1dd7d09db2ab8295f85e72b0543ef67ff431b2d624264cea6c7a9ce4e1f190029" - assert {:error, :no_creation_data} = Verifier.evaluate_authenticity(contract_address.hash, params) - end + contract_address = insert(:contract_address, contract_code: deployed_bytecode) - test "return {:error, :deployed_bytecode}" do - contract_source_code = - "#{File.cwd!()}/test/support/fixture/smart_contract/issue_5431.sol" - |> File.read!() - - contract_creation_code = - "0x608060405234801561001057600080fd5b506040516104e13803806104e183398181016040528101906100329190610165565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a350506101a5565b600080fd5b6000819050919050565b61010c816100f9565b811461011757600080fd5b50565b60008151905061012981610103565b92915050565b6000819050919050565b6101428161012f565b811461014d57600080fd5b50565b60008151905061015f81610139565b92915050565b6000806040838503121561017c5761017b6100f4565b5b600061018a8582860161011a565b925050602061019b85828601610150565b9150509250929050565b61032d806101b46000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063893d20e81461003b578063a6f9dae114610059575b600080fd5b610043610075565b604051610050919061022a565b60405180910390f35b610073600480360381019061006e9190610276565b61009e565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461012c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161012390610300565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610214826101e9565b9050919050565b61022481610209565b82525050565b600060208201905061023f600083018461021b565b92915050565b600080fd5b61025381610209565b811461025e57600080fd5b50565b6000813590506102708161024a565b92915050565b60006020828403121561028c5761028b610245565b5b600061029a84828501610261565b91505092915050565b600082825260208201905092915050565b7f43616c6c6572206973206e6f74206f776e657200000000000000000000000000600082015250565b60006102ea6013836102a3565b91506102f5826102b4565b602082019050919050565b60006020820190508181036000830152610319816102dd565b905091905056fea164736f6c6343000808000afffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040000000000000000000000000000000000000000000000000000000000005886" - - contract_address = insert(:contract_address, contract_code: "0x") - - :transaction - |> insert( - created_contract_address_hash: contract_address.hash, - input: contract_creation_code - ) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => contract_source_code, - "compiler_version" => "v0.8.8+commit.dddeac2f", - "evm_version" => "default", - "name" => "Owner", - "optimization" => false, - "autodetect_constructor_args" => true - } - - assert {:error, :deployed_bytecode} = Verifier.evaluate_authenticity(contract_address.hash, params) - end - end + input_data = %Data{ + bytes: Base.decode16!(input, case: :lower) + } - describe "tests from constructor_arguments_test.exs" do - test "verifies with require messages" do - source_code = - "#{File.cwd!()}/test/support/fixture/smart_contract/home_bridge.sol" - |> File.read!() - - constructor_arguments = - "000000000000000000000000fb5a36f0e12cef9f88d95f0e02cad4ba183336dc0000000000000000000000000000000000000000000000000000000000000032" + :transaction + |> insert(created_contract_address_hash: contract_address.hash, input: input_data) + |> with_block(status: :ok) - input = - "608060405234801561001057600080fd5b50604051604080610c2e8339810180604052604081101561003057600080fd5b5080516020909101516001600160a01b038216610098576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180610c0b6023913960400191505060405180910390fd5b60648111156100f2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526034815260200180610bd76034913960400191505060405180910390fd5b600180546001600160a01b0319166001600160a01b039390931692909217909155600255610ab2806101256000396000f3fe6080604052600436106100555760003560e01c806305dab2881461005a5780630b1ec76014610081578063236459c7146100b2578063397bc6411461010b578063b60d428814610135578063f176cde71461013f575b600080fd5b34801561006657600080fd5b5061006f610184565b60408051918252519081900360200190f35b34801561008d57600080fd5b5061009661018a565b604080516001600160a01b039092168252519081900360200190f35b3480156100be57600080fd5b506100f7600480360360808110156100d557600080fd5b50803590602081013590604081013590606001356001600160a01b0316610199565b604080519115158252519081900360200190f35b34801561011757600080fd5b506100f76004803603602081101561012e57600080fd5b50356103c7565b61013d6103df565b005b34801561014b57600080fd5b5061013d6004803603608081101561016257600080fd5b50803590602081013590604081013590606001356001600160a01b03166103e1565b60025481565b6001546001600160a01b031681565b60006001600160a01b0382166101e357604051600160e51b62461bcd028152600401808060200182810382526027815260200180610a3a6027913960400191505060405180910390fd5b6000831161023b5760408051600160e51b62461bcd02815260206004820152601760248201527f616d6f756e74206d757374206e6f74206265207a65726f000000000000000000604482015290519081900360640190fd5b60408051602080820188905281830187905260608083018790526001600160a01b038616901b608083015282516074818403018152609490920183528151918101919091206000818152918290529190206002015460ff16156102e85760408051600160e51b62461bcd02815260206004820152601a60248201527f7472616e7366657220616c726561647920636f6d706c65746564000000000000604482015290519081900360640190fd5b600081815260208190526040812060010190805b82548110156103b15760015483546001600160a01b039091169063facd743b9085908490811061032857fe5b600091825260209182902001546040805163ffffffff851660e01b81526001600160a01b0390921660048301525160248083019392829003018186803b15801561037157600080fd5b505afa158015610385573d6000803e3d6000fd5b505050506040513d602081101561039b57600080fd5b5051156103a9576001820191505b6001016102fc565b506103ba61070c565b1115979650505050505050565b60006020819052908152604090206002015460ff1681565b565b60408051602080820187905281830186905260608083018690526001600160a01b038516901b608083015282516074818403018152609490920183528151918101919091206000818152918290529190206002015460ff161561048e5760408051600160e51b62461bcd02815260206004820152601a60248201527f7472616e7366657220616c726561647920636f6d706c65746564000000000000604482015290519081900360640190fd5b60015460408051600160e01b63facd743b02815233600482015290516001600160a01b039092169163facd743b91602480820192602092909190829003018186803b1580156104dc57600080fd5b505afa1580156104f0573d6000803e3d6000fd5b505050506040513d602081101561050657600080fd5b505161054657604051600160e51b62461bcd028152600401808060200182810382526026815260200180610a616026913960400191505060405180910390fd5b6001600160a01b03821661058e57604051600160e51b62461bcd028152600401808060200182810382526027815260200180610a3a6027913960400191505060405180910390fd5b600083116105e65760408051600160e51b62461bcd02815260206004820152601760248201527f616d6f756e74206d757374206e6f74206265207a65726f000000000000000000604482015290519081900360640190fd5b6105f0813361079f565b156106485760408051868152602081018690528082018590526001600160a01b0384166060820152905133917fdee96a12459a8c17d4cf9571d9ab18de19fa1055adff514e2d25595382d218df919081900360800190a25b61065181610830565b1561070557600081815260208190526040812060028101805460ff19166001908117909155610682929101906109da565b6040516000906001600160a01b0384169085156108fc0290869084818181858888f1604080518c8152602081018c90528082018b90526001600160a01b038a166060820152821515608082015290519196507f546c8621785b0cc9f951c75b68621fbdfce93ba6df3943b1271813c3598852d1955081900360a0019350915050a1505b5050505050565b60006064600254600160009054906101000a90046001600160a01b03166001600160a01b031663d6832ea96040518163ffffffff1660e01b815260040160206040518083038186803b15801561076157600080fd5b505afa158015610775573d6000803e3d6000fd5b505050506040513d602081101561078b57600080fd5b5051026063018161079857fe5b0490505b90565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16156107d05750600061082a565b506000828152602081815260408083206001600160a01b038516808552818452918420805460ff19166001908117909155848452908101805480830182559085529290932090910180546001600160a01b03191690911790555b92915050565b60008061083b61070c565b600084815260208190526040902060010154909150811115610861576000915050610893565b61086a83610898565b60008381526020819052604090206001015481111561088d576000915050610893565b60019150505b919050565b6000818152602081905260408120600101905b81548110156109d55760015482546001600160a01b039091169063facd743b908490849081106108d757fe5b600091825260209182902001546040805163ffffffff851660e01b81526001600160a01b0390921660048301525160248083019392829003018186803b15801561092057600080fd5b505afa158015610934573d6000803e3d6000fd5b505050506040513d602081101561094a57600080fd5b505115610959576001016109d0565b81548290600019810190811061096b57fe5b9060005260206000200160009054906101000a90046001600160a01b031682828154811061099557fe5b600091825260209091200180546001600160a01b0319166001600160a01b039290921691909117905581546109ce8360001983016109fb565b505b6108ab565b505050565b50805460008255906000526020600020908101906109f89190610a1b565b50565b8154818355818111156109d5576000838152602090206109d59181019083015b61079c91905b80821115610a355760008155600101610a21565b509056fe726563697069656e74206d757374206e6f7420626520746865207a65726f2061646472657373216d7573742062652076616c696461746f7220746f20636f6e6669726d207472616e7366657273a165627a7a7230582034f1dd7d09db2ab8295f85e72b0543ef67ff431b2d624264cea6c7a9ce4e1f1900295f76616c696461746f7273526571756972656450657263656e74206d757374206265206265747765656e203020616e642031303070726f7879206d757374206e6f7420626520746865207a65726f206164647265737321000000000000000000000000fb5a36f0e12cef9f88d95f0e02cad4ba183336dc0000000000000000000000000000000000000000000000000000000000000032" + params = %{ + "contract_source_code" => source_code, + "compiler_version" => "v0.5.8+commit.23d335f2", + "evm_version" => "default", + "name" => "HomeBridge", + "optimization" => true, + "optimization_runs" => @optimization_runs, + "autodetect_constructor_args" => true + } - deployed_bytecode = - "0x6080604052600436106100555760003560e01c806305dab2881461005a5780630b1ec76014610081578063236459c7146100b2578063397bc6411461010b578063b60d428814610135578063f176cde71461013f575b600080fd5b34801561006657600080fd5b5061006f610184565b60408051918252519081900360200190f35b34801561008d57600080fd5b5061009661018a565b604080516001600160a01b039092168252519081900360200190f35b3480156100be57600080fd5b506100f7600480360360808110156100d557600080fd5b50803590602081013590604081013590606001356001600160a01b0316610199565b604080519115158252519081900360200190f35b34801561011757600080fd5b506100f76004803603602081101561012e57600080fd5b50356103c7565b61013d6103df565b005b34801561014b57600080fd5b5061013d6004803603608081101561016257600080fd5b50803590602081013590604081013590606001356001600160a01b03166103e1565b60025481565b6001546001600160a01b031681565b60006001600160a01b0382166101e357604051600160e51b62461bcd028152600401808060200182810382526027815260200180610a3a6027913960400191505060405180910390fd5b6000831161023b5760408051600160e51b62461bcd02815260206004820152601760248201527f616d6f756e74206d757374206e6f74206265207a65726f000000000000000000604482015290519081900360640190fd5b60408051602080820188905281830187905260608083018790526001600160a01b038616901b608083015282516074818403018152609490920183528151918101919091206000818152918290529190206002015460ff16156102e85760408051600160e51b62461bcd02815260206004820152601a60248201527f7472616e7366657220616c726561647920636f6d706c65746564000000000000604482015290519081900360640190fd5b600081815260208190526040812060010190805b82548110156103b15760015483546001600160a01b039091169063facd743b9085908490811061032857fe5b600091825260209182902001546040805163ffffffff851660e01b81526001600160a01b0390921660048301525160248083019392829003018186803b15801561037157600080fd5b505afa158015610385573d6000803e3d6000fd5b505050506040513d602081101561039b57600080fd5b5051156103a9576001820191505b6001016102fc565b506103ba61070c565b1115979650505050505050565b60006020819052908152604090206002015460ff1681565b565b60408051602080820187905281830186905260608083018690526001600160a01b038516901b608083015282516074818403018152609490920183528151918101919091206000818152918290529190206002015460ff161561048e5760408051600160e51b62461bcd02815260206004820152601a60248201527f7472616e7366657220616c726561647920636f6d706c65746564000000000000604482015290519081900360640190fd5b60015460408051600160e01b63facd743b02815233600482015290516001600160a01b039092169163facd743b91602480820192602092909190829003018186803b1580156104dc57600080fd5b505afa1580156104f0573d6000803e3d6000fd5b505050506040513d602081101561050657600080fd5b505161054657604051600160e51b62461bcd028152600401808060200182810382526026815260200180610a616026913960400191505060405180910390fd5b6001600160a01b03821661058e57604051600160e51b62461bcd028152600401808060200182810382526027815260200180610a3a6027913960400191505060405180910390fd5b600083116105e65760408051600160e51b62461bcd02815260206004820152601760248201527f616d6f756e74206d757374206e6f74206265207a65726f000000000000000000604482015290519081900360640190fd5b6105f0813361079f565b156106485760408051868152602081018690528082018590526001600160a01b0384166060820152905133917fdee96a12459a8c17d4cf9571d9ab18de19fa1055adff514e2d25595382d218df919081900360800190a25b61065181610830565b1561070557600081815260208190526040812060028101805460ff19166001908117909155610682929101906109da565b6040516000906001600160a01b0384169085156108fc0290869084818181858888f1604080518c8152602081018c90528082018b90526001600160a01b038a166060820152821515608082015290519196507f546c8621785b0cc9f951c75b68621fbdfce93ba6df3943b1271813c3598852d1955081900360a0019350915050a1505b5050505050565b60006064600254600160009054906101000a90046001600160a01b03166001600160a01b031663d6832ea96040518163ffffffff1660e01b815260040160206040518083038186803b15801561076157600080fd5b505afa158015610775573d6000803e3d6000fd5b505050506040513d602081101561078b57600080fd5b5051026063018161079857fe5b0490505b90565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16156107d05750600061082a565b506000828152602081815260408083206001600160a01b038516808552818452918420805460ff19166001908117909155848452908101805480830182559085529290932090910180546001600160a01b03191690911790555b92915050565b60008061083b61070c565b600084815260208190526040902060010154909150811115610861576000915050610893565b61086a83610898565b60008381526020819052604090206001015481111561088d576000915050610893565b60019150505b919050565b6000818152602081905260408120600101905b81548110156109d55760015482546001600160a01b039091169063facd743b908490849081106108d757fe5b600091825260209182902001546040805163ffffffff851660e01b81526001600160a01b0390921660048301525160248083019392829003018186803b15801561092057600080fd5b505afa158015610934573d6000803e3d6000fd5b505050506040513d602081101561094a57600080fd5b505115610959576001016109d0565b81548290600019810190811061096b57fe5b9060005260206000200160009054906101000a90046001600160a01b031682828154811061099557fe5b600091825260209091200180546001600160a01b0319166001600160a01b039290921691909117905581546109ce8360001983016109fb565b505b6108ab565b505050565b50805460008255906000526020600020908101906109f89190610a1b565b50565b8154818355818111156109d5576000838152602090206109d59181019083015b61079c91905b80821115610a355760008155600101610a21565b509056fe726563697069656e74206d757374206e6f7420626520746865207a65726f2061646472657373216d7573742062652076616c696461746f7220746f20636f6e6669726d207472616e7366657273a165627a7a7230582034f1dd7d09db2ab8295f85e72b0543ef67ff431b2d624264cea6c7a9ce4e1f190029" + assert {:ok, + %{ + abi: abi, + constructor_arguments: ^constructor_arguments + }} = Verifier.evaluate_authenticity(contract_address.hash, params) - contract_address = insert(:contract_address, contract_code: deployed_bytecode) + assert abi != nil + end - input_data = %Data{ - bytes: Base.decode16!(input, case: :lower) - } + test "verifies with string in keccak256" do + source_code = + "#{File.cwd!()}/test/support/fixture/smart_contract/ERC677.sol" + |> File.read!() - :transaction - |> insert(created_contract_address_hash: contract_address.hash, input: input_data) - |> with_block(status: :ok) + input = + "60806040523480156200001157600080fd5b5060405162002f3f38038062002f3f833981810160405260a08110156200003757600080fd5b8101908080516401000000008111156200005057600080fd5b820160208101848111156200006457600080fd5b81516401000000008111828201871017156200007f57600080fd5b505092919060200180516401000000008111156200009c57600080fd5b82016020810184811115620000b057600080fd5b8151640100000000811182820187101715620000cb57600080fd5b50506020820151604080840151606090940151600080546001600160a01b031916331780825592519497509295509287928792879287928792879287926012928592859285926001600160a01b0316917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a38251620001559060049060208601906200058d565b5081516200016b9060059060208501906200058d565b506006805460ff191660ff92909216919091179055505060405180605262002eed8239604080519182900360520182208651602097880120838301835260018085527f310000000000000000000000000000000000000000000000000000000000000094890194909452825180890192909252818301527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6606082015260808101929092523060a0808401919091528151808403909101815260c090920190528051908501206007555062000257926001600160a01b038716925062001e46620003ec821b17901c9050565b80156200027e57506200027e826001600160a01b0316620003ec60201b62001e461760201c565b8015620002a55750620002a5816001600160a01b0316620003ec60201b62001e461760201c565b6200031157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f6e6f74206120636f6e7472616374206164647265737300000000000000000000604482015290519081900360640190fd5b607b6200032884826001600160e01b03620003f216565b600a80546001600160a01b038087166001600160a01b03199283168117909355600b8054878316908416179055600c8054918616919092161790556040805183815290517f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968859181900360200190a250506001600160a01b036000819052600d6020527fa934977eb9828ba1f50591af02c98441645b4f0e916e0fecb4cc8e9c633dade280546001600160a01b03191690911790555062000632975050505050505050565b3b151590565b6001600160a01b0382166200046857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b62000484816003546200051160201b62001de51790919060201c565b6003556001600160a01b038216600090815260016020908152604090912054620004b991839062001de562000511821b17901c565b6001600160a01b03831660008181526001602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6000828201838110156200058657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620005d057805160ff191683800117855562000600565b8280016001018555821562000600579182015b8281111562000600578251825591602001919060010190620005e3565b506200060e92915062000612565b5090565b6200062f91905b808211156200060e576000815560010162000619565b90565b6128ab80620006426000396000f3fe608060405234801561001057600080fd5b50600436106102325760003560e01c8063726600ce11610130578063a457c2d7116100b8578063dd62ed3e1161007c578063dd62ed3e1461075f578063f2d5d56b1461078d578063f2fde38b146107b9578063fbb2a53f146107df578063ff9e884d146107e757610232565b8063a457c2d71461069d578063a9059cbb146106c9578063b753a98c146106f5578063bb35783b14610721578063c794c7691461075757610232565b80638f32d59b116100ff5780638f32d59b146105b55780638fcbaf0c146105bd57806395d89b41146106175780639712fdf81461061f5780639da38e2f1461064557610232565b8063726600ce146105595780637a13685a1461057f5780637ecebe00146105875780638da5cb5b146105ad57610232565b806337fb7e21116101be57806354fd4d501161018257806354fd4d50146104ed57806369ffa08a146104f55780636e15d21b1461052357806370a082311461052b578063715018a61461055157610232565b806337fb7e21146103c657806339509351146103ea5780634000aea01461041657806340c10f191461049b5780634bcb88bc146104c757610232565b8063238a3fe111610205578063238a3fe11461033657806323b872dd1461036257806330adf81f14610398578063313ce567146103a05780633644e515146103be57610232565b806304df017d1461023757806306fdde031461025f578063095ea7b3146102dc57806318160ddd1461031c575b600080fd5b61025d6004803603602081101561024d57600080fd5b50356001600160a01b0316610815565b005b610267610a56565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102a1578181015183820152602001610289565b50505050905090810190601f1680156102ce5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610308600480360360408110156102f257600080fd5b506001600160a01b038135169060200135610aec565b604080519115158252519081900360200190f35b610324610b02565b60408051918252519081900360200190f35b6103086004803603604081101561034c57600080fd5b506001600160a01b038135169060200135610b08565b6103086004803603606081101561037857600080fd5b506001600160a01b03813581169160208101359091169060400135610b8d565b610324610baf565b6103a8610bd3565b6040805160ff9092168252519081900360200190f35b610324610bdc565b6103ce610be2565b604080516001600160a01b039092168252519081900360200190f35b6103086004803603604081101561040057600080fd5b506001600160a01b038135169060200135610bf1565b6103086004803603606081101561042c57600080fd5b6001600160a01b038235169160208101359181019060608101604082013564010000000081111561045c57600080fd5b82018360208201111561046e57600080fd5b8035906020019184600183028401116401000000008311171561049057600080fd5b509092509050610c32565b610308600480360360408110156104b157600080fd5b506001600160a01b038135169060200135610dd2565b6103ce600480360360208110156104dd57600080fd5b50356001600160a01b0316610e80565b610267610e9b565b61025d6004803603604081101561050b57600080fd5b506001600160a01b0381358116916020013516610eb8565b6103ce611083565b6103246004803603602081101561054157600080fd5b50356001600160a01b0316611092565b61025d6110ad565b6103086004803603602081101561056f57600080fd5b50356001600160a01b0316611133565b6103ce61116d565b6103246004803603602081101561059d57600080fd5b50356001600160a01b031661117c565b6103ce61118e565b61030861119d565b61025d60048036036101008110156105d457600080fd5b506001600160a01b038135811691602081013590911690604081013590606081013590608081013515159060ff60a0820135169060c08101359060e001356111ae565b610267611446565b61025d6004803603602081101561063557600080fd5b50356001600160a01b03166114a7565b61064d6116e6565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610689578181015183820152602001610671565b505050509050019250505060405180910390f35b610308600480360360408110156106b357600080fd5b506001600160a01b03813516906020013561182a565b610308600480360360408110156106df57600080fd5b506001600160a01b038135169060200135611866565b61025d6004803603604081101561070b57600080fd5b506001600160a01b03813516906020013561187d565b61025d6004803603606081101561073757600080fd5b506001600160a01b03813581169160208101359091169060400135611888565b6103ce611899565b6103246004803603604081101561077557600080fd5b506001600160a01b03813581169160200135166118a4565b61025d600480360360408110156107a357600080fd5b506001600160a01b0381351690602001356118cf565b61025d600480360360208110156107cf57600080fd5b50356001600160a01b03166118da565b61032461192d565b610324600480360360408110156107fd57600080fd5b506001600160a01b0381358116916020013516611933565b61081d61119d565b61085c576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b61086581611133565b6108ad576040805162461bcd60e51b8152602060048201526014602482015273189c9a5919d9481a5cdb89dd08195e1a5cdd195960621b604482015290519081900360640190fd5b6001600160a01b038082166000908152600d6020526040812054908290526000805160206126cb833981519152549082169190811680610929576040805162461bcd60e51b81526020600482015260126024820152711e995c9bc81859191c995cdcc8199bdd5b9960721b604482015290519081900360640190fd5b836001600160a01b0316816001600160a01b0316146109c8576001600160a01b038082166000908152600d602052604090205491925090811690811480159061097a57506001600160a01b03811615155b6109c3576040805162461bcd60e51b81526020600482015260156024820152741a5b9d985b1a59081859191c995cdcc8199bdd5b99605a1b604482015290519081900360640190fd5b610929565b6001600160a01b038083166000908152600d602052604080822080548488166001600160a01b0319918216179091559287168252902080549091169055600e54610a1990600163ffffffff61195016565b600e556040516001600160a01b038516907f5d9d5034656cb3ebfb0655057cd7f9b4077a9b42ff42ce223cbac5bc586d212690600090a250505050565b60048054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610ae25780601f10610ab757610100808354040283529160200191610ae2565b820191906000526020600020905b815481529060010190602001808311610ac557829003601f168201915b5050505050905090565b6000610af93384846119ad565b50600192915050565b60035490565b600a546000906001600160a01b0316331480610b2e5750600b546001600160a01b031633145b80610b435750600c546001600160a01b031633145b610b83576040805162461bcd60e51b815260206004820152600c60248201526b3bb937b7339039b2b73232b960a11b604482015290519081900360640190fd5b610af98383611a99565b6000610b9a848484611bd9565b610ba5848484611c2c565b5060019392505050565b7fea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb81565b60065460ff1690565b60075481565b600a546001600160a01b031681565b3360008181526002602090815260408083206001600160a01b03871684529091528120549091610af9918590610c2d908663ffffffff611de516565b6119ad565b6000846001600160a01b03811615801590610c5657506001600160a01b0381163014155b610c9f576040805162461bcd60e51b81526020600482015260156024820152741b9bdd0818481d985b1a59081c9958da5c1a595b9d605a1b604482015290519081900360640190fd5b610ca98686611a99565b856001600160a01b0316336001600160a01b03167fe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c1687878760405180848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f1916909201829003965090945050505050a3610d37866001600160a01b0316611e46565b15610dc657610d7e33878787878080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611e4c92505050565b610dc6576040805162461bcd60e51b815260206004820152601460248201527318dbdb9d1c9858dd0818d85b1b0819985a5b195960621b604482015290519081900360640190fd5b50600195945050505050565b6000610ddd33611133565b610e2e576040805162461bcd60e51b815260206004820152601860248201527f63616c6c6572206973206e6f7420746865206272696467650000000000000000604482015290519081900360640190fd5b610e388383612039565b6040805183815290516001600160a01b038516917f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885919081900360200190a250600192915050565b600d602052600090815260409020546001600160a01b031681565b604051806040016040528060018152602001603160f81b81525081565b610ec061119d565b610eff576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b806001600160a01b03811615801590610f2157506001600160a01b0381163014155b610f6a576040805162461bcd60e51b81526020600482015260156024820152741b9bdd0818481d985b1a59081c9958da5c1a595b9d605a1b604482015290519081900360640190fd5b6001600160a01b038316610fe8576040513031906001600160a01b0384169082156108fc029083906000818181858888f19350505050610fe2578083604051610fb290612621565b6001600160a01b039091168152604051908190036020019082f080158015610fde573d6000803e3d6000fd5b5050505b5061107e565b604080516370a0823160e01b8152306004820152905184916000916001600160a01b038416916370a08231916024808301926020929190829003018186803b15801561103357600080fd5b505afa158015611047573d6000803e3d6000fd5b505050506040513d602081101561105d57600080fd5b5051905061107b6001600160a01b038316858363ffffffff61212b16565b50505b505050565b600c546001600160a01b031681565b6001600160a01b031660009081526001602052604090205490565b6110b561119d565b6110f4576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b6040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b5c1b195b595b9d1959608a1b604482015290519081900360640190fd5b60006001600160a01b038281161480159061116757506001600160a01b038281166000908152600d60205260409020541615155b92915050565b600b546001600160a01b031681565b60086020526000908152604090205481565b6000546001600160a01b031690565b6000546001600160a01b0316331490565b8415806111c25750846111bf61217d565b11155b611204576040805162461bcd60e51b815260206004820152600e60248201526d696e76616c69642065787069727960901b604482015290519081900360640190fd5b600754604080517fea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb6020808301919091526001600160a01b03808d16838501528b166060830152608082018a905260a0820189905287151560c0808401919091528351808403909101815260e08301845280519082012061190160f01b610100840152610102830194909452610122808301949094528251808303909401845261014282018084528451948201949094206000909452610162820180845284905260ff87166101828301526101a282018690526101c2820185905291516001926101e2808401939192601f1981019281900390910190855afa15801561130e573d6000803e3d6000fd5b505050602060405103516001600160a01b0316896001600160a01b03161461137d576040805162461bcd60e51b815260206004820152601f60248201527f696e76616c6964207369676e6174757265206f7220706172616d657465727300604482015290519081900360640190fd5b6001600160a01b038916600090815260086020526040902080546001810190915587146113e1576040805162461bcd60e51b815260206004820152600d60248201526c696e76616c6964206e6f6e636560981b604482015290519081900360640190fd5b6000856113ef5760006113f3565b6000195b90506114008a8a836119ad565b8561140c57600061140e565b865b6001600160a01b039a8b1660009081526009602090815260408083209c909d1682529a909a5299909820989098555050505050505050565b60058054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610ae25780601f10610ab757610100808354040283529160200191610ae2565b6114af61119d565b6114ee576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b6032600e541061152f5760405162461bcd60e51b81526004018080602001828103825260288152602001806127816028913960400191505060405180910390fd5b611541816001600160a01b0316611e46565b61158b576040805162461bcd60e51b81526020600482015260166024820152756e6f74206120636f6e7472616374206164647265737360501b604482015290519081900360640190fd5b61159481611133565b156115de576040805162461bcd60e51b815260206004820152601560248201527462726964676520616c72656164792065786973747360581b604482015290519081900360640190fd5b6001600160a01b036000819052600d6020526000805160206126cb833981519152541680611653576040805162461bcd60e51b815260206004820152601c60248201527f666972737420627269646765206973207a65726f206164647265737300000000604482015290519081900360640190fd5b600d6020526000805160206126cb83398151915280546001600160a01b03199081166001600160a01b038581169182179093556000908152604090208054909116918316919091179055600e546116ab906001611de5565b600e556040516001600160a01b038316907f3cda433c5679ae4c6a5dea50840e222a42cba3695e4663de4366be899348422190600090a25050565b606080600e54604051908082528060200260200182016040528015611715578160200160208202803883390190505b506001600160a01b036000818152600d6020526000805160206126cb83398151915254929350911680611784576040805162461bcd60e51b81526020600482015260126024820152711e995c9bc81859191c995cdcc8199bdd5b9960721b604482015290519081900360640190fd5b6001600160a01b038181161461182257808383815181106117a157fe5b6001600160a01b039283166020918202929092018101919091529181166000908152600d90925260409091205460019290920191168061181d576040805162461bcd60e51b81526020600482015260126024820152711e995c9bc81859191c995cdcc8199bdd5b9960721b604482015290519081900360640190fd5b611784565b509091505090565b3360008181526002602090815260408083206001600160a01b03871684529091528120549091610af9918590610c2d908663ffffffff61195016565b60006118728383611a99565b610af9338484611c2c565b61107e338383610b8d565b611893838383610b8d565b50505050565b6001600160a01b0381565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205490565b61107e823383610b8d565b6118e261119d565b611921576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b61192a81612181565b50565b600e5481565b600960209081526000928352604080842090915290825290205481565b6000828211156119a7576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b6001600160a01b0383166119f25760405162461bcd60e51b81526004018080602001828103825260248152602001806127ce6024913960400191505060405180910390fd5b6001600160a01b038216611a375760405162461bcd60e51b81526004018080602001828103825260228152602001806126a96022913960400191505060405180910390fd5b6001600160a01b03808416600081815260026020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b600a546000906001600160a01b0316331480611abf5750600b546001600160a01b031633145b80611ad45750600c546001600160a01b031633145b15611b885733600090815260016020526040902054611af9908363ffffffff61195016565b33600090815260016020526040808220929092556001600160a01b03851681522054611b2b908363ffffffff611de516565b6001600160a01b0384166000818152600160209081526040918290209390935580518581529051919233927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a3506001611b95565b611b928383612221565b90505b8061107e576040805162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015290519081900360640190fd5b6000611be684848461222e565b905080611893576040805162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015290519081900360640190fd5b611c3e826001600160a01b0316611e46565b8015611c655750604080516000815260208101909152611c6390849084908490611e4c565b155b1561107e57611c7382611133565b15611caf5760405162461bcd60e51b81526004018080602001828103825260258152602001806127166025913960400191505060405180910390fd5b600a546001600160a01b0383811691161415611cfc5760405162461bcd60e51b815260040180806020018281038252602b8152602001806126eb602b913960400191505060405180910390fd5b600b546001600160a01b0383811691161415611d495760405162461bcd60e51b815260040180806020018281038252602e815260200180612849602e913960400191505060405180910390fd5b600c546001600160a01b0383811691161415611d965760405162461bcd60e51b815260040180806020018281038252602d81526020018061281c602d913960400191505060405180910390fd5b604080516001600160a01b0380861682528416602082015280820183905290517f11249f0fc79fc134a15a10d1da8291b79515bf987e036ced05b9ec119614070b9181900360600190a1505050565b600082820183811015611e3f576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b3b151590565b6000606060405180606001604052806026815260200161275b6026913990506000856001600160a01b03168288878760405160240180846001600160a01b03166001600160a01b0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b83811015611ed9578181015183820152602001611ec1565b50505050905090810190601f168015611f065780820380516001836020036101000a031916815260200191505b50945050505050604051602081830303815290604052906040518082805190602001908083835b60208310611f4c5780518252601f199092019160209182019101611f2d565b51815160001960209485036101000a01908116901991909116179052604080519490920184900390932092860180516001600160e01b03166001600160e01b031990941693909317835251855190945084935090508083835b60208310611fc45780518252601f199092019160209182019101611fa5565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612026576040519150601f19603f3d011682016040523d82523d6000602084013e61202b565b606091505b509098975050505050505050565b6001600160a01b038216612094576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b6003546120a7908263ffffffff611de516565b6003556001600160a01b0382166000908152600160205260409020546120d3908263ffffffff611de516565b6001600160a01b03831660008181526001602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261107e908490612325565b4290565b6001600160a01b0381166121c65760405162461bcd60e51b81526004018080602001828103825260268152602001806126836026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000610af93384846124dd565b600061223b8484846124dd565b6001600160a01b0384163314610ba557600061225785336118a4565b9050600019811461227c576122778533610c2d848763ffffffff61195016565b61231f565b6001600160a01b038516600090815260096020908152604080832033845290915290205415806122d657506122af61217d565b6001600160a01b038616600090815260096020908152604080832033845290915290205410155b61231f576040805162461bcd60e51b8152602060048201526015602482015274195e1c1a5c9e481a5cc81a5b881d1a19481c185cdd605a1b604482015290519081900360640190fd5b50610ba5565b612337826001600160a01b0316611e46565b612388576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106123c65780518252601f1990920191602091820191016123a7565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612428576040519150601f19603f3d011682016040523d82523d6000602084013e61242d565b606091505b509150915081612484576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115611893578080602001905160208110156124a057600080fd5b50516118935760405162461bcd60e51b815260040180806020018281038252602a8152602001806127f2602a913960400191505060405180910390fd5b6001600160a01b0383166125225760405162461bcd60e51b81526004018080602001828103825260258152602001806127a96025913960400191505060405180910390fd5b6001600160a01b0382166125675760405162461bcd60e51b81526004018080602001828103825260238152602001806126606023913960400191505060405180910390fd5b6001600160a01b038316600090815260016020526040902054612590908263ffffffff61195016565b6001600160a01b0380851660009081526001602052604080822093909355908416815220546125c5908263ffffffff611de516565b6001600160a01b0380841660008181526001602090815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b60328061262e8339019056fe60806040526040516032380380603283398181016040526020811015602357600080fd5b50516001600160a01b038116fffe45524332303a207472616e7366657220746f20746865207a65726f20616464726573734f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f2061646472657373a934977eb9828ba1f50591af02c98441645b4f0e916e0fecb4cc8e9c633dade2796f752063616e2774207472616e7366657220746f20446973747269627574696f6e20636f6e7472616374796f752063616e2774207472616e7366657220746f2062726964676520636f6e74726163744f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726f6e546f6b656e5472616e7366657228616464726573732c75696e743235362c62797465732963616e277420616464206f6e65206d6f7265206272696467652064756520746f2061206c696d697445524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f20616464726573735361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564796f752063616e2774207472616e7366657220746f2041647669736f727352657761726420636f6e7472616374796f752063616e2774207472616e7366657220746f20507269766174654f66666572696e6720636f6e7472616374a265627a7a72305820fbfacd4b36dace16f9fd57104c8aafe8b5a519b29f407207baf7bb6b917422a764736f6c634300050a0032454950373132446f6d61696e28737472696e67206e616d652c737472696e672076657273696f6e2c75696e7432353620636861696e49642c6164647265737320766572696679696e67436f6e74726163742900000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000348448061f604f4adf7ba714460c03cc6eb5b9f4000000000000000000000000348448061f604f4adf7ba714460c03cc6eb5b9f4000000000000000000000000348448061f604f4adf7ba714460c03cc6eb5b9f40000000000000000000000000000000000000000000000000000000000000003717765000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037177650000000000000000000000000000000000000000000000000000000000" - params = %{ - "contract_source_code" => source_code, - "compiler_version" => "v0.5.8+commit.23d335f2", - "evm_version" => "default", - "name" => "HomeBridge", - "optimization" => true, - "optimization_runs" => 200, - "autodetect_constructor_args" => true - } + deployed_bytecode = + "0x608060405234801561001057600080fd5b50600436106102325760003560e01c8063726600ce11610130578063a457c2d7116100b8578063dd62ed3e1161007c578063dd62ed3e1461075f578063f2d5d56b1461078d578063f2fde38b146107b9578063fbb2a53f146107df578063ff9e884d146107e757610232565b8063a457c2d71461069d578063a9059cbb146106c9578063b753a98c146106f5578063bb35783b14610721578063c794c7691461075757610232565b80638f32d59b116100ff5780638f32d59b146105b55780638fcbaf0c146105bd57806395d89b41146106175780639712fdf81461061f5780639da38e2f1461064557610232565b8063726600ce146105595780637a13685a1461057f5780637ecebe00146105875780638da5cb5b146105ad57610232565b806337fb7e21116101be57806354fd4d501161018257806354fd4d50146104ed57806369ffa08a146104f55780636e15d21b1461052357806370a082311461052b578063715018a61461055157610232565b806337fb7e21146103c657806339509351146103ea5780634000aea01461041657806340c10f191461049b5780634bcb88bc146104c757610232565b8063238a3fe111610205578063238a3fe11461033657806323b872dd1461036257806330adf81f14610398578063313ce567146103a05780633644e515146103be57610232565b806304df017d1461023757806306fdde031461025f578063095ea7b3146102dc57806318160ddd1461031c575b600080fd5b61025d6004803603602081101561024d57600080fd5b50356001600160a01b0316610815565b005b610267610a56565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102a1578181015183820152602001610289565b50505050905090810190601f1680156102ce5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610308600480360360408110156102f257600080fd5b506001600160a01b038135169060200135610aec565b604080519115158252519081900360200190f35b610324610b02565b60408051918252519081900360200190f35b6103086004803603604081101561034c57600080fd5b506001600160a01b038135169060200135610b08565b6103086004803603606081101561037857600080fd5b506001600160a01b03813581169160208101359091169060400135610b8d565b610324610baf565b6103a8610bd3565b6040805160ff9092168252519081900360200190f35b610324610bdc565b6103ce610be2565b604080516001600160a01b039092168252519081900360200190f35b6103086004803603604081101561040057600080fd5b506001600160a01b038135169060200135610bf1565b6103086004803603606081101561042c57600080fd5b6001600160a01b038235169160208101359181019060608101604082013564010000000081111561045c57600080fd5b82018360208201111561046e57600080fd5b8035906020019184600183028401116401000000008311171561049057600080fd5b509092509050610c32565b610308600480360360408110156104b157600080fd5b506001600160a01b038135169060200135610dd2565b6103ce600480360360208110156104dd57600080fd5b50356001600160a01b0316610e80565b610267610e9b565b61025d6004803603604081101561050b57600080fd5b506001600160a01b0381358116916020013516610eb8565b6103ce611083565b6103246004803603602081101561054157600080fd5b50356001600160a01b0316611092565b61025d6110ad565b6103086004803603602081101561056f57600080fd5b50356001600160a01b0316611133565b6103ce61116d565b6103246004803603602081101561059d57600080fd5b50356001600160a01b031661117c565b6103ce61118e565b61030861119d565b61025d60048036036101008110156105d457600080fd5b506001600160a01b038135811691602081013590911690604081013590606081013590608081013515159060ff60a0820135169060c08101359060e001356111ae565b610267611446565b61025d6004803603602081101561063557600080fd5b50356001600160a01b03166114a7565b61064d6116e6565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610689578181015183820152602001610671565b505050509050019250505060405180910390f35b610308600480360360408110156106b357600080fd5b506001600160a01b03813516906020013561182a565b610308600480360360408110156106df57600080fd5b506001600160a01b038135169060200135611866565b61025d6004803603604081101561070b57600080fd5b506001600160a01b03813516906020013561187d565b61025d6004803603606081101561073757600080fd5b506001600160a01b03813581169160208101359091169060400135611888565b6103ce611899565b6103246004803603604081101561077557600080fd5b506001600160a01b03813581169160200135166118a4565b61025d600480360360408110156107a357600080fd5b506001600160a01b0381351690602001356118cf565b61025d600480360360208110156107cf57600080fd5b50356001600160a01b03166118da565b61032461192d565b610324600480360360408110156107fd57600080fd5b506001600160a01b0381358116916020013516611933565b61081d61119d565b61085c576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b61086581611133565b6108ad576040805162461bcd60e51b8152602060048201526014602482015273189c9a5919d9481a5cdb89dd08195e1a5cdd195960621b604482015290519081900360640190fd5b6001600160a01b038082166000908152600d6020526040812054908290526000805160206126cb833981519152549082169190811680610929576040805162461bcd60e51b81526020600482015260126024820152711e995c9bc81859191c995cdcc8199bdd5b9960721b604482015290519081900360640190fd5b836001600160a01b0316816001600160a01b0316146109c8576001600160a01b038082166000908152600d602052604090205491925090811690811480159061097a57506001600160a01b03811615155b6109c3576040805162461bcd60e51b81526020600482015260156024820152741a5b9d985b1a59081859191c995cdcc8199bdd5b99605a1b604482015290519081900360640190fd5b610929565b6001600160a01b038083166000908152600d602052604080822080548488166001600160a01b0319918216179091559287168252902080549091169055600e54610a1990600163ffffffff61195016565b600e556040516001600160a01b038516907f5d9d5034656cb3ebfb0655057cd7f9b4077a9b42ff42ce223cbac5bc586d212690600090a250505050565b60048054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610ae25780601f10610ab757610100808354040283529160200191610ae2565b820191906000526020600020905b815481529060010190602001808311610ac557829003601f168201915b5050505050905090565b6000610af93384846119ad565b50600192915050565b60035490565b600a546000906001600160a01b0316331480610b2e5750600b546001600160a01b031633145b80610b435750600c546001600160a01b031633145b610b83576040805162461bcd60e51b815260206004820152600c60248201526b3bb937b7339039b2b73232b960a11b604482015290519081900360640190fd5b610af98383611a99565b6000610b9a848484611bd9565b610ba5848484611c2c565b5060019392505050565b7fea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb81565b60065460ff1690565b60075481565b600a546001600160a01b031681565b3360008181526002602090815260408083206001600160a01b03871684529091528120549091610af9918590610c2d908663ffffffff611de516565b6119ad565b6000846001600160a01b03811615801590610c5657506001600160a01b0381163014155b610c9f576040805162461bcd60e51b81526020600482015260156024820152741b9bdd0818481d985b1a59081c9958da5c1a595b9d605a1b604482015290519081900360640190fd5b610ca98686611a99565b856001600160a01b0316336001600160a01b03167fe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c1687878760405180848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f1916909201829003965090945050505050a3610d37866001600160a01b0316611e46565b15610dc657610d7e33878787878080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611e4c92505050565b610dc6576040805162461bcd60e51b815260206004820152601460248201527318dbdb9d1c9858dd0818d85b1b0819985a5b195960621b604482015290519081900360640190fd5b50600195945050505050565b6000610ddd33611133565b610e2e576040805162461bcd60e51b815260206004820152601860248201527f63616c6c6572206973206e6f7420746865206272696467650000000000000000604482015290519081900360640190fd5b610e388383612039565b6040805183815290516001600160a01b038516917f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885919081900360200190a250600192915050565b600d602052600090815260409020546001600160a01b031681565b604051806040016040528060018152602001603160f81b81525081565b610ec061119d565b610eff576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b806001600160a01b03811615801590610f2157506001600160a01b0381163014155b610f6a576040805162461bcd60e51b81526020600482015260156024820152741b9bdd0818481d985b1a59081c9958da5c1a595b9d605a1b604482015290519081900360640190fd5b6001600160a01b038316610fe8576040513031906001600160a01b0384169082156108fc029083906000818181858888f19350505050610fe2578083604051610fb290612621565b6001600160a01b039091168152604051908190036020019082f080158015610fde573d6000803e3d6000fd5b5050505b5061107e565b604080516370a0823160e01b8152306004820152905184916000916001600160a01b038416916370a08231916024808301926020929190829003018186803b15801561103357600080fd5b505afa158015611047573d6000803e3d6000fd5b505050506040513d602081101561105d57600080fd5b5051905061107b6001600160a01b038316858363ffffffff61212b16565b50505b505050565b600c546001600160a01b031681565b6001600160a01b031660009081526001602052604090205490565b6110b561119d565b6110f4576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b6040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b5c1b195b595b9d1959608a1b604482015290519081900360640190fd5b60006001600160a01b038281161480159061116757506001600160a01b038281166000908152600d60205260409020541615155b92915050565b600b546001600160a01b031681565b60086020526000908152604090205481565b6000546001600160a01b031690565b6000546001600160a01b0316331490565b8415806111c25750846111bf61217d565b11155b611204576040805162461bcd60e51b815260206004820152600e60248201526d696e76616c69642065787069727960901b604482015290519081900360640190fd5b600754604080517fea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb6020808301919091526001600160a01b03808d16838501528b166060830152608082018a905260a0820189905287151560c0808401919091528351808403909101815260e08301845280519082012061190160f01b610100840152610102830194909452610122808301949094528251808303909401845261014282018084528451948201949094206000909452610162820180845284905260ff87166101828301526101a282018690526101c2820185905291516001926101e2808401939192601f1981019281900390910190855afa15801561130e573d6000803e3d6000fd5b505050602060405103516001600160a01b0316896001600160a01b03161461137d576040805162461bcd60e51b815260206004820152601f60248201527f696e76616c6964207369676e6174757265206f7220706172616d657465727300604482015290519081900360640190fd5b6001600160a01b038916600090815260086020526040902080546001810190915587146113e1576040805162461bcd60e51b815260206004820152600d60248201526c696e76616c6964206e6f6e636560981b604482015290519081900360640190fd5b6000856113ef5760006113f3565b6000195b90506114008a8a836119ad565b8561140c57600061140e565b865b6001600160a01b039a8b1660009081526009602090815260408083209c909d1682529a909a5299909820989098555050505050505050565b60058054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610ae25780601f10610ab757610100808354040283529160200191610ae2565b6114af61119d565b6114ee576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b6032600e541061152f5760405162461bcd60e51b81526004018080602001828103825260288152602001806127816028913960400191505060405180910390fd5b611541816001600160a01b0316611e46565b61158b576040805162461bcd60e51b81526020600482015260166024820152756e6f74206120636f6e7472616374206164647265737360501b604482015290519081900360640190fd5b61159481611133565b156115de576040805162461bcd60e51b815260206004820152601560248201527462726964676520616c72656164792065786973747360581b604482015290519081900360640190fd5b6001600160a01b036000819052600d6020526000805160206126cb833981519152541680611653576040805162461bcd60e51b815260206004820152601c60248201527f666972737420627269646765206973207a65726f206164647265737300000000604482015290519081900360640190fd5b600d6020526000805160206126cb83398151915280546001600160a01b03199081166001600160a01b038581169182179093556000908152604090208054909116918316919091179055600e546116ab906001611de5565b600e556040516001600160a01b038316907f3cda433c5679ae4c6a5dea50840e222a42cba3695e4663de4366be899348422190600090a25050565b606080600e54604051908082528060200260200182016040528015611715578160200160208202803883390190505b506001600160a01b036000818152600d6020526000805160206126cb83398151915254929350911680611784576040805162461bcd60e51b81526020600482015260126024820152711e995c9bc81859191c995cdcc8199bdd5b9960721b604482015290519081900360640190fd5b6001600160a01b038181161461182257808383815181106117a157fe5b6001600160a01b039283166020918202929092018101919091529181166000908152600d90925260409091205460019290920191168061181d576040805162461bcd60e51b81526020600482015260126024820152711e995c9bc81859191c995cdcc8199bdd5b9960721b604482015290519081900360640190fd5b611784565b509091505090565b3360008181526002602090815260408083206001600160a01b03871684529091528120549091610af9918590610c2d908663ffffffff61195016565b60006118728383611a99565b610af9338484611c2c565b61107e338383610b8d565b611893838383610b8d565b50505050565b6001600160a01b0381565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205490565b61107e823383610b8d565b6118e261119d565b611921576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b61192a81612181565b50565b600e5481565b600960209081526000928352604080842090915290825290205481565b6000828211156119a7576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b6001600160a01b0383166119f25760405162461bcd60e51b81526004018080602001828103825260248152602001806127ce6024913960400191505060405180910390fd5b6001600160a01b038216611a375760405162461bcd60e51b81526004018080602001828103825260228152602001806126a96022913960400191505060405180910390fd5b6001600160a01b03808416600081815260026020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b600a546000906001600160a01b0316331480611abf5750600b546001600160a01b031633145b80611ad45750600c546001600160a01b031633145b15611b885733600090815260016020526040902054611af9908363ffffffff61195016565b33600090815260016020526040808220929092556001600160a01b03851681522054611b2b908363ffffffff611de516565b6001600160a01b0384166000818152600160209081526040918290209390935580518581529051919233927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a3506001611b95565b611b928383612221565b90505b8061107e576040805162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015290519081900360640190fd5b6000611be684848461222e565b905080611893576040805162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015290519081900360640190fd5b611c3e826001600160a01b0316611e46565b8015611c655750604080516000815260208101909152611c6390849084908490611e4c565b155b1561107e57611c7382611133565b15611caf5760405162461bcd60e51b81526004018080602001828103825260258152602001806127166025913960400191505060405180910390fd5b600a546001600160a01b0383811691161415611cfc5760405162461bcd60e51b815260040180806020018281038252602b8152602001806126eb602b913960400191505060405180910390fd5b600b546001600160a01b0383811691161415611d495760405162461bcd60e51b815260040180806020018281038252602e815260200180612849602e913960400191505060405180910390fd5b600c546001600160a01b0383811691161415611d965760405162461bcd60e51b815260040180806020018281038252602d81526020018061281c602d913960400191505060405180910390fd5b604080516001600160a01b0380861682528416602082015280820183905290517f11249f0fc79fc134a15a10d1da8291b79515bf987e036ced05b9ec119614070b9181900360600190a1505050565b600082820183811015611e3f576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b3b151590565b6000606060405180606001604052806026815260200161275b6026913990506000856001600160a01b03168288878760405160240180846001600160a01b03166001600160a01b0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b83811015611ed9578181015183820152602001611ec1565b50505050905090810190601f168015611f065780820380516001836020036101000a031916815260200191505b50945050505050604051602081830303815290604052906040518082805190602001908083835b60208310611f4c5780518252601f199092019160209182019101611f2d565b51815160001960209485036101000a01908116901991909116179052604080519490920184900390932092860180516001600160e01b03166001600160e01b031990941693909317835251855190945084935090508083835b60208310611fc45780518252601f199092019160209182019101611fa5565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612026576040519150601f19603f3d011682016040523d82523d6000602084013e61202b565b606091505b509098975050505050505050565b6001600160a01b038216612094576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b6003546120a7908263ffffffff611de516565b6003556001600160a01b0382166000908152600160205260409020546120d3908263ffffffff611de516565b6001600160a01b03831660008181526001602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261107e908490612325565b4290565b6001600160a01b0381166121c65760405162461bcd60e51b81526004018080602001828103825260268152602001806126836026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000610af93384846124dd565b600061223b8484846124dd565b6001600160a01b0384163314610ba557600061225785336118a4565b9050600019811461227c576122778533610c2d848763ffffffff61195016565b61231f565b6001600160a01b038516600090815260096020908152604080832033845290915290205415806122d657506122af61217d565b6001600160a01b038616600090815260096020908152604080832033845290915290205410155b61231f576040805162461bcd60e51b8152602060048201526015602482015274195e1c1a5c9e481a5cc81a5b881d1a19481c185cdd605a1b604482015290519081900360640190fd5b50610ba5565b612337826001600160a01b0316611e46565b612388576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106123c65780518252601f1990920191602091820191016123a7565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612428576040519150601f19603f3d011682016040523d82523d6000602084013e61242d565b606091505b509150915081612484576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115611893578080602001905160208110156124a057600080fd5b50516118935760405162461bcd60e51b815260040180806020018281038252602a8152602001806127f2602a913960400191505060405180910390fd5b6001600160a01b0383166125225760405162461bcd60e51b81526004018080602001828103825260258152602001806127a96025913960400191505060405180910390fd5b6001600160a01b0382166125675760405162461bcd60e51b81526004018080602001828103825260238152602001806126606023913960400191505060405180910390fd5b6001600160a01b038316600090815260016020526040902054612590908263ffffffff61195016565b6001600160a01b0380851660009081526001602052604080822093909355908416815220546125c5908263ffffffff611de516565b6001600160a01b0380841660008181526001602090815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b60328061262e8339019056fe60806040526040516032380380603283398181016040526020811015602357600080fd5b50516001600160a01b038116fffe45524332303a207472616e7366657220746f20746865207a65726f20616464726573734f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f2061646472657373a934977eb9828ba1f50591af02c98441645b4f0e916e0fecb4cc8e9c633dade2796f752063616e2774207472616e7366657220746f20446973747269627574696f6e20636f6e7472616374796f752063616e2774207472616e7366657220746f2062726964676520636f6e74726163744f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726f6e546f6b656e5472616e7366657228616464726573732c75696e743235362c62797465732963616e277420616464206f6e65206d6f7265206272696467652064756520746f2061206c696d697445524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f20616464726573735361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564796f752063616e2774207472616e7366657220746f2041647669736f727352657761726420636f6e7472616374796f752063616e2774207472616e7366657220746f20507269766174654f66666572696e6720636f6e7472616374a265627a7a72305820fbfacd4b36dace16f9fd57104c8aafe8b5a519b29f407207baf7bb6b917422a764736f6c634300050a0032" - assert {:ok, - %{ - abi: abi, - constructor_arguments: ^constructor_arguments - }} = Verifier.evaluate_authenticity(contract_address.hash, params) + contract_address = insert(:contract_address, contract_code: deployed_bytecode) - assert abi != nil - end + input_data = %Data{ + bytes: Base.decode16!(input, case: :lower) + } - test "verifies with string in keccak256" do - source_code = - "#{File.cwd!()}/test/support/fixture/smart_contract/ERC677.sol" - |> File.read!() - - input = - "60806040523480156200001157600080fd5b5060405162002f3f38038062002f3f833981810160405260a08110156200003757600080fd5b8101908080516401000000008111156200005057600080fd5b820160208101848111156200006457600080fd5b81516401000000008111828201871017156200007f57600080fd5b505092919060200180516401000000008111156200009c57600080fd5b82016020810184811115620000b057600080fd5b8151640100000000811182820187101715620000cb57600080fd5b50506020820151604080840151606090940151600080546001600160a01b031916331780825592519497509295509287928792879287928792879287926012928592859285926001600160a01b0316917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a38251620001559060049060208601906200058d565b5081516200016b9060059060208501906200058d565b506006805460ff191660ff92909216919091179055505060405180605262002eed8239604080519182900360520182208651602097880120838301835260018085527f310000000000000000000000000000000000000000000000000000000000000094890194909452825180890192909252818301527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6606082015260808101929092523060a0808401919091528151808403909101815260c090920190528051908501206007555062000257926001600160a01b038716925062001e46620003ec821b17901c9050565b80156200027e57506200027e826001600160a01b0316620003ec60201b62001e461760201c565b8015620002a55750620002a5816001600160a01b0316620003ec60201b62001e461760201c565b6200031157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f6e6f74206120636f6e7472616374206164647265737300000000000000000000604482015290519081900360640190fd5b607b6200032884826001600160e01b03620003f216565b600a80546001600160a01b038087166001600160a01b03199283168117909355600b8054878316908416179055600c8054918616919092161790556040805183815290517f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968859181900360200190a250506001600160a01b036000819052600d6020527fa934977eb9828ba1f50591af02c98441645b4f0e916e0fecb4cc8e9c633dade280546001600160a01b03191690911790555062000632975050505050505050565b3b151590565b6001600160a01b0382166200046857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b62000484816003546200051160201b62001de51790919060201c565b6003556001600160a01b038216600090815260016020908152604090912054620004b991839062001de562000511821b17901c565b6001600160a01b03831660008181526001602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6000828201838110156200058657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620005d057805160ff191683800117855562000600565b8280016001018555821562000600579182015b8281111562000600578251825591602001919060010190620005e3565b506200060e92915062000612565b5090565b6200062f91905b808211156200060e576000815560010162000619565b90565b6128ab80620006426000396000f3fe608060405234801561001057600080fd5b50600436106102325760003560e01c8063726600ce11610130578063a457c2d7116100b8578063dd62ed3e1161007c578063dd62ed3e1461075f578063f2d5d56b1461078d578063f2fde38b146107b9578063fbb2a53f146107df578063ff9e884d146107e757610232565b8063a457c2d71461069d578063a9059cbb146106c9578063b753a98c146106f5578063bb35783b14610721578063c794c7691461075757610232565b80638f32d59b116100ff5780638f32d59b146105b55780638fcbaf0c146105bd57806395d89b41146106175780639712fdf81461061f5780639da38e2f1461064557610232565b8063726600ce146105595780637a13685a1461057f5780637ecebe00146105875780638da5cb5b146105ad57610232565b806337fb7e21116101be57806354fd4d501161018257806354fd4d50146104ed57806369ffa08a146104f55780636e15d21b1461052357806370a082311461052b578063715018a61461055157610232565b806337fb7e21146103c657806339509351146103ea5780634000aea01461041657806340c10f191461049b5780634bcb88bc146104c757610232565b8063238a3fe111610205578063238a3fe11461033657806323b872dd1461036257806330adf81f14610398578063313ce567146103a05780633644e515146103be57610232565b806304df017d1461023757806306fdde031461025f578063095ea7b3146102dc57806318160ddd1461031c575b600080fd5b61025d6004803603602081101561024d57600080fd5b50356001600160a01b0316610815565b005b610267610a56565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102a1578181015183820152602001610289565b50505050905090810190601f1680156102ce5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610308600480360360408110156102f257600080fd5b506001600160a01b038135169060200135610aec565b604080519115158252519081900360200190f35b610324610b02565b60408051918252519081900360200190f35b6103086004803603604081101561034c57600080fd5b506001600160a01b038135169060200135610b08565b6103086004803603606081101561037857600080fd5b506001600160a01b03813581169160208101359091169060400135610b8d565b610324610baf565b6103a8610bd3565b6040805160ff9092168252519081900360200190f35b610324610bdc565b6103ce610be2565b604080516001600160a01b039092168252519081900360200190f35b6103086004803603604081101561040057600080fd5b506001600160a01b038135169060200135610bf1565b6103086004803603606081101561042c57600080fd5b6001600160a01b038235169160208101359181019060608101604082013564010000000081111561045c57600080fd5b82018360208201111561046e57600080fd5b8035906020019184600183028401116401000000008311171561049057600080fd5b509092509050610c32565b610308600480360360408110156104b157600080fd5b506001600160a01b038135169060200135610dd2565b6103ce600480360360208110156104dd57600080fd5b50356001600160a01b0316610e80565b610267610e9b565b61025d6004803603604081101561050b57600080fd5b506001600160a01b0381358116916020013516610eb8565b6103ce611083565b6103246004803603602081101561054157600080fd5b50356001600160a01b0316611092565b61025d6110ad565b6103086004803603602081101561056f57600080fd5b50356001600160a01b0316611133565b6103ce61116d565b6103246004803603602081101561059d57600080fd5b50356001600160a01b031661117c565b6103ce61118e565b61030861119d565b61025d60048036036101008110156105d457600080fd5b506001600160a01b038135811691602081013590911690604081013590606081013590608081013515159060ff60a0820135169060c08101359060e001356111ae565b610267611446565b61025d6004803603602081101561063557600080fd5b50356001600160a01b03166114a7565b61064d6116e6565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610689578181015183820152602001610671565b505050509050019250505060405180910390f35b610308600480360360408110156106b357600080fd5b506001600160a01b03813516906020013561182a565b610308600480360360408110156106df57600080fd5b506001600160a01b038135169060200135611866565b61025d6004803603604081101561070b57600080fd5b506001600160a01b03813516906020013561187d565b61025d6004803603606081101561073757600080fd5b506001600160a01b03813581169160208101359091169060400135611888565b6103ce611899565b6103246004803603604081101561077557600080fd5b506001600160a01b03813581169160200135166118a4565b61025d600480360360408110156107a357600080fd5b506001600160a01b0381351690602001356118cf565b61025d600480360360208110156107cf57600080fd5b50356001600160a01b03166118da565b61032461192d565b610324600480360360408110156107fd57600080fd5b506001600160a01b0381358116916020013516611933565b61081d61119d565b61085c576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b61086581611133565b6108ad576040805162461bcd60e51b8152602060048201526014602482015273189c9a5919d9481a5cdb89dd08195e1a5cdd195960621b604482015290519081900360640190fd5b6001600160a01b038082166000908152600d6020526040812054908290526000805160206126cb833981519152549082169190811680610929576040805162461bcd60e51b81526020600482015260126024820152711e995c9bc81859191c995cdcc8199bdd5b9960721b604482015290519081900360640190fd5b836001600160a01b0316816001600160a01b0316146109c8576001600160a01b038082166000908152600d602052604090205491925090811690811480159061097a57506001600160a01b03811615155b6109c3576040805162461bcd60e51b81526020600482015260156024820152741a5b9d985b1a59081859191c995cdcc8199bdd5b99605a1b604482015290519081900360640190fd5b610929565b6001600160a01b038083166000908152600d602052604080822080548488166001600160a01b0319918216179091559287168252902080549091169055600e54610a1990600163ffffffff61195016565b600e556040516001600160a01b038516907f5d9d5034656cb3ebfb0655057cd7f9b4077a9b42ff42ce223cbac5bc586d212690600090a250505050565b60048054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610ae25780601f10610ab757610100808354040283529160200191610ae2565b820191906000526020600020905b815481529060010190602001808311610ac557829003601f168201915b5050505050905090565b6000610af93384846119ad565b50600192915050565b60035490565b600a546000906001600160a01b0316331480610b2e5750600b546001600160a01b031633145b80610b435750600c546001600160a01b031633145b610b83576040805162461bcd60e51b815260206004820152600c60248201526b3bb937b7339039b2b73232b960a11b604482015290519081900360640190fd5b610af98383611a99565b6000610b9a848484611bd9565b610ba5848484611c2c565b5060019392505050565b7fea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb81565b60065460ff1690565b60075481565b600a546001600160a01b031681565b3360008181526002602090815260408083206001600160a01b03871684529091528120549091610af9918590610c2d908663ffffffff611de516565b6119ad565b6000846001600160a01b03811615801590610c5657506001600160a01b0381163014155b610c9f576040805162461bcd60e51b81526020600482015260156024820152741b9bdd0818481d985b1a59081c9958da5c1a595b9d605a1b604482015290519081900360640190fd5b610ca98686611a99565b856001600160a01b0316336001600160a01b03167fe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c1687878760405180848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f1916909201829003965090945050505050a3610d37866001600160a01b0316611e46565b15610dc657610d7e33878787878080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611e4c92505050565b610dc6576040805162461bcd60e51b815260206004820152601460248201527318dbdb9d1c9858dd0818d85b1b0819985a5b195960621b604482015290519081900360640190fd5b50600195945050505050565b6000610ddd33611133565b610e2e576040805162461bcd60e51b815260206004820152601860248201527f63616c6c6572206973206e6f7420746865206272696467650000000000000000604482015290519081900360640190fd5b610e388383612039565b6040805183815290516001600160a01b038516917f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885919081900360200190a250600192915050565b600d602052600090815260409020546001600160a01b031681565b604051806040016040528060018152602001603160f81b81525081565b610ec061119d565b610eff576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b806001600160a01b03811615801590610f2157506001600160a01b0381163014155b610f6a576040805162461bcd60e51b81526020600482015260156024820152741b9bdd0818481d985b1a59081c9958da5c1a595b9d605a1b604482015290519081900360640190fd5b6001600160a01b038316610fe8576040513031906001600160a01b0384169082156108fc029083906000818181858888f19350505050610fe2578083604051610fb290612621565b6001600160a01b039091168152604051908190036020019082f080158015610fde573d6000803e3d6000fd5b5050505b5061107e565b604080516370a0823160e01b8152306004820152905184916000916001600160a01b038416916370a08231916024808301926020929190829003018186803b15801561103357600080fd5b505afa158015611047573d6000803e3d6000fd5b505050506040513d602081101561105d57600080fd5b5051905061107b6001600160a01b038316858363ffffffff61212b16565b50505b505050565b600c546001600160a01b031681565b6001600160a01b031660009081526001602052604090205490565b6110b561119d565b6110f4576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b6040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b5c1b195b595b9d1959608a1b604482015290519081900360640190fd5b60006001600160a01b038281161480159061116757506001600160a01b038281166000908152600d60205260409020541615155b92915050565b600b546001600160a01b031681565b60086020526000908152604090205481565b6000546001600160a01b031690565b6000546001600160a01b0316331490565b8415806111c25750846111bf61217d565b11155b611204576040805162461bcd60e51b815260206004820152600e60248201526d696e76616c69642065787069727960901b604482015290519081900360640190fd5b600754604080517fea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb6020808301919091526001600160a01b03808d16838501528b166060830152608082018a905260a0820189905287151560c0808401919091528351808403909101815260e08301845280519082012061190160f01b610100840152610102830194909452610122808301949094528251808303909401845261014282018084528451948201949094206000909452610162820180845284905260ff87166101828301526101a282018690526101c2820185905291516001926101e2808401939192601f1981019281900390910190855afa15801561130e573d6000803e3d6000fd5b505050602060405103516001600160a01b0316896001600160a01b03161461137d576040805162461bcd60e51b815260206004820152601f60248201527f696e76616c6964207369676e6174757265206f7220706172616d657465727300604482015290519081900360640190fd5b6001600160a01b038916600090815260086020526040902080546001810190915587146113e1576040805162461bcd60e51b815260206004820152600d60248201526c696e76616c6964206e6f6e636560981b604482015290519081900360640190fd5b6000856113ef5760006113f3565b6000195b90506114008a8a836119ad565b8561140c57600061140e565b865b6001600160a01b039a8b1660009081526009602090815260408083209c909d1682529a909a5299909820989098555050505050505050565b60058054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610ae25780601f10610ab757610100808354040283529160200191610ae2565b6114af61119d565b6114ee576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b6032600e541061152f5760405162461bcd60e51b81526004018080602001828103825260288152602001806127816028913960400191505060405180910390fd5b611541816001600160a01b0316611e46565b61158b576040805162461bcd60e51b81526020600482015260166024820152756e6f74206120636f6e7472616374206164647265737360501b604482015290519081900360640190fd5b61159481611133565b156115de576040805162461bcd60e51b815260206004820152601560248201527462726964676520616c72656164792065786973747360581b604482015290519081900360640190fd5b6001600160a01b036000819052600d6020526000805160206126cb833981519152541680611653576040805162461bcd60e51b815260206004820152601c60248201527f666972737420627269646765206973207a65726f206164647265737300000000604482015290519081900360640190fd5b600d6020526000805160206126cb83398151915280546001600160a01b03199081166001600160a01b038581169182179093556000908152604090208054909116918316919091179055600e546116ab906001611de5565b600e556040516001600160a01b038316907f3cda433c5679ae4c6a5dea50840e222a42cba3695e4663de4366be899348422190600090a25050565b606080600e54604051908082528060200260200182016040528015611715578160200160208202803883390190505b506001600160a01b036000818152600d6020526000805160206126cb83398151915254929350911680611784576040805162461bcd60e51b81526020600482015260126024820152711e995c9bc81859191c995cdcc8199bdd5b9960721b604482015290519081900360640190fd5b6001600160a01b038181161461182257808383815181106117a157fe5b6001600160a01b039283166020918202929092018101919091529181166000908152600d90925260409091205460019290920191168061181d576040805162461bcd60e51b81526020600482015260126024820152711e995c9bc81859191c995cdcc8199bdd5b9960721b604482015290519081900360640190fd5b611784565b509091505090565b3360008181526002602090815260408083206001600160a01b03871684529091528120549091610af9918590610c2d908663ffffffff61195016565b60006118728383611a99565b610af9338484611c2c565b61107e338383610b8d565b611893838383610b8d565b50505050565b6001600160a01b0381565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205490565b61107e823383610b8d565b6118e261119d565b611921576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b61192a81612181565b50565b600e5481565b600960209081526000928352604080842090915290825290205481565b6000828211156119a7576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b6001600160a01b0383166119f25760405162461bcd60e51b81526004018080602001828103825260248152602001806127ce6024913960400191505060405180910390fd5b6001600160a01b038216611a375760405162461bcd60e51b81526004018080602001828103825260228152602001806126a96022913960400191505060405180910390fd5b6001600160a01b03808416600081815260026020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b600a546000906001600160a01b0316331480611abf5750600b546001600160a01b031633145b80611ad45750600c546001600160a01b031633145b15611b885733600090815260016020526040902054611af9908363ffffffff61195016565b33600090815260016020526040808220929092556001600160a01b03851681522054611b2b908363ffffffff611de516565b6001600160a01b0384166000818152600160209081526040918290209390935580518581529051919233927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a3506001611b95565b611b928383612221565b90505b8061107e576040805162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015290519081900360640190fd5b6000611be684848461222e565b905080611893576040805162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015290519081900360640190fd5b611c3e826001600160a01b0316611e46565b8015611c655750604080516000815260208101909152611c6390849084908490611e4c565b155b1561107e57611c7382611133565b15611caf5760405162461bcd60e51b81526004018080602001828103825260258152602001806127166025913960400191505060405180910390fd5b600a546001600160a01b0383811691161415611cfc5760405162461bcd60e51b815260040180806020018281038252602b8152602001806126eb602b913960400191505060405180910390fd5b600b546001600160a01b0383811691161415611d495760405162461bcd60e51b815260040180806020018281038252602e815260200180612849602e913960400191505060405180910390fd5b600c546001600160a01b0383811691161415611d965760405162461bcd60e51b815260040180806020018281038252602d81526020018061281c602d913960400191505060405180910390fd5b604080516001600160a01b0380861682528416602082015280820183905290517f11249f0fc79fc134a15a10d1da8291b79515bf987e036ced05b9ec119614070b9181900360600190a1505050565b600082820183811015611e3f576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b3b151590565b6000606060405180606001604052806026815260200161275b6026913990506000856001600160a01b03168288878760405160240180846001600160a01b03166001600160a01b0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b83811015611ed9578181015183820152602001611ec1565b50505050905090810190601f168015611f065780820380516001836020036101000a031916815260200191505b50945050505050604051602081830303815290604052906040518082805190602001908083835b60208310611f4c5780518252601f199092019160209182019101611f2d565b51815160001960209485036101000a01908116901991909116179052604080519490920184900390932092860180516001600160e01b03166001600160e01b031990941693909317835251855190945084935090508083835b60208310611fc45780518252601f199092019160209182019101611fa5565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612026576040519150601f19603f3d011682016040523d82523d6000602084013e61202b565b606091505b509098975050505050505050565b6001600160a01b038216612094576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b6003546120a7908263ffffffff611de516565b6003556001600160a01b0382166000908152600160205260409020546120d3908263ffffffff611de516565b6001600160a01b03831660008181526001602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261107e908490612325565b4290565b6001600160a01b0381166121c65760405162461bcd60e51b81526004018080602001828103825260268152602001806126836026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000610af93384846124dd565b600061223b8484846124dd565b6001600160a01b0384163314610ba557600061225785336118a4565b9050600019811461227c576122778533610c2d848763ffffffff61195016565b61231f565b6001600160a01b038516600090815260096020908152604080832033845290915290205415806122d657506122af61217d565b6001600160a01b038616600090815260096020908152604080832033845290915290205410155b61231f576040805162461bcd60e51b8152602060048201526015602482015274195e1c1a5c9e481a5cc81a5b881d1a19481c185cdd605a1b604482015290519081900360640190fd5b50610ba5565b612337826001600160a01b0316611e46565b612388576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106123c65780518252601f1990920191602091820191016123a7565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612428576040519150601f19603f3d011682016040523d82523d6000602084013e61242d565b606091505b509150915081612484576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115611893578080602001905160208110156124a057600080fd5b50516118935760405162461bcd60e51b815260040180806020018281038252602a8152602001806127f2602a913960400191505060405180910390fd5b6001600160a01b0383166125225760405162461bcd60e51b81526004018080602001828103825260258152602001806127a96025913960400191505060405180910390fd5b6001600160a01b0382166125675760405162461bcd60e51b81526004018080602001828103825260238152602001806126606023913960400191505060405180910390fd5b6001600160a01b038316600090815260016020526040902054612590908263ffffffff61195016565b6001600160a01b0380851660009081526001602052604080822093909355908416815220546125c5908263ffffffff611de516565b6001600160a01b0380841660008181526001602090815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b60328061262e8339019056fe60806040526040516032380380603283398181016040526020811015602357600080fd5b50516001600160a01b038116fffe45524332303a207472616e7366657220746f20746865207a65726f20616464726573734f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f2061646472657373a934977eb9828ba1f50591af02c98441645b4f0e916e0fecb4cc8e9c633dade2796f752063616e2774207472616e7366657220746f20446973747269627574696f6e20636f6e7472616374796f752063616e2774207472616e7366657220746f2062726964676520636f6e74726163744f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726f6e546f6b656e5472616e7366657228616464726573732c75696e743235362c62797465732963616e277420616464206f6e65206d6f7265206272696467652064756520746f2061206c696d697445524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f20616464726573735361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564796f752063616e2774207472616e7366657220746f2041647669736f727352657761726420636f6e7472616374796f752063616e2774207472616e7366657220746f20507269766174654f66666572696e6720636f6e7472616374a265627a7a72305820fbfacd4b36dace16f9fd57104c8aafe8b5a519b29f407207baf7bb6b917422a764736f6c634300050a0032454950373132446f6d61696e28737472696e67206e616d652c737472696e672076657273696f6e2c75696e7432353620636861696e49642c6164647265737320766572696679696e67436f6e74726163742900000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000348448061f604f4adf7ba714460c03cc6eb5b9f4000000000000000000000000348448061f604f4adf7ba714460c03cc6eb5b9f4000000000000000000000000348448061f604f4adf7ba714460c03cc6eb5b9f40000000000000000000000000000000000000000000000000000000000000003717765000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037177650000000000000000000000000000000000000000000000000000000000" - - deployed_bytecode = - "0x608060405234801561001057600080fd5b50600436106102325760003560e01c8063726600ce11610130578063a457c2d7116100b8578063dd62ed3e1161007c578063dd62ed3e1461075f578063f2d5d56b1461078d578063f2fde38b146107b9578063fbb2a53f146107df578063ff9e884d146107e757610232565b8063a457c2d71461069d578063a9059cbb146106c9578063b753a98c146106f5578063bb35783b14610721578063c794c7691461075757610232565b80638f32d59b116100ff5780638f32d59b146105b55780638fcbaf0c146105bd57806395d89b41146106175780639712fdf81461061f5780639da38e2f1461064557610232565b8063726600ce146105595780637a13685a1461057f5780637ecebe00146105875780638da5cb5b146105ad57610232565b806337fb7e21116101be57806354fd4d501161018257806354fd4d50146104ed57806369ffa08a146104f55780636e15d21b1461052357806370a082311461052b578063715018a61461055157610232565b806337fb7e21146103c657806339509351146103ea5780634000aea01461041657806340c10f191461049b5780634bcb88bc146104c757610232565b8063238a3fe111610205578063238a3fe11461033657806323b872dd1461036257806330adf81f14610398578063313ce567146103a05780633644e515146103be57610232565b806304df017d1461023757806306fdde031461025f578063095ea7b3146102dc57806318160ddd1461031c575b600080fd5b61025d6004803603602081101561024d57600080fd5b50356001600160a01b0316610815565b005b610267610a56565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102a1578181015183820152602001610289565b50505050905090810190601f1680156102ce5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610308600480360360408110156102f257600080fd5b506001600160a01b038135169060200135610aec565b604080519115158252519081900360200190f35b610324610b02565b60408051918252519081900360200190f35b6103086004803603604081101561034c57600080fd5b506001600160a01b038135169060200135610b08565b6103086004803603606081101561037857600080fd5b506001600160a01b03813581169160208101359091169060400135610b8d565b610324610baf565b6103a8610bd3565b6040805160ff9092168252519081900360200190f35b610324610bdc565b6103ce610be2565b604080516001600160a01b039092168252519081900360200190f35b6103086004803603604081101561040057600080fd5b506001600160a01b038135169060200135610bf1565b6103086004803603606081101561042c57600080fd5b6001600160a01b038235169160208101359181019060608101604082013564010000000081111561045c57600080fd5b82018360208201111561046e57600080fd5b8035906020019184600183028401116401000000008311171561049057600080fd5b509092509050610c32565b610308600480360360408110156104b157600080fd5b506001600160a01b038135169060200135610dd2565b6103ce600480360360208110156104dd57600080fd5b50356001600160a01b0316610e80565b610267610e9b565b61025d6004803603604081101561050b57600080fd5b506001600160a01b0381358116916020013516610eb8565b6103ce611083565b6103246004803603602081101561054157600080fd5b50356001600160a01b0316611092565b61025d6110ad565b6103086004803603602081101561056f57600080fd5b50356001600160a01b0316611133565b6103ce61116d565b6103246004803603602081101561059d57600080fd5b50356001600160a01b031661117c565b6103ce61118e565b61030861119d565b61025d60048036036101008110156105d457600080fd5b506001600160a01b038135811691602081013590911690604081013590606081013590608081013515159060ff60a0820135169060c08101359060e001356111ae565b610267611446565b61025d6004803603602081101561063557600080fd5b50356001600160a01b03166114a7565b61064d6116e6565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610689578181015183820152602001610671565b505050509050019250505060405180910390f35b610308600480360360408110156106b357600080fd5b506001600160a01b03813516906020013561182a565b610308600480360360408110156106df57600080fd5b506001600160a01b038135169060200135611866565b61025d6004803603604081101561070b57600080fd5b506001600160a01b03813516906020013561187d565b61025d6004803603606081101561073757600080fd5b506001600160a01b03813581169160208101359091169060400135611888565b6103ce611899565b6103246004803603604081101561077557600080fd5b506001600160a01b03813581169160200135166118a4565b61025d600480360360408110156107a357600080fd5b506001600160a01b0381351690602001356118cf565b61025d600480360360208110156107cf57600080fd5b50356001600160a01b03166118da565b61032461192d565b610324600480360360408110156107fd57600080fd5b506001600160a01b0381358116916020013516611933565b61081d61119d565b61085c576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b61086581611133565b6108ad576040805162461bcd60e51b8152602060048201526014602482015273189c9a5919d9481a5cdb89dd08195e1a5cdd195960621b604482015290519081900360640190fd5b6001600160a01b038082166000908152600d6020526040812054908290526000805160206126cb833981519152549082169190811680610929576040805162461bcd60e51b81526020600482015260126024820152711e995c9bc81859191c995cdcc8199bdd5b9960721b604482015290519081900360640190fd5b836001600160a01b0316816001600160a01b0316146109c8576001600160a01b038082166000908152600d602052604090205491925090811690811480159061097a57506001600160a01b03811615155b6109c3576040805162461bcd60e51b81526020600482015260156024820152741a5b9d985b1a59081859191c995cdcc8199bdd5b99605a1b604482015290519081900360640190fd5b610929565b6001600160a01b038083166000908152600d602052604080822080548488166001600160a01b0319918216179091559287168252902080549091169055600e54610a1990600163ffffffff61195016565b600e556040516001600160a01b038516907f5d9d5034656cb3ebfb0655057cd7f9b4077a9b42ff42ce223cbac5bc586d212690600090a250505050565b60048054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610ae25780601f10610ab757610100808354040283529160200191610ae2565b820191906000526020600020905b815481529060010190602001808311610ac557829003601f168201915b5050505050905090565b6000610af93384846119ad565b50600192915050565b60035490565b600a546000906001600160a01b0316331480610b2e5750600b546001600160a01b031633145b80610b435750600c546001600160a01b031633145b610b83576040805162461bcd60e51b815260206004820152600c60248201526b3bb937b7339039b2b73232b960a11b604482015290519081900360640190fd5b610af98383611a99565b6000610b9a848484611bd9565b610ba5848484611c2c565b5060019392505050565b7fea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb81565b60065460ff1690565b60075481565b600a546001600160a01b031681565b3360008181526002602090815260408083206001600160a01b03871684529091528120549091610af9918590610c2d908663ffffffff611de516565b6119ad565b6000846001600160a01b03811615801590610c5657506001600160a01b0381163014155b610c9f576040805162461bcd60e51b81526020600482015260156024820152741b9bdd0818481d985b1a59081c9958da5c1a595b9d605a1b604482015290519081900360640190fd5b610ca98686611a99565b856001600160a01b0316336001600160a01b03167fe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c1687878760405180848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f1916909201829003965090945050505050a3610d37866001600160a01b0316611e46565b15610dc657610d7e33878787878080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611e4c92505050565b610dc6576040805162461bcd60e51b815260206004820152601460248201527318dbdb9d1c9858dd0818d85b1b0819985a5b195960621b604482015290519081900360640190fd5b50600195945050505050565b6000610ddd33611133565b610e2e576040805162461bcd60e51b815260206004820152601860248201527f63616c6c6572206973206e6f7420746865206272696467650000000000000000604482015290519081900360640190fd5b610e388383612039565b6040805183815290516001600160a01b038516917f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885919081900360200190a250600192915050565b600d602052600090815260409020546001600160a01b031681565b604051806040016040528060018152602001603160f81b81525081565b610ec061119d565b610eff576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b806001600160a01b03811615801590610f2157506001600160a01b0381163014155b610f6a576040805162461bcd60e51b81526020600482015260156024820152741b9bdd0818481d985b1a59081c9958da5c1a595b9d605a1b604482015290519081900360640190fd5b6001600160a01b038316610fe8576040513031906001600160a01b0384169082156108fc029083906000818181858888f19350505050610fe2578083604051610fb290612621565b6001600160a01b039091168152604051908190036020019082f080158015610fde573d6000803e3d6000fd5b5050505b5061107e565b604080516370a0823160e01b8152306004820152905184916000916001600160a01b038416916370a08231916024808301926020929190829003018186803b15801561103357600080fd5b505afa158015611047573d6000803e3d6000fd5b505050506040513d602081101561105d57600080fd5b5051905061107b6001600160a01b038316858363ffffffff61212b16565b50505b505050565b600c546001600160a01b031681565b6001600160a01b031660009081526001602052604090205490565b6110b561119d565b6110f4576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b6040805162461bcd60e51b815260206004820152600f60248201526e1b9bdd081a5b5c1b195b595b9d1959608a1b604482015290519081900360640190fd5b60006001600160a01b038281161480159061116757506001600160a01b038281166000908152600d60205260409020541615155b92915050565b600b546001600160a01b031681565b60086020526000908152604090205481565b6000546001600160a01b031690565b6000546001600160a01b0316331490565b8415806111c25750846111bf61217d565b11155b611204576040805162461bcd60e51b815260206004820152600e60248201526d696e76616c69642065787069727960901b604482015290519081900360640190fd5b600754604080517fea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb6020808301919091526001600160a01b03808d16838501528b166060830152608082018a905260a0820189905287151560c0808401919091528351808403909101815260e08301845280519082012061190160f01b610100840152610102830194909452610122808301949094528251808303909401845261014282018084528451948201949094206000909452610162820180845284905260ff87166101828301526101a282018690526101c2820185905291516001926101e2808401939192601f1981019281900390910190855afa15801561130e573d6000803e3d6000fd5b505050602060405103516001600160a01b0316896001600160a01b03161461137d576040805162461bcd60e51b815260206004820152601f60248201527f696e76616c6964207369676e6174757265206f7220706172616d657465727300604482015290519081900360640190fd5b6001600160a01b038916600090815260086020526040902080546001810190915587146113e1576040805162461bcd60e51b815260206004820152600d60248201526c696e76616c6964206e6f6e636560981b604482015290519081900360640190fd5b6000856113ef5760006113f3565b6000195b90506114008a8a836119ad565b8561140c57600061140e565b865b6001600160a01b039a8b1660009081526009602090815260408083209c909d1682529a909a5299909820989098555050505050505050565b60058054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015610ae25780601f10610ab757610100808354040283529160200191610ae2565b6114af61119d565b6114ee576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b6032600e541061152f5760405162461bcd60e51b81526004018080602001828103825260288152602001806127816028913960400191505060405180910390fd5b611541816001600160a01b0316611e46565b61158b576040805162461bcd60e51b81526020600482015260166024820152756e6f74206120636f6e7472616374206164647265737360501b604482015290519081900360640190fd5b61159481611133565b156115de576040805162461bcd60e51b815260206004820152601560248201527462726964676520616c72656164792065786973747360581b604482015290519081900360640190fd5b6001600160a01b036000819052600d6020526000805160206126cb833981519152541680611653576040805162461bcd60e51b815260206004820152601c60248201527f666972737420627269646765206973207a65726f206164647265737300000000604482015290519081900360640190fd5b600d6020526000805160206126cb83398151915280546001600160a01b03199081166001600160a01b038581169182179093556000908152604090208054909116918316919091179055600e546116ab906001611de5565b600e556040516001600160a01b038316907f3cda433c5679ae4c6a5dea50840e222a42cba3695e4663de4366be899348422190600090a25050565b606080600e54604051908082528060200260200182016040528015611715578160200160208202803883390190505b506001600160a01b036000818152600d6020526000805160206126cb83398151915254929350911680611784576040805162461bcd60e51b81526020600482015260126024820152711e995c9bc81859191c995cdcc8199bdd5b9960721b604482015290519081900360640190fd5b6001600160a01b038181161461182257808383815181106117a157fe5b6001600160a01b039283166020918202929092018101919091529181166000908152600d90925260409091205460019290920191168061181d576040805162461bcd60e51b81526020600482015260126024820152711e995c9bc81859191c995cdcc8199bdd5b9960721b604482015290519081900360640190fd5b611784565b509091505090565b3360008181526002602090815260408083206001600160a01b03871684529091528120549091610af9918590610c2d908663ffffffff61195016565b60006118728383611a99565b610af9338484611c2c565b61107e338383610b8d565b611893838383610b8d565b50505050565b6001600160a01b0381565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205490565b61107e823383610b8d565b6118e261119d565b611921576040805162461bcd60e51b8152602060048201819052602482015260008051602061273b833981519152604482015290519081900360640190fd5b61192a81612181565b50565b600e5481565b600960209081526000928352604080842090915290825290205481565b6000828211156119a7576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b6001600160a01b0383166119f25760405162461bcd60e51b81526004018080602001828103825260248152602001806127ce6024913960400191505060405180910390fd5b6001600160a01b038216611a375760405162461bcd60e51b81526004018080602001828103825260228152602001806126a96022913960400191505060405180910390fd5b6001600160a01b03808416600081815260026020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b600a546000906001600160a01b0316331480611abf5750600b546001600160a01b031633145b80611ad45750600c546001600160a01b031633145b15611b885733600090815260016020526040902054611af9908363ffffffff61195016565b33600090815260016020526040808220929092556001600160a01b03851681522054611b2b908363ffffffff611de516565b6001600160a01b0384166000818152600160209081526040918290209390935580518581529051919233927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a3506001611b95565b611b928383612221565b90505b8061107e576040805162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015290519081900360640190fd5b6000611be684848461222e565b905080611893576040805162461bcd60e51b815260206004820152600f60248201526e1d1c985b9cd9995c8819985a5b1959608a1b604482015290519081900360640190fd5b611c3e826001600160a01b0316611e46565b8015611c655750604080516000815260208101909152611c6390849084908490611e4c565b155b1561107e57611c7382611133565b15611caf5760405162461bcd60e51b81526004018080602001828103825260258152602001806127166025913960400191505060405180910390fd5b600a546001600160a01b0383811691161415611cfc5760405162461bcd60e51b815260040180806020018281038252602b8152602001806126eb602b913960400191505060405180910390fd5b600b546001600160a01b0383811691161415611d495760405162461bcd60e51b815260040180806020018281038252602e815260200180612849602e913960400191505060405180910390fd5b600c546001600160a01b0383811691161415611d965760405162461bcd60e51b815260040180806020018281038252602d81526020018061281c602d913960400191505060405180910390fd5b604080516001600160a01b0380861682528416602082015280820183905290517f11249f0fc79fc134a15a10d1da8291b79515bf987e036ced05b9ec119614070b9181900360600190a1505050565b600082820183811015611e3f576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b3b151590565b6000606060405180606001604052806026815260200161275b6026913990506000856001600160a01b03168288878760405160240180846001600160a01b03166001600160a01b0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b83811015611ed9578181015183820152602001611ec1565b50505050905090810190601f168015611f065780820380516001836020036101000a031916815260200191505b50945050505050604051602081830303815290604052906040518082805190602001908083835b60208310611f4c5780518252601f199092019160209182019101611f2d565b51815160001960209485036101000a01908116901991909116179052604080519490920184900390932092860180516001600160e01b03166001600160e01b031990941693909317835251855190945084935090508083835b60208310611fc45780518252601f199092019160209182019101611fa5565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612026576040519150601f19603f3d011682016040523d82523d6000602084013e61202b565b606091505b509098975050505050505050565b6001600160a01b038216612094576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b6003546120a7908263ffffffff611de516565b6003556001600160a01b0382166000908152600160205260409020546120d3908263ffffffff611de516565b6001600160a01b03831660008181526001602090815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261107e908490612325565b4290565b6001600160a01b0381166121c65760405162461bcd60e51b81526004018080602001828103825260268152602001806126836026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000610af93384846124dd565b600061223b8484846124dd565b6001600160a01b0384163314610ba557600061225785336118a4565b9050600019811461227c576122778533610c2d848763ffffffff61195016565b61231f565b6001600160a01b038516600090815260096020908152604080832033845290915290205415806122d657506122af61217d565b6001600160a01b038616600090815260096020908152604080832033845290915290205410155b61231f576040805162461bcd60e51b8152602060048201526015602482015274195e1c1a5c9e481a5cc81a5b881d1a19481c185cdd605a1b604482015290519081900360640190fd5b50610ba5565b612337826001600160a01b0316611e46565b612388576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106123c65780518252601f1990920191602091820191016123a7565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612428576040519150601f19603f3d011682016040523d82523d6000602084013e61242d565b606091505b509150915081612484576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115611893578080602001905160208110156124a057600080fd5b50516118935760405162461bcd60e51b815260040180806020018281038252602a8152602001806127f2602a913960400191505060405180910390fd5b6001600160a01b0383166125225760405162461bcd60e51b81526004018080602001828103825260258152602001806127a96025913960400191505060405180910390fd5b6001600160a01b0382166125675760405162461bcd60e51b81526004018080602001828103825260238152602001806126606023913960400191505060405180910390fd5b6001600160a01b038316600090815260016020526040902054612590908263ffffffff61195016565b6001600160a01b0380851660009081526001602052604080822093909355908416815220546125c5908263ffffffff611de516565b6001600160a01b0380841660008181526001602090815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b60328061262e8339019056fe60806040526040516032380380603283398181016040526020811015602357600080fd5b50516001600160a01b038116fffe45524332303a207472616e7366657220746f20746865207a65726f20616464726573734f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f2061646472657373a934977eb9828ba1f50591af02c98441645b4f0e916e0fecb4cc8e9c633dade2796f752063616e2774207472616e7366657220746f20446973747269627574696f6e20636f6e7472616374796f752063616e2774207472616e7366657220746f2062726964676520636f6e74726163744f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726f6e546f6b656e5472616e7366657228616464726573732c75696e743235362c62797465732963616e277420616464206f6e65206d6f7265206272696467652064756520746f2061206c696d697445524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f20616464726573735361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564796f752063616e2774207472616e7366657220746f2041647669736f727352657761726420636f6e7472616374796f752063616e2774207472616e7366657220746f20507269766174654f66666572696e6720636f6e7472616374a265627a7a72305820fbfacd4b36dace16f9fd57104c8aafe8b5a519b29f407207baf7bb6b917422a764736f6c634300050a0032" - - contract_address = insert(:contract_address, contract_code: deployed_bytecode) - - input_data = %Data{ - bytes: Base.decode16!(input, case: :lower) - } - - :transaction - |> insert(created_contract_address_hash: contract_address.hash, input: input_data) - |> with_block(status: :ok) - - params = %{ - "contract_source_code" => source_code, - "compiler_version" => "v0.5.10+commit.5a6ea5b1", - "evm_version" => "default", - "name" => "ERC677MultiBridgeToken", - "optimization" => true, - "optimization_runs" => 200, - "autodetect_constructor_args" => true - } - - assert {:ok, - %{ - abi: abi, - constructor_arguments: - "00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000348448061f604f4adf7ba714460c03cc6eb5b9f4000000000000000000000000348448061f604f4adf7ba714460c03cc6eb5b9f4000000000000000000000000348448061f604f4adf7ba714460c03cc6eb5b9f40000000000000000000000000000000000000000000000000000000000000003717765000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037177650000000000000000000000000000000000000000000000000000000000" - }} = Verifier.evaluate_authenticity(contract_address.hash, params) - - assert abi != nil + :transaction + |> insert(created_contract_address_hash: contract_address.hash, input: input_data) + |> with_block(status: :ok) + + params = %{ + "contract_source_code" => source_code, + "compiler_version" => "v0.5.10+commit.5a6ea5b1", + "evm_version" => "default", + "name" => "ERC677MultiBridgeToken", + "optimization" => true, + "optimization_runs" => @optimization_runs, + "autodetect_constructor_args" => true + } + + assert {:ok, + %{ + abi: abi, + constructor_arguments: + "00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000348448061f604f4adf7ba714460c03cc6eb5b9f4000000000000000000000000348448061f604f4adf7ba714460c03cc6eb5b9f4000000000000000000000000348448061f604f4adf7ba714460c03cc6eb5b9f40000000000000000000000000000000000000000000000000000000000000003717765000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037177650000000000000000000000000000000000000000000000000000000000" + }} = Verifier.evaluate_authenticity(contract_address.hash, params) + + assert abi != nil + end end end end diff --git a/apps/explorer/test/explorer/smart_contract/vyper/publisher_test.exs b/apps/explorer/test/explorer/smart_contract/vyper/publisher_test.exs index be977cbd48ab..7e31394cccaa 100644 --- a/apps/explorer/test/explorer/smart_contract/vyper/publisher_test.exs +++ b/apps/explorer/test/explorer/smart_contract/vyper/publisher_test.exs @@ -1,80 +1,82 @@ -defmodule Explorer.SmartContract.Vyper.PublisherTest do - use ExUnit.Case, async: true +if Application.compile_env(:explorer, :chain_type) !== :zksync do + defmodule Explorer.SmartContract.Vyper.PublisherTest do + use ExUnit.Case, async: true - use Explorer.DataCase + use Explorer.DataCase - doctest Explorer.SmartContract.Vyper.Publisher + doctest Explorer.SmartContract.Vyper.Publisher - @moduletag timeout: :infinity + @moduletag timeout: :infinity - alias Explorer.Chain.{SmartContract} - alias Explorer.Factory - alias Explorer.SmartContract.Vyper.Publisher + alias Explorer.Chain.{SmartContract} + alias Explorer.Factory + alias Explorer.SmartContract.Vyper.Publisher - setup do - configuration = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, enabled: false) + setup do + configuration = Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour) + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, enabled: false) - on_exit(fn -> - Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, configuration) - end) - end + on_exit(fn -> + Application.put_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour, configuration) + end) + end - describe "publish/2" do - test "with valid data creates a smart_contract" do - contract_code_info = Factory.contract_code_info_vyper() + describe "publish/2" do + test "with valid data creates a smart_contract" do + contract_code_info = Factory.contract_code_info_vyper() - contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) - :transaction - |> insert(created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) - |> with_block(status: :ok) + :transaction + |> insert(created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) + |> with_block(status: :ok) - valid_attrs = %{ - "contract_source_code" => contract_code_info.source_code, - "compiler_version" => contract_code_info.version, - "name" => contract_code_info.name - } + valid_attrs = %{ + "contract_source_code" => contract_code_info.source_code, + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name + } - response = Publisher.publish(contract_address.hash, valid_attrs) - assert {:ok, %SmartContract{} = smart_contract} = response + response = Publisher.publish(contract_address.hash, valid_attrs) + assert {:ok, %SmartContract{} = smart_contract} = response - assert smart_contract.address_hash == contract_address.hash - assert smart_contract.name == valid_attrs["name"] - assert smart_contract.compiler_version == valid_attrs["compiler_version"] - assert smart_contract.contract_source_code == valid_attrs["contract_source_code"] - assert is_nil(smart_contract.constructor_arguments) - assert smart_contract.abi == contract_code_info.abi - end + assert smart_contract.address_hash == contract_address.hash + assert smart_contract.name == valid_attrs["name"] + assert smart_contract.compiler_version == valid_attrs["compiler_version"] + assert smart_contract.contract_source_code == valid_attrs["contract_source_code"] + assert is_nil(smart_contract.constructor_arguments) + assert smart_contract.abi == contract_code_info.abi + end - test "allows to re-verify vyper contracts" do - contract_code_info = Factory.contract_code_info_vyper() + test "allows to re-verify vyper contracts" do + contract_code_info = Factory.contract_code_info_vyper() - contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) - :transaction - |> insert(created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) - |> with_block(status: :ok) + :transaction + |> insert(created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) + |> with_block(status: :ok) - valid_attrs = %{ - "contract_source_code" => contract_code_info.source_code, - "compiler_version" => contract_code_info.version, - "name" => contract_code_info.name - } + valid_attrs = %{ + "contract_source_code" => contract_code_info.source_code, + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name + } - response = Publisher.publish(contract_address.hash, valid_attrs) - assert {:ok, %SmartContract{}} = response + response = Publisher.publish(contract_address.hash, valid_attrs) + assert {:ok, %SmartContract{}} = response - updated_name = "AnotherContractName" + updated_name = "AnotherContractName" - valid_attrs = - valid_attrs - |> Map.put("name", updated_name) + valid_attrs = + valid_attrs + |> Map.put("name", updated_name) - response = Publisher.publish(contract_address.hash, valid_attrs) - assert {:ok, %SmartContract{} = smart_contract} = response + response = Publisher.publish(contract_address.hash, valid_attrs) + assert {:ok, %SmartContract{} = smart_contract} = response - assert smart_contract.name == valid_attrs["name"] + assert smart_contract.name == valid_attrs["name"] + end end end end diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index 4c090b69549a..7d823a81c0f9 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -63,6 +63,12 @@ defmodule Explorer.Factory do alias Ueberauth.Auth.Info alias Ueberauth.Auth + if Application.compile_env(:explorer, :chain_type) == :zksync do + @optimization_runs "1" + else + @optimization_runs 1 + end + def account_identity_factory do %Identity{ uid: sequence("github|"), @@ -446,7 +452,7 @@ defmodule Explorer.Factory do ], version: "v0.8.4+commit.c7e474f2", optimized: true, - optimization_runs: 1, + optimization_runs: @optimization_runs, constructor_args: "00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000756b5b30000000000000000000000000000000000000000000000000000000000000002000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000006ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8f0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000371776500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003657771000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097177657177657177650000000000000000000000000000000000000000000000" } From ab2445dfe2f9db58b2064894c0f00e4271d1055c Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Fri, 16 Aug 2024 16:36:14 +0300 Subject: [PATCH 082/363] ETH Sepolia internal transactions shrink --- .../publish-docker-image-for-eth-sepolia.yml | 72 ++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-docker-image-for-eth-sepolia.yml b/.github/workflows/publish-docker-image-for-eth-sepolia.yml index 1c137b4d86d9..2b7976927c3f 100644 --- a/.github/workflows/publish-docker-image-for-eth-sepolia.yml +++ b/.github/workflows/publish-docker-image-for-eth-sepolia.yml @@ -89,4 +89,74 @@ jobs: CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=ethereum \ No newline at end of file + CHAIN_TYPE=ethereum + + - name: Build and push Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }}-shrink-internal-txs + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=ethereum + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }}-shrink-internal-txs + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=ethereum + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }}-shrink-internal-txs + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=ethereum + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file From 0009cae77cfc982702bbb64554633d38e8fe6d17 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Fri, 16 Aug 2024 18:25:23 +0400 Subject: [PATCH 083/363] fix: Fix raw-trace test (#10606) --- .../controllers/api/v2/transaction_controller_test.exs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs index 312a92a7e4bb..5b771b7ce662 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs @@ -1300,15 +1300,16 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do "gasUsed" => "0x7d37", "input" => "0xb118e2db0000000000000000000000000000000000000000000000000000000000000008", "output" => "0x", - "value" => "0x174876e800" + "value" => "0x174876e800", + "transactionHash" => to_string(transaction.hash) } - expect(EthereumJSONRPC.Mox, :json_rpc, fn _, _ -> {:ok, raw_trace} end) + expect(EthereumJSONRPC.Mox, :json_rpc, fn _, _ -> {:ok, [raw_trace]} end) request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/raw-trace") assert response = json_response(request, 200) - assert response == raw_trace + assert response == [raw_trace] end test "returns correct error", %{conn: conn} do From cfb1fbae3b7adaf41fabbbece7908c70b792328e Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Fri, 16 Aug 2024 17:47:06 +0300 Subject: [PATCH 084/363] 6.8.0 --- CHANGELOG.md | 141 ++++++++++++++++++++++++++++++ apps/ethereum_jsonrpc/mix.exs | 2 +- apps/explorer/mix.exs | 2 +- apps/indexer/mix.exs | 2 +- docker-compose/docker-compose.yml | 2 +- docker/Makefile | 2 +- mix.exs | 2 +- rel/config.exs | 2 +- 8 files changed, 148 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bad1ddd88aae..ae73675d9090 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,146 @@ # Changelog +## 6.8.0 + +### 🚀 Features + +- Support smart-contract verification in zkSync ([#10500](https://github.com/blockscout/blockscout/issues/10500)) +- Add icon for secondary coin ([#10241](https://github.com/blockscout/blockscout/issues/10241)) +- Integrate Cryptorank API ([#10550](https://github.com/blockscout/blockscout/issues/10550)) +- Enhance /api/v2/smart-contracts/:hash API endpoint ([#10558](https://github.com/blockscout/blockscout/issues/10558)) +- Add method name to transactions CSV export ([#10579](https://github.com/blockscout/blockscout/issues/10579)) +- Add /api/v2/proxy/metadata/addresses endpoint ([#10585](https://github.com/blockscout/blockscout/issues/10585)) +- More descriptive status for Arbitrum message for the transaction view ([#10593](https://github.com/blockscout/blockscout/issues/10593)) +- Add internal_transactions to Tx interpreter request ([#10347](https://github.com/blockscout/blockscout/issues/10347)) +- Add token decimals to token transfers CSV export ([#10589](https://github.com/blockscout/blockscout/issues/10589)) +- Add DELETE /api/v2/import/token-info method ([#10580](https://github.com/blockscout/blockscout/issues/10580)) +- Add block number to token transfer object in API v2 endpoint ([#10591](https://github.com/blockscout/blockscout/issues/10591)) +- L1 tx associated with Arbitrum message in /api/v2/transactions/{txHash} ([#10590](https://github.com/blockscout/blockscout/issues/10590)) +- No rate limit API key ([#10515](https://github.com/blockscout/blockscout/issues/10515)) +- Support for `:celo` chain type ([#10564](https://github.com/blockscout/blockscout/issues/10564)) +- Public IPFS gateway URL ([#10511](https://github.com/blockscout/blockscout/issues/10511)) +- Add CSV_EXPORT_LIMIT env ([#10497](https://github.com/blockscout/blockscout/issues/10497)) +- Backfiller for omitted WETH transfers ([#10466](https://github.com/blockscout/blockscout/issues/10466)) +- Add INDEXER_DISABLE_REPLACED_TRANSACTION_FETCHER env ([#10485](https://github.com/blockscout/blockscout/issues/10485)) +- Revisited approach to catchup missed Arbitrum messages ([#10374](https://github.com/blockscout/blockscout/issues/10374)) +- Missing Arbitrum batches re-discovery ([#10446](https://github.com/blockscout/blockscout/issues/10446)) +- Add memory metrics for OnDemand fetchers ([#10425](https://github.com/blockscout/blockscout/issues/10425)) +- Add Celestia blobs support to Optimism batches fetcher ([#10199](https://github.com/blockscout/blockscout/issues/10199)) +- AnyTrust and Celestia support as DA for Arbitrum batches ([#10144](https://github.com/blockscout/blockscout/issues/10144)) +- Broadcast updates about new Arbitrum batches and L1-L2 messages through WebSocket ([#10272](https://github.com/blockscout/blockscout/issues/10272)) + +### 🐛 Bug Fixes + +- Fix raw-trace test ([#10606](https://github.com/blockscout/blockscout/issues/10606)) +- Fix internal transaction validation ([#10443](https://github.com/blockscout/blockscout/issues/10443)) +- Fix internal transactions runner test for zetachain ([#10576](https://github.com/blockscout/blockscout/issues/10576)) +- Filter out incorrect L1-to-L2 Arbitrum messages ([#10570](https://github.com/blockscout/blockscout/issues/10570)) +- Fetch contract methods decoding candidates sorted by inserted_at ([#10529](https://github.com/blockscout/blockscout/issues/10529)) +- Sanitize topic value before making db query ([#10481](https://github.com/blockscout/blockscout/issues/10481)) +- Fix :checkout_timeout error on NFT fetching ([#10429](https://github.com/blockscout/blockscout/issues/10429)) +- Proper handling confirmations for Arbitrum rollup block in the middle of a batch ([#10482](https://github.com/blockscout/blockscout/issues/10482)) +- Sanitize contractURI response ([#10479](https://github.com/blockscout/blockscout/issues/10479)) +- Use token_type from tt instead of token ([#10555](https://github.com/blockscout/blockscout/issues/10555)) +- Non-consensus logs in JSON RPC and ETH RPC APIs ([#10545](https://github.com/blockscout/blockscout/issues/10545)) +- Fix address_to_logs consensus filtering ([#10528](https://github.com/blockscout/blockscout/issues/10528)) +- Error on internal transactions CSV export ([#10495](https://github.com/blockscout/blockscout/issues/10495)) +- Extend block search range for `getblocknobytime` method ([#10475](https://github.com/blockscout/blockscout/issues/10475)) +- Move recon dep to explorer mix.exs ([#10487](https://github.com/blockscout/blockscout/issues/10487)) +- Add missing condition to fetch_min_missing_block_cache ([#10478](https://github.com/blockscout/blockscout/issues/10478)) +- Mud api format fixes ([#10362](https://github.com/blockscout/blockscout/issues/10362)) +- Add no overlapping constraint to missing_block_ranges ([#10449](https://github.com/blockscout/blockscout/issues/10449)) +- Avoid infinite loop during batch block range binary search ([#10436](https://github.com/blockscout/blockscout/issues/10436)) +- Fix "key :bytes not found in: nil" issue ([#10435](https://github.com/blockscout/blockscout/issues/10435)) +- Missing clauses in MetadataPreloader functions ([#10439](https://github.com/blockscout/blockscout/issues/10439)) +- Code compiler test ([#10454](https://github.com/blockscout/blockscout/issues/10454)) +- Include internal transactions in state change ([#10210](https://github.com/blockscout/blockscout/issues/10210)) +- Race condition in cache tests ([#10441](https://github.com/blockscout/blockscout/issues/10441)) +- Fix on-demand fetchers metrics ([#10431](https://github.com/blockscout/blockscout/issues/10431)) +- Transactions and token transfers block_consensus ([#10285](https://github.com/blockscout/blockscout/issues/10285)) +- Allow fetching image from properties -> image prop in token instance metadata ([#10380](https://github.com/blockscout/blockscout/issues/10380)) +- Filter out internal transactions belonging to reorg ([#10330](https://github.com/blockscout/blockscout/issues/10330)) +- Fix logs sorting in API v1 ([#10405](https://github.com/blockscout/blockscout/issues/10405)) +- Fix flickering transaction_estimated_count/1 test ([#10403](https://github.com/blockscout/blockscout/issues/10403)) +- Fix flickering "updates cache if initial value is zero" tests ([#10402](https://github.com/blockscout/blockscout/issues/10402)) +- /addresses empty list flickering test fix ([#10400](https://github.com/blockscout/blockscout/issues/10400)) +- Fix missing expectation in mock_beacon_storage_pointer_request ([#10399](https://github.com/blockscout/blockscout/issues/10399)) +- Fix /stats/charts/market test ([#10392](https://github.com/blockscout/blockscout/issues/10392)) +- Alternative way to detect blocks range for ArbitrumOne batches ([#10295](https://github.com/blockscout/blockscout/issues/10295)) +- Fix exchange rate flickering test ([#10383](https://github.com/blockscout/blockscout/issues/10383)) +- Fix gas price oracle flickering test ([#10381](https://github.com/blockscout/blockscout/issues/10381)) +- Fix address controller flickering test ([#10382](https://github.com/blockscout/blockscout/issues/10382)) +- Empty revert reasons in geth variant ([#10243](https://github.com/blockscout/blockscout/issues/10243)) +- Proper handling for re-discovered Arbitrum batches ([#10326](https://github.com/blockscout/blockscout/issues/10326)) +- Proper lookup of confirmed Arbitrum cross-chain messages ([#10322](https://github.com/blockscout/blockscout/issues/10322)) +- Indexer first block usage to halt Arbitrum missed messages discovery ([#10280](https://github.com/blockscout/blockscout/issues/10280)) + +### 📚 Documentation + +- Refine PR template +- Move note in README.md higher for visibility ([#10450](https://github.com/blockscout/blockscout/issues/10450)) + +### ⚡ Performance + +- Speed up worlds list query ([#10556](https://github.com/blockscout/blockscout/issues/10556)) +- Reduce LookUpSmartContractSourcesOnDemand fetcher footprint ([#10457](https://github.com/blockscout/blockscout/issues/10457)) + +### ⚙️ Miscellaneous Tasks + +- Shrink internal transactions ([#10567](https://github.com/blockscout/blockscout/issues/10567)) +- Upgrade WS client ([#10407](https://github.com/blockscout/blockscout/issues/10407)) +- Add API endpoint for OP batch blocks ([#10566](https://github.com/blockscout/blockscout/issues/10566)) +- Public metrics config API endpoint ([#10568](https://github.com/blockscout/blockscout/issues/10568)) +- Add workflow to generate separate pre-release indexer/API images for Arbitrum +- Fix some comments ([#10519](https://github.com/blockscout/blockscout/issues/10519)) +- Set Geth as default JSON RPC Variant ([#10509](https://github.com/blockscout/blockscout/issues/10509)) +- Return ex_abi core lib dependency ([#10470](https://github.com/blockscout/blockscout/issues/10470)) +- Add recon dependency ([#10486](https://github.com/blockscout/blockscout/issues/10486)) +- Manage Solidityscan platform id via runtime variable ([#10473](https://github.com/blockscout/blockscout/issues/10473)) +- Add test for broadcasting fetched_bytecode message ([#10244](https://github.com/blockscout/blockscout/issues/10244)) +- Disable public metrics by default, set 1 day as default period of update ([#10469](https://github.com/blockscout/blockscout/issues/10469)) +- Move eth_bytecode_db_lookup_started event to smart contract related event handler ([#10462](https://github.com/blockscout/blockscout/issues/10462)) +- Token transfers broadcast optimization ([#10465](https://github.com/blockscout/blockscout/issues/10465)) +- Remove catchup sequence logic ([#10415](https://github.com/blockscout/blockscout/issues/10415)) +- Remove single implementation name, address from API v2 response ([#10390](https://github.com/blockscout/blockscout/issues/10390)) +- Refactor init functions to use continue if needed ([#10300](https://github.com/blockscout/blockscout/issues/10300)) +- Update buildkit builders ([#10377](https://github.com/blockscout/blockscout/issues/10377)) + +### New ENV Variables + +| Variable | Description | Parameters | +| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | +| `ETHEREUM_JSONRPC_FALLBACK_WS_URL` | The fallback WebSockets RPC endpoint used to subscribe to the `newHeads` subscription alerting the indexer to fetch new blocks. Implemented in [#10407](https://github.com/blockscout/blockscout/pull/10407). |

Version: v6.8.0+
Default: (empty)
Applications: Indexer

| +| `ETHEREUM_JSONRPC_WS_RETRY_INTERVAL` | The interval between retries of connecting to WebSocket RPC endpoint after the previous attempt is failed. Implemented in [#10407](https://github.com/blockscout/blockscout/pull/10407). |

Version: v6.8.0+
Default: 1m
Applications: Indexer

| +| `DATABASE_EVENT_URL` | Variable to define the Postgres Database endpoint that will be used by event listener process. Applicable for separate indexer and API setup. More info in related PR. Implemented in [#10164](https://github.com/blockscout/blockscout/pull/10164). |

Version: v6.8.0+
Default: (empty)
Applications: API

| +| `PUBLIC_METRICS_ENABLED` | Variable to enable running queries at /public-metrics endpoint. Implemented in [#10469](https://github.com/blockscout/blockscout/pull/10469). |

Version: v6.8.0+
Default: false
Applications: API

| +| `PUBLIC_METRICS_UPDATE_PERIOD_HOURS` | Public metrics update period in hours at /public-metrics endpoint. Implemented in [#10469](https://github.com/blockscout/blockscout/pull/10469). |

Version: v6.8.0+
Default: 24
Applications: API

| +| `SHRINK_INTERNAL_TRANSACTIONS_ENABLED` | Variable to enable internal transactions shrinking logic. Implemented in [#10567](https://github.com/blockscout/blockscout/pull/10567). |

Version: v6.8.0+
Default: false
Applications: API, Indexer

| +| `SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE` | Batch size of the shrink internal transactions migration. Implemented in [#10567](https://github.com/blockscout/blockscout/pull/10567). |

Version: v6.8.0+
Default: 1000
Applications: API, Indexer

| +| `SHRINK_INTERNAL_TRANSACTIONS_CONCURRENCY` | Concurrency of the shrink internal transactions migration. Implemented in [#10567](https://github.com/blockscout/blockscout/pull/10567). |

Version: v6.8.0+
Default: 1
Applications: API, Indexer

| +| `IPFS_PUBLIC_GATEWAY_URL` | IPFS public gateway url which is used by frontend to display IPFS resources such as token instance image. |

Version: v6.8.0+
Default: https://ipfs.io/ipfs
Applications: API

| +| `INDEXER_TOKEN_INSTANCE_RETRY_MAX_REFETCH_INTERVAL` | Maximum interval between attempts to fetch token instance metadata. [Time format](backend-env-variables.md#time-format). Implemented in [#10027](https://github.com/blockscout/blockscout/pull/10027). |

Version: v6.8.0+
Default: 168h
Applications: Indexer

| +| `INDEXER_TOKEN_INSTANCE_RETRY_EXPONENTIAL_TIMEOUT_BASE` | Base to calculate exponential timeout. Implemented in [#10027](https://github.com/blockscout/blockscout/pull/10027). |

Version: v6.8.0+
Default: 2
Applications: Indexer

| +| `INDEXER_TOKEN_INSTANCE_RETRY_EXPONENTIAL_TIMEOUT_COEFF` | Coefficient to calculate exponential timeout. Implemented in [#10027](https://github.com/blockscout/blockscout/pull/10027). |

Version: v6.8.0+
Default: 100
Applications: Indexer

| +| `MISSING_BALANCE_OF_TOKENS_WINDOW_SIZE` | Minimal blocks count until the next token balance request will be executed for tokens that doesn't implement `balanceOf` function. Implemented in [#10142](https://github.com/blockscout/blockscout/pull/10142) |

Version: v6.8.0+
Default: 100
Applications: Indexer

| +| `ETHEREUM_JSONRPC_GETH_ALLOW_EMPTY_TRACES` | Allow transactions to not have internal transactions. Implemented in [#10200](https://github.com/blockscout/blockscout/pull/10200) |

Version: v6.8.0+
Default: false
Applications: Indexer

| +| `INDEXER_DISABLE_REPLACED_TRANSACTION_FETCHER` | If `true`, `Indexer.Fetcher.ReplacedTransaction` fetcher doesn't run |

Version: v6.8.0+
Default: false
Applications: Indexer

| +| `SANITIZE_INCORRECT_WETH_BATCH_SIZE` | Number of token transfers to sanitize in the batch. Implemented in [#10134](https://github.com/blockscout/blockscout/pull/10134) |

Version: v6.8.0+
Default: 100
Applications: API, Indexer

| +| `SANITIZE_INCORRECT_WETH_CONCURRENCY` | Number of parallel sanitizing token transfer batches processing. Implemented in [#10134](https://github.com/blockscout/blockscout/pull/10134) |

Version: v6.8.0+
Default: 1
Applications: API, Indexer

| +| `MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_BATCH_SIZE` | Number of logs to process in the batch. Implemented in [#10466](https://github.com/blockscout/blockscout/pull/10466) |

Version: v6.8.0+
Default: 50
Applications: API, Indexer

| +| `MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_CONCURRENCY`| Number of parallel logs batches processing. Implemented in [#10466](https://github.com/blockscout/blockscout/pull/10466) |

Version: v6.8.0+
Default: 5
Applications: API, Indexer

| +| `MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_TIMEOUT` | Time interval between checks if queue are not empty. The same timeout multiplied by 2 used between checks if qeueue are not full. Implemented in [#10466](https://github.com/blockscout/blockscout/pull/10466) |

Version: v6.8.0+
Default: 250ms
Applications: API, Indexer

| +| `EXCHANGE_RATES_SOURCE` | Source for native coin and tokens price fetching. Possible values are: `coin_gecko`, `coin_market_cap` or `mobula`. |

Version: v6.8.0+
Default: coin_gecko
Applications: API, Indexer

| +| `EXCHANGE_RATES_SECONDARY_COIN_SOURCE` | Source for secondary coin fetching. Possible values are: `coin_gecko`, `coin_market_cap` or `mobula`. |

Version: v6.8.0+
Default: coin_gecko
Applications: API, Indexer

| +| `TOKEN_EXCHANGE_RATES_SOURCE` | Sets the source for tokens price fetching. Available values are `coin_gecko`, `cryptorank`. Implemented in [#10550](https://github.com/blockscout/blockscout/pull/10550). |

Version: v6.8.0+
Default: coin_gecko
Applications: API, Indexer

| +| `EXCHANGE_RATES_CRYPTORANK_SECONDARY_COIN_ID` | Sets Cryptorank coin ID for secondary coin market chart. Implemented in [#10550](https://github.com/blockscout/blockscout/pull/10550). |

Version: v6.8.0+
Default: (empty)
Applications: API, Indexer

| +| `EXCHANGE_RATES_CRYPTORANK_PLATFORM_ID` | Sets Cryptorank platform ID. Implemented in [#10550](https://github.com/blockscout/blockscout/pull/10550). |

Version: v6.8.0+
Default: (empty)
Applications: API, Indexer

| +| `EXCHANGE_RATES_CRYPTORANK_BASE_URL` | If set, overrides the Cryptorank API url. Implemented in [#10550](https://github.com/blockscout/blockscout/pull/10550). |

Version: v6.8.0+
Default: https://api.cryptorank.io/v1/
Applications: API, Indexer

| +| `EXCHANGE_RATES_CRYPTORANK_API_KEY` | Cryptorank API key. Current implementation uses dedicated beta Cryptorank API endpoint. If you want to integrate Cryptorank price fetching you should contact Cryptorank to receive an API key. Implemented in [#10550](https://github.com/blockscout/blockscout/pull/10550). |

Version: v6.8.0+
Default: (empty)
Applications: API, Indexer

| +| `EXCHANGE_RATES_CRYPTORANK_COIN_ID` | Sets Cryptorank coin ID. Implemented in [#10550](https://github.com/blockscout/blockscout/pull/10550). |

Version: v6.8.0+
Default: (empty)
Applications: API, Indexer

| +| `EXCHANGE_RATES_CRYPTORANK_LIMIT` | Sets the maximum number of token prices returned in a single request. Implemented in [#10550](https://github.com/blockscout/blockscout/pull/10550). |

Version: v6.8.0+
Default: 1000
Applications: API, Indexer

| +| `WHITELISTED_WETH_CONTRACTS` | Comma-separated list of smart-contract address hashes of WETH-like tokens which deposit and withdrawal events you'd like to index. Implemented in [#10134](https://github.com/blockscout/blockscout/pull/10134) |

Version: v6.8.0+
Default: (empty)
Applications: API, Indexer

| +| `API_NO_RATE_LIMIT_API_KEY` | API key with no rate limit. Implemented in [#10515](https://github.com/blockscout/blockscout/pull/10515) |

Version: v6.8.0+
Default: (empty)
Applications: API

| + ## 6.7.2 ### 🐛 Bug Fixes diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs index b7c5bee4ad18..767b4b8d12b8 100644 --- a/apps/ethereum_jsonrpc/mix.exs +++ b/apps/ethereum_jsonrpc/mix.exs @@ -23,7 +23,7 @@ defmodule EthereumJsonrpc.MixProject do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "6.7.2" + version: "6.8.0" ] end diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index c7920b8fb9a4..1dde22eb7f7f 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -24,7 +24,7 @@ defmodule Explorer.Mixfile do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "6.7.2", + version: "6.8.0", xref: [exclude: [BlockScoutWeb.Routers.WebRouter.Helpers, Indexer.Helper]] ] end diff --git a/apps/indexer/mix.exs b/apps/indexer/mix.exs index e01d7be44d89..f8fca0ae95f9 100644 --- a/apps/indexer/mix.exs +++ b/apps/indexer/mix.exs @@ -14,7 +14,7 @@ defmodule Indexer.MixProject do elixirc_paths: elixirc_paths(Mix.env()), lockfile: "../../mix.lock", start_permanent: Mix.env() == :prod, - version: "6.7.2", + version: "6.8.0", xref: [ exclude: [ Explorer.Chain.Optimism.Deposit, diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index 167df068935f..f58826568644 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -37,7 +37,7 @@ services: CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED: "" CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL: "" ADMIN_PANEL_ENABLED: "" - RELEASE_VERSION: 6.7.2 + RELEASE_VERSION: 6.8.0 links: - db:database environment: diff --git a/docker/Makefile b/docker/Makefile index 8264a37dbb87..966169956309 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -10,7 +10,7 @@ STATS_CONTAINER_NAME := stats STATS_DB_CONTAINER_NAME := stats-db PROXY_CONTAINER_NAME := proxy PG_CONTAINER_NAME := postgres -RELEASE_VERSION ?= '6.7.2' +RELEASE_VERSION ?= '6.8.0' TAG := $(RELEASE_VERSION)-commit-$(shell git log -1 --pretty=format:"%h") STABLE_TAG := $(RELEASE_VERSION) diff --git a/mix.exs b/mix.exs index b07340331ee5..37b0f7cb803d 100644 --- a/mix.exs +++ b/mix.exs @@ -7,7 +7,7 @@ defmodule BlockScout.Mixfile do [ # app: :block_scout, # aliases: aliases(config_env()), - version: "6.7.2", + version: "6.8.0", apps_path: "apps", deps: deps(), dialyzer: dialyzer(), diff --git a/rel/config.exs b/rel/config.exs index 9090ce16b8e6..7cebe1dfff5a 100644 --- a/rel/config.exs +++ b/rel/config.exs @@ -71,7 +71,7 @@ end # will be used by default release :blockscout do - set version: "6.7.2-beta" + set version: "6.8.0-beta" set applications: [ :runtime_tools, block_scout_web: :permanent, From 33225b5a27ab57e6b38e20015d663db4a96214e7 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Mon, 19 Aug 2024 12:45:13 +0400 Subject: [PATCH 085/363] fix: Add SHRINK_INTERNAL_TRANSACTIONS_ENABLED arg to Dockerfile (#10616) --- docker/Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 3431a7182c5c..6e49e61b0c28 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -27,6 +27,8 @@ ARG BRIDGED_TOKENS_ENABLED ENV BRIDGED_TOKENS_ENABLED=${BRIDGED_TOKENS_ENABLED} ARG MUD_INDEXER_ENABLED ENV MUD_INDEXER_ENABLED=${MUD_INDEXER_ENABLED} +ARG SHRINK_INTERNAL_TRANSACTIONS_ENABLED +ENV SHRINK_INTERNAL_TRANSACTIONS_ENABLED=${SHRINK_INTERNAL_TRANSACTIONS_ENABLED} # Cache elixir deps ADD mix.exs mix.lock ./ @@ -76,6 +78,8 @@ ARG CHAIN_TYPE ENV CHAIN_TYPE=${CHAIN_TYPE} ARG BRIDGED_TOKENS_ENABLED ENV BRIDGED_TOKENS_ENABLED=${BRIDGED_TOKENS_ENABLED} +ARG SHRINK_INTERNAL_TRANSACTIONS_ENABLED +ENV SHRINK_INTERNAL_TRANSACTIONS_ENABLED=${SHRINK_INTERNAL_TRANSACTIONS_ENABLED} ARG BLOCKSCOUT_VERSION ENV BLOCKSCOUT_VERSION=${BLOCKSCOUT_VERSION} From a9c2a08716941db3363f8016ce37dbf638a08c1d Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 19 Aug 2024 13:35:18 +0300 Subject: [PATCH 086/363] workflows for Docker image generation with shrunk internal transactions table --- .../publish-docker-image-for-arbitrum.yml | 72 ++++++++++- .../publish-docker-image-for-optimism.yml | 72 ++++++++++- .../publish-docker-image-for-zksync.yml | 113 +++++++++++++++++- .github/workflows/release-arbitrum.yml | 72 ++++++++++- .github/workflows/release-eth.yml | 72 ++++++++++- .github/workflows/release-optimism.yml | 72 ++++++++++- .github/workflows/release-zksync.yml | 72 ++++++++++- .github/workflows/release.yml | 90 ++++++++++++++ 8 files changed, 628 insertions(+), 7 deletions(-) diff --git a/.github/workflows/publish-docker-image-for-arbitrum.yml b/.github/workflows/publish-docker-image-for-arbitrum.yml index fe2621d727ba..19757155583c 100644 --- a/.github/workflows/publish-docker-image-for-arbitrum.yml +++ b/.github/workflows/publish-docker-image-for-arbitrum.yml @@ -89,4 +89,74 @@ jobs: CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=arbitrum \ No newline at end of file + CHAIN_TYPE=arbitrum + + - name: Build and push Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=arbitrum + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=arbitrum + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=arbitrum + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/.github/workflows/publish-docker-image-for-optimism.yml b/.github/workflows/publish-docker-image-for-optimism.yml index d10cf7fe79d6..e640ce6f5916 100644 --- a/.github/workflows/publish-docker-image-for-optimism.yml +++ b/.github/workflows/publish-docker-image-for-optimism.yml @@ -89,4 +89,74 @@ jobs: CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=optimism \ No newline at end of file + CHAIN_TYPE=optimism + + - name: Build and push Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=optimism + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=optimism + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-indexer-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=optimism + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/.github/workflows/publish-docker-image-for-zksync.yml b/.github/workflows/publish-docker-image-for-zksync.yml index bcf784f35cb0..508355156091 100644 --- a/.github/workflows/publish-docker-image-for-zksync.yml +++ b/.github/workflows/publish-docker-image-for-zksync.yml @@ -23,7 +23,7 @@ jobs: docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - - name: Build and push Docker image + - name: Build and push Docker image (indexer + API) uses: docker/build-push-action@v5 with: context: . @@ -44,4 +44,115 @@ jobs: CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zksync + + - name: Build and push Docker image (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zksync + + - name: Build and push Docker image (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zksync + + - name: Build and push Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zksync + + - name: Build and push Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zksync + + - name: Build and push Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=zksync \ No newline at end of file diff --git a/.github/workflows/release-arbitrum.yml b/.github/workflows/release-arbitrum.yml index 68334f9d55b0..0d701ff27288 100644 --- a/.github/workflows/release-arbitrum.yml +++ b/.github/workflows/release-arbitrum.yml @@ -91,4 +91,74 @@ jobs: CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=arbitrum \ No newline at end of file + CHAIN_TYPE=arbitrum + + - name: Build and push Docker image for Arbitrum (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-arbitrum:${{ env.RELEASE_VERSION }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=arbitrum + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Arbitrum (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-arbitrum:${{ env.RELEASE_VERSION }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=arbitrum + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Arbitrum (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-arbitrum:${{ env.RELEASE_VERSION }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=arbitrum + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/.github/workflows/release-eth.yml b/.github/workflows/release-eth.yml index bc139d870b10..310e20f185fe 100644 --- a/.github/workflows/release-eth.yml +++ b/.github/workflows/release-eth.yml @@ -91,4 +91,74 @@ jobs: ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=ethereum \ No newline at end of file + CHAIN_TYPE=ethereum + + - name: Build and push Docker image for Ethereum (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-ethereum:${{ env.RELEASE_VERSION }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=ethereum + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Ethereum (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-ethereum:${{ env.RELEASE_VERSION }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=ethereum + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Ethereum (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-ethereum:${{ env.RELEASE_VERSION }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=ethereum + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/.github/workflows/release-optimism.yml b/.github/workflows/release-optimism.yml index 0f9977675978..9ddfd4da0f01 100644 --- a/.github/workflows/release-optimism.yml +++ b/.github/workflows/release-optimism.yml @@ -91,4 +91,74 @@ jobs: ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=optimism \ No newline at end of file + CHAIN_TYPE=optimism + + - name: Build and push Docker image for Optimism (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-optimism:${{ env.RELEASE_VERSION }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=optimism + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Optimism (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-optimism:${{ env.RELEASE_VERSION }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=optimism + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Optimism (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-optimism:${{ env.RELEASE_VERSION }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=optimism + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/.github/workflows/release-zksync.yml b/.github/workflows/release-zksync.yml index e0d01276387e..e974e44b9ce8 100644 --- a/.github/workflows/release-zksync.yml +++ b/.github/workflows/release-zksync.yml @@ -92,4 +92,74 @@ jobs: ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=zksync \ No newline at end of file + CHAIN_TYPE=zksync + + - name: Build and push Docker image for ZkSync (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zksync:${{ env.RELEASE_VERSION }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zksync + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for ZkSync (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zksync:${{ env.RELEASE_VERSION }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zksync + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for ZkSync (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zksync:${{ env.RELEASE_VERSION }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zksync + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 385b836cb907..271a47935e6e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -113,6 +113,96 @@ jobs: BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta RELEASE_VERSION=${{ env.RELEASE_VERSION }} + - name: Build & Push Core Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + cache-from: type=registry,ref=blockscout/blockscout:buildcache + cache-to: type=registry,ref=blockscout/blockscout:buildcache,mode=max + tags: blockscout/blockscout:${{ env.RELEASE_VERSION }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + DECODE_NOT_A_CONTRACT_CALLS=false + MIXPANEL_URL= + MIXPANEL_TOKEN= + AMPLITUDE_URL= + AMPLITUDE_API_KEY= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build & Push Core Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + cache-from: type=registry,ref=blockscout/blockscout:buildcache + cache-to: type=registry,ref=blockscout/blockscout:buildcache,mode=max + tags: blockscout/blockscout:${{ env.RELEASE_VERSION }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + DECODE_NOT_A_CONTRACT_CALLS=false + MIXPANEL_URL= + MIXPANEL_TOKEN= + AMPLITUDE_URL= + AMPLITUDE_API_KEY= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build & Push Core Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + cache-from: type=registry,ref=blockscout/blockscout:buildcache + cache-to: type=registry,ref=blockscout/blockscout:buildcache,mode=max + tags: blockscout/blockscout:${{ env.RELEASE_VERSION }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + DECODE_NOT_A_CONTRACT_CALLS=false + MIXPANEL_URL= + MIXPANEL_TOKEN= + AMPLITUDE_URL= + AMPLITUDE_API_KEY= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + # - name: Send release announcement to Slack workflow # id: slack # uses: slackapi/slack-github-action@v1.24.0 From 11644455ba3c6d4dd431660e76ca192d42fc35b8 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:16:51 +0400 Subject: [PATCH 087/363] fix: Fix fetch_first_trace tests (#10618) --- .../api/rpc/transaction_controller_test.exs | 10 ++++++++++ .../block_scout_web/views/transaction_view_test.exs | 5 +++++ apps/explorer/test/explorer/chain_test.exs | 5 +++++ 3 files changed, 20 insertions(+) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs index 913849e8ca30..0ecd2fd52a2d 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs @@ -736,6 +736,9 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do "txhash" => "#{transaction.hash}" } + init_config = Application.get_env(:ethereum_jsonrpc, EthereumJSONRPC.Geth) + Application.put_env(:ethereum_jsonrpc, EthereumJSONRPC.Geth, tracer: "call_tracer", debug_trace_timeout: "5s") + assert response = conn |> get("/api", params) @@ -744,6 +747,8 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do assert response["result"]["revertReason"] == hex_reason assert response["status"] == "1" assert response["message"] == "OK" + + Application.put_env(:ethereum_jsonrpc, EthereumJSONRPC.Geth, init_config) end end @@ -836,6 +841,9 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do "txhash" => "#{transaction.hash}" } + init_config = Application.get_env(:ethereum_jsonrpc, EthereumJSONRPC.Geth) + Application.put_env(:ethereum_jsonrpc, EthereumJSONRPC.Geth, tracer: "call_tracer", debug_trace_timeout: "5s") + assert response = conn |> get("/api", params) @@ -844,6 +852,8 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do assert response["result"]["revertReason"] in ["", "0x"] assert response["status"] == "1" assert response["message"] == "OK" + + Application.put_env(:ethereum_jsonrpc, EthereumJSONRPC.Geth, init_config) end defp resolve_schema(result \\ %{}) do diff --git a/apps/block_scout_web/test/block_scout_web/views/transaction_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/transaction_view_test.exs index a28f2b579e90..ea53a2a970a7 100644 --- a/apps/block_scout_web/test/block_scout_web/views/transaction_view_test.exs +++ b/apps/block_scout_web/test/block_scout_web/views/transaction_view_test.exs @@ -366,6 +366,9 @@ defmodule BlockScoutWeb.TransactionViewTest do end ) + init_config = Application.get_env(:ethereum_jsonrpc, EthereumJSONRPC.Geth) + Application.put_env(:ethereum_jsonrpc, EthereumJSONRPC.Geth, tracer: "call_tracer", debug_trace_timeout: "5s") + revert_reason = TransactionView.transaction_revert_reason(transaction, nil) assert revert_reason == @@ -373,6 +376,8 @@ defmodule BlockScoutWeb.TransactionViewTest do [ {"reason", "string", "UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT"} ]} + + Application.put_env(:ethereum_jsonrpc, EthereumJSONRPC.Geth, init_config) end end end diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index a57dac010c36..7ec3a65922b1 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -4324,6 +4324,9 @@ defmodule Explorer.ChainTest do end ) + init_config = Application.get_env(:ethereum_jsonrpc, EthereumJSONRPC.Geth) + Application.put_env(:ethereum_jsonrpc, EthereumJSONRPC.Geth, tracer: "call_tracer", debug_trace_timeout: "5s") + assert Chain.transaction_to_revert_reason(transaction) == hex_reason assert Transaction.decoded_revert_reason(transaction, hex_reason) == { @@ -4332,6 +4335,8 @@ defmodule Explorer.ChainTest do "Error(string reason)", [{"reason", "string", "No credit of that type"}] } + + Application.put_env(:ethereum_jsonrpc, EthereumJSONRPC.Geth, init_config) end end From da88e66dfb1eddfaf65fe25add36af01d2a486b5 Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Tue, 20 Aug 2024 01:46:44 -0600 Subject: [PATCH 088/363] fix: proper default value of gas used for dropped Arbitrum transactions (#10619) --- .../lib/block_scout_web/views/api/v2/arbitrum_view.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex index e41089b65fd2..e35adcb29e13 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex @@ -617,8 +617,8 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do # for the transaction prior to the migration to Arbitrum specific BS build. gas_used_for_l1 = Map.get(arbitrum_tx, :gas_used_for_l1, 0) || 0 - gas_used = Map.get(arbitrum_tx, :gas_used, 0) - gas_price = Map.get(arbitrum_tx, :gas_price, 0) + gas_used = Map.get(arbitrum_tx, :gas_used, 0) || 0 + gas_price = Map.get(arbitrum_tx, :gas_price, 0) || 0 gas_used_for_l2 = gas_used From 6789774764d5988510c920a7ec66b2f652e36b6b Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 20 Aug 2024 12:15:54 +0300 Subject: [PATCH 089/363] Change status name in the comment --- apps/explorer/lib/explorer/chain/transaction.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index e6e48e2fa8fb..3ef684d2354f 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -430,7 +430,7 @@ defmodule Explorer.Chain.Transaction do processing. * `error` - the `error` from the last `t:Explorer.Chain.InternalTransaction.t/0` in `internal_transactions` that caused `status` to be `:error`. Only set after `internal_transactions_index_at` is set AND if there was an error. - Also, `error` is set if transaction is replaced/dropped + Also, `error` is set if transaction is dropped/replaced * `forks` - copies of this transactions that were collated into `uncles` not on the primary consensus of the chain. * `from_address` - the source of `value` * `from_address_hash` - foreign key of `from_address` From 2ddac132d51f2beeba51a17d171ebce1cde5de92 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:10:01 +0400 Subject: [PATCH 090/363] chore: Run shrink internal transactions migration for indexer instance only (#10631) --- apps/explorer/lib/explorer/application.ex | 10 +++++++++- config/config_helper.exs | 5 +++++ config/runtime.exs | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index 2e7638cfd07e..2e64c600855a 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -143,7 +143,7 @@ defmodule Explorer.Application do configure(Explorer.Migrator.TokenTransferBlockConsensus), configure(Explorer.Migrator.RestoreOmittedWETHTransfers), configure_chain_type_dependent_process(Explorer.Chain.Cache.StabilityValidatorsCounters, :stability), - configure(Explorer.Migrator.ShrinkInternalTransactions) + configure_mode_dependent_process(Explorer.Migrator.ShrinkInternalTransactions, :indexer) ] |> List.flatten() @@ -209,6 +209,14 @@ defmodule Explorer.Application do end end + defp configure_mode_dependent_process(process, mode) do + if Application.get_env(:explorer, :mode) in [mode, :all] do + process + else + [] + end + end + defp sc_microservice_configure(process) do if Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour)[:eth_bytecode_db?] do process diff --git a/config/config_helper.exs b/config/config_helper.exs index ffaf0ad0dbcb..9b37f0954469 100644 --- a/config/config_helper.exs +++ b/config/config_helper.exs @@ -322,6 +322,11 @@ defmodule ConfigHelper do @spec chain_type() :: atom() | nil def chain_type, do: parse_catalog_value("CHAIN_TYPE", @supported_chain_types, true, "default") + @supported_modes ["all", "indexer", "api"] + + @spec mode :: atom() + def mode, do: parse_catalog_value("MODE", @supported_modes, true, "all") + @spec eth_call_url(String.t() | nil) :: String.t() | nil def eth_call_url(default \\ nil) do System.get_env("ETHEREUM_JSONRPC_ETH_CALL_URL") || System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || default diff --git a/config/runtime.exs b/config/runtime.exs index 4bbb936c3d7f..7c59765fdd23 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -218,6 +218,7 @@ checksum_function = System.get_env("CHECKSUM_FUNCTION") exchange_rates_coin = System.get_env("EXCHANGE_RATES_COIN") config :explorer, + mode: ConfigHelper.mode(), coin: System.get_env("COIN") || exchange_rates_coin || "ETH", coin_name: System.get_env("COIN_NAME") || exchange_rates_coin || "ETH", allowed_solidity_evm_versions: From 012fbcc3c5543cdd64e6031398a7cb9f7451c3d8 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Wed, 21 Aug 2024 16:26:01 +0400 Subject: [PATCH 091/363] fix: Change mode env name (#10636) --- config/config_helper.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config_helper.exs b/config/config_helper.exs index 9b37f0954469..540f887a0a99 100644 --- a/config/config_helper.exs +++ b/config/config_helper.exs @@ -325,7 +325,7 @@ defmodule ConfigHelper do @supported_modes ["all", "indexer", "api"] @spec mode :: atom() - def mode, do: parse_catalog_value("MODE", @supported_modes, true, "all") + def mode, do: parse_catalog_value("APPLICATION_MODE", @supported_modes, true, "all") @spec eth_call_url(String.t() | nil) :: String.t() | nil def eth_call_url(default \\ nil) do From 1b3f84bad73c875e3425d09c59ce1a6a45db00bf Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 22 Aug 2024 10:19:46 +0300 Subject: [PATCH 092/363] fix: Better detection IPFS links in NFT metadata fetcher (#10638) * fix: Better detection IPFS links in NFT metadata fetcher * Fix tests * Remove bypass from tests --- .../lib/explorer/token/metadata_retriever.ex | 85 ++++++++++--------- .../token/metadata_retriever_test.exs | 27 ++++-- cspell.json | 1 + 3 files changed, 66 insertions(+), 47 deletions(-) diff --git a/apps/explorer/lib/explorer/token/metadata_retriever.ex b/apps/explorer/lib/explorer/token/metadata_retriever.ex index 959add1e0900..228fb91a0bec 100644 --- a/apps/explorer/lib/explorer/token/metadata_retriever.ex +++ b/apps/explorer/lib/explorer/token/metadata_retriever.ex @@ -13,7 +13,6 @@ defmodule Explorer.Token.MetadataRetriever do @no_uri_error "no uri" @vm_execution_error "VM execution error" - @ipfs_protocol "ipfs://" @invalid_base64_data "invalid data:application/json;base64" # https://eips.ethereum.org/EIPS/eip-1155#metadata @@ -515,31 +514,11 @@ defmodule Explorer.Token.MetadataRetriever do end end - # CIDv0 IPFS links # https://docs.ipfs.tech/concepts/content-addressing/#version-0-v0 - defp fetch_json_from_uri({:ok, ["Qm" <> _ = result]}, _, token_id, hex_token_id, from_base_uri?) do - if String.length(result) == 46 do - ipfs? = true - fetch_json_from_uri({:ok, [ipfs_link(result)]}, ipfs?, token_id, hex_token_id, from_base_uri?) - else - Logger.warning(["Unknown metadata format result #{inspect(result)}."], fetcher: :token_instances) - - {:error, truncate_error(result)} - end - end - defp fetch_json_from_uri({:ok, ["'" <> token_uri]}, ipfs?, token_id, hex_token_id, from_base_uri?) do token_uri = token_uri |> String.split("'") |> List.first() fetch_metadata_inner(token_uri, ipfs?, token_id, hex_token_id, from_base_uri?) end - defp fetch_json_from_uri({:ok, ["http://" <> _ = token_uri]}, ipfs?, token_id, hex_token_id, from_base_uri?) do - fetch_metadata_inner(token_uri, ipfs?, token_id, hex_token_id, from_base_uri?) - end - - defp fetch_json_from_uri({:ok, ["https://" <> _ = token_uri]}, ipfs?, token_id, hex_token_id, from_base_uri?) do - fetch_metadata_inner(token_uri, ipfs?, token_id, hex_token_id, from_base_uri?) - end - defp fetch_json_from_uri( {:ok, [type = "data:application/json;utf8," <> json]}, ipfs?, @@ -587,35 +566,63 @@ defmodule Explorer.Token.MetadataRetriever do {:error, @invalid_base64_data} end - defp fetch_json_from_uri({:ok, ["#{@ipfs_protocol}ipfs/" <> right]}, _ipfs?, _token_id, hex_token_id, _from_base_uri?) do - fetch_from_ipfs(right, hex_token_id) + defp fetch_json_from_uri({:ok, [token_uri_string]}, ipfs?, token_id, hex_token_id, from_base_uri?) do + fetch_from_ipfs?(token_uri_string, ipfs?, token_id, hex_token_id, from_base_uri?) end - defp fetch_json_from_uri({:ok, ["ipfs/" <> right]}, _ipfs?, _token_id, hex_token_id, _from_base_uri?) do - fetch_from_ipfs(right, hex_token_id) - end + defp fetch_json_from_uri(uri, _ipfs?, _token_id, _hex_token_id, _from_base_uri?) do + Logger.warning(["Unknown metadata uri format #{inspect(uri)}."], fetcher: :token_instances) - defp fetch_json_from_uri({:ok, [@ipfs_protocol <> right]}, _ipfs?, _token_id, hex_token_id, _from_base_uri?) do - fetch_from_ipfs(right, hex_token_id) + {:error, "unknown metadata uri format"} end - defp fetch_json_from_uri({:ok, [json]}, _ipfs?, _token_id, hex_token_id, _from_base_uri?) do - json = ExplorerHelper.decode_json(json, true) + # credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity + defp fetch_from_ipfs?(token_uri_string, ipfs?, token_id, hex_token_id, from_base_uri?) do + case URI.parse(token_uri_string) do + %URI{scheme: "ipfs", path: path} -> + resource_id = + case path do + "/ipfs/" <> resource_id -> + resource_id + + "/" <> resource_id -> + resource_id + end + + fetch_from_ipfs(resource_id, hex_token_id) + + %URI{scheme: _, path: "/ipfs/" <> resource_id} -> + fetch_from_ipfs(resource_id, hex_token_id) + + %URI{scheme: _, path: "ipfs/" <> resource_id} -> + fetch_from_ipfs(resource_id, hex_token_id) + + %URI{scheme: scheme} when not is_nil(scheme) -> + fetch_metadata_inner(token_uri_string, ipfs?, token_id, hex_token_id, from_base_uri?) + + %URI{path: path} -> + case path do + "Qm" <> <<_::binary-size(44)>> = resource_id -> + fetch_from_ipfs(resource_id, hex_token_id) - check_type(json, hex_token_id) + # todo: rewrite for strict CID v1 support + "bafybe" <> _ = resource_id -> + fetch_from_ipfs(resource_id, hex_token_id) + + _ -> + json = ExplorerHelper.decode_json(token_uri_string, true) + + check_type(json, hex_token_id) + end + end rescue e -> - Logger.warning(["Unknown metadata format #{inspect(json)}.", Exception.format(:error, e, __STACKTRACE__)], + Logger.warning( + ["Unknown metadata format #{inspect(token_uri_string)}.", Exception.format(:error, e, __STACKTRACE__)], fetcher: :token_instances ) - {:error, "invalid json"} - end - - defp fetch_json_from_uri(uri, _ipfs?, _token_id, _hex_token_id, _from_base_uri?) do - Logger.warning(["Unknown metadata uri format #{inspect(uri)}."], fetcher: :token_instances) - - {:error, "unknown metadata uri format"} + {:error, "invalid token_uri_string"} end defp fetch_json_from_json_string(json, ipfs?, token_id, hex_token_id, from_base_uri?, type) do diff --git a/apps/explorer/test/explorer/token/metadata_retriever_test.exs b/apps/explorer/test/explorer/token/metadata_retriever_test.exs index 97d59bd1dd26..1b73ff222534 100644 --- a/apps/explorer/test/explorer/token/metadata_retriever_test.exs +++ b/apps/explorer/test/explorer/token/metadata_retriever_test.exs @@ -885,7 +885,7 @@ defmodule Explorer.Token.MetadataRetrieverTest do }} end - test "fetches image from ipfs link directly", %{bypass: bypass} do + test "fetches image from ipfs link directly" do path = "/ipfs/bafybeig6nlmyzui7llhauc52j2xo5hoy4lzp6442lkve5wysdvjkizxonu" json = """ @@ -894,14 +894,19 @@ defmodule Explorer.Token.MetadataRetrieverTest do } """ - Bypass.expect(bypass, "GET", path, fn conn -> - Conn.resp(conn, 200, json) + Application.put_env(:explorer, :http_adapter, Explorer.Mox.HTTPoison) + + Explorer.Mox.HTTPoison + |> expect(:get, fn "https://ipfs.io/ipfs/bafybeig6nlmyzui7llhauc52j2xo5hoy4lzp6442lkve5wysdvjkizxonu", + _headers, + _options -> + {:ok, %HTTPoison.Response{status_code: 200, body: json}} end) data = {:ok, [ - "http://localhost:#{bypass.port}#{path}" + path ]} assert {:ok, @@ -912,7 +917,7 @@ defmodule Explorer.Token.MetadataRetrieverTest do }} == MetadataRetriever.fetch_json(data) end - test "Fetches metadata from ipfs", %{bypass: bypass} do + test "Fetches metadata from ipfs" do path = "/ipfs/bafybeid4ed2ua7fwupv4nx2ziczr3edhygl7ws3yx6y2juon7xakgj6cfm/51.json" json = """ @@ -921,14 +926,19 @@ defmodule Explorer.Token.MetadataRetrieverTest do } """ - Bypass.expect(bypass, "GET", path, fn conn -> - Conn.resp(conn, 200, json) + Application.put_env(:explorer, :http_adapter, Explorer.Mox.HTTPoison) + + Explorer.Mox.HTTPoison + |> expect(:get, fn "https://ipfs.io/ipfs/bafybeid4ed2ua7fwupv4nx2ziczr3edhygl7ws3yx6y2juon7xakgj6cfm/51.json", + _headers, + _options -> + {:ok, %HTTPoison.Response{status_code: 200, body: json}} end) data = {:ok, [ - "http://localhost:#{bypass.port}#{path}" + path ]} {:ok, @@ -937,6 +947,7 @@ defmodule Explorer.Token.MetadataRetrieverTest do }} = MetadataRetriever.fetch_json(data) assert "ipfs://bafybeihxuj3gxk7x5p36amzootyukbugmx3pw7dyntsrohg3se64efkuga/51.png" == Map.get(metadata, "image") + Application.put_env(:explorer, :http_adapter, HTTPoison) end test "Fetches metadata from '${url}'", %{bypass: bypass} do diff --git a/cspell.json b/cspell.json index 7eda2f9f160c..6d3d91b6b3e6 100644 --- a/cspell.json +++ b/cspell.json @@ -41,6 +41,7 @@ "badhash", "badnumber", "badpassword", + "bafybe", "bafybeid", "bafybeig", "bafybeihxuj", From e7f14c3dcd528f180896aa9989efd4b2c431a642 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Thu, 22 Aug 2024 13:03:56 +0400 Subject: [PATCH 093/363] fix: Fix mode dependent processes starting (#10641) --- apps/explorer/lib/explorer/application.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index 2e64c600855a..8159ef8179ff 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -210,7 +210,7 @@ defmodule Explorer.Application do end defp configure_mode_dependent_process(process, mode) do - if Application.get_env(:explorer, :mode) in [mode, :all] do + if should_start?(process) and Application.get_env(:explorer, :mode) in [mode, :all] do process else [] From 8a65bb047090aabab14f5f8351554d169108c982 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 22 Aug 2024 12:13:25 +0300 Subject: [PATCH 094/363] fix: Fix bug in update_replaced_transactions query (#10634) * fix: Fix bug in update_replaced_transactions query * Fix condition for error --- apps/explorer/lib/explorer/chain.ex | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 899bc290a393..473c2e0e76a8 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2738,19 +2738,23 @@ defmodule Explorer.Chain do |> select_repo(options).all() end + @doc """ + Query to return all pending transactions + """ + @spec pending_transactions_query(Ecto.Queryable.t()) :: Ecto.Queryable.t() def pending_transactions_query(query) do from(transaction in query, where: is_nil(transaction.block_hash) and (is_nil(transaction.error) or transaction.error != "dropped/replaced") ) end + @doc """ + Returns pending transactions list from the DB + """ + @spec pending_transactions_list() :: Ecto.Schema.t() | term() def pending_transactions_list do - query = - from(transaction in Transaction, - where: is_nil(transaction.block_hash) and (is_nil(transaction.error) or transaction.error != "dropped/replaced") - ) - - query + Transaction + |> pending_transactions_query() |> Repo.all(timeout: :infinity) end @@ -3877,13 +3881,17 @@ defmodule Explorer.Chain do |> Enum.uniq() if Enum.empty?(filters) do - {:ok, []} + {0, []} else query = filters |> Enum.reduce(Transaction, fn {nonce, from_address}, query -> from(t in query, - or_where: t.nonce == ^nonce and t.from_address_hash == ^from_address and is_nil(t.block_hash) + or_where: + t.nonce == ^nonce and + t.from_address_hash == ^from_address and + is_nil(t.block_hash) and + (is_nil(t.error) or t.error != "dropped/replaced") ) end) # Enforce Transaction ShareLocks order (see docs: sharelocks.md) From 9498d11ec061b41e2bfdc8b698ba28ca3de103c0 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Thu, 22 Aug 2024 12:17:11 +0300 Subject: [PATCH 095/363] Update CHANGELOG --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae73675d9090..ede8167511f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,13 @@ ### 🐛 Bug Fixes +- Fix bug in update_replaced_transactions query ([#10634](https://github.com/blockscout/blockscout/issues/10634)) +- Fix mode dependent processes starting ([#10641](https://github.com/blockscout/blockscout/issues/10641)) +- Better detection IPFS links in NFT metadata fetcher ([#10638](https://github.com/blockscout/blockscout/issues/10638)) +- Change mode env name ([#10636](https://github.com/blockscout/blockscout/issues/10636)) +- Proper default value of gas used for dropped Arbitrum transactions ([#10619](https://github.com/blockscout/blockscout/issues/10619)) +- Fix fetch_first_trace tests ([#10618](https://github.com/blockscout/blockscout/issues/10618)) +- Add SHRINK_INTERNAL_TRANSACTIONS_ENABLED arg to Dockerfile ([#10616](https://github.com/blockscout/blockscout/issues/10616)) - Fix raw-trace test ([#10606](https://github.com/blockscout/blockscout/issues/10606)) - Fix internal transaction validation ([#10443](https://github.com/blockscout/blockscout/issues/10443)) - Fix internal transactions runner test for zetachain ([#10576](https://github.com/blockscout/blockscout/issues/10576)) @@ -86,6 +93,7 @@ ### ⚙️ Miscellaneous Tasks +- Run shrink internal transactions migration for indexer instance only ([#10631](https://github.com/blockscout/blockscout/issues/10631)) - Shrink internal transactions ([#10567](https://github.com/blockscout/blockscout/issues/10567)) - Upgrade WS client ([#10407](https://github.com/blockscout/blockscout/issues/10407)) - Add API endpoint for OP batch blocks ([#10566](https://github.com/blockscout/blockscout/issues/10566)) From 89ca542f01f5e6cb54895f9ac2539ee97940b2a9 Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Thu, 22 Aug 2024 09:19:33 -0400 Subject: [PATCH 096/363] Make Dockerfile use specified user with uid/gid (#10070) * Make Dockerfile use specified user with uid/gid * Disable password access for blockscout user Co-authored-by: Kirill Fedoseev * Use chown during final copy --------- Co-authored-by: Kirill Fedoseev --- docker/Dockerfile | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 6e49e61b0c28..e0bd205c91d0 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -82,13 +82,21 @@ ARG SHRINK_INTERNAL_TRANSACTIONS_ENABLED ENV SHRINK_INTERNAL_TRANSACTIONS_ENABLED=${SHRINK_INTERNAL_TRANSACTIONS_ENABLED} ARG BLOCKSCOUT_VERSION ENV BLOCKSCOUT_VERSION=${BLOCKSCOUT_VERSION} +ARG BLOCKSCOUT_USER=blockscout +ARG BLOCKSCOUT_GROUP=blockscout +ARG BLOCKSCOUT_UID=10001 +ARG BLOCKSCOUT_GID=10001 -RUN apk --no-cache --update add jq curl +RUN apk --no-cache --update add jq curl && \ + addgroup --system --gid ${BLOCKSCOUT_GID} ${BLOCKSCOUT_GROUP} && \ + adduser --system --uid ${BLOCKSCOUT_UID} --ingroup ${BLOCKSCOUT_GROUP} --disabled-password ${BLOCKSCOUT_USER} WORKDIR /app -COPY --from=builder /opt/release/blockscout . -COPY --from=builder /app/apps/explorer/node_modules ./node_modules -COPY --from=builder /app/config/config_helper.exs ./config/config_helper.exs -COPY --from=builder /app/config/config_helper.exs /app/releases/${RELEASE_VERSION}/config_helper.exs -COPY --from=builder /app/config/assets/precompiles-arbitrum.json ./config/assets/precompiles-arbitrum.json +COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /opt/release/blockscout . +COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/apps/explorer/node_modules ./node_modules +COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/config/config_helper.exs ./config/config_helper.exs +COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/config/config_helper.exs /app/releases/${RELEASE_VERSION}/config_helper.exs +COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/config/assets/precompiles-arbitrum.json ./config/assets/precompiles-arbitrum.json + +USER ${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} From e02978ca4ad36f726fdcfa45bead1566d675fb86 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 22 Aug 2024 17:51:00 +0300 Subject: [PATCH 097/363] fix: Change default shrink internal_transactions table migration params (#10644) * Update CHANGELOG * fix: Change default parameters for Shrink internal_transactions table migration * Update CHANGELOG --- CHANGELOG.md | 4 +++- config/runtime.exs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ede8167511f6..df8d9130aaeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ ### 🐛 Bug Fixes +- Change default shrink internal_transactions table migration params ([#10644](https://github.com/blockscout/blockscout/pull/10644)) - Fix bug in update_replaced_transactions query ([#10634](https://github.com/blockscout/blockscout/issues/10634)) - Fix mode dependent processes starting ([#10641](https://github.com/blockscout/blockscout/issues/10641)) - Better detection IPFS links in NFT metadata fetcher ([#10638](https://github.com/blockscout/blockscout/issues/10638)) @@ -93,6 +94,7 @@ ### ⚙️ Miscellaneous Tasks +- Make Dockerfile use specified user with uid/gid ([#10070](https://github.com/blockscout/blockscout/pull/10070)) - Run shrink internal transactions migration for indexer instance only ([#10631](https://github.com/blockscout/blockscout/issues/10631)) - Shrink internal transactions ([#10567](https://github.com/blockscout/blockscout/issues/10567)) - Upgrade WS client ([#10407](https://github.com/blockscout/blockscout/issues/10407)) @@ -123,7 +125,7 @@ | `PUBLIC_METRICS_ENABLED` | Variable to enable running queries at /public-metrics endpoint. Implemented in [#10469](https://github.com/blockscout/blockscout/pull/10469). |

Version: v6.8.0+
Default: false
Applications: API

| | `PUBLIC_METRICS_UPDATE_PERIOD_HOURS` | Public metrics update period in hours at /public-metrics endpoint. Implemented in [#10469](https://github.com/blockscout/blockscout/pull/10469). |

Version: v6.8.0+
Default: 24
Applications: API

| | `SHRINK_INTERNAL_TRANSACTIONS_ENABLED` | Variable to enable internal transactions shrinking logic. Implemented in [#10567](https://github.com/blockscout/blockscout/pull/10567). |

Version: v6.8.0+
Default: false
Applications: API, Indexer

| -| `SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE` | Batch size of the shrink internal transactions migration. Implemented in [#10567](https://github.com/blockscout/blockscout/pull/10567). |

Version: v6.8.0+
Default: 1000
Applications: API, Indexer

| +| `SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE` | Batch size of the shrink internal transactions migration. Implemented in [#10567](https://github.com/blockscout/blockscout/pull/10567). |

Version: v6.8.0+
Default: 10000
Applications: API, Indexer

| | `SHRINK_INTERNAL_TRANSACTIONS_CONCURRENCY` | Concurrency of the shrink internal transactions migration. Implemented in [#10567](https://github.com/blockscout/blockscout/pull/10567). |

Version: v6.8.0+
Default: 1
Applications: API, Indexer

| | `IPFS_PUBLIC_GATEWAY_URL` | IPFS public gateway url which is used by frontend to display IPFS resources such as token instance image. |

Version: v6.8.0+
Default: https://ipfs.io/ipfs
Applications: API

| | `INDEXER_TOKEN_INSTANCE_RETRY_MAX_REFETCH_INTERVAL` | Maximum interval between attempts to fetch token instance metadata. [Time format](backend-env-variables.md#time-format). Implemented in [#10027](https://github.com/blockscout/blockscout/pull/10027). |

Version: v6.8.0+
Default: 168h
Applications: Indexer

| diff --git a/config/runtime.exs b/config/runtime.exs index 7c59765fdd23..7077e777185b 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -617,8 +617,8 @@ config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, config :explorer, Explorer.Migrator.ShrinkInternalTransactions, enabled: ConfigHelper.parse_bool_env_var("SHRINK_INTERNAL_TRANSACTIONS_ENABLED"), - batch_size: ConfigHelper.parse_integer_env_var("SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE", 1000), - concurrency: ConfigHelper.parse_integer_env_var("SHRINK_INTERNAL_TRANSACTIONS_CONCURRENCY", 1) + batch_size: ConfigHelper.parse_integer_env_var("SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE", 10000), + concurrency: ConfigHelper.parse_integer_env_var("SHRINK_INTERNAL_TRANSACTIONS_CONCURRENCY", 10) config :explorer, Explorer.Chain.BridgedToken, eth_omni_bridge_mediator: System.get_env("BRIDGED_TOKENS_ETH_OMNI_BRIDGE_MEDIATOR"), From d731fe6f61fe051cbe1c46e4b54735cdcf582b5a Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Thu, 22 Aug 2024 19:12:10 +0300 Subject: [PATCH 098/363] Extend workflow publish-docker-image-for-eth --- .../publish-docker-image-for-eth.yml | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-docker-image-for-eth.yml b/.github/workflows/publish-docker-image-for-eth.yml index 8b9989e4fb6a..af169c5e1efa 100644 --- a/.github/workflows/publish-docker-image-for-eth.yml +++ b/.github/workflows/publish-docker-image-for-eth.yml @@ -24,7 +24,7 @@ jobs: docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - - name: Build and push Docker image + - name: Build and push Docker image (indexer + API) uses: docker/build-push-action@v5 with: context: . @@ -45,4 +45,48 @@ jobs: CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=ethereum + + - name: Build and push Docker image (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=ethereum + + - name: Build and push Docker image (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=ethereum \ No newline at end of file From 4d94d09ed8741cf6667e9a755f55c7168e70ca12 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Thu, 22 Aug 2024 19:49:15 +0300 Subject: [PATCH 099/363] Fix ETH workflow --- .github/workflows/publish-docker-image-for-eth.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-docker-image-for-eth.yml b/.github/workflows/publish-docker-image-for-eth.yml index af169c5e1efa..081e187ebf2c 100644 --- a/.github/workflows/publish-docker-image-for-eth.yml +++ b/.github/workflows/publish-docker-image-for-eth.yml @@ -53,7 +53,7 @@ jobs: context: . file: ./docker/Dockerfile push: true - tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-indexer + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-experimental-indexer labels: ${{ steps.setup.outputs.docker-labels }} platforms: | linux/amd64 @@ -75,7 +75,7 @@ jobs: context: . file: ./docker/Dockerfile push: true - tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-api + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-experimental-api labels: ${{ steps.setup.outputs.docker-labels }} platforms: | linux/amd64 From 6bf68baee8c7e4ac5eb51c7fc8e10bc96d14d7d5 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 23 Aug 2024 13:58:00 +0300 Subject: [PATCH 100/363] fix: Fix for metadata detection at ipfs protocol (#10646) --- CHANGELOG.md | 1 + .../lib/explorer/token/metadata_retriever.ex | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df8d9130aaeb..49016a68c37a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ ### 🐛 Bug Fixes +- Fix for metadata detection at ipfs protocol([#10646](https://github.com/blockscout/blockscout/pull/10646)) - Change default shrink internal_transactions table migration params ([#10644](https://github.com/blockscout/blockscout/pull/10644)) - Fix bug in update_replaced_transactions query ([#10634](https://github.com/blockscout/blockscout/issues/10634)) - Fix mode dependent processes starting ([#10641](https://github.com/blockscout/blockscout/issues/10641)) diff --git a/apps/explorer/lib/explorer/token/metadata_retriever.ex b/apps/explorer/lib/explorer/token/metadata_retriever.ex index 228fb91a0bec..30d0c9968343 100644 --- a/apps/explorer/lib/explorer/token/metadata_retriever.ex +++ b/apps/explorer/lib/explorer/token/metadata_retriever.ex @@ -579,14 +579,14 @@ defmodule Explorer.Token.MetadataRetriever do # credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity defp fetch_from_ipfs?(token_uri_string, ipfs?, token_id, hex_token_id, from_base_uri?) do case URI.parse(token_uri_string) do - %URI{scheme: "ipfs", path: path} -> + %URI{scheme: "ipfs", host: host, path: path} -> resource_id = - case path do - "/ipfs/" <> resource_id -> - resource_id - - "/" <> resource_id -> - resource_id + if host == "ipfs" do + "/" <> resource_id = path + resource_id + else + # credo:disable-for-next-line + if is_nil(path), do: host, else: host <> path end fetch_from_ipfs(resource_id, hex_token_id) From 1093584787cdf70defe95ebfa55fb7171dbf05e8 Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Mon, 26 Aug 2024 11:55:57 +0300 Subject: [PATCH 101/363] fix: `nil` in `OrderedCache` (#10647) * fix: `nil` in `OrderedCache` * Update CHANGELOG --------- Co-authored-by: Viktor Baranov --- CHANGELOG.md | 1 + apps/explorer/lib/explorer/chain.ex | 6 +++--- apps/explorer/lib/explorer/chain/address.ex | 2 +- .../lib/explorer/chain/ordered_cache.ex | 21 +++++++++++++++++++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49016a68c37a..ae615279d45e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ ### 🐛 Bug Fixes +- nil in OrderedCache ([#10647](https://github.com/blockscout/blockscout/pull/10647)) - Fix for metadata detection at ipfs protocol([#10646](https://github.com/blockscout/blockscout/pull/10646)) - Change default shrink internal_transactions table migration params ([#10644](https://github.com/blockscout/blockscout/pull/10644)) - Fix bug in update_replaced_transactions query ([#10634](https://github.com/blockscout/blockscout/issues/10634)) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 473c2e0e76a8..c5f6d2d93ec8 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -1638,7 +1638,7 @@ defmodule Explorer.Chain do end defp block_from_cache(block_type, paging_options, necessity_by_association, options) do - case Blocks.take_enough(paging_options.page_size) do + case Blocks.atomic_take_enough(paging_options.page_size) do nil -> elements = fetch_blocks(block_type, paging_options, necessity_by_association, options) @@ -1652,7 +1652,7 @@ defmodule Explorer.Chain do end def uncles_from_cache(block_type, paging_options, necessity_by_association, options) do - case Uncles.take_enough(paging_options.page_size) do + case Uncles.atomic_take_enough(paging_options.page_size) do nil -> elements = fetch_blocks(block_type, paging_options, necessity_by_association, options) @@ -2620,7 +2620,7 @@ defmodule Explorer.Chain do if is_nil(paging_options.key) or paging_options.page_number == 1 do paging_options.page_size |> Kernel.+(1) - |> Transactions.take_enough() + |> Transactions.atomic_take_enough() |> case do nil -> transactions = fetch_recent_collated_transactions_for_rap(paging_options, necessity_by_association) diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index ad8f66980d0a..e93197069860 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -328,7 +328,7 @@ defmodule Explorer.Chain.Address do if is_nil(paging_options.key) do paging_options.page_size - |> Accounts.take_enough() + |> Accounts.atomic_take_enough() |> case do nil -> get_addresses(options) diff --git a/apps/explorer/lib/explorer/chain/ordered_cache.ex b/apps/explorer/lib/explorer/chain/ordered_cache.ex index 9d2e797cddb6..b27a79490bd8 100644 --- a/apps/explorer/lib/explorer/chain/ordered_cache.ex +++ b/apps/explorer/lib/explorer/chain/ordered_cache.ex @@ -112,6 +112,11 @@ defmodule Explorer.Chain.OrderedCache do """ @callback take_enough(integer()) :: [element] | nil + @doc """ + Behaves like `take_enough/1`, but addresses [#10445](https://github.com/blockscout/blockscout/issues/10445). + """ + @callback atomic_take_enough(integer()) :: [element] | nil + @doc """ Adds an element, or a list of elements, to the cache. When the cache is full, only the most prevailing elements will be stored, based @@ -204,6 +209,22 @@ defmodule Explorer.Chain.OrderedCache do end end + @impl OrderedCache + def atomic_take_enough(amount) do + items = + cache_name() + |> ConCache.ets() + |> :ets.tab2list() + + if amount <= Enum.count(items) - 1 do + items + |> Enum.reject(fn {key, _value} -> key == ids_list_key() end) + |> Enum.sort(&prevails?/2) + |> Enum.take(amount) + |> Enum.map(fn {_key, value} -> value end) + end + end + ### Updating function def remove_deleted_from_index({:delete, _cache_pid, id}) do From 7eac1ed96781f39bee09a5f23e5aa5ab4a243c86 Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Mon, 26 Aug 2024 11:55:57 +0300 Subject: [PATCH 102/363] fix: `nil` in `OrderedCache` (#10647) * fix: `nil` in `OrderedCache` * Update CHANGELOG --------- Co-authored-by: Viktor Baranov --- CHANGELOG.md | 1 + apps/explorer/lib/explorer/chain.ex | 6 +++--- apps/explorer/lib/explorer/chain/address.ex | 2 +- .../lib/explorer/chain/ordered_cache.ex | 21 +++++++++++++++++++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49016a68c37a..ae615279d45e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ ### 🐛 Bug Fixes +- nil in OrderedCache ([#10647](https://github.com/blockscout/blockscout/pull/10647)) - Fix for metadata detection at ipfs protocol([#10646](https://github.com/blockscout/blockscout/pull/10646)) - Change default shrink internal_transactions table migration params ([#10644](https://github.com/blockscout/blockscout/pull/10644)) - Fix bug in update_replaced_transactions query ([#10634](https://github.com/blockscout/blockscout/issues/10634)) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 473c2e0e76a8..c5f6d2d93ec8 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -1638,7 +1638,7 @@ defmodule Explorer.Chain do end defp block_from_cache(block_type, paging_options, necessity_by_association, options) do - case Blocks.take_enough(paging_options.page_size) do + case Blocks.atomic_take_enough(paging_options.page_size) do nil -> elements = fetch_blocks(block_type, paging_options, necessity_by_association, options) @@ -1652,7 +1652,7 @@ defmodule Explorer.Chain do end def uncles_from_cache(block_type, paging_options, necessity_by_association, options) do - case Uncles.take_enough(paging_options.page_size) do + case Uncles.atomic_take_enough(paging_options.page_size) do nil -> elements = fetch_blocks(block_type, paging_options, necessity_by_association, options) @@ -2620,7 +2620,7 @@ defmodule Explorer.Chain do if is_nil(paging_options.key) or paging_options.page_number == 1 do paging_options.page_size |> Kernel.+(1) - |> Transactions.take_enough() + |> Transactions.atomic_take_enough() |> case do nil -> transactions = fetch_recent_collated_transactions_for_rap(paging_options, necessity_by_association) diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index ad8f66980d0a..e93197069860 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -328,7 +328,7 @@ defmodule Explorer.Chain.Address do if is_nil(paging_options.key) do paging_options.page_size - |> Accounts.take_enough() + |> Accounts.atomic_take_enough() |> case do nil -> get_addresses(options) diff --git a/apps/explorer/lib/explorer/chain/ordered_cache.ex b/apps/explorer/lib/explorer/chain/ordered_cache.ex index 9d2e797cddb6..b27a79490bd8 100644 --- a/apps/explorer/lib/explorer/chain/ordered_cache.ex +++ b/apps/explorer/lib/explorer/chain/ordered_cache.ex @@ -112,6 +112,11 @@ defmodule Explorer.Chain.OrderedCache do """ @callback take_enough(integer()) :: [element] | nil + @doc """ + Behaves like `take_enough/1`, but addresses [#10445](https://github.com/blockscout/blockscout/issues/10445). + """ + @callback atomic_take_enough(integer()) :: [element] | nil + @doc """ Adds an element, or a list of elements, to the cache. When the cache is full, only the most prevailing elements will be stored, based @@ -204,6 +209,22 @@ defmodule Explorer.Chain.OrderedCache do end end + @impl OrderedCache + def atomic_take_enough(amount) do + items = + cache_name() + |> ConCache.ets() + |> :ets.tab2list() + + if amount <= Enum.count(items) - 1 do + items + |> Enum.reject(fn {key, _value} -> key == ids_list_key() end) + |> Enum.sort(&prevails?/2) + |> Enum.take(amount) + |> Enum.map(fn {_key, value} -> value end) + end + end + ### Updating function def remove_deleted_from_index({:delete, _cache_pid, id}) do From da3fd0457a176792b882c73dde42b6459f8335c5 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 26 Aug 2024 13:02:56 +0400 Subject: [PATCH 103/363] Add pre-release workflow for Redstone --- .github/workflows/pre-release-redstone.yml | 100 +++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 .github/workflows/pre-release-redstone.yml diff --git a/.github/workflows/pre-release-redstone.yml b/.github/workflows/pre-release-redstone.yml new file mode 100644 index 000000000000..180ab79ce3b7 --- /dev/null +++ b/.github/workflows/pre-release-redstone.yml @@ -0,0 +1,100 @@ +name: Pre-release for Redstone + +on: + workflow_dispatch: + inputs: + number: + type: number + required: true + +env: + OTP_VERSION: ${{ vars.OTP_VERSION }} + ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + steps: + - uses: actions/checkout@v4 + - name: Setup repo + uses: ./.github/actions/setup-repo + id: setup + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + docker-remote-multi-platform: true + docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} + docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} + + - name: Build and push Docker image for Redstone (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-redstone:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=optimism + MUD_INDEXER_ENABLED=true + + - name: Build and push Docker image for Redstone (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-redstone:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=optimism + MUD_INDEXER_ENABLED=true + + - name: Build and push Docker image for Redstone (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-redstone:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=optimism + MUD_INDEXER_ENABLED=true \ No newline at end of file From 3d4f9cb2a6b98f0850f148e8c57ce1e8c49bf06e Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Mon, 26 Aug 2024 20:01:15 +0400 Subject: [PATCH 104/363] fix: logs list serialization (#10565) * fix: logs factory usage * fix: type conversions * chore: mix format * fix: log factory --- .../api/rpc/eth_controller_test.exs | 81 ++++++++++++++-- .../api/rpc/logs_controller_test.exs | 28 +++++- apps/explorer/lib/explorer/etherscan/logs.ex | 1 + apps/explorer/test/explorer/chain_test.exs | 11 +-- .../test/explorer/etherscan/logs_test.exs | 97 +++++++++++++++++-- 5 files changed, 190 insertions(+), 28 deletions(-) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs index 48646b342e37..1e25c003ffa7 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs @@ -256,6 +256,7 @@ defmodule BlockScoutWeb.API.RPC.EthControllerTest do insert(:log, block: block, + block_number: block.number, address: address, transaction: transaction, data: "0x020202", @@ -333,13 +334,37 @@ defmodule BlockScoutWeb.API.RPC.EthControllerTest do transaction3 = insert(:transaction, from_address: address) |> with_block(block3) transaction4 = insert(:transaction, from_address: address) |> with_block(block4) - insert(:log, address: address, transaction: transaction1, data: "0x010101", block_number: block1.number) + insert(:log, + address: address, + transaction: transaction1, + data: "0x010101", + block: block1, + block_number: block1.number + ) - insert(:log, address: address, transaction: transaction2, data: "0x020202", block_number: block2.number) + insert(:log, + address: address, + transaction: transaction2, + data: "0x020202", + block: block2, + block_number: block2.number + ) - insert(:log, address: address, transaction: transaction3, data: "0x030303", block_number: block3.number) + insert(:log, + address: address, + transaction: transaction3, + data: "0x030303", + block: block3, + block_number: block3.number + ) - insert(:log, address: address, transaction: transaction4, data: "0x040404", block_number: block4.number) + insert(:log, + address: address, + transaction: transaction4, + data: "0x040404", + block: block4, + block_number: block4.number + ) params = params(api_params, [%{"address" => to_string(address.hash), "fromBlock" => 1, "toBlock" => 2}]) @@ -363,11 +388,29 @@ defmodule BlockScoutWeb.API.RPC.EthControllerTest do transaction2 = insert(:transaction, from_address: address) |> with_block(block2) transaction3 = insert(:transaction, from_address: address) |> with_block(block3) - insert(:log, address: address, transaction: transaction1, data: "0x010101", block_number: block1.number) + insert(:log, + address: address, + transaction: transaction1, + data: "0x010101", + block: block1, + block_number: block1.number + ) - insert(:log, address: address, transaction: transaction2, data: "0x020202", block_number: block2.number) + insert(:log, + address: address, + transaction: transaction2, + data: "0x020202", + block: block2, + block_number: block2.number + ) - insert(:log, address: address, transaction: transaction3, data: "0x030303", block_number: block3.number) + insert(:log, + address: address, + transaction: transaction3, + data: "0x030303", + block: block3, + block_number: block3.number + ) params = params(api_params, [%{"address" => to_string(address.hash), "blockHash" => to_string(block2.hash)}]) @@ -391,11 +434,29 @@ defmodule BlockScoutWeb.API.RPC.EthControllerTest do transaction2 = insert(:transaction, from_address: address) |> with_block(block2) transaction3 = insert(:transaction, from_address: address) |> with_block(block3) - insert(:log, address: address, transaction: transaction1, data: "0x010101", block_number: block1.number) + insert(:log, + address: address, + transaction: transaction1, + data: "0x010101", + block: block1, + block_number: block1.number + ) - insert(:log, address: address, transaction: transaction2, data: "0x020202", block_number: block2.number) + insert(:log, + address: address, + transaction: transaction2, + data: "0x020202", + block: block2, + block_number: block2.number + ) - insert(:log, address: address, transaction: transaction3, data: "0x030303", block_number: block3.number) + insert(:log, + address: address, + transaction: transaction3, + data: "0x030303", + block: block3, + block_number: block3.number + ) params = params(api_params, [%{"address" => to_string(address.hash), "fromBlock" => "earliest", "toBlock" => "earliest"}]) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/logs_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/logs_controller_test.exs index 2691f1e7e825..df82a98a6a1c 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/logs_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/logs_controller_test.exs @@ -296,7 +296,13 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do |> insert(to_address: contract_address) |> with_block() - log = insert(:log, address: contract_address, transaction: transaction, block_number: transaction.block_number) + log = + insert(:log, + address: contract_address, + transaction: transaction, + block: block, + block_number: transaction.block_number + ) params = %{ "module" => "logs", @@ -353,12 +359,14 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do insert(:log, address: contract_address, transaction: transaction_block1, + block: transaction_block1.block, block_number: transaction_block1.block_number ) insert(:log, address: contract_address, transaction: transaction_block2, + block: transaction_block2.block, block_number: transaction_block2.block_number ) @@ -406,12 +414,14 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do insert(:log, address: contract_address, transaction: transaction_block1, + block: transaction_block1.block, block_number: transaction_block1.block_number ) insert(:log, address: contract_address, transaction: transaction_block2, + block: transaction_block2.block, block_number: transaction_block2.block_number ) @@ -450,12 +460,16 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do log1_details = [ address: contract_address, transaction: transaction, + block: block, + block_number: block.number, first_topic: topic(@first_topic_hex_string_1) ] log2_details = [ address: contract_address, transaction: transaction, + block: block, + block_number: block.number, first_topic: topic(@first_topic_hex_string_2) ] @@ -508,6 +522,8 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do log1_details = [ address: contract_address, transaction: transaction, + block: block, + block_number: block.number, first_topic: topic(@first_topic_hex_string_1), second_topic: topic(@second_topic_hex_string_1) ] @@ -515,6 +531,8 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do log2_details = [ address: contract_address, transaction: transaction, + block: block, + block_number: block.number, first_topic: topic(@first_topic_hex_string_2), second_topic: topic(@second_topic_hex_string_2) ] @@ -557,6 +575,8 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do log1_details = [ address: contract_address, transaction: transaction, + block: block, + block_number: block.number, first_topic: topic(@first_topic_hex_string_1), second_topic: topic(@second_topic_hex_string_1) ] @@ -564,6 +584,8 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do log2_details = [ address: contract_address, transaction: transaction, + block: block, + block_number: block.number, first_topic: topic(@first_topic_hex_string_2), second_topic: topic(@second_topic_hex_string_2) ] @@ -605,6 +627,8 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do log1_details = [ address: contract_address, transaction: transaction, + block: block, + block_number: block.number, first_topic: topic(@first_topic_hex_string_1), second_topic: topic(@second_topic_hex_string_1), third_topic: topic(@third_topic_hex_string_1), @@ -614,6 +638,8 @@ defmodule BlockScoutWeb.API.RPC.LogsControllerTest do log2_details = [ address: contract_address, transaction: transaction, + block: block, + block_number: block.number, first_topic: topic(@first_topic_hex_string_1), second_topic: topic(@second_topic_hex_string_1), third_topic: topic(@third_topic_hex_string_1), diff --git a/apps/explorer/lib/explorer/etherscan/logs.ex b/apps/explorer/lib/explorer/etherscan/logs.ex index b22dbbd3b885..3cce390ea530 100644 --- a/apps/explorer/lib/explorer/etherscan/logs.ex +++ b/apps/explorer/lib/explorer/etherscan/logs.ex @@ -103,6 +103,7 @@ defmodule Explorer.Etherscan.Logs do ) all_transaction_logs_query + |> Chain.wrapped_union_subquery() |> order_by([log], asc: log.block_number, asc: log.index) |> Repo.replica().all() else diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 7ec3a65922b1..bf8a8bb17d1c 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -280,9 +280,7 @@ defmodule Explorer.ChainTest do |> insert(to_address: address) |> with_block() - _log1 = insert(:log, transaction: transaction, index: 1, address: address, block_number: transaction.block_number) - - 2..51 + 1..51 |> Enum.map(fn index -> insert(:log, block: transaction.block, @@ -292,7 +290,6 @@ defmodule Explorer.ChainTest do block_number: transaction.block_number ) end) - |> Enum.map(& &1.index) paging_options1 = %PagingOptions{page_size: 1} @@ -3039,7 +3036,7 @@ defmodule Explorer.ChainTest do |> insert() |> with_block(block) - insert(:log, address: address, transaction: transaction) + insert(:log, address: address, transaction: transaction, block: block, block_number: block.number) balance = insert(:unfetched_balance, address_hash: address.hash, block_number: block.number) @@ -3229,7 +3226,7 @@ defmodule Explorer.ChainTest do |> insert() |> with_block(log_block) - insert(:log, address: miner, transaction: log_transaction) + insert(:log, address: miner, transaction: log_transaction, block: log_block, block_number: log_block.number) insert(:unfetched_balance, address_hash: miner.hash, block_number: log_block.number) from_internal_transaction_block = insert(:block) @@ -3310,7 +3307,7 @@ defmodule Explorer.ChainTest do |> insert() |> with_block(block) - insert(:log, address: miner, transaction: log_transaction) + insert(:log, address: miner, transaction: log_transaction, block: block, block_number: block.number) from_internal_transaction_transaction = :transaction diff --git a/apps/explorer/test/explorer/etherscan/logs_test.exs b/apps/explorer/test/explorer/etherscan/logs_test.exs index b5953407f83c..02cc8f3e0262 100644 --- a/apps/explorer/test/explorer/etherscan/logs_test.exs +++ b/apps/explorer/test/explorer/etherscan/logs_test.exs @@ -65,7 +65,7 @@ defmodule Explorer.Etherscan.LogsTest do |> insert(to_address: contract_address, block_timestamp: block.timestamp) |> with_block(block) - log = insert(:log, address: contract_address, block_number: block.number, transaction: transaction) + log = insert(:log, address: contract_address, block: block, block_number: block.number, transaction: transaction) filter = %{ from_block: block.number, @@ -99,7 +99,12 @@ defmodule Explorer.Etherscan.LogsTest do |> insert(to_address: contract_address) |> with_block() - insert_list(2, :log, address: contract_address, transaction: transaction, block_number: block.number) + insert_list(2, :log, + address: contract_address, + transaction: transaction, + block_number: block.number, + block: block + ) filter = %{ from_block: block.number, @@ -130,8 +135,19 @@ defmodule Explorer.Etherscan.LogsTest do |> insert(to_address: contract_address) |> with_block(second_block) - insert(:log, address: contract_address, transaction: transaction_block1, block_number: first_block.number) - insert(:log, address: contract_address, transaction: transaction_block2, block_number: second_block.number) + insert(:log, + address: contract_address, + transaction: transaction_block1, + block: first_block, + block_number: first_block.number + ) + + insert(:log, + address: contract_address, + transaction: transaction_block2, + block: second_block, + block_number: second_block.number + ) filter = %{ from_block: second_block.number, @@ -163,8 +179,19 @@ defmodule Explorer.Etherscan.LogsTest do |> insert(to_address: contract_address) |> with_block(second_block) - insert(:log, address: contract_address, transaction: transaction_block1, block_number: first_block.number) - insert(:log, address: contract_address, transaction: transaction_block2, block_number: second_block.number) + insert(:log, + address: contract_address, + transaction: transaction_block1, + block: first_block, + block_number: first_block.number + ) + + insert(:log, + address: contract_address, + transaction: transaction_block2, + block: second_block, + block_number: second_block.number + ) filter = %{ from_block: first_block.number, @@ -188,7 +215,12 @@ defmodule Explorer.Etherscan.LogsTest do |> with_block() inserted_records = - insert_list(2000, :log, address: contract_address, transaction: transaction, block_number: block.number) + insert_list(2000, :log, + address: contract_address, + transaction: transaction, + block_number: block.number, + block: block + ) filter = %{ from_block: block.number, @@ -230,12 +262,16 @@ defmodule Explorer.Etherscan.LogsTest do log1_details = [ address: contract_address, transaction: transaction, + block: block, + block_number: block.number, first_topic: topic(@first_topic_hex_string_1) ] log2_details = [ address: contract_address, transaction: transaction, + block: block, + block_number: block.number, first_topic: topic(@first_topic_hex_string_2) ] @@ -266,6 +302,8 @@ defmodule Explorer.Etherscan.LogsTest do log1_details = [ address: contract_address, transaction: transaction, + block: block, + block_number: block.number, first_topic: topic(@first_topic_hex_string_1), second_topic: topic(@second_topic_hex_string_1) ] @@ -273,6 +311,8 @@ defmodule Explorer.Etherscan.LogsTest do log2_details = [ address: contract_address, transaction: transaction, + block: block, + block_number: block.number, first_topic: topic(@first_topic_hex_string_1), second_topic: topic(@second_topic_hex_string_2) ] @@ -307,6 +347,8 @@ defmodule Explorer.Etherscan.LogsTest do log1_details = [ address: contract_address, transaction: transaction, + block: block, + block_number: block.number, first_topic: topic(@first_topic_hex_string_1), second_topic: topic(@second_topic_hex_string_1) ] @@ -314,6 +356,8 @@ defmodule Explorer.Etherscan.LogsTest do log2_details = [ address: contract_address, transaction: transaction, + block: block, + block_number: block.number, first_topic: topic(@first_topic_hex_string_2), second_topic: topic(@second_topic_hex_string_2) ] @@ -346,6 +390,7 @@ defmodule Explorer.Etherscan.LogsTest do log1_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_1), block_number: block.number ] @@ -353,6 +398,7 @@ defmodule Explorer.Etherscan.LogsTest do log2_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_2), block_number: block.number ] @@ -385,6 +431,7 @@ defmodule Explorer.Etherscan.LogsTest do log1_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_1), second_topic: topic(@second_topic_hex_string_1), block_number: block.number @@ -393,6 +440,7 @@ defmodule Explorer.Etherscan.LogsTest do log2_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_2), second_topic: topic(@second_topic_hex_string_2), block_number: block.number @@ -428,6 +476,7 @@ defmodule Explorer.Etherscan.LogsTest do log1_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_1), second_topic: topic(@second_topic_hex_string_1), third_topic: topic(@third_topic_hex_string_1), @@ -437,6 +486,7 @@ defmodule Explorer.Etherscan.LogsTest do log2_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_2), second_topic: topic(@second_topic_hex_string_2), third_topic: topic(@third_topic_hex_string_2), @@ -446,6 +496,7 @@ defmodule Explorer.Etherscan.LogsTest do log3_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_3), second_topic: topic(@second_topic_hex_string_3), third_topic: topic(@third_topic_hex_string_3), @@ -488,6 +539,7 @@ defmodule Explorer.Etherscan.LogsTest do log1_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_1), second_topic: topic(@second_topic_hex_string_1), third_topic: topic(@third_topic_hex_string_1), @@ -497,6 +549,7 @@ defmodule Explorer.Etherscan.LogsTest do log2_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_2), second_topic: topic(@second_topic_hex_string_2), third_topic: topic(@third_topic_hex_string_2), @@ -506,6 +559,7 @@ defmodule Explorer.Etherscan.LogsTest do log3_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_3), second_topic: topic(@second_topic_hex_string_3), third_topic: topic(@third_topic_hex_string_3), @@ -548,6 +602,7 @@ defmodule Explorer.Etherscan.LogsTest do log1_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_1), second_topic: topic(@second_topic_hex_string_1), third_topic: topic(@third_topic_hex_string_1), @@ -557,6 +612,7 @@ defmodule Explorer.Etherscan.LogsTest do log2_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_1), second_topic: topic(@second_topic_hex_string_2), third_topic: topic(@third_topic_hex_string_1), @@ -566,6 +622,7 @@ defmodule Explorer.Etherscan.LogsTest do log3_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_1), second_topic: topic(@second_topic_hex_string_1), third_topic: topic(@third_topic_hex_string_1), @@ -608,6 +665,7 @@ defmodule Explorer.Etherscan.LogsTest do log1_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_1), second_topic: topic(@second_topic_hex_string_1), block_number: block.number @@ -616,6 +674,7 @@ defmodule Explorer.Etherscan.LogsTest do log2_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_2), second_topic: topic(@second_topic_hex_string_2), third_topic: topic(@third_topic_hex_string_2), @@ -626,6 +685,7 @@ defmodule Explorer.Etherscan.LogsTest do log3_details = [ address: contract_address, transaction: transaction, + block: block, first_topic: topic(@first_topic_hex_string_1), second_topic: topic(@second_topic_hex_string_1), third_topic: topic(@third_topic_hex_string_1), @@ -686,9 +746,26 @@ defmodule Explorer.Etherscan.LogsTest do |> insert(to_address: contract_address) |> with_block(third_block) - insert(:log, address: contract_address, transaction: transaction_block3) - insert(:log, address: contract_address, transaction: transaction_block1) - insert(:log, address: contract_address, transaction: transaction_block2) + insert(:log, + address: contract_address, + transaction: transaction_block3, + block: third_block, + block_number: third_block.number + ) + + insert(:log, + address: contract_address, + transaction: transaction_block1, + block: first_block, + block_number: first_block.number + ) + + insert(:log, + address: contract_address, + transaction: transaction_block2, + block: second_block, + block_number: second_block.number + ) filter = %{ from_block: first_block.number, From ec1493e6a9c87626b401b81d5b577c573cb6743d Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 26 Aug 2024 20:04:54 +0400 Subject: [PATCH 105/363] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae615279d45e..dd653ea9e31d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ ### 🐛 Bug Fixes +- Logs list serialization ([#10565](https://github.com/blockscout/blockscout/issues/10565)) - nil in OrderedCache ([#10647](https://github.com/blockscout/blockscout/pull/10647)) - Fix for metadata detection at ipfs protocol([#10646](https://github.com/blockscout/blockscout/pull/10646)) - Change default shrink internal_transactions table migration params ([#10644](https://github.com/blockscout/blockscout/pull/10644)) From 2c72aee5dbb89a42dee4b888283ca88c5b4bcac5 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Wed, 28 Aug 2024 12:30:02 +0400 Subject: [PATCH 106/363] Change back default params for shrink internal transactions migration --- CHANGELOG.md | 3 +-- config/runtime.exs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd653ea9e31d..3f6d967ab1c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,6 @@ - Logs list serialization ([#10565](https://github.com/blockscout/blockscout/issues/10565)) - nil in OrderedCache ([#10647](https://github.com/blockscout/blockscout/pull/10647)) - Fix for metadata detection at ipfs protocol([#10646](https://github.com/blockscout/blockscout/pull/10646)) -- Change default shrink internal_transactions table migration params ([#10644](https://github.com/blockscout/blockscout/pull/10644)) - Fix bug in update_replaced_transactions query ([#10634](https://github.com/blockscout/blockscout/issues/10634)) - Fix mode dependent processes starting ([#10641](https://github.com/blockscout/blockscout/issues/10641)) - Better detection IPFS links in NFT metadata fetcher ([#10638](https://github.com/blockscout/blockscout/issues/10638)) @@ -128,7 +127,7 @@ | `PUBLIC_METRICS_ENABLED` | Variable to enable running queries at /public-metrics endpoint. Implemented in [#10469](https://github.com/blockscout/blockscout/pull/10469). |

Version: v6.8.0+
Default: false
Applications: API

| | `PUBLIC_METRICS_UPDATE_PERIOD_HOURS` | Public metrics update period in hours at /public-metrics endpoint. Implemented in [#10469](https://github.com/blockscout/blockscout/pull/10469). |

Version: v6.8.0+
Default: 24
Applications: API

| | `SHRINK_INTERNAL_TRANSACTIONS_ENABLED` | Variable to enable internal transactions shrinking logic. Implemented in [#10567](https://github.com/blockscout/blockscout/pull/10567). |

Version: v6.8.0+
Default: false
Applications: API, Indexer

| -| `SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE` | Batch size of the shrink internal transactions migration. Implemented in [#10567](https://github.com/blockscout/blockscout/pull/10567). |

Version: v6.8.0+
Default: 10000
Applications: API, Indexer

| +| `SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE` | Batch size of the shrink internal transactions migration. Implemented in [#10567](https://github.com/blockscout/blockscout/pull/10567). |

Version: v6.8.0+
Default: 1000
Applications: API, Indexer

| | `SHRINK_INTERNAL_TRANSACTIONS_CONCURRENCY` | Concurrency of the shrink internal transactions migration. Implemented in [#10567](https://github.com/blockscout/blockscout/pull/10567). |

Version: v6.8.0+
Default: 1
Applications: API, Indexer

| | `IPFS_PUBLIC_GATEWAY_URL` | IPFS public gateway url which is used by frontend to display IPFS resources such as token instance image. |

Version: v6.8.0+
Default: https://ipfs.io/ipfs
Applications: API

| | `INDEXER_TOKEN_INSTANCE_RETRY_MAX_REFETCH_INTERVAL` | Maximum interval between attempts to fetch token instance metadata. [Time format](backend-env-variables.md#time-format). Implemented in [#10027](https://github.com/blockscout/blockscout/pull/10027). |

Version: v6.8.0+
Default: 168h
Applications: Indexer

| diff --git a/config/runtime.exs b/config/runtime.exs index 7077e777185b..e3c9e798bc25 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -617,7 +617,7 @@ config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, config :explorer, Explorer.Migrator.ShrinkInternalTransactions, enabled: ConfigHelper.parse_bool_env_var("SHRINK_INTERNAL_TRANSACTIONS_ENABLED"), - batch_size: ConfigHelper.parse_integer_env_var("SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE", 10000), + batch_size: ConfigHelper.parse_integer_env_var("SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE", 1000), concurrency: ConfigHelper.parse_integer_env_var("SHRINK_INTERNAL_TRANSACTIONS_CONCURRENCY", 10) config :explorer, Explorer.Chain.BridgedToken, From b4b8ecf4a5e1b4a3f3158c8c0ddb7e3edfeb5c5f Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 28 Aug 2024 15:23:50 +0400 Subject: [PATCH 107/363] feature: Detect Diamond proxy pattern on unverified proxy smart-contract (#10665) --- CHANGELOG.md | 1 + .../api/v2/smart_contract_controller_test.exs | 14 +-- .../explorer/chain/smart_contract/proxy.ex | 94 ++++++++++++++++--- .../chain/smart_contract/proxy/basic.ex | 17 +++- .../chain/smart_contract/proxy/eip_2535.ex | 5 +- .../proxy/models/implementation.ex | 17 +++- apps/explorer/lib/test_helper.ex | 39 ++++++++ 7 files changed, 159 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f6d967ab1c8..036e872976ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### 🚀 Features +- Detect Diamond proxy pattern on unverified proxy smart-contract ([#10665](https://github.com/blockscout/blockscout/pull/10665)) - Support smart-contract verification in zkSync ([#10500](https://github.com/blockscout/blockscout/issues/10500)) - Add icon for secondary coin ([#10241](https://github.com/blockscout/blockscout/issues/10241)) - Integrate Cryptorank API ([#10550](https://github.com/blockscout/blockscout/issues/10550)) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs index 03534093c5fd..082f8db55ddd 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs @@ -954,7 +954,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ - "proxy_type" => nil, + "proxy_type" => "unknown", "implementations" => [], "has_custom_methods_read" => false, "has_custom_methods_write" => false, @@ -1159,8 +1159,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ - "proxy_type" => nil, - "implementations" => [], + "proxy_type" => "eip1967", + "implementations" => [%{"address" => formatted_implementation_address_hash_string, "name" => nil}], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -1284,8 +1284,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ - "proxy_type" => nil, - "implementations" => [], + "proxy_type" => "eip1967", + "implementations" => [%{"address" => formatted_implementation_address_hash_string, "name" => nil}], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -1409,8 +1409,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ - "proxy_type" => nil, - "implementations" => [], + "proxy_type" => "eip1967", + "implementations" => [%{"address" => formatted_implementation_address_hash_string, "name" => nil}], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex index f4ea719eb789..4d9a8bd7cfc3 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex @@ -292,14 +292,64 @@ defmodule Explorer.Chain.SmartContract.Proxy do proxy_type: atom() } def get_implementation_address_hash_string_eip1822(proxy_address_hash, proxy_abi, go_to_fallback?) do - get_implementation_address_hash_string_by_module(EIP1822, :eip1822, [proxy_address_hash, proxy_abi, go_to_fallback?]) + get_implementation_address_hash_string_by_module( + EIP1822, + :eip1822, + [ + proxy_address_hash, + proxy_abi, + go_to_fallback? + ], + :get_implementation_address_hash_string_eip2535 + ) + end + + @doc """ + Returns EIP-2535 implementation address or tries next proxy pattern + """ + @spec get_implementation_address_hash_string_eip2535(Hash.Address.t(), any(), bool()) :: %{ + implementation_address_hash_strings: [String.t() | :error | nil], + proxy_type: atom() + } + def get_implementation_address_hash_string_eip2535(proxy_address_hash, proxy_abi, go_to_fallback?) do + get_implementation_address_hash_string_by_module(EIP2535, :eip2535, [proxy_address_hash, proxy_abi, go_to_fallback?]) end defp get_implementation_address_hash_string_by_module( module, proxy_type, - [proxy_address_hash, proxy_abi, go_to_fallback?] = args, + args, next_func \\ :fallback_proxy_detection + ) + + defp get_implementation_address_hash_string_by_module( + EIP2535 = module, + :eip2535 = proxy_type, + [proxy_address_hash, proxy_abi, go_to_fallback?] = args, + next_func + ) do + implementation_address_hash_strings = module.get_implementation_address_hash_strings(proxy_address_hash) + + if !is_nil(implementation_address_hash_strings) && implementation_address_hash_strings !== [] && + implementation_address_hash_strings !== :error do + %{implementation_address_hash_strings: implementation_address_hash_strings, proxy_type: proxy_type} + else + do_get_implementation_address_hash_string_by_module( + implementation_address_hash_strings, + proxy_address_hash, + proxy_abi, + go_to_fallback?, + next_func, + args + ) + end + end + + defp get_implementation_address_hash_string_by_module( + module, + proxy_type, + [proxy_address_hash, proxy_abi, go_to_fallback?] = args, + next_func ) do implementation_address_hash_string = module.get_implementation_address_hash_string(proxy_address_hash) @@ -307,23 +357,41 @@ defmodule Explorer.Chain.SmartContract.Proxy do implementation_address_hash_string !== :error do %{implementation_address_hash_strings: [implementation_address_hash_string], proxy_type: proxy_type} else - cond do - next_func !== :fallback_proxy_detection -> - apply(__MODULE__, next_func, args) + do_get_implementation_address_hash_string_by_module( + implementation_address_hash_string, + proxy_address_hash, + proxy_abi, + go_to_fallback?, + next_func, + args + ) + end + end - go_to_fallback? && next_func == :fallback_proxy_detection -> - fallback_value = implementation_fallback_value(implementation_address_hash_string) + defp do_get_implementation_address_hash_string_by_module( + implementation_value, + proxy_address_hash, + proxy_abi, + go_to_fallback?, + next_func, + args + ) do + cond do + next_func !== :fallback_proxy_detection -> + apply(__MODULE__, next_func, args) - apply(__MODULE__, :fallback_proxy_detection, [proxy_address_hash, proxy_abi, fallback_value]) + go_to_fallback? && next_func == :fallback_proxy_detection -> + fallback_value = implementation_fallback_value(implementation_value) - true -> - implementation_fallback_value(implementation_address_hash_string) - end + apply(__MODULE__, :fallback_proxy_detection, [proxy_address_hash, proxy_abi, fallback_value]) + + true -> + implementation_fallback_value(implementation_value) end end - defp implementation_fallback_value(implementation_address_hash_string) do - value = if implementation_address_hash_string == :error, do: :error, else: [] + defp implementation_fallback_value(implementation_value) do + value = if implementation_value == :error, do: :error, else: [] %{implementation_address_hash_strings: value, proxy_type: :unknown} end diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy/basic.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy/basic.ex index d0d6a4fb5429..aff7b417fe63 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy/basic.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy/basic.ex @@ -9,7 +9,8 @@ defmodule Explorer.Chain.SmartContract.Proxy.Basic do @doc """ Gets implementation hash string of proxy contract from getter. """ - @spec get_implementation_address_hash_string(binary, binary, SmartContract.abi()) :: nil | binary() | [binary()] + @spec get_implementation_address_hash_string(binary, binary, SmartContract.abi()) :: + nil | :error | binary() | [binary()] def get_implementation_address_hash_string(signature, proxy_address_hash, abi) do implementation_address = case Reader.query_contract( @@ -20,8 +21,14 @@ defmodule Explorer.Chain.SmartContract.Proxy.Basic do }, false ) do - %{^signature => {:ok, [result]}} -> result - _ -> nil + %{^signature => {:ok, [result]}} -> + result + + %{^signature => {:error, _}} -> + :error + + _ -> + nil end adds_0x_to_address(implementation_address) @@ -30,9 +37,11 @@ defmodule Explorer.Chain.SmartContract.Proxy.Basic do @doc """ Adds 0x to address at the beginning """ - @spec adds_0x_to_address(nil | binary()) :: nil | binary() | [binary()] + @spec adds_0x_to_address(nil | :error | binary()) :: nil | :error | binary() | [binary()] def adds_0x_to_address(nil), do: nil + def adds_0x_to_address(:error), do: :error + def adds_0x_to_address(addresses) when is_list(addresses) do addresses |> Enum.map(fn address -> adds_0x_to_address(address) end) diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy/eip_2535.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy/eip_2535.ex index cc0d374e60c5..4e9aaf9313d9 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy/eip_2535.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy/eip_2535.ex @@ -19,7 +19,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.EIP2535 do } ] - @spec get_implementation_address_hash_strings(Hash.Address.t()) :: nil | [binary] + @spec get_implementation_address_hash_strings(Hash.Address.t()) :: nil | :error | [binary] def get_implementation_address_hash_strings(proxy_address_hash) do case @facet_addresses_signature |> Basic.get_implementation_address_hash_string( @@ -29,6 +29,9 @@ defmodule Explorer.Chain.SmartContract.Proxy.EIP2535 do implementation_addresses when is_list(implementation_addresses) -> implementation_addresses + :error -> + :error + _ -> nil end diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex index f6e4d9c50199..4b78a0d00575 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex @@ -94,7 +94,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do @doc """ Returns the last implementation updated_at for the given smart-contract address hash """ - @spec get_proxy_implementation_updated_at(Hash.Address.t() | nil, Keyword.t()) :: DateTime.t() + @spec get_proxy_implementation_updated_at(Hash.Address.t() | nil, Keyword.t()) :: DateTime.t() | nil def get_proxy_implementation_updated_at(proxy_address_hash, options) do proxy_address_hash |> get_proxy_implementations_query() @@ -140,9 +140,20 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do {updated_smart_contract, implementation_address_fetched?} = if check_implementation_refetch_necessity(implementation_updated_at) do - SmartContract.address_hash_to_smart_contract_with_bytecode_twin(address_hash, options) + {smart_contract_with_bytecode_twin, implementation_address_fetched?} = + SmartContract.address_hash_to_smart_contract_with_bytecode_twin(address_hash, options) + + if smart_contract_with_bytecode_twin do + {smart_contract_with_bytecode_twin, implementation_address_fetched?} + else + {smart_contract, implementation_address_fetched?} + end else - {smart_contract, false} + if implementation_updated_at do + {smart_contract, true} + else + {smart_contract, false} + end end get_implementation( diff --git a/apps/explorer/lib/test_helper.ex b/apps/explorer/lib/test_helper.ex index e82a742b83e4..bc674636f802 100644 --- a/apps/explorer/lib/test_helper.ex +++ b/apps/explorer/lib/test_helper.ex @@ -89,6 +89,43 @@ defmodule Explorer.TestHelper do end) end + def mock_eip_2535_storage_pointer_request( + mox, + error?, + resp \\ "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" + ) do + response = + if error?, + do: {:error, "error"}, + else: + {:ok, + [ + %{ + id: 0, + jsonrpc: "2.0", + result: resp + } + ]} + + expect(mox, :json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_call", + params: [ + %{ + data: "0x52ef6b2c", + to: _ + }, + "latest" + ] + } + ], + _options -> + response + end) + end + def get_eip1967_implementation_non_zero_address(address_hash_string) do EthereumJSONRPC.Mox |> mock_logic_storage_pointer_request(false) @@ -102,6 +139,7 @@ defmodule Explorer.TestHelper do |> mock_beacon_storage_pointer_request(false) |> mock_oz_storage_pointer_request(false) |> mock_eip_1822_storage_pointer_request(false) + |> mock_eip_2535_storage_pointer_request(false) end def get_eip1967_implementation_error_response do @@ -110,6 +148,7 @@ defmodule Explorer.TestHelper do |> mock_beacon_storage_pointer_request(true) |> mock_oz_storage_pointer_request(true) |> mock_eip_1822_storage_pointer_request(true) + |> mock_eip_2535_storage_pointer_request(true) end def fetch_token_uri_mock(url, token_contract_address_hash_string) do From 54bc32ddea4c8f6dcfe36106a6f56edc53a10fd4 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Thu, 29 Aug 2024 06:38:43 +0400 Subject: [PATCH 108/363] Fix Optimism release workflow --- .github/workflows/release-optimism.yml | 47 +++++++++++++------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/.github/workflows/release-optimism.yml b/.github/workflows/release-optimism.yml index 9ddfd4da0f01..c27c3bb47957 100644 --- a/.github/workflows/release-optimism.yml +++ b/.github/workflows/release-optimism.yml @@ -1,6 +1,7 @@ name: Release for Optimism on: + workflow_dispatch: release: types: [published] @@ -93,29 +94,29 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=optimism - - name: Build and push Docker image for Optimism (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 - with: - context: . - file: ./docker/Dockerfile - push: true - tags: blockscout/blockscout-optimism:${{ env.RELEASE_VERSION }}-shrink-internal-txs - labels: ${{ steps.setup.outputs.docker-labels }} - platforms: | - linux/amd64 - linux/arm64/v8 - build-args: | - DISABLE_WEBAPP=false - API_V1_READ_METHODS_DISABLED=false - API_V1_WRITE_METHODS_DISABLED=false - CACHE_EXCHANGE_RATES_PERIOD= - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= - ADMIN_PANEL_ENABLED=false - BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta - RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=optimism - SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + - name: Build and push Docker image for Optimism (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-optimism:${{ env.RELEASE_VERSION }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=optimism + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Optimism (indexer + shrink internal transactions) uses: docker/build-push-action@v5 From cd1f130c93a1f4fa4f359547f08b7e609620b455 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Thu, 29 Aug 2024 08:10:55 +0400 Subject: [PATCH 109/363] Fix main release workflow --- .github/workflows/release-optimism.yml | 1 - .github/workflows/release.yml | 59 +++++++++++++------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.github/workflows/release-optimism.yml b/.github/workflows/release-optimism.yml index c27c3bb47957..44daccfe17ed 100644 --- a/.github/workflows/release-optimism.yml +++ b/.github/workflows/release-optimism.yml @@ -1,7 +1,6 @@ name: Release for Optimism on: - workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 271a47935e6e..cf624162bc1f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,7 @@ name: Release on: + workflow_dispatch: release: types: [published] @@ -113,35 +114,35 @@ jobs: BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta RELEASE_VERSION=${{ env.RELEASE_VERSION }} - - name: Build & Push Core Docker image (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 - with: - context: . - file: ./docker/Dockerfile - push: true - cache-from: type=registry,ref=blockscout/blockscout:buildcache - cache-to: type=registry,ref=blockscout/blockscout:buildcache,mode=max - tags: blockscout/blockscout:${{ env.RELEASE_VERSION }}-shrink-internal-txs - labels: ${{ steps.setup.outputs.docker-labels }} - platforms: | - linux/amd64 - linux/arm64/v8 - build-args: | - DISABLE_WEBAPP=false - API_V1_READ_METHODS_DISABLED=false - API_V1_WRITE_METHODS_DISABLED=false - CACHE_EXCHANGE_RATES_PERIOD= - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= - ADMIN_PANEL_ENABLED=false - DECODE_NOT_A_CONTRACT_CALLS=false - MIXPANEL_URL= - MIXPANEL_TOKEN= - AMPLITUDE_URL= - AMPLITUDE_API_KEY= - BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta - RELEASE_VERSION=${{ env.RELEASE_VERSION }} - SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + - name: Build & Push Core Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + cache-from: type=registry,ref=blockscout/blockscout:buildcache + cache-to: type=registry,ref=blockscout/blockscout:buildcache,mode=max + tags: blockscout/blockscout:${{ env.RELEASE_VERSION }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + DECODE_NOT_A_CONTRACT_CALLS=false + MIXPANEL_URL= + MIXPANEL_TOKEN= + AMPLITUDE_URL= + AMPLITUDE_API_KEY= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build & Push Core Docker image (indexer + shrink internal transactions) uses: docker/build-push-action@v5 From 7c1be4695d7cbdca3b629b8a96f12a56cdd39711 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 2 Sep 2024 11:59:22 +0300 Subject: [PATCH 110/363] Remove workflow_dispatch trigger from release workflows --- .github/workflows/release-fuse.yml | 1 - .github/workflows/release-polygon-edge.yml | 1 - .github/workflows/release-polygon-zkevm.yml | 1 - .github/workflows/release-stability.yml | 1 - .github/workflows/release-suave.yml | 1 - .github/workflows/release-zksync.yml | 1 - .github/workflows/release.yml | 1 - 7 files changed, 7 deletions(-) diff --git a/.github/workflows/release-fuse.yml b/.github/workflows/release-fuse.yml index 1a9e41004e3a..0315d76c6545 100644 --- a/.github/workflows/release-fuse.yml +++ b/.github/workflows/release-fuse.yml @@ -1,7 +1,6 @@ name: Release for Fuse on: - workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-polygon-edge.yml b/.github/workflows/release-polygon-edge.yml index b2a3ee4c3737..b31ff15ac713 100644 --- a/.github/workflows/release-polygon-edge.yml +++ b/.github/workflows/release-polygon-edge.yml @@ -1,7 +1,6 @@ name: Release for Polygon Edge on: - workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-polygon-zkevm.yml b/.github/workflows/release-polygon-zkevm.yml index 101068fa38bb..fd4f6ff21c44 100644 --- a/.github/workflows/release-polygon-zkevm.yml +++ b/.github/workflows/release-polygon-zkevm.yml @@ -1,7 +1,6 @@ name: Release for Polygon zkEVM on: - workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-stability.yml b/.github/workflows/release-stability.yml index 2d6b9284b358..aa82ac6d391f 100644 --- a/.github/workflows/release-stability.yml +++ b/.github/workflows/release-stability.yml @@ -1,7 +1,6 @@ name: Release for Stability on: - workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-suave.yml b/.github/workflows/release-suave.yml index 8ac9dadc3e5b..e71101f495c1 100644 --- a/.github/workflows/release-suave.yml +++ b/.github/workflows/release-suave.yml @@ -1,7 +1,6 @@ name: Release for SUAVE on: - workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-zksync.yml b/.github/workflows/release-zksync.yml index e974e44b9ce8..15439457312c 100644 --- a/.github/workflows/release-zksync.yml +++ b/.github/workflows/release-zksync.yml @@ -1,7 +1,6 @@ name: Release for ZkSync on: - workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cf624162bc1f..427836ce3880 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,7 +1,6 @@ name: Release on: - workflow_dispatch: release: types: [published] From a495d77c46dfc5a4e552fbecbb582fbd7c2f2a99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:31:46 +0300 Subject: [PATCH 111/363] chore(deps-dev): bump @babel/preset-env in /apps/block_scout_web/assets (#10680) Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.24.7 to 7.25.4. - [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.25.4/packages/babel-preset-env) --- updated-dependencies: - dependency-name: "@babel/preset-env" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 1042 ++++++++--------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 504 insertions(+), 540 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 17aa96e35b9a..138427535b1c 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -72,7 +72,7 @@ }, "devDependencies": { "@babel/core": "^7.24.7", - "@babel/preset-env": "^7.24.7", + "@babel/preset-env": "^7.25.4", "autoprefixer": "^10.4.19", "babel-loader": "^9.1.3", "copy-webpack-plugin": "^12.0.2", @@ -224,9 +224,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", + "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", "engines": { "node": ">=6.9.0" } @@ -266,11 +266,11 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", "dependencies": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.25.6", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -304,13 +304,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -332,19 +332,17 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.7.tgz", - "integrity": "sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz", + "integrity": "sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", + "@babel/helper-member-expression-to-functions": "^7.24.8", "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", + "@babel/helper-replace-supers": "^7.25.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/traverse": "^7.25.4", "semver": "^6.3.1" }, "engines": { @@ -355,9 +353,9 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", - "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", + "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", @@ -387,48 +385,14 @@ "@babel/core": "^7.4.0-0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz", - "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", + "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==", "dev": true, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -447,15 +411,14 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", "@babel/helper-module-imports": "^7.24.7", "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" }, "engines": { "node": ">=6.9.0" @@ -477,22 +440,22 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", - "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", + "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-wrap-function": "^7.24.7" + "@babel/helper-wrap-function": "^7.25.0", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -502,14 +465,14 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", - "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", + "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" + "@babel/helper-member-expression-to-functions": "^7.24.8", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -543,21 +506,10 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "engines": { "node": ">=6.9.0" } @@ -571,23 +523,22 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", - "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", + "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -620,9 +571,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", + "dependencies": { + "@babel/types": "^7.25.6" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -631,13 +585,28 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", - "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", + "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", + "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -647,12 +616,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.7.tgz", - "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz", + "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -679,13 +648,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", - "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", + "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -999,15 +968,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", - "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz", + "integrity": "sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-remap-async-to-generator": "^7.25.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/traverse": "^7.25.4" }, "engines": { "node": ">=6.9.0" @@ -1049,12 +1018,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", - "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", + "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1064,13 +1033,13 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", - "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz", + "integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.25.4", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1097,18 +1066,16 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz", - "integrity": "sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz", + "integrity": "sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-replace-supers": "^7.25.0", + "@babel/traverse": "^7.25.4", "globals": "^11.1.0" }, "engines": { @@ -1135,12 +1102,12 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz", - "integrity": "sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz", + "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1180,6 +1147,22 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", + "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-dynamic-import": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", @@ -1245,14 +1228,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", - "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", + "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.1" }, "engines": { "node": ">=6.9.0" @@ -1278,12 +1261,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", + "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1340,13 +1323,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz", - "integrity": "sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz", + "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-simple-access": "^7.24.7" }, "engines": { @@ -1357,15 +1340,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", - "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", + "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", "dev": true, "dependencies": { - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1504,12 +1487,12 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.7.tgz", - "integrity": "sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz", + "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, @@ -1536,13 +1519,13 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", - "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz", + "integrity": "sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.25.4", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1708,12 +1691,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.7.tgz", - "integrity": "sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz", + "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1770,13 +1753,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", - "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz", + "integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1786,19 +1769,20 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", - "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.4.tgz", + "integrity": "sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.4", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -1819,29 +1803,30 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.4", "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.24.7", - "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", + "@babel/plugin-transform-class-properties": "^7.25.4", "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.24.7", + "@babel/plugin-transform-classes": "^7.25.4", "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-dotall-regex": "^7.24.7", "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", "@babel/plugin-transform-dynamic-import": "^7.24.7", "@babel/plugin-transform-exponentiation-operator": "^7.24.7", "@babel/plugin-transform-export-namespace-from": "^7.24.7", "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", + "@babel/plugin-transform-literals": "^7.25.2", "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", "@babel/plugin-transform-member-expression-literals": "^7.24.7", "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-modules-systemjs": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-modules-systemjs": "^7.25.0", "@babel/plugin-transform-modules-umd": "^7.24.7", "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", "@babel/plugin-transform-new-target": "^7.24.7", @@ -1850,9 +1835,9 @@ "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-object-super": "^7.24.7", "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.25.4", "@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-property-literals": "^7.24.7", "@babel/plugin-transform-regenerator": "^7.24.7", @@ -1861,16 +1846,16 @@ "@babel/plugin-transform-spread": "^7.24.7", "@babel/plugin-transform-sticky-regex": "^7.24.7", "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.7", + "@babel/plugin-transform-typeof-symbol": "^7.24.8", "@babel/plugin-transform-unicode-escapes": "^7.24.7", "@babel/plugin-transform-unicode-property-regex": "^7.24.7", "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.4", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.31.0", + "core-js-compat": "^3.37.1", "semver": "^6.3.1" }, "engines": { @@ -1954,31 +1939,28 @@ } }, "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.6", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1987,11 +1969,11 @@ } }, "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "dependencies": { - "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, @@ -4889,22 +4871,22 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", - "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-corejs3/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.1.tgz", - "integrity": "sha512-o7SDgTJuvx5vLKD6SFvkydkSMBvahDKGiNJzG22IZYXhiqoe9efY7zocICBgzHV4IRg5wdgl2nEL/tulKIEIbA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", + "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", @@ -5271,9 +5253,9 @@ ] }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "funding": [ { "type": "opencollective", @@ -5289,10 +5271,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -5542,9 +5524,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001605", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", - "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==", + "version": "1.0.30001655", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz", + "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==", "funding": [ { "type": "opencollective", @@ -5993,11 +5975,11 @@ } }, "node_modules/core-js-compat": { - "version": "3.36.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz", - "integrity": "sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==", + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", + "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", "dependencies": { - "browserslist": "^4.23.0" + "browserslist": "^4.23.3" }, "funding": { "type": "opencollective", @@ -6971,9 +6953,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.692", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.692.tgz", - "integrity": "sha512-d5rZRka9n2Y3MkWRN74IoAsxR0HK3yaAt7T50e3iT9VZmCCQDT3geXUO5ZRMhDToa1pkCeQXuNo+0g+NfDOVPA==" + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", + "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==" }, "node_modules/elliptic": { "version": "6.5.4", @@ -7265,9 +7247,9 @@ } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "engines": { "node": ">=6" } @@ -13179,9 +13161,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -16725,9 +16707,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "funding": [ { "type": "opencollective", @@ -16743,8 +16725,8 @@ } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -18090,9 +18072,9 @@ } }, "@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==" + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", + "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==" }, "@babel/core": { "version": "7.24.7", @@ -18124,11 +18106,11 @@ } }, "@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", "requires": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.25.6", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -18153,13 +18135,13 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "requires": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -18180,26 +18162,24 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.7.tgz", - "integrity": "sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz", + "integrity": "sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", + "@babel/helper-member-expression-to-functions": "^7.24.8", "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", + "@babel/helper-replace-supers": "^7.25.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/traverse": "^7.25.4", "semver": "^6.3.1" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", - "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", + "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.24.7", @@ -18220,39 +18200,14 @@ "semver": "^6.1.2" } }, - "@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "requires": { - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "requires": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "requires": { - "@babel/types": "^7.24.7" - } - }, "@babel/helper-member-expression-to-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz", - "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", + "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==", "dev": true, "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.8" } }, "@babel/helper-module-imports": { @@ -18265,15 +18220,14 @@ } }, "@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", "requires": { - "@babel/helper-environment-visitor": "^7.24.7", "@babel/helper-module-imports": "^7.24.7", "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" } }, "@babel/helper-optimise-call-expression": { @@ -18286,30 +18240,30 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==" + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==" }, "@babel/helper-remap-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", - "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", + "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-wrap-function": "^7.24.7" + "@babel/helper-wrap-function": "^7.25.0", + "@babel/traverse": "^7.25.0" } }, "@babel/helper-replace-supers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", - "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", + "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", "dev": true, "requires": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" + "@babel/helper-member-expression-to-functions": "^7.24.8", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/traverse": "^7.25.0" } }, "@babel/helper-simple-access": { @@ -18331,18 +18285,10 @@ "@babel/types": "^7.24.7" } }, - "@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "requires": { - "@babel/types": "^7.24.7" - } - }, "@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==" + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==" }, "@babel/helper-validator-identifier": { "version": "7.24.7", @@ -18350,20 +18296,19 @@ "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==" }, "@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==" + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==" }, "@babel/helper-wrap-function": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", - "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", + "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.0", + "@babel/types": "^7.25.0" } }, "@babel/helpers": { @@ -18387,27 +18332,39 @@ } }, "@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==" + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", + "requires": { + "@babel/types": "^7.25.6" + } }, "@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", - "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", + "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", "dev": true, "requires": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.3" + } + }, + "@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", + "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.24.8" } }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.7.tgz", - "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz", + "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" } }, "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { @@ -18422,13 +18379,13 @@ } }, "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", - "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", + "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", "dev": true, "requires": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.0" } }, "@babel/plugin-proposal-private-property-in-object": { @@ -18638,15 +18595,15 @@ } }, "@babel/plugin-transform-async-generator-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", - "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz", + "integrity": "sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==", "dev": true, "requires": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-remap-async-to-generator": "^7.25.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/traverse": "^7.25.4" } }, "@babel/plugin-transform-async-to-generator": { @@ -18670,22 +18627,22 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", - "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", + "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" } }, "@babel/plugin-transform-class-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", - "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz", + "integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.25.4", + "@babel/helper-plugin-utils": "^7.24.8" } }, "@babel/plugin-transform-class-static-block": { @@ -18700,18 +18657,16 @@ } }, "@babel/plugin-transform-classes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz", - "integrity": "sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz", + "integrity": "sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-replace-supers": "^7.25.0", + "@babel/traverse": "^7.25.4", "globals": "^11.1.0" } }, @@ -18726,12 +18681,12 @@ } }, "@babel/plugin-transform-destructuring": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz", - "integrity": "sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz", + "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" } }, "@babel/plugin-transform-dotall-regex": { @@ -18753,6 +18708,16 @@ "@babel/helper-plugin-utils": "^7.24.7" } }, + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", + "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8" + } + }, "@babel/plugin-transform-dynamic-import": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", @@ -18794,14 +18759,14 @@ } }, "@babel/plugin-transform-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", - "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", + "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", "dev": true, "requires": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.1" } }, "@babel/plugin-transform-json-strings": { @@ -18815,12 +18780,12 @@ } }, "@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", + "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" } }, "@babel/plugin-transform-logical-assignment-operators": { @@ -18853,26 +18818,26 @@ } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz", - "integrity": "sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz", + "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-simple-access": "^7.24.7" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", - "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", + "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.0" } }, "@babel/plugin-transform-modules-umd": { @@ -18957,12 +18922,12 @@ } }, "@babel/plugin-transform-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.7.tgz", - "integrity": "sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz", + "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", "@babel/plugin-syntax-optional-chaining": "^7.8.3" } @@ -18977,13 +18942,13 @@ } }, "@babel/plugin-transform-private-methods": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", - "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz", + "integrity": "sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.25.4", + "@babel/helper-plugin-utils": "^7.24.8" } }, "@babel/plugin-transform-private-property-in-object": { @@ -19088,12 +19053,12 @@ } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.7.tgz", - "integrity": "sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz", + "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" } }, "@babel/plugin-transform-unicode-escapes": { @@ -19126,29 +19091,30 @@ } }, "@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", - "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz", + "integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8" } }, "@babel/preset-env": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", - "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.4.tgz", + "integrity": "sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.25.4", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -19169,29 +19135,30 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.4", "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.24.7", - "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", + "@babel/plugin-transform-class-properties": "^7.25.4", "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.24.7", + "@babel/plugin-transform-classes": "^7.25.4", "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-dotall-regex": "^7.24.7", "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", "@babel/plugin-transform-dynamic-import": "^7.24.7", "@babel/plugin-transform-exponentiation-operator": "^7.24.7", "@babel/plugin-transform-export-namespace-from": "^7.24.7", "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", + "@babel/plugin-transform-literals": "^7.25.2", "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", "@babel/plugin-transform-member-expression-literals": "^7.24.7", "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-modules-systemjs": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-modules-systemjs": "^7.25.0", "@babel/plugin-transform-modules-umd": "^7.24.7", "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", "@babel/plugin-transform-new-target": "^7.24.7", @@ -19200,9 +19167,9 @@ "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-object-super": "^7.24.7", "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.25.4", "@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-property-literals": "^7.24.7", "@babel/plugin-transform-regenerator": "^7.24.7", @@ -19211,16 +19178,16 @@ "@babel/plugin-transform-spread": "^7.24.7", "@babel/plugin-transform-sticky-regex": "^7.24.7", "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.7", + "@babel/plugin-transform-typeof-symbol": "^7.24.8", "@babel/plugin-transform-unicode-escapes": "^7.24.7", "@babel/plugin-transform-unicode-property-regex": "^7.24.7", "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.4", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.31.0", + "core-js-compat": "^3.37.1", "semver": "^6.3.1" }, "dependencies": { @@ -19285,38 +19252,35 @@ } }, "@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "requires": { "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" } }, "@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", "requires": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.6", "debug": "^4.3.1", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "requires": { - "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" } @@ -21539,19 +21503,19 @@ } }, "babel-plugin-polyfill-corejs3": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", - "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" }, "dependencies": { "@babel/helper-define-polyfill-provider": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.1.tgz", - "integrity": "sha512-o7SDgTJuvx5vLKD6SFvkydkSMBvahDKGiNJzG22IZYXhiqoe9efY7zocICBgzHV4IRg5wdgl2nEL/tulKIEIbA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", + "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", "dev": true, "requires": { "@babel/helper-compilation-targets": "^7.22.6", @@ -21840,14 +21804,14 @@ } }, "browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "requires": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" } }, "bs58": { @@ -22038,9 +22002,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001605", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", - "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==" + "version": "1.0.30001655", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz", + "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==" }, "caseless": { "version": "0.12.0", @@ -22380,11 +22344,11 @@ "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==" }, "core-js-compat": { - "version": "3.36.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz", - "integrity": "sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==", + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", + "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", "requires": { - "browserslist": "^4.23.0" + "browserslist": "^4.23.3" } }, "core-util-is": { @@ -23091,9 +23055,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron-to-chromium": { - "version": "1.4.692", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.692.tgz", - "integrity": "sha512-d5rZRka9n2Y3MkWRN74IoAsxR0HK3yaAt7T50e3iT9VZmCCQDT3geXUO5ZRMhDToa1pkCeQXuNo+0g+NfDOVPA==" + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", + "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==" }, "elliptic": { "version": "6.5.4", @@ -23334,9 +23298,9 @@ } }, "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" }, "escape-html": { "version": "1.0.3", @@ -27927,9 +27891,9 @@ "dev": true }, "node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" }, "normalize-path": { "version": "3.0.0", @@ -30464,12 +30428,12 @@ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" } }, "uri-js": { diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 37bd523e3179..c7e1aa2e052d 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -84,7 +84,7 @@ }, "devDependencies": { "@babel/core": "^7.24.7", - "@babel/preset-env": "^7.24.7", + "@babel/preset-env": "^7.25.4", "autoprefixer": "^10.4.19", "babel-loader": "^9.1.3", "copy-webpack-plugin": "^12.0.2", From 6220d312347ce48d2c448e19a51233408bfd17b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:32:06 +0300 Subject: [PATCH 112/363] chore(deps): bump gettext from 0.25.0 to 0.26.1 (#10657) Bumps [gettext](https://github.com/elixir-gettext/gettext) from 0.25.0 to 0.26.1. - [Changelog](https://github.com/elixir-gettext/gettext/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-gettext/gettext/compare/v0.25.0...v0.26.1) --- updated-dependencies: - dependency-name: gettext dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/mix.exs | 2 +- mix.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs index f2ebd1589dd8..a81c8c5c6f40 100644 --- a/apps/block_scout_web/mix.exs +++ b/apps/block_scout_web/mix.exs @@ -106,7 +106,7 @@ defmodule BlockScoutWeb.Mixfile do # HTML CSS selectors for Phoenix controller tests {:floki, "~> 0.31"}, {:flow, "~> 1.2"}, - {:gettext, "~> 0.25.0"}, + {:gettext, "~> 0.26.1"}, {:hammer, "~> 6.0"}, {:httpoison, "~> 2.0"}, {:indexer, in_umbrella: true, runtime: false}, diff --git a/mix.lock b/mix.lock index f4841a96400c..e299988c2a5f 100644 --- a/mix.lock +++ b/mix.lock @@ -57,14 +57,14 @@ "ex_utils": {:hex, :ex_utils, "0.1.7", "2c133e0bcdc49a858cf8dacf893308ebc05bc5fba501dc3d2935e65365ec0bf3", [:mix], [], "hexpm", "66d4fe75285948f2d1e69c2a5ddd651c398c813574f8d36a9eef11dc20356ef6"}, "exactor": {:hex, :exactor, "2.2.4", "5efb4ddeb2c48d9a1d7c9b465a6fffdd82300eb9618ece5d34c3334d5d7245b1", [:mix], [], "hexpm", "1222419f706e01bfa1095aec9acf6421367dcfab798a6f67c54cf784733cd6b5"}, "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm", "32e95820a97cffea67830e91514a2ad53b888850442d6d395f53a1ac60c82e07"}, - "expo": {:hex, :expo, "1.0.0", "647639267e088717232f4d4451526e7a9de31a3402af7fcbda09b27e9a10395a", [:mix], [], "hexpm", "18d2093d344d97678e8a331ca0391e85d29816f9664a25653fd7e6166827827c"}, + "expo": {:hex, :expo, "1.0.1", "f9e2f984f5b8d195815d52d0ba264798c12c8d2f2606f76fa4c60e8ebe39474d", [:mix], [], "hexpm", "f250b33274e3e56513644858c116f255d35c767c2b8e96a512fe7839ef9306a1"}, "exvcr": {:hex, :exvcr, "0.15.1", "772db4d065f5136c6a984c302799a79e4ade3e52701c95425fa2229dd6426886", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:finch, "~> 0.16", [hex: :finch, repo: "hexpm", optional: true]}, {:httpoison, "~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "de4fc18b1d672d9b72bc7468735e19779aa50ea963a1f859ef82cd9e294b13e3"}, "file_info": {:hex, :file_info, "0.0.4", "2e0e77f211e833f38ead22cb29ce53761d457d80b3ffe0ffe0eb93880b0963b2", [:mix], [{:mimetype_parser, "~> 0.1.2", [hex: :mimetype_parser, repo: "hexpm", optional: false]}], "hexpm", "50e7ad01c2c8b9339010675fe4dc4a113b8d6ca7eddce24d1d74fd0e762781a5"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"}, "flow": {:hex, :flow, "1.2.4", "1dd58918287eb286656008777cb32714b5123d3855956f29aa141ebae456922d", [:mix], [{:gen_stage, "~> 1.0", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm", "874adde96368e71870f3510b91e35bc31652291858c86c0e75359cbdd35eb211"}, "gen_stage": {:hex, :gen_stage, "1.2.1", "19d8b5e9a5996d813b8245338a28246307fd8b9c99d1237de199d21efc4c76a1", [:mix], [], "hexpm", "83e8be657fa05b992ffa6ac1e3af6d57aa50aace8f691fcf696ff02f8335b001"}, - "gettext": {:hex, :gettext, "0.25.0", "98a95a862a94e2d55d24520dd79256a15c87ea75b49673a2e2f206e6ebc42e5d", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "38e5d754e66af37980a94fb93bb20dcde1d2361f664b0a19f01e87296634051f"}, + "gettext": {:hex, :gettext, "0.26.1", "38e14ea5dcf962d1fc9f361b63ea07c0ce715a8ef1f9e82d3dfb8e67e0416715", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "01ce56f188b9dc28780a52783d6529ad2bc7124f9744e571e1ee4ea88bf08734"}, "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~>2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, "hammer": {:hex, :hammer, "6.2.1", "5ae9c33e3dceaeb42de0db46bf505bd9c35f259c8defb03390cd7556fea67ee2", [:mix], [{:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm", "b9476d0c13883d2dc0cc72e786bac6ac28911fba7cc2e04b70ce6a6d9c4b2bdc"}, "hammer_backend_redis": {:hex, :hammer_backend_redis, "6.1.2", "eb296bb4924928e24135308b2afc189201fd09411c870c6bbadea444a49b2f2c", [:mix], [{:hammer, "~> 6.0", [hex: :hammer, repo: "hexpm", optional: false]}, {:redix, "~> 1.1", [hex: :redix, repo: "hexpm", optional: false]}], "hexpm", "217ea066278910543a5e9b577d5bf2425419446b94fe76bdd9f255f39feec9fa"}, From 855bca09dfa5678b6a3b9c8809bb6ed0688df661 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:32:34 +0300 Subject: [PATCH 113/363] chore(deps): bump cldr_utils from 2.28.0 to 2.28.1 (#10625) Bumps [cldr_utils](https://github.com/elixir-cldr/cldr_utils) from 2.28.0 to 2.28.1. - [Release notes](https://github.com/elixir-cldr/cldr_utils/releases) - [Changelog](https://github.com/elixir-cldr/cldr_utils/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-cldr/cldr_utils/commits) --- updated-dependencies: - dependency-name: cldr_utils dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index e299988c2a5f..ec057ea83645 100644 --- a/mix.lock +++ b/mix.lock @@ -16,7 +16,7 @@ "castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"}, "cbor": {:hex, :cbor, "1.0.1", "39511158e8ea5a57c1fcb9639aaa7efde67129678fee49ebbda780f6f24959b0", [:mix], [], "hexpm", "5431acbe7a7908f17f6a9cd43311002836a34a8ab01876918d8cfb709cd8b6a2"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, - "cldr_utils": {:hex, :cldr_utils, "2.28.0", "ce309d11b79fc13e1f22f808b5e3c1647102b01b11734ca8cb0296ca6d406fe4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "e7ac4bcea0fdbc11b5295ef30dd7b18d0922512399361af06a97198e57d23742"}, + "cldr_utils": {:hex, :cldr_utils, "2.28.1", "3d85c835e1d0b7bceb9feed1647025ff7df59180246f13b582422f12b1afd52c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "79a5f645481d09b1372962384aa275d67d69273e73e3b38a9fee363eb57c2b79"}, "cloak": {:hex, :cloak, "1.1.4", "aba387b22ea4d80d92d38ab1890cc528b06e0e7ef2a4581d71c3fdad59e997e7", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "92b20527b9aba3d939fab0dd32ce592ff86361547cfdc87d74edce6f980eb3d7"}, "cloak_ecto": {:hex, :cloak_ecto, "1.3.0", "0de127c857d7452ba3c3367f53fb814b0410ff9c680a8d20fbe8b9a3c57a1118", [:mix], [{:cloak, "~> 1.1.1", [hex: :cloak, repo: "hexpm", optional: false]}, {:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm", "314beb0c123b8a800418ca1d51065b27ba3b15f085977e65c0f7b2adab2de1cc"}, "coerce": {:hex, :coerce, "1.0.1", "211c27386315dc2894ac11bc1f413a0e38505d808153367bd5c6e75a4003d096", [:mix], [], "hexpm", "b44a691700f7a1a15b4b7e2ff1fa30bebd669929ac8aa43cffe9e2f8bf051cf1"}, From ccef29b356867c86fe0eaa171dfd7de26988c92f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:33:06 +0300 Subject: [PATCH 114/363] chore(deps): bump luxon in /apps/block_scout_web/assets (#10683) Bumps [luxon](https://github.com/moment/luxon) from 3.4.4 to 3.5.0. - [Changelog](https://github.com/moment/luxon/blob/master/CHANGELOG.md) - [Commits](https://github.com/moment/luxon/compare/3.4.4...3.5.0) --- updated-dependencies: - dependency-name: luxon dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 138427535b1c..677b57b58664 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -44,7 +44,7 @@ "lodash.omit": "^4.5.0", "lodash.rangeright": "^4.2.0", "lodash.reduce": "^4.6.0", - "luxon": "^3.4.4", + "luxon": "^3.5.0", "malihu-custom-scrollbar-plugin": "3.1.5", "mixpanel-browser": "^2.54.1", "moment": "^2.30.1", @@ -12597,9 +12597,9 @@ "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=" }, "node_modules/luxon": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", - "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", "engines": { "node": ">=12" } @@ -27424,9 +27424,9 @@ "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=" }, "luxon": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", - "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==" + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==" }, "make-dir": { "version": "4.0.0", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index c7e1aa2e052d..8cee0591e125 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -56,7 +56,7 @@ "lodash.omit": "^4.5.0", "lodash.rangeright": "^4.2.0", "lodash.reduce": "^4.6.0", - "luxon": "^3.4.4", + "luxon": "^3.5.0", "malihu-custom-scrollbar-plugin": "3.1.5", "mixpanel-browser": "^2.54.1", "moment": "^2.30.1", From a7ee1cef89edec0946d328402dd8dae8f1f9b8a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:33:59 +0300 Subject: [PATCH 115/363] chore(deps): bump postgrex from 0.19.0 to 0.19.1 (#10622) Bumps [postgrex](https://github.com/elixir-ecto/postgrex) from 0.19.0 to 0.19.1. - [Release notes](https://github.com/elixir-ecto/postgrex/releases) - [Changelog](https://github.com/elixir-ecto/postgrex/blob/master/CHANGELOG.md) - [Commits](https://github.com/elixir-ecto/postgrex/compare/v0.19.0...v0.19.1) --- updated-dependencies: - dependency-name: postgrex dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index ec057ea83645..e96174965964 100644 --- a/mix.lock +++ b/mix.lock @@ -110,7 +110,7 @@ "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, - "postgrex": {:hex, :postgrex, "0.19.0", "f7d50e50cb42e0a185f5b9a6095125a9ab7e4abccfbe2ab820ab9aa92b71dbab", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "dba2d2a0a8637defbf2307e8629cb2526388ba7348f67d04ec77a5d6a72ecfae"}, + "postgrex": {:hex, :postgrex, "0.19.1", "73b498508b69aded53907fe48a1fee811be34cc720e69ef4ccd568c8715495ea", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "8bac7885a18f381e091ec6caf41bda7bb8c77912bb0e9285212829afe5d8a8f8"}, "prometheus": {:hex, :prometheus, "4.11.0", "b95f8de8530f541bd95951e18e355a840003672e5eda4788c5fa6183406ba29a", [:mix, :rebar3], [{:quantile_estimator, "~> 0.2.1", [hex: :quantile_estimator, repo: "hexpm", optional: false]}], "hexpm", "719862351aabf4df7079b05dc085d2bbcbe3ac0ac3009e956671b1d5ab88247d"}, "prometheus_ecto": {:hex, :prometheus_ecto, "1.4.3", "3dd4da1812b8e0dbee81ea58bb3b62ed7588f2eae0c9e97e434c46807ff82311", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm", "8d66289f77f913b37eda81fd287340c17e61a447549deb28efc254532b2bed82"}, "prometheus_ex": {:git, "https://github.com/lanodan/prometheus.ex", "31f7fbe4b71b79ba27efc2a5085746c4011ceb8f", [branch: "fix/elixir-1.14"]}, From b1e942f19ba95f7d4eda8a2dec295133b4b6d2a4 Mon Sep 17 00:00:00 2001 From: varasev <33550681+varasev@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:07:29 +0400 Subject: [PATCH 116/363] fix: Truncate token symbol in Explorer.Chain.PolygonZkevm.BridgeL1Token (#10688) Co-authored-by: POA <33550681+poa@users.noreply.github.com> --- apps/explorer/lib/explorer/chain/polygon_zkevm/reader.ex | 8 ++++++++ apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge.ex | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain/polygon_zkevm/reader.ex b/apps/explorer/lib/explorer/chain/polygon_zkevm/reader.ex index 2e5daf818497..4f71a2c3644b 100644 --- a/apps/explorer/lib/explorer/chain/polygon_zkevm/reader.ex +++ b/apps/explorer/lib/explorer/chain/polygon_zkevm/reader.ex @@ -337,6 +337,14 @@ defmodule Explorer.Chain.PolygonZkevm.Reader do end end + @doc """ + Filters token symbol (cannot be longer than 16 characters). + """ + @spec sanitize_symbol(String.t()) :: String.t() + def sanitize_symbol(symbol) do + String.slice(symbol, 0, 16) + end + defp page_batches(query, %PagingOptions{key: nil}), do: query defp page_batches(query, %PagingOptions{key: {number}}) do diff --git a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge.ex b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge.ex index 98c8ef53b7f0..1f54f1bc7b5b 100644 --- a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge.ex +++ b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge.ex @@ -473,7 +473,7 @@ defmodule Indexer.Fetcher.PolygonZkevm.Bridge do defp get_new_data(data, request, response) do if atomized_key(request.method_id) == :symbol do - Map.put(data, :symbol, response) + Map.put(data, :symbol, Reader.sanitize_symbol(response)) else Map.put(data, :decimals, Reader.sanitize_decimals(response)) end From 58fcb0c765fd033589826e032f5cf3fb0a0a8c87 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:13:37 +0400 Subject: [PATCH 117/363] chore: Change shrink internal transactions migration default batch_size (#10689) --- config/runtime.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/runtime.exs b/config/runtime.exs index e3c9e798bc25..eb18ab001f19 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -617,7 +617,7 @@ config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, config :explorer, Explorer.Migrator.ShrinkInternalTransactions, enabled: ConfigHelper.parse_bool_env_var("SHRINK_INTERNAL_TRANSACTIONS_ENABLED"), - batch_size: ConfigHelper.parse_integer_env_var("SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE", 1000), + batch_size: ConfigHelper.parse_integer_env_var("SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE", 100), concurrency: ConfigHelper.parse_integer_env_var("SHRINK_INTERNAL_TRANSACTIONS_CONCURRENCY", 10) config :explorer, Explorer.Chain.BridgedToken, From 2e905c076d5ec1d0f544e51b8b0e3d5c638ce234 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:20:36 +0300 Subject: [PATCH 118/363] chore(deps-dev): bump @babel/core in /apps/block_scout_web/assets (#10679) Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.24.7 to 7.25.2. - [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.25.2/packages/babel-core) --- updated-dependencies: - dependency-name: "@babel/core" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 66 +++++++++---------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 677b57b58664..67d7ebbe1dde 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.15" }, "devDependencies": { - "@babel/core": "^7.24.7", + "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.4", "autoprefixer": "^10.4.19", "babel-loader": "^9.1.3", @@ -232,20 +232,20 @@ } }, "node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -545,12 +545,12 @@ } }, "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", + "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.6" }, "engines": { "node": ">=6.9.0" @@ -18077,20 +18077,20 @@ "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==" }, "@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "requires": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -18312,12 +18312,12 @@ } }, "@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", + "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", "requires": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.6" } }, "@babel/highlight": { diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 8cee0591e125..0036b86e80f2 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -83,7 +83,7 @@ "xss": "^1.0.15" }, "devDependencies": { - "@babel/core": "^7.24.7", + "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.4", "autoprefixer": "^10.4.19", "babel-loader": "^9.1.3", From a970a998632258d27f99650f528a46d1b7b5d2bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:24:53 +0300 Subject: [PATCH 119/363] chore(deps): bump core-js in /apps/block_scout_web/assets (#10684) Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.37.1 to 3.38.1. - [Release notes](https://github.com/zloirock/core-js/releases) - [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md) - [Commits](https://github.com/zloirock/core-js/commits/v3.38.1/packages/core-js) --- updated-dependencies: - dependency-name: core-js dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 67d7ebbe1dde..4b4848db5be7 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -17,7 +17,7 @@ "chart.js": "^4.4.3", "chartjs-adapter-luxon": "^1.3.1", "clipboard": "^2.0.11", - "core-js": "^3.37.1", + "core-js": "^3.38.1", "crypto-browserify": "^3.12.0", "dropzone": "^5.9.3", "eth-net-props": "^1.0.41", @@ -5965,9 +5965,9 @@ } }, "node_modules/core-js": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz", - "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==", + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", + "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -22339,9 +22339,9 @@ } }, "core-js": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz", - "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==" + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", + "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==" }, "core-js-compat": { "version": "3.38.1", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 0036b86e80f2..9a0116122250 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -29,7 +29,7 @@ "chart.js": "^4.4.3", "chartjs-adapter-luxon": "^1.3.1", "clipboard": "^2.0.11", - "core-js": "^3.37.1", + "core-js": "^3.38.1", "crypto-browserify": "^3.12.0", "dropzone": "^5.9.3", "eth-net-props": "^1.0.41", From d2dde382306405466dde1ba0a5c480b004caab01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:26:36 +0300 Subject: [PATCH 120/363] chore(deps-dev): bump webpack in /apps/block_scout_web/assets (#10671) Bumps [webpack](https://github.com/webpack/webpack) from 5.93.0 to 5.94.0. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.93.0...v5.94.0) --- updated-dependencies: - dependency-name: webpack dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 72 ++++--------------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 16 insertions(+), 58 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 4b4848db5be7..bf0c87e4e03d 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -92,7 +92,7 @@ "sass": "^1.77.8", "sass-loader": "^14.2.1", "style-loader": "^4.0.0", - "webpack": "^5.93.0", + "webpack": "^5.94.0", "webpack-cli": "^5.1.4" }, "engines": { @@ -3599,26 +3599,6 @@ "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz", "integrity": "sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==" }, - "node_modules/@types/eslint": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", - "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -7043,9 +7023,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", - "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -17425,12 +17405,11 @@ } }, "node_modules/webpack": { - "version": "5.93.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", - "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, "dependencies": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", @@ -17439,7 +17418,7 @@ "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -20433,26 +20412,6 @@ "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz", "integrity": "sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==" }, - "@types/eslint": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", - "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", - "dev": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", - "dev": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -23134,9 +23093,9 @@ } }, "enhanced-resolve": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", - "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, "requires": { "graceful-fs": "^4.2.4", @@ -31036,12 +30995,11 @@ "dev": true }, "webpack": { - "version": "5.93.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", - "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, "requires": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", @@ -31050,7 +31008,7 @@ "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 9a0116122250..7ecb6015f4b4 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -104,7 +104,7 @@ "sass": "^1.77.8", "sass-loader": "^14.2.1", "style-loader": "^4.0.0", - "webpack": "^5.93.0", + "webpack": "^5.94.0", "webpack-cli": "^5.1.4" }, "jest": { From 480b763c8eb3bc4a161d5e6fd86e3589ac62ebe0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:04:48 +0300 Subject: [PATCH 121/363] chore(deps): bump @fortawesome/fontawesome-free (#10681) Bumps [@fortawesome/fontawesome-free](https://github.com/FortAwesome/Font-Awesome) from 6.5.2 to 6.6.0. - [Release notes](https://github.com/FortAwesome/Font-Awesome/releases) - [Changelog](https://github.com/FortAwesome/Font-Awesome/blob/6.x/CHANGELOG.md) - [Commits](https://github.com/FortAwesome/Font-Awesome/compare/6.5.2...6.6.0) --- updated-dependencies: - dependency-name: "@fortawesome/fontawesome-free" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 15 +++++++-------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index bf0c87e4e03d..a624f0ee2a61 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -8,7 +8,7 @@ "license": "GPL-3.0", "dependencies": { "@amplitude/analytics-browser": "^2.9.3", - "@fortawesome/fontawesome-free": "^6.5.2", + "@fortawesome/fontawesome-free": "^6.6.0", "@tarekraafat/autocomplete.js": "^10.2.7", "@walletconnect/web3-provider": "^1.8.0", "assert": "^2.1.0", @@ -2548,10 +2548,9 @@ } }, "node_modules/@fortawesome/fontawesome-free": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz", - "integrity": "sha512-hRILoInAx8GNT5IMkrtIt9blOdrqHOnPBH+k70aWUAqPZPgopb9G5EQJFpaBx/S8zp2fC+mPW349Bziuk1o28Q==", - "hasInstallScript": true, + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.6.0.tgz", + "integrity": "sha512-60G28ke/sXdtS9KZCpZSHHkCbdsOGEhIUGlwq6yhY74UpTiToIh8np7A8yphhM4BWsvNFtIvLpi4co+h9Mr9Ow==", "engines": { "node": ">=6" } @@ -19610,9 +19609,9 @@ } }, "@fortawesome/fontawesome-free": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.2.tgz", - "integrity": "sha512-hRILoInAx8GNT5IMkrtIt9blOdrqHOnPBH+k70aWUAqPZPgopb9G5EQJFpaBx/S8zp2fC+mPW349Bziuk1o28Q==" + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.6.0.tgz", + "integrity": "sha512-60G28ke/sXdtS9KZCpZSHHkCbdsOGEhIUGlwq6yhY74UpTiToIh8np7A8yphhM4BWsvNFtIvLpi4co+h9Mr9Ow==" }, "@humanwhocodes/config-array": { "version": "0.11.14", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 7ecb6015f4b4..0e3cd81eb649 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -19,7 +19,7 @@ "eslint": "eslint js/**" }, "dependencies": { - "@fortawesome/fontawesome-free": "^6.5.2", + "@fortawesome/fontawesome-free": "^6.6.0", "@amplitude/analytics-browser": "^2.9.3", "@tarekraafat/autocomplete.js": "^10.2.7", "@walletconnect/web3-provider": "^1.8.0", From 72ed84be6425ca81946c08e205f53cabe7545536 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:06:07 +0300 Subject: [PATCH 122/363] chore(deps-dev): bump postcss in /apps/block_scout_web/assets (#10682) Bumps [postcss](https://github.com/postcss/postcss) from 8.4.39 to 8.4.42. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.4.39...8.4.42) --- updated-dependencies: - dependency-name: postcss dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 a624f0ee2a61..3979c62ff9ce 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -87,7 +87,7 @@ "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "mini-css-extract-plugin": "^2.9.0", - "postcss": "^8.4.39", + "postcss": "^8.4.42", "postcss-loader": "^8.1.1", "sass": "^1.77.8", "sass-loader": "^14.2.1", @@ -13772,9 +13772,9 @@ } }, "node_modules/postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "version": "8.4.42", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.42.tgz", + "integrity": "sha512-hywKUQB9Ra4dR1mGhldy5Aj1X3MWDSIA1cEi+Uy0CjheLvP6Ual5RlwMCh8i/X121yEDLDIKBsrCQ8ba3FDMfQ==", "dev": true, "funding": [ { @@ -28299,9 +28299,9 @@ "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" }, "postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "version": "8.4.42", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.42.tgz", + "integrity": "sha512-hywKUQB9Ra4dR1mGhldy5Aj1X3MWDSIA1cEi+Uy0CjheLvP6Ual5RlwMCh8i/X121yEDLDIKBsrCQ8ba3FDMfQ==", "dev": true, "requires": { "nanoid": "^3.3.7", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 0e3cd81eb649..133619fce372 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -99,7 +99,7 @@ "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "mini-css-extract-plugin": "^2.9.0", - "postcss": "^8.4.39", + "postcss": "^8.4.42", "postcss-loader": "^8.1.1", "sass": "^1.77.8", "sass-loader": "^14.2.1", From 1a179bb8551e5e5ecb04c80c44d2886958e63fde Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:25:42 +0300 Subject: [PATCH 123/363] chore(deps): bump telemetry from 1.2.1 to 1.3.0 (#10656) Bumps [telemetry](https://github.com/beam-telemetry/telemetry) from 1.2.1 to 1.3.0. - [Changelog](https://github.com/beam-telemetry/telemetry/blob/main/CHANGELOG.md) - [Commits](https://github.com/beam-telemetry/telemetry/compare/v1.2.1...v1.3.0) --- updated-dependencies: - dependency-name: telemetry dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/explorer/mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index 1dde22eb7f7f..56345b2e1dd9 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -110,7 +110,7 @@ defmodule Explorer.Mixfile do # `:spandex` tracing of `:ecto` {:spandex_ecto, "~> 0.7.0"}, # Attach `:prometheus_ecto` to `:ecto` - {:telemetry, "~> 1.2.1"}, + {:telemetry, "~> 1.3.0"}, # `Timex.Duration` for `Explorer.Counters.AverageBlockTime.average_block_time/0` {:timex, "~> 3.7.1"}, {:con_cache, "~> 1.0"}, diff --git a/mix.lock b/mix.lock index e96174965964..0d4ce3f3bfe6 100644 --- a/mix.lock +++ b/mix.lock @@ -133,7 +133,7 @@ "spandex_phoenix": {:hex, :spandex_phoenix, "1.1.0", "9cff829d05258dd49a227c56711b19b69a8fd5d4873d8e9a92a4f4097e7322ab", [:mix], [{:phoenix, "~> 1.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.3", [hex: :plug, repo: "hexpm", optional: false]}, {:spandex, "~> 2.2 or ~> 3.0", [hex: :spandex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "265fe05c1736485fbb75d66ef7576682ebf6428c391dd54d22217f612fd4ddad"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, - "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, + "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "tesla": {:hex, :tesla, "1.12.1", "fe2bf4250868ee72e5d8b8dfa408d13a00747c41b7237b6aa3b9a24057346681", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2391efc6243d37ead43afd0327b520314c7b38232091d4a440c1212626fdd6e7"}, "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, "toml": {:hex, :toml, "0.6.2", "38f445df384a17e5d382befe30e3489112a48d3ba4c459e543f748c2f25dd4d1", [:mix], [], "hexpm", "d013e45126d74c0c26a38d31f5e8e9b83ea19fc752470feb9a86071ca5a672fa"}, From 22f3ff9943f974ab247b286dc4b44bdf58c8486d Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 2 Sep 2024 20:30:39 +0300 Subject: [PATCH 124/363] fix: Fix gettext usage warning (#10693) --- apps/block_scout_web/lib/block_scout_web.ex | 7 ++++--- apps/block_scout_web/lib/block_scout_web/gettext.ex | 2 +- .../lib/block_scout_web/views/block_transaction_view.ex | 2 +- .../lib/block_scout_web/views/internal_transaction_view.ex | 2 +- .../lib/block_scout_web/views/transaction_view.ex | 2 +- .../lib/block_scout_web/views/wei_helper.ex | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web.ex b/apps/block_scout_web/lib/block_scout_web.ex index 7cdc6b3633c4..1ce9cca0e7da 100644 --- a/apps/block_scout_web/lib/block_scout_web.ex +++ b/apps/block_scout_web/lib/block_scout_web.ex @@ -25,7 +25,7 @@ defmodule BlockScoutWeb do import BlockScoutWeb.Controller import BlockScoutWeb.Router.Helpers import BlockScoutWeb.Routers.WebRouter.Helpers, except: [static_path: 2] - import BlockScoutWeb.Gettext + use Gettext, backend: BlockScoutWeb.Gettext import BlockScoutWeb.ErrorHelper import BlockScoutWeb.Routers.AccountRouter.Helpers, except: [static_path: 2] import Plug.Conn @@ -49,7 +49,6 @@ defmodule BlockScoutWeb do import BlockScoutWeb.{ CurrencyHelper, ErrorHelper, - Gettext, Router.Helpers, TabHelper, Tokens.Helper, @@ -57,6 +56,8 @@ defmodule BlockScoutWeb do WeiHelper } + use Gettext, backend: BlockScoutWeb.Gettext + import BlockScoutWeb.Routers.AccountRouter.Helpers, except: [static_path: 2] import Explorer.Chain.CurrencyHelper, only: [divide_decimals: 2] @@ -78,7 +79,7 @@ defmodule BlockScoutWeb do quote do use Phoenix.Channel - import BlockScoutWeb.Gettext + use Gettext, backend: BlockScoutWeb.Gettext end end diff --git a/apps/block_scout_web/lib/block_scout_web/gettext.ex b/apps/block_scout_web/lib/block_scout_web/gettext.ex index 3d54e804bf98..bb3588f5f64e 100644 --- a/apps/block_scout_web/lib/block_scout_web/gettext.ex +++ b/apps/block_scout_web/lib/block_scout_web/gettext.ex @@ -20,5 +20,5 @@ defmodule BlockScoutWeb.Gettext do See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage. """ - use Gettext, otp_app: :block_scout_web + use Gettext.Backend, otp_app: :block_scout_web end diff --git a/apps/block_scout_web/lib/block_scout_web/views/block_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/block_transaction_view.ex index aef7f4124d03..025f2782d5c6 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/block_transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/block_transaction_view.ex @@ -1,7 +1,7 @@ defmodule BlockScoutWeb.BlockTransactionView do use BlockScoutWeb, :view - import BlockScoutWeb.Gettext, only: [gettext: 1] + use Gettext, backend: BlockScoutWeb.Gettext def block_not_found_message({:ok, true}) do gettext("Easy Cowboy! This block does not exist yet!") diff --git a/apps/block_scout_web/lib/block_scout_web/views/internal_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/internal_transaction_view.ex index 191be34d7d94..3d4cafd9f3f0 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/internal_transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/internal_transaction_view.ex @@ -3,7 +3,7 @@ defmodule BlockScoutWeb.InternalTransactionView do alias Explorer.Chain.InternalTransaction - import BlockScoutWeb.Gettext + use Gettext, backend: BlockScoutWeb.Gettext @doc """ Returns the formatted string for the type of the internal transaction. diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex index 64eafbccde88..07e6f7396775 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex @@ -11,7 +11,7 @@ defmodule BlockScoutWeb.TransactionView do alias Explorer.ExchangeRates.Token alias Timex.Duration - import BlockScoutWeb.Gettext + use Gettext, backend: BlockScoutWeb.Gettext import BlockScoutWeb.AddressView, only: [from_address_hash: 1, short_token_id: 2, tag_name_to_label: 1] import BlockScoutWeb.Tokens.Helper diff --git a/apps/block_scout_web/lib/block_scout_web/views/wei_helper.ex b/apps/block_scout_web/lib/block_scout_web/views/wei_helper.ex index 72c1c1ee1f3a..188303e0c9eb 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/wei_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/wei_helper.ex @@ -3,7 +3,7 @@ defmodule BlockScoutWeb.WeiHelper do Helper functions for interacting with `t:Explorer.Chain.Wei.t/0` values. """ - import BlockScoutWeb.Gettext + use Gettext, backend: BlockScoutWeb.Gettext alias BlockScoutWeb.CldrHelper alias Explorer.Chain.Wei From 53f79f6ee1a37eebcb1fe09404d9175fec118557 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 08:49:12 +0300 Subject: [PATCH 125/363] chore(deps): bump recon from 2.5.5 to 2.5.6 (#10695) Bumps [recon](https://github.com/ferd/recon) from 2.5.5 to 2.5.6. - [Changelog](https://github.com/ferd/recon/blob/master/CHANGELOG.md) - [Commits](https://github.com/ferd/recon/compare/2.5.5...2.5.6) --- updated-dependencies: - dependency-name: recon dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 0d4ce3f3bfe6..a75ce62299e5 100644 --- a/mix.lock +++ b/mix.lock @@ -122,7 +122,7 @@ "que": {:hex, :que, "0.10.1", "788ed0ec92ed69bdf9cfb29bf41a94ca6355b8d44959bd0669cf706e557ac891", [:mix], [{:ex_utils, "~> 0.1.6", [hex: :ex_utils, repo: "hexpm", optional: false]}, {:memento, "~> 0.3.0", [hex: :memento, repo: "hexpm", optional: false]}], "hexpm", "a737b365253e75dbd24b2d51acc1d851049e87baae08cd0c94e2bc5cd65088d5"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "ratio": {:hex, :ratio, "2.4.2", "c8518f3536d49b1b00d88dd20d49f8b11abb7819638093314a6348139f14f9f9", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:numbers, "~> 5.2.0", [hex: :numbers, repo: "hexpm", optional: false]}], "hexpm", "441ef6f73172a3503de65ccf1769030997b0d533b1039422f1e5e0e0b4cbf89e"}, - "recon": {:hex, :recon, "2.5.5", "c108a4c406fa301a529151a3bb53158cadc4064ec0c5f99b03ddb8c0e4281bdf", [:mix, :rebar3], [], "hexpm", "632a6f447df7ccc1a4a10bdcfce71514412b16660fe59deca0fcf0aa3c054404"}, + "recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"}, "redix": {:hex, :redix, "1.5.1", "a2386971e69bf23630fb3a215a831b5478d2ee7dc9ea7ac811ed89186ab5d7b7", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:nimble_options, "~> 0.5.0 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "85224eb2b683c516b80d472eb89b76067d5866913bf0be59d646f550de71f5c4"}, "remote_ip": {:hex, :remote_ip, "1.2.0", "fb078e12a44414f4cef5a75963c33008fe169b806572ccd17257c208a7bc760f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2ff91de19c48149ce19ed230a81d377186e4412552a597d6a5137373e5877cb7"}, "rustler_precompiled": {:hex, :rustler_precompiled, "0.7.1", "ecadf02cc59a0eccbaed6c1937303a5827fbcf60010c541595e6d3747d3d0f9f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "b9e4657b99a1483ea31502e1d58c464bedebe9028808eda45c3a429af4550c66"}, From 0dab9884ebb7f710df210683e721dbc8fb50551d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 09:22:01 +0300 Subject: [PATCH 126/363] chore(deps): bump ex_cldr_units from 3.17.1 to 3.17.2 (#10621) Bumps [ex_cldr_units](https://github.com/elixir-cldr/cldr_units) from 3.17.1 to 3.17.2. - [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.17.2) --- updated-dependencies: - dependency-name: ex_cldr_units dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mix.lock b/mix.lock index a75ce62299e5..94d5950f9ebf 100644 --- a/mix.lock +++ b/mix.lock @@ -43,11 +43,11 @@ "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_abi": {:hex, :ex_abi, "0.8.0", "bb08827bd8d71dbb311c69ac55a008669dfabe2ce5b58d65f97c08c0aba60ec6", [:mix], [{:ex_keccak, "~> 0.7.5", [hex: :ex_keccak, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "bbdae12c186aeeb4c53dd7c7c57f457923602db315aa1f66d7427467c8ad77af"}, - "ex_cldr": {:hex, :ex_cldr, "2.40.0", "624717778dbf0a8cd307f1576eabbd44470c16190172abf293fed24150440a5a", [:mix], [{:cldr_utils, "~> 2.28", [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", "113394b6dd23aaf7912da583aab103d9cf082b9821bc4a6e287543a895af7cb4"}, + "ex_cldr": {:hex, :ex_cldr, "2.40.1", "c1fcb0cd9d2a70d28f4540a99f32127e7f1813e0db109d65ab29dea5337ae266", [:mix], [{:cldr_utils, "~> 2.28", [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", "509810702e8e81991851d9426ffe6b34b48b7b9baa12922e7b3fb8f6368606f3"}, "ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.16.2", "670d96cc4fb18cfebd82488ed687742683be2d0725d66ec051578d4b13539aa8", [:mix], [{:ex_cldr, "~> 2.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "2ccfac2838f4df8c8e5424dbc68eb2f3ac9eeb45e10365050901f7ac7a914ce1"}, - "ex_cldr_lists": {:hex, :ex_cldr_lists, "2.11.0", "1d39e75f0e493ccc95adfc85c55b4ca34f0771626350ce326d9ab8813d91444e", [: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", "8132b30a5506ae8a09e5c9a21c23fd60c8837ce6c3a1de9966d813eb78951695"}, - "ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.33.2", "c5587a8d84214d9cc42e7827e4c3bed2aa9e52505a55b10540020725954ded2c", [: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.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, "~> 2.16", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "49f1dbaddc1ad6e3f496a97fa425d25b3ae89e8178ce0416d9909deaf2e5ad80"}, - "ex_cldr_units": {:hex, :ex_cldr_units, "3.17.1", "f03c7a138113511af903d0d2205b5cc01df1e599c28839ca2e1e78b7ca0bf2a2", [:mix], [{:cldr_utils, "~> 2.25", [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.33.0", [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", "7de1bf7ff7599cf4da9dd0f1a7b6e19ca8db83b883301f5dcd0f565aa5eaec8c"}, + "ex_cldr_lists": {:hex, :ex_cldr_lists, "2.11.1", "ad18f861d7c5ca82aac6d173469c6a2339645c96790172ab0aa255b64fb7303b", [: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", "00161c04510ccb3f18b19a6b8562e50c21f1e9c15b8ff4c934bea5aad0b4ade2"}, + "ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.33.3", "9fedcf279a17d19abdf8872738472326e82378d90ec2dd9756a0c84558c86b36", [: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.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, "~> 2.16", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "4a0d90d06710c1499528d5f536c539379a73a68d4679c55375198a798d138442"}, + "ex_cldr_units": {:hex, :ex_cldr_units, "3.17.2", "b0483d5c61c6c8649aafdcafc7372dd71a7a30f52dd4c9b072576467bf721454", [:mix], [{:cldr_utils, "~> 2.25", [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.33.0", [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", "457d76c6e3b548bd7aba3c7b5d157213be2842d1162c2283abf81d9e2f1e1fc7"}, "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, "ex_json_schema": {:hex, :ex_json_schema, "0.10.2", "7c4b8c1481fdeb1741e2ce66223976edfb9bccebc8014f6aec35d4efe964fb71", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "37f43be60f8407659d4d0155a7e45e7f406dab1f827051d3d35858a709baf6a6"}, "ex_keccak": {:hex, :ex_keccak, "0.7.5", "f3b733173510d48ae9a1ea1de415e694b2651f35c787e63f33b5ed0013fbfd35", [:mix], [{:rustler, ">= 0.0.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.7", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "8a5e1cb7f96fff5e480ff6a121477b90c4fd8c150984086dffd98819f5d83763"}, From a88ad33c73d873515a98284ef80451e9d378f59c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 10:13:52 +0300 Subject: [PATCH 127/363] chore(deps): bump ex_cldr from 2.40.0 to 2.40.1 (#10623) Bumps [ex_cldr](https://github.com/elixir-cldr/cldr) from 2.40.0 to 2.40.1. - [Release notes](https://github.com/elixir-cldr/cldr/releases) - [Changelog](https://github.com/elixir-cldr/cldr/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-cldr/cldr/compare/v2.40.0...v2.40.1) --- updated-dependencies: - dependency-name: ex_cldr dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From 0a9e24faccc156b5bd75a0fc4122c0bd73137833 Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Tue, 3 Sep 2024 20:26:10 +0900 Subject: [PATCH 128/363] chore: fix flaking explorer tests (#10676) --- .../migrator/sanitize_incorrect_weth_token_transfers_test.exs | 2 +- apps/explorer/test/explorer/token/metadata_retriever_test.exs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/explorer/test/explorer/migrator/sanitize_incorrect_weth_token_transfers_test.exs b/apps/explorer/test/explorer/migrator/sanitize_incorrect_weth_token_transfers_test.exs index 834c3d8054ee..b2f4be309b68 100644 --- a/apps/explorer/test/explorer/migrator/sanitize_incorrect_weth_token_transfers_test.exs +++ b/apps/explorer/test/explorer/migrator/sanitize_incorrect_weth_token_transfers_test.exs @@ -132,7 +132,7 @@ defmodule Explorer.Migrator.SanitizeIncorrectWETHTokenTransfersTest do %{token_contract_address_hash: ^whitelisted_token_address_hash}, %{token_contract_address_hash: ^whitelisted_token_address_hash}, %{token_contract_address_hash: ^whitelisted_token_address_hash} - ] = transfers = Repo.all(TokenTransfer) + ] = transfers = Repo.all(TokenTransfer, order_by: [asc: :block_number, asc: :log_index]) withdrawal = Enum.at(transfers, 1) deposit = Enum.at(transfers, 2) diff --git a/apps/explorer/test/explorer/token/metadata_retriever_test.exs b/apps/explorer/test/explorer/token/metadata_retriever_test.exs index 1b73ff222534..00d3ac5107a9 100644 --- a/apps/explorer/test/explorer/token/metadata_retriever_test.exs +++ b/apps/explorer/test/explorer/token/metadata_retriever_test.exs @@ -915,6 +915,8 @@ defmodule Explorer.Token.MetadataRetrieverTest do "image" => "https://ipfs.io/ipfs/bafybeig6nlmyzui7llhauc52j2xo5hoy4lzp6442lkve5wysdvjkizxonu" } }} == MetadataRetriever.fetch_json(data) + + Application.put_env(:explorer, :http_adapter, HTTPoison) end test "Fetches metadata from ipfs" do From 4b28ff30b1c0f66ad6c910be20f8147935675c91 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 3 Sep 2024 17:58:29 +0300 Subject: [PATCH 129/363] Add Release pre-release workflows for CELO --- .github/workflows/pre-release-celo.yml | 97 ++++++++++++++++++++++++++ .github/workflows/release-celo.yml | 95 +++++++++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 .github/workflows/pre-release-celo.yml create mode 100644 .github/workflows/release-celo.yml diff --git a/.github/workflows/pre-release-celo.yml b/.github/workflows/pre-release-celo.yml new file mode 100644 index 000000000000..c493692792ea --- /dev/null +++ b/.github/workflows/pre-release-celo.yml @@ -0,0 +1,97 @@ +name: Pre-release for CELO + +on: + workflow_dispatch: + inputs: + number: + type: number + required: true + +env: + OTP_VERSION: ${{ vars.OTP_VERSION }} + ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + steps: + - uses: actions/checkout@v4 + - name: Setup repo + uses: ./.github/actions/setup-repo + id: setup + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + docker-remote-multi-platform: true + docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} + docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} + + - name: Build and push Docker image for CELO (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-celo:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=celo + + - name: Build and push Docker image for CELO (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-celo:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=celo + + - name: Build and push Docker image for CELO (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-celo:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=celo \ No newline at end of file diff --git a/.github/workflows/release-celo.yml b/.github/workflows/release-celo.yml new file mode 100644 index 000000000000..27ccc18ddc9a --- /dev/null +++ b/.github/workflows/release-celo.yml @@ -0,0 +1,95 @@ +name: Release for Celo + +on: + workflow_dispatch: + release: + types: [published] + +env: + OTP_VERSION: ${{ vars.OTP_VERSION }} + ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + steps: + - uses: actions/checkout@v4 + - name: Setup repo + uses: ./.github/actions/setup-repo + id: setup + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + docker-remote-multi-platform: true + docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} + docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} + + - name: Build and push Docker image for CELO (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-celo:latest, blockscout/blockscout-celo:${{ env.RELEASE_VERSION }} + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=celo + + - name: Build and push Docker image for CELO (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-celo:${{ env.RELEASE_VERSION }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=celo + + - name: Build and push Docker image for CELO (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-celo:${{ env.RELEASE_VERSION }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=celo \ No newline at end of file From b049e7c13b9237529c97c61286b626b8faec05c0 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 3 Sep 2024 18:00:17 +0300 Subject: [PATCH 130/363] Remove workflow_dispatch trigger from CELO release workflow --- .github/workflows/release-celo.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release-celo.yml b/.github/workflows/release-celo.yml index 27ccc18ddc9a..fd503e9e4497 100644 --- a/.github/workflows/release-celo.yml +++ b/.github/workflows/release-celo.yml @@ -1,7 +1,6 @@ name: Release for Celo on: - workflow_dispatch: release: types: [published] From dcf88c0c81aa344dd3be4e51c7fcc79cc0dc5a9c Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 3 Sep 2024 20:57:29 +0300 Subject: [PATCH 131/363] Change branch for eth to production-eth --- .github/workflows/publish-docker-image-for-eth.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-docker-image-for-eth.yml b/.github/workflows/publish-docker-image-for-eth.yml index 081e187ebf2c..0f35962eeec2 100644 --- a/.github/workflows/publish-docker-image-for-eth.yml +++ b/.github/workflows/publish-docker-image-for-eth.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: push: branches: - - production-eth-experimental + - production-eth jobs: push_to_registry: name: Push Docker image to Docker Hub From acd60fb4bf912c1dda48f65c985e16bf2a3646c0 Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Wed, 4 Sep 2024 12:34:14 +0300 Subject: [PATCH 132/363] feat: Celo API updates (#10629) * fix: use atom for matching the `:chain_type` * feat: add celo epoch number to `/api/v2/stats` response * chore: display base fee in wei * feat: add token info to base fee section * fix: process review comments --- .../controllers/api/v2/stats_controller.ex | 8 ++- .../block_scout_web/views/api/v2/celo_view.ex | 68 ++++++++++++------- apps/explorer/lib/explorer/chain.ex | 6 +- .../lib/explorer/chain/celo/reader.ex | 28 +++++++- 4 files changed, 80 insertions(+), 30 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex index be905edcd06b..804974f88ebb 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex @@ -190,12 +190,18 @@ defmodule BlockScoutWeb.API.V2.StatsController do end end - "optimism" -> + :optimism -> defp add_chain_type_fields(response) do import Explorer.Counters.LastOutputRootSizeCounter, only: [fetch: 1] response |> Map.put("last_output_root_size", fetch(@api_true)) end + :celo -> + defp add_chain_type_fields(response) do + import Explorer.Chain.Celo.Reader, only: [last_block_epoch_number: 0] + response |> Map.put("celo", %{"epoch_number" => last_block_epoch_number()}) + end + _ -> defp add_chain_type_fields(response), do: response end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex index eab95a7559ca..ac22f8ae1318 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex @@ -14,7 +14,7 @@ defmodule BlockScoutWeb.API.V2.CeloView do alias Explorer.Chain.Celo.Helper, as: CeloHelper alias Explorer.Chain.Celo.{ElectionReward, EpochReward} alias Explorer.Chain.Hash - alias Explorer.Chain.{Block, Transaction} + alias Explorer.Chain.{Block, Token, Transaction, Wei} @address_params [ necessity_by_association: %{ @@ -97,20 +97,26 @@ defmodule BlockScoutWeb.API.V2.CeloView do end def render("celo_base_fee.json", %Block{} = block) do - # For the blocks, where both FeeHandler and Governance contracts aren't - # deployed, the base fee is not burnt, but refunded to transaction sender, - # so we return nil in this case. - - base_fee = Block.burnt_fees(block.transactions, block.base_fee_per_gas) - - fee_handler_base_fee_breakdown( - base_fee, - block.number - ) || - governance_base_fee_breakdown( - base_fee, - block.number - ) + block.transactions + |> Block.burnt_fees(block.base_fee_per_gas) + |> Wei.cast() + |> case do + {:ok, base_fee} -> + # For the blocks, where both FeeHandler and Governance contracts aren't + # deployed, the base fee is not burnt, but refunded to transaction sender, + # so we return nil in this case. + fee_handler_base_fee_breakdown( + base_fee, + block.number + ) || + governance_base_fee_breakdown( + base_fee, + block.number + ) + + _ -> + nil + end end def render("celo_election_rewards.json", %{ @@ -246,7 +252,7 @@ defmodule BlockScoutWeb.API.V2.CeloView do # Get the breakdown of the base fee for the case when FeeHandler is a contract # that receives the base fee. - @spec fee_handler_base_fee_breakdown(Decimal.t(), Block.block_number()) :: + @spec fee_handler_base_fee_breakdown(Wei.t(), Block.block_number()) :: %{ :recipient => %{optional(String.t()) => any()}, :amount => float(), @@ -265,13 +271,14 @@ defmodule BlockScoutWeb.API.V2.CeloView do {:ok, %{"address" => fee_beneficiary_address_hash}} <- CeloCoreContracts.get_event(:fee_handler, :fee_beneficiary_set, block_number), {:ok, %{"value" => burn_fraction_fixidity_lib}} <- - CeloCoreContracts.get_event(:fee_handler, :burn_fraction_set, block_number) do + CeloCoreContracts.get_event(:fee_handler, :burn_fraction_set, block_number), + {:ok, celo_token_address_hash} <- CeloCoreContracts.get_address(:celo_token, block_number) do burn_fraction = burn_fraction_decimal(burn_fraction_fixidity_lib) - burnt_amount = Decimal.mult(base_fee, burn_fraction) + burnt_amount = Wei.mult(base_fee, burn_fraction) burnt_percentage = Decimal.mult(burn_fraction, 100) - carbon_offsetting_amount = Decimal.sub(base_fee, burnt_amount) + carbon_offsetting_amount = Wei.sub(base_fee, burnt_amount) carbon_offsetting_percentage = Decimal.sub(100, burnt_percentage) celo_burn_address_hash_string = dead_address_hash_string() @@ -311,18 +318,25 @@ defmodule BlockScoutWeb.API.V2.CeloView do } ) + celo_token = Token.get_by_contract_address_hash(celo_token_address_hash, api?: true) + %{ recipient: fee_handler_contract_address_info, amount: base_fee, + token: + TokenView.render("token.json", %{ + token: celo_token, + contract_address_hash: celo_token.contract_address_hash + }), breakdown: [ %{ address: burn_address_info, - amount: Decimal.to_float(burnt_amount), + amount: burnt_amount, percentage: Decimal.to_float(burnt_percentage) }, %{ address: fee_beneficiary_address_info, - amount: Decimal.to_float(carbon_offsetting_amount), + amount: carbon_offsetting_amount, percentage: Decimal.to_float(carbon_offsetting_percentage) } ] @@ -337,7 +351,7 @@ defmodule BlockScoutWeb.API.V2.CeloView do # # Note that the base fee is not burnt in this case, but simply kept on the # contract balance. - @spec governance_base_fee_breakdown(Decimal.t(), Block.block_number()) :: + @spec governance_base_fee_breakdown(Wei.t(), Block.block_number()) :: %{ :recipient => %{optional(String.t()) => any()}, :amount => float(), @@ -353,7 +367,8 @@ defmodule BlockScoutWeb.API.V2.CeloView do defp governance_base_fee_breakdown(base_fee, block_number) do with {:ok, address_hash_string} when not is_nil(address_hash_string) <- CeloCoreContracts.get_address(:governance, block_number), - {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string) do + {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, celo_token_address_hash} <- CeloCoreContracts.get_address(:celo_token, block_number) do address = address_hash # todo: Querying database in the view is not a good practice. Consider @@ -370,9 +385,16 @@ defmodule BlockScoutWeb.API.V2.CeloView do address_hash ) + celo_token = Token.get_by_contract_address_hash(celo_token_address_hash, api?: true) + %{ recipient: address_with_info, amount: base_fee, + token: + TokenView.render("token.json", %{ + token: celo_token, + contract_address_hash: celo_token.contract_address_hash + }), breakdown: [] } else diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index c5f6d2d93ec8..2b40f4d879d8 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2105,11 +2105,11 @@ defmodule Explorer.Chain do {:error, :not_found} """ - @spec max_consensus_block_number() :: {:ok, Block.block_number()} | {:error, :not_found} - def max_consensus_block_number do + @spec max_consensus_block_number(Keyword.t()) :: {:ok, Block.block_number()} | {:error, :not_found} + def max_consensus_block_number(options \\ []) do Block |> where(consensus: true) - |> Repo.aggregate(:max, :number) + |> select_repo(options).aggregate(:max, :number) |> case do nil -> {:error, :not_found} number -> {:ok, number} diff --git a/apps/explorer/lib/explorer/chain/celo/reader.ex b/apps/explorer/lib/explorer/chain/celo/reader.ex index d1c2dfff923b..35a534e9e55d 100644 --- a/apps/explorer/lib/explorer/chain/celo/reader.ex +++ b/apps/explorer/lib/explorer/chain/celo/reader.ex @@ -9,11 +9,13 @@ defmodule Explorer.Chain.Celo.Reader do only: [ select_repo: 1, join_associations: 2, - default_paging_options: 0 + default_paging_options: 0, + max_consensus_block_number: 1 ] - alias Explorer.Chain.Cache.CeloCoreContracts - alias Explorer.Chain.Celo.ElectionReward + alias Explorer.Chain.Block + alias Explorer.Chain.Cache.{Blocks, CeloCoreContracts} + alias Explorer.Chain.Celo.{ElectionReward, Helper} alias Explorer.Chain.{Hash, Token, Wei} @election_reward_types ElectionReward.types() @@ -190,4 +192,24 @@ defmodule Explorer.Chain.Celo.Reader do {reward_type_atom, Map.get(token_atom_to_token, token_atom)} end) end + + @doc """ + Retrieves the epoch number of the last fetched block. + """ + @spec last_block_epoch_number(Keyword.t()) :: Block.block_number() | nil + def last_block_epoch_number(options \\ []) do + block_number = + 1 + |> Blocks.atomic_take_enough() + |> case do + [%Block{number: number}] -> {:ok, number} + nil -> max_consensus_block_number(options) + end + |> case do + {:ok, number} -> number + _ -> nil + end + + block_number && Helper.block_number_to_epoch_number(block_number) + end end From 4732c011b6b2ba9f4dad0e0fd8237ab152b78f4f Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Wed, 4 Sep 2024 13:55:36 +0400 Subject: [PATCH 133/363] chore: Extend missing balanceOf function with :unable_to_decode error (#10713) --- apps/indexer/lib/indexer/fetcher/token_balance.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/indexer/lib/indexer/fetcher/token_balance.ex b/apps/indexer/lib/indexer/fetcher/token_balance.ex index 643ed9fdc00a..1f808effc6db 100644 --- a/apps/indexer/lib/indexer/fetcher/token_balance.ex +++ b/apps/indexer/lib/indexer/fetcher/token_balance.ex @@ -181,6 +181,7 @@ defmodule Indexer.Fetcher.TokenBalance do defp handle_failed_balances(failed_token_balances) do {missing_balance_of_balances, other_failed_balances} = Enum.split_with(failed_token_balances, fn + %{error: :unable_to_decode} -> true %{error: error} when is_binary(error) -> error =~ "execution reverted" _ -> false end) From f6f703cf65c519c2de0af6385fd6012afac5f934 Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Wed, 4 Sep 2024 13:30:15 +0300 Subject: [PATCH 134/363] fix: allow disabling group votes fetcher independently of epoch block fetcher (#10673) * fix: allow disabling group votes fetcher independently of epoch block fetcher * fix: add address and group to primary key * chore: add new var to `common-blockscout.env` --- .../chain/celo/validator_group_vote.ex | 4 +-- .../runner/celo/validator_group_votes.ex | 6 +++- ...dress_and_group_address_to_primary_key.exs | 33 +++++++++++++++++++ config/runtime.exs | 7 ++-- docker-compose/envs/common-blockscout.env | 1 + 5 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 apps/explorer/priv/celo/migrations/20240830094610_add_account_address_and_group_address_to_primary_key.exs diff --git a/apps/explorer/lib/explorer/chain/celo/validator_group_vote.ex b/apps/explorer/lib/explorer/chain/celo/validator_group_vote.ex index dcb639e49f56..221b1c62c0b8 100644 --- a/apps/explorer/lib/explorer/chain/celo/validator_group_vote.ex +++ b/apps/explorer/lib/explorer/chain/celo/validator_group_vote.ex @@ -24,12 +24,14 @@ defmodule Explorer.Chain.Celo.ValidatorGroupVote do typed_schema "celo_validator_group_votes" do belongs_to(:account_address, Address, foreign_key: :account_address_hash, + primary_key: true, references: :hash, type: Hash.Address ) belongs_to(:group_address, Address, foreign_key: :group_address_hash, + primary_key: true, references: :hash, type: Hash.Address ) @@ -43,7 +45,6 @@ defmodule Explorer.Chain.Celo.ValidatorGroupVote do belongs_to(:block, Block, foreign_key: :block_hash, - primary_key: true, references: :hash, type: Hash.Full, null: false @@ -70,7 +71,6 @@ defmodule Explorer.Chain.Celo.ValidatorGroupVote do |> validate_required(@required_attrs) |> foreign_key_constraint(:account_address_hash) |> foreign_key_constraint(:group_address_hash) - |> foreign_key_constraint(:block_hash) |> foreign_key_constraint(:transaction_hash) end end diff --git a/apps/explorer/lib/explorer/chain/import/runner/celo/validator_group_votes.ex b/apps/explorer/lib/explorer/chain/import/runner/celo/validator_group_votes.ex index 618b0bae52e6..10cd12c5b30d 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/celo/validator_group_votes.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/celo/validator_group_votes.ex @@ -78,7 +78,11 @@ defmodule Explorer.Chain.Import.Runner.Celo.ValidatorGroupVotes do returning: true, timeout: timeout, timestamps: timestamps, - conflict_target: :transaction_hash, + conflict_target: [ + :transaction_hash, + :account_address_hash, + :group_address_hash + ], on_conflict: :replace_all ) diff --git a/apps/explorer/priv/celo/migrations/20240830094610_add_account_address_and_group_address_to_primary_key.exs b/apps/explorer/priv/celo/migrations/20240830094610_add_account_address_and_group_address_to_primary_key.exs new file mode 100644 index 000000000000..d03857ba7e52 --- /dev/null +++ b/apps/explorer/priv/celo/migrations/20240830094610_add_account_address_and_group_address_to_primary_key.exs @@ -0,0 +1,33 @@ +defmodule Explorer.Repo.Celo.Migrations.AddAccountAddressAndGroupAddressToPrimaryKey do + use Ecto.Migration + + def up do + execute(""" + ALTER TABLE celo_validator_group_votes + DROP CONSTRAINT celo_validator_group_votes_pkey + """) + + execute(""" + ALTER TABLE celo_validator_group_votes + ADD CONSTRAINT celo_validator_group_votes_pkey + PRIMARY KEY ( + transaction_hash, + account_address_hash, + group_address_hash + ) + """) + end + + def down do + execute(""" + ALTER TABLE celo_validator_group_votes + DROP CONSTRAINT celo_validator_group_votes_pkey + """) + + execute(""" + ALTER TABLE celo_validator_group_votes + ADD CONSTRAINT celo_validator_group_votes_pkey + PRIMARY KEY transaction_hash + """) + end +end diff --git a/config/runtime.exs b/config/runtime.exs index eb18ab001f19..f3ffebf53bae 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -1037,12 +1037,15 @@ config :indexer, Indexer.Fetcher.PolygonZkevm.TransactionBatch.Supervisor, config :indexer, Indexer.Fetcher.Celo.ValidatorGroupVotes, batch_size: ConfigHelper.parse_integer_env_var("INDEXER_CELO_VALIDATOR_GROUP_VOTES_BATCH_SIZE", 200_000) +config :indexer, Indexer.Fetcher.Celo.ValidatorGroupVotes.Supervisor, + enabled: + ConfigHelper.chain_type() == :celo and + not ConfigHelper.parse_bool_env_var("INDEXER_DISABLE_CELO_VALIDATOR_GROUP_VOTES_FETCHER") + celo_epoch_fetchers_enabled? = ConfigHelper.chain_type() == :celo and not ConfigHelper.parse_bool_env_var("INDEXER_DISABLE_CELO_EPOCH_FETCHER") -config :indexer, Indexer.Fetcher.Celo.ValidatorGroupVotes.Supervisor, enabled: celo_epoch_fetchers_enabled? - config :indexer, Indexer.Fetcher.Celo.EpochBlockOperations.Supervisor, enabled: celo_epoch_fetchers_enabled?, disabled?: not celo_epoch_fetchers_enabled? diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index df233cddedf9..bf5a2daac6b0 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -263,6 +263,7 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # CELO_CORE_CONTRACTS= # INDEXER_CELO_VALIDATOR_GROUP_VOTES_BATCH_SIZE=200000 # INDEXER_DISABLE_CELO_EPOCH_FETCHER=false +# INDEXER_DISABLE_CELO_VALIDATOR_GROUP_VOTES_FETCHER=false # INDEXER_ARBITRUM_MISSED_MESSAGES_BLOCKS_DEPTH= # INDEXER_REALTIME_FETCHER_MAX_GAP= # INDEXER_FETCHER_INIT_QUERY_LIMIT= From 40961e39de616be9057810793e1ed8f46e5ee120 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Wed, 4 Sep 2024 15:34:02 +0300 Subject: [PATCH 135/363] chore: shrink sample response for the trace in Filecoin chain type --- .../lib/ethereum_jsonrpc/filecoin.ex | 4955 +---------------- 1 file changed, 1 insertion(+), 4954 deletions(-) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/filecoin.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/filecoin.ex index c221c71f4b51..2d2e17822b9c 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/filecoin.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/filecoin.ex @@ -547,4960 +547,7 @@ defmodule EthereumJSONRPC.Filecoin do "transactionHash": "0x86ccda9dc76bd37c7201a6da1e10260bf984590efc6b221635c8dd33cc520067", "transactionPosition": 18 }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000000fce75", - "to": "0xff0000000000000000000000000000000001b5d7", - "gas": "0x2985b7c", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000279850a8382004082014082024081820d5902408fc9978c8c1861e78438a099a06de4d9b807567e3eb578fde1ddbd7c88c23ced96d89887d40e41888f6a359ba0d98f3e89d8003c62be48cb4f7a6d8132329e1e18480a9e89a39da05dbe12d48d027bdeac20d8d46c2e9889ab76a7800cffc27212d72abb73090cc21af180b27add3ee619abcc012a98145bbd466dfeaad245901a7f1106a77ee7bf9818680a028229cbb9c6c1c32b329a01fd9ca68308c92399fac66c67707d3efd760e9b5a0d6a6fad89a425c6ec2bc654e1693c3679059c9cb9543366f77db1c65728b88a98d317b17dc66e3bfe2d30c85cd1e883be09ef4fc6ab3b50a684042e30627cd01b67d964932e9ec0d8608ab66b7da8dc9fd99480e517bc58aa752fd9bddcfaa4aba5f59101d108684a40a8ff3d954d5a58c230fa03ab135a17fe691663d1eceeff68d541a69eb913f0fe3b266dcf66543ee36e374c6596525ade50dd4845347e445773a29476a841d05c79df4ddb1904aaae2473cc9bcba41de75f0072bfe0b7df94aa3231044d2d1f8ca32bb7d4707920918ad9b31854e6791623c69c5cf6bbd57a7589ad39c8cb72459b24099d210fa1b4e11351185315739e2853a81ddb02286907bd9804026f2b59ea68462a4e56263960a2a8f4c20f625f87e13d47cedb499fdf4cc034f010fc170b505540200603103d360231fe050e5d769ce47f97ecf0ee862a6225f91fb57b2c5fdddf11e96f69f68742371714ef808477c8ca21f339ca1273b0aea3c066ad68f49784cc6ec88a7dd3af0408e61e6dcffbd19b9811107d960c137c7225b0c783ccb69194d1915085781a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d6900000000000000" - }, - "result": { - "gasUsed": "0x226bdfd", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xdfe4b81a94cce7b6ca32cc497e32a8447786ceac845e366a9b2fcbcf16971a6d", - "transactionPosition": 19 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002c2afd", - "to": "0xff000000000000000000000000000000002c2c61", - "gas": "0x2ca9cf3", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000072818187081a000114cbd82a5829000182e20381e802202a0b230182f9610882b85622ee618ffa5933e6e6fc3e70253837708f7fae630a1a0037df72811a045b19341a004f65a4d82a5828000181e203922020fa592a0514dce829ad6e2bbe0fc789d094183e20304be069b5b9393182ffa6030000000000000000000000000000" - }, - "result": { - "gasUsed": "0x1df238c", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x76fae58091c030e252484105473c8950ef633dd02b4ac8892b341bf46c77d528", - "transactionPosition": 20 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002c2c61", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x2bf36a3", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x76fae58091c030e252484105473c8950ef633dd02b4ac8892b341bf46c77d528", - "transactionPosition": 20 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002c2c61", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x2ae715b", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x76fae58091c030e252484105473c8950ef633dd02b4ac8892b341bf46c77d528", - "transactionPosition": 20 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002c2c61", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x29c5bea", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a004f65a4811a045b19340000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x477778", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e203922020fa592a0514dce829ad6e2bbe0fc789d094183e20304be069b5b9393182ffa603000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x76fae58091c030e252484105473c8950ef633dd02b4ac8892b341bf46c77d528", - "transactionPosition": 20 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002c2afd", - "to": "0xff000000000000000000000000000000002c2c61", - "gas": "0x2e2d67e", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000072818187081a0001160fd82a5829000182e20381e80220ed53117c4c68300789b739922ced240b43091dcddfed253062cf17677f9775141a0037e0e5811a045b37331a004f66fcd82a5828000181e203922020c775d47a111888a1ad17dd5989a957c59e7a2e89cb7df8639a15556593cb382f0000000000000000000000000000" - }, - "result": { - "gasUsed": "0x1f29209", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xbc62a61e0be0e8f6ae09e21ad10f6d79c9a8b8ebc46f8ce076dc0dbe1d6ed4a9", - "transactionPosition": 21 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002c2c61", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x2d7702e", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xbc62a61e0be0e8f6ae09e21ad10f6d79c9a8b8ebc46f8ce076dc0dbe1d6ed4a9", - "transactionPosition": 21 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002c2c61", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x2c6aae6", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xbc62a61e0be0e8f6ae09e21ad10f6d79c9a8b8ebc46f8ce076dc0dbe1d6ed4a9", - "transactionPosition": 21 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002c2c61", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x2b49575", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a004f66fc811a045b37330000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x4769b4", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e203922020c775d47a111888a1ad17dd5989a957c59e7a2e89cb7df8639a15556593cb382f000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xbc62a61e0be0e8f6ae09e21ad10f6d79c9a8b8ebc46f8ce076dc0dbe1d6ed4a9", - "transactionPosition": 21 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001ee034", - "to": "0xff000000000000000000000000000000001ee031", - "gas": "0x1b3a043", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f385181e8182004081820d58c0a7a02e8dc005044807e4888bd0823f61ae169669e7a27a3b7e3912b8fba0eea593c30732f4480a1fa0e285867e448306b2a2ddea74ce0511ace188bd92cee9e2352a2f1b70a1903b268fd124d1e8e6f92eefbd3bc7a0fc42a477ace39972b9290963cba107a2a981b7c74c1a3dcb6be6a2b7a01e8224501b0a4371f1a3970c0664b313bfcf7be778165db0e2e4bf634da61561da28de009a0cba108ff65d334ac272e4b3ab6e8e26fc23cc09439290bef48866abadb58407af3dced632acf3a71a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d6900000000000000000000000000" - }, - "result": { - "gasUsed": "0x1696c22", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xcfeff915c7a3e06a7aafbb9f1a20ef023615aecea6cb6b004dc87ead55f63702", - "transactionPosition": 22 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002af885", - "to": "0xff000000000000000000000000000000002afa9a", - "gas": "0x466a958", - "value": "0x1a7b47481750efae", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000789821a0001928c5907808a218c1f6baaa41a2cdb879b1b7bf7cc7a382348970e0520fea435085566ff96b13118cf3c9e35bcac55bee865ff570fa7ec2b266e385a473c7e0e77b1080bb1392cd15fa24f39afd648cbf45b2b99d23aa58e9da9e75476fd4617cee6b3bd4708bf6b5afc9237e1977040f8e0bc63c5d825fbb6c3e471f073a6a96f27f9c30b98658dc8e49f6b7aa65d02aef573b613b579c462df8e45632b979c1baff7445e5645163020e17829f0aa7effd318343387715eb7a7eeed87e366620522a4dbac8e916703773123ad4a1347f8e59285eaeb6db1d236a503cbcb2c19a9ac9c6d2cb110adc7685a38df6873db056779f3a7b06cbac8650260481fdc488b9e2b75a7a3676f237768f3d45dc824ced53e6e34b8b004a457f28eef44cadb4334afea05145b1ea8103363bf1b88db266f14971d72b024f9b7f7b8ed9fa4100c388eabd162af274f019d2ef4bd90d53a903ebd5ca39e0478f6173bbd8a576648f2291f9eede03ce9c41aae4d56489652d0a74e7275f599a8aababb97676b5538ad1960cc8260a4e8a6b218ecbd54c8b5b91457ae56d3cf199e740499ddcd1eca823550817a43c1e53d4e407a7e7443f4f038e337ab1e959c101179efd603f2f9b7e40e6255be393e233ccdf601f2e423e898c286776a0712c4db8ed22e8619923849828d10f7cdddbce7e3bb2d51ecd9c3401ac3762fc44f4848f25fc2e8f22059e43ce8b9d39e628ddee9330ba22cb55da67aab985e6022e877568011d3e3eee84df1cad6cc924a426ae8c338140777f6e592c7872a85357931e586ad3f3684c884994495d20c30c5ec5f6de0244355de4b1fde1117b1068f64372ecdcda183e8215032a2d18157d59eb1aa0e09bb1e85fbcc0ab978e1860f6f457cb821a266bdfd197c53516264feb015c8562144cb4fa2c8a6918c6d5e08d3c050136f2905d373c219149dd6c37662da580148312067cbc440a9adb3ecd5df0cb01eb6ad1d4fcd7399b3fe43bcb7397abb7fe9323a71a2a290ab2b0e955979de28827c98e38fbc7c65d63a22e4f6dcd5077e182801e7821d59408cfb01bf70be7a5d063a48b51fa3c6899da9e902cb9305a68f74f4e32ecc6186130cec8daf78f4d40ec869286130ed41228373e496471376ab5646ee44b6f8ab4c4c9210557b7ec166fb4582fc8bdf2fe12a80e37cba8cabf8c7456b75a305aa0aabb6dc1e040ca088a28672bcad1a17c4b661611cae0b5d968c04df8388a06e69fb302cde54f5a1619c0275b1d7349a41561ce6476a8ba3bf3ab1b4f291dfa72cff5696f4203c733f78f8168ffbb2c97392db60ab160a6f42f1c3b8957e24104283deb670393aa20c5cac99bb54489620f16e0838fdb5b7f92515929745b58c2769d26374cca5d94cfb14e133fd3df40497a1fa9ac70a9e9a295ff659b04aaf81a5436dabde72e10af28a00a8f05cd6b3e6bc75847ca7515fd27d4b2cee97e21594d4a5b659a0504e77ccfcbeb082043f73dbc1484e5504b09c048c8a3a6c2db1278ac2205c78a48263e9ada4646dec7d27f28392667d3fb49dc101a5d1b6aade897bb92c692b956b9a83d7d2abfbdac1707a9d369cfc5260ce380f67c3ae6337e34c272ea646632f2c73465b1a13b358cdad9d4583a59cc3be1c8f2094282560aa83435074686f6abec5b4c895452ae74f92d9bbf6e9ec9728d07c397aef8611d09e75c00dfc297af2d724877127d5abca2f6a530ebc768617ad077d4712b26fba3cab83c6c74a7119a6623cb9a712eddadbd7811fd3948337dbdaa311f9891f96a324c20b56ef6d73450c5d3804dac0e51fc07fc82eddf441c9412e13efb701115362bf844ceb88793a131e136a0ca9935b7025a6682afbb8685c9b058c833dcd81e93e6dac958a31f5b942c4578ddb058ae644dddcb43fd9f785510c7a2bde7532b3f84b48e565cfb0ec1f55284ef355df662f091a401a52122823b46697eb141523b39dd9eeb73e10df2793a4e2107ee299405800df9e95340723b18e0d6c46fd7c4236ceaf34594a4c652b22145361f277fc5b3427d47e5b6c51db78e5b00050bc616c2e6efb54eced079a1a7186c080ccc3bab5989e0df45115d314a95664fff63ec61f73ec34c22033105410d1158e44456ba982984508a8c650d912c1d3fab14f7a12935ea1142eccb0748ddd33f1bcc07cd5614e24257ad9a656cfc4a8589042de2b29aa641b0e59b8fb2d83a0600a72c43d44b0da9f7df90985b27787686d14dfdfba29c895dce4ea9b827f5dea7d12d0536e36a90f98cd8e9078ab343ddc20a35dedf9e46010513eca0141bde7b2ab975abe920e201e7c55375eb7c1fe56b6677eed219a59d85c72e04ca348fae0171c2b4b33dd80e743954b80945cbc97c225e28be27d67c7425a9d0331f74919002b5cc2a120f51725bddf5bd8661f5a6c5909561f579536577473a6c8fadc7ca5b0a415d49e78441c3cc0d3b4e26d089bb515fb1a47d7a54814349b6e1cfebd137c2cfe94d3bd2ddcf7ae976a61f32638e55e0e290b6fdf379fda9428124380187eea5fc2a7ef55c7135b432d95b4916e594c3331dd28419a263202c48deb804ceb2119646cd56f1a907a9b4e723d582d1762c0c667b4ad1fe7887531540f19018896f660d57efbeece42880a36772b04b2b438adfa4b3f8c5c2474cad4d99d2e55a165ae536fd7713f3564464fcc1e193c170a0965bc4f0d1bfa0000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x70ac08", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xa714c6be95aa4ef44699b9098e264c0adead1caeffac75013c413a7a9798dcad", - "transactionPosition": 23 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002afa9a", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x4271824", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008338808821a002afa9a1a0001928c811a045b25725820ebf616e5e6a85ffef1fe235d974420b8a1c0a70d4fa60d21d2215f7e2aac05ff58207bb5ec49d8afc9b6444096fb05e348b164acb4a9305a4fa411def29c62edc61f5907808a218c1f6baaa41a2cdb879b1b7bf7cc7a382348970e0520fea435085566ff96b13118cf3c9e35bcac55bee865ff570fa7ec2b266e385a473c7e0e77b1080bb1392cd15fa24f39afd648cbf45b2b99d23aa58e9da9e75476fd4617cee6b3bd4708bf6b5afc9237e1977040f8e0bc63c5d825fbb6c3e471f073a6a96f27f9c30b98658dc8e49f6b7aa65d02aef573b613b579c462df8e45632b979c1baff7445e5645163020e17829f0aa7effd318343387715eb7a7eeed87e366620522a4dbac8e916703773123ad4a1347f8e59285eaeb6db1d236a503cbcb2c19a9ac9c6d2cb110adc7685a38df6873db056779f3a7b06cbac8650260481fdc488b9e2b75a7a3676f237768f3d45dc824ced53e6e34b8b004a457f28eef44cadb4334afea05145b1ea8103363bf1b88db266f14971d72b024f9b7f7b8ed9fa4100c388eabd162af274f019d2ef4bd90d53a903ebd5ca39e0478f6173bbd8a576648f2291f9eede03ce9c41aae4d56489652d0a74e7275f599a8aababb97676b5538ad1960cc8260a4e8a6b218ecbd54c8b5b91457ae56d3cf199e740499ddcd1eca823550817a43c1e53d4e407a7e7443f4f038e337ab1e959c101179efd603f2f9b7e40e6255be393e233ccdf601f2e423e898c286776a0712c4db8ed22e8619923849828d10f7cdddbce7e3bb2d51ecd9c3401ac3762fc44f4848f25fc2e8f22059e43ce8b9d39e628ddee9330ba22cb55da67aab985e6022e877568011d3e3eee84df1cad6cc924a426ae8c338140777f6e592c7872a85357931e586ad3f3684c884994495d20c30c5ec5f6de0244355de4b1fde1117b1068f64372ecdcda183e8215032a2d18157d59eb1aa0e09bb1e85fbcc0ab978e1860f6f457cb821a266bdfd197c53516264feb015c8562144cb4fa2c8a6918c6d5e08d3c050136f2905d373c219149dd6c37662da580148312067cbc440a9adb3ecd5df0cb01eb6ad1d4fcd7399b3fe43bcb7397abb7fe9323a71a2a290ab2b0e955979de28827c98e38fbc7c65d63a22e4f6dcd5077e182801e7821d59408cfb01bf70be7a5d063a48b51fa3c6899da9e902cb9305a68f74f4e32ecc6186130cec8daf78f4d40ec869286130ed41228373e496471376ab5646ee44b6f8ab4c4c9210557b7ec166fb4582fc8bdf2fe12a80e37cba8cabf8c7456b75a305aa0aabb6dc1e040ca088a28672bcad1a17c4b661611cae0b5d968c04df8388a06e69fb302cde54f5a1619c0275b1d7349a41561ce6476a8ba3bf3ab1b4f291dfa72cff5696f4203c733f78f8168ffbb2c97392db60ab160a6f42f1c3b8957e24104283deb670393aa20c5cac99bb54489620f16e0838fdb5b7f92515929745b58c2769d26374cca5d94cfb14e133fd3df40497a1fa9ac70a9e9a295ff659b04aaf81a5436dabde72e10af28a00a8f05cd6b3e6bc75847ca7515fd27d4b2cee97e21594d4a5b659a0504e77ccfcbeb082043f73dbc1484e5504b09c048c8a3a6c2db1278ac2205c78a48263e9ada4646dec7d27f28392667d3fb49dc101a5d1b6aade897bb92c692b956b9a83d7d2abfbdac1707a9d369cfc5260ce380f67c3ae6337e34c272ea646632f2c73465b1a13b358cdad9d4583a59cc3be1c8f2094282560aa83435074686f6abec5b4c895452ae74f92d9bbf6e9ec9728d07c397aef8611d09e75c00dfc297af2d724877127d5abca2f6a530ebc768617ad077d4712b26fba3cab83c6c74a7119a6623cb9a712eddadbd7811fd3948337dbdaa311f9891f96a324c20b56ef6d73450c5d3804dac0e51fc07fc82eddf441c9412e13efb701115362bf844ceb88793a131e136a0ca9935b7025a6682afbb8685c9b058c833dcd81e93e6dac958a31f5b942c4578ddb058ae644dddcb43fd9f785510c7a2bde7532b3f84b48e565cfb0ec1f55284ef355df662f091a401a52122823b46697eb141523b39dd9eeb73e10df2793a4e2107ee299405800df9e95340723b18e0d6c46fd7c4236ceaf34594a4c652b22145361f277fc5b3427d47e5b6c51db78e5b00050bc616c2e6efb54eced079a1a7186c080ccc3bab5989e0df45115d314a95664fff63ec61f73ec34c22033105410d1158e44456ba982984508a8c650d912c1d3fab14f7a12935ea1142eccb0748ddd33f1bcc07cd5614e24257ad9a656cfc4a8589042de2b29aa641b0e59b8fb2d83a0600a72c43d44b0da9f7df90985b27787686d14dfdfba29c895dce4ea9b827f5dea7d12d0536e36a90f98cd8e9078ab343ddc20a35dedf9e46010513eca0141bde7b2ab975abe920e201e7c55375eb7c1fe56b6677eed219a59d85c72e04ca348fae0171c2b4b33dd80e743954b80945cbc97c225e28be27d67c7425a9d0331f74919002b5cc2a120f51725bddf5bd8661f5a6c5909561f579536577473a6c8fadc7ca5b0a415d49e78441c3cc0d3b4e26d089bb515fb1a47d7a54814349b6e1cfebd137c2cfe94d3bd2ddcf7ae976a61f32638e55e0e290b6fdf379fda9428124380187eea5fc2a7ef55c7135b432d95b4916e594c3331dd28419a263202c48deb804ceb2119646cd56f1a907a9b4e723d582d1762c0c667b4ad1fe7887531540f19018896f660d57efbeece42880a36772b04b2b438adfa4b3f8c5c2474cad4d99d2e55a165ae536fd7713f3564464fcc1e193c170a0965bc4f0d1bfad82a5829000182e20381e80220301293ec00df8e1cf2d9bf338279beaaa9a9a471701f60f290da4576068acf01d82a5828000181e203922020d56877a200472eb74a6521299547efbfe80cce790d635b75516278eb1731941900000000000000000000000000" - }, - "result": { - "gasUsed": "0x2ea25d1", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xa714c6be95aa4ef44699b9098e264c0adead1caeffac75013c413a7a9798dcad", - "transactionPosition": 23 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001c17d8", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x1c5e5bc", - "value": "0x2db18102f13817", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000054400ebaf70000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x160c586", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x52aaaa21fb1cee8882aef391c310f8a26e6dc9a5d3cb968f2f384477dd6851a9", - "transactionPosition": 24 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff000000000000000000000000000000001c17eb", - "gas": "0x1b32788", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x13a470", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000128345008c8ab4014400d8af70814400e3af700000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x52aaaa21fb1cee8882aef391c310f8a26e6dc9a5d3cb968f2f384477dd6851a9", - "transactionPosition": 24 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001f3b4d", - "to": "0xff000000000000000000000000000000001ee777", - "gas": "0x199d203", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f285108182004081820d58c0962920987d861c8b39327edda8e2adeb06abdb019d3aee70b32a296b43255088b551b860b2047e2362f6a9a8912ab69ca9ba5d09d7bcbfa7d5876af95f05fae5c17c6addc4914812aeb2480cd0fc42494879621ee9917e51828638cbb37f8ac50fb1113b1867527112595aa44ec24261ace85a81af4ca83ad5f3f2cbf1968c7b0172351332ff5b9163b9347f898f329294387bcbb1c4f82a2ac478d1db646a0be71d297d2fe1969e37bc5540e90cd7978d45bd21f3e042c45afd6ee8eb2dab1a1a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d690000000000000000000000000000" - }, - "result": { - "gasUsed": "0x154c312", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x8bfc485414cfc17776059a5ea837757f4df29dde3956b025bffb9dcc9706b3ff", - "transactionPosition": 25 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce37d", - "to": "0xff000000000000000000000000000000002ce3ba", - "gas": "0x41f9a33", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000007878219656359078089b3567e087d9f038414ae554aea4607bc0558961eca169c7a8d57e5bb6408861bedd976ad9c30ac5cba0d0da3af1de68cef5b46465cc730f3ced2fbdb031d392b950ce381b7f12967fc57998aa6f668cc8c5b553fb27ce9dc831e13666d13c007a2516ebe2ab906bd22cbab92d58f874b6f98a4680aa6d8790de18a3090df8b0ae3c90adf836c7b7b3f39830623867ea9c0e007f23be47549243aa4ce36277271a70e527ed4241d8496a27925d45e6898914cf1fbd2d118751b5b6cd292adc18891ab78a9153cacda89d3019f1ca81569cc4570725c70fde9d293bea660ce216b720bf0d27bba7121f33bdb9fb450e385b2d68d890711e47e0e7c98f0e38e0a4cbbaf8137be683ec840c651f4051b00c3ed7e825a6a05fcd81b98502c8c0d280b33fef4c8fe2c0d8dc8bb3d31f96558004f6a437dcd9c4c784ec5f19f9718480de4cbd8c354687bf9c80f0056eca127a37a5a80cd2f268ad24e5e7506b627ef5989354ac09beb5e95e9e202b4c0675b6ea01843afd38ce840a0a97d3083771e956a1040fd7bb21ff1040cd3d025beb0e32ce10166acf3365941e7606b9d021df6ad162f58daa88591e69d94ae13330dafe128e8fc8098ec8011d88e40058cda8de05fc93cbf3b1e81abd599f3ba177f0908627e954fb703a027c72c0bcd4fb9085f59cd9bf2237fb7476e866cc5b9d52fa6269f912f49f7bec55000e8e9166c10f6b0a587b9075cbbb06e9129ee4cf2ad0d222bcff3d3db550ea803bd3d993feb24128f4f27081caf42c65f39bdba76215f32815ed5db1df935bad67101314cade9087e8db56416dda958e20cb1b3ddd5df3739d66d06564f1acf725706aedd6c2a9ec15ec1371690e9581a75f29ff5a09a629e45ae13e6d5d94a5bba0242a2bb4ddabda70b62a3717a942dab67d4112d8b7325aa5cf54233f8c686e9d6b275123dfd791f93db568f8cccb0bea7cf8deaee33696314f092b04b6c8ae4b26e64d3fa436bb49d87205719324c2196db84b758440ee9ebf8b38f60799c26ff8e6237d92f183d94e3213cbe5285b8a4deeb80bc450899c97a0c3383ad17bcdf337599a7461bc3cd7a7b82116d715109cf7564dedc7ff071d502452267776e485740886090a92ebbf802e37ac635966fd8ce8157b93a0fb442f73207996136049fdcd81be00bbb015918683d16427df44576b27ebef8471bbc5fd1bac62006314c1117086f3352c36a7c68abe2054f70d999155445a87431d0cc42df77a1a1d909b92e78cebe4260e86821b1015ab8072b72b255072a7b87b1afbb78f2604c783d84e4a586f248d26720f5402046a48aec8b8999fb958fcd5c1ce873a3b96c95c9508e349d0df5608404fb0a0a9e459c746eb9fb655f4a6d719d3117042c76e202cc3b1d530c8a6f313f2d160d193596244aacf5e704055004579c2a83c5963d8ab595604d10bf98e9d80681c698bf8b9bcde33df3bf3169ab754f5f1fd862adf9250aa422e1611ebac1eba3cc793662894288a724fbd791e89dcdd7a3bc74ff9aee5294be60e991748a2fa432205cf2226cb232967edb4999a3bf0643be318de1296f684b86cceb1dd56b2cdc486c8ec1a6d788568b835318fb355fdac8f9a34cd68725d7cd120372a7f636002fb17f1d10ddb0e674a7e86b98391a77d862325b5195109ebfdb3d8a1e27a7f79d0f7c567cad351826467a63ef149cf5dbfab8399a4012da12af9d4ff6598b0ae54c05775dcf85c0191d38e339e222ead9376c8ee915df379c5b1b42abe15048667290fe02ce9783cc46f10d35bd600d530292992b08ef959940dabc733c9e60fec0ccb388a427a3bf07ded414b478c91d2026df0a52cf2c25aeea9849c86480cfff3600c43174f5d4451df4962a4d9cebb9e2f11797679d4d28533baafa05c253c25a4f62ab3d0190278e8c20fd72662a023e60e0e3eab027090e07716dce1251cf730690b2bf033236d3fe537e153201455032f691e2516a02c1e0a999ceb02c5709dd672c70897838f57a4f6a854ae13c157d5b006677ca44af39133b0663ee2e2beb1fe4a68a846ce3cc2114bf80f91e187da1f0b250fddfdfde59eee0b656da275deb9490dbc7289643c91c94500771e373fdc338251f35567d0bf69a1281300e5f8f5f2df9c3ae790e5d8493c9176ae7048496383654e519118d249be0e47252aeeeec73ce3954722f4ecbfd6dd0a931e35970085692b3c1592022fa557733d6125793516ca85a7b27816862c7db7ecf74a5c01a50caa3997f023a836c410178c402c610b48b97854c8c3b2522a3c73a176c08f86a3d9bcd31c513d54107ed0e87ecbe6a8eef753d7ef748b49ce8e07f0240214d85463e7cb2cede2738ae1ae0ae628abe17f62b3e75cafa9b5065e5469a13bd04e1c7f96924d7fa6464191b9ead0479821d0114ba27f1cdbc1cb935e1fed086c8a53dc38245a373bc68cdd8f01a84d9c5fe093ff831c7ab0606d1179e52877caae79c9112cc5af723d1d6bfaa0515b8313eaa7c65daef498853daccf0345999610f6aa633d04805b14c1a5a34b9c3a75f758f3004e62464fe3f23255148ee02029002108728d91a4fb0f8af2a2c3a44f3d97c810e03a926116f125a42c9d8da8c93da016e97f3e1b50a2fb838b23eab8b279f95fcc21cedbf474b3457ac3a3d791f5d188b694285ec2d1169ef8015203e11a8f28ab520c57b37b9390cecb600000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x695660", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc181546e031e0c986a730e2567075d746fa9a148c12c16b15c36e689dba86a87", - "transactionPosition": 26 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3ba", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x3e72861", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002ce3ba196563811a045041cf5820cf92e6b2348466d49f180699709bf0afcc372ea7d8543074c0b5c724a7ecb6a7582039251fcab982839627fe199c46354a2e52a24e2f438c66c568c62466f8aca8ab59078089b3567e087d9f038414ae554aea4607bc0558961eca169c7a8d57e5bb6408861bedd976ad9c30ac5cba0d0da3af1de68cef5b46465cc730f3ced2fbdb031d392b950ce381b7f12967fc57998aa6f668cc8c5b553fb27ce9dc831e13666d13c007a2516ebe2ab906bd22cbab92d58f874b6f98a4680aa6d8790de18a3090df8b0ae3c90adf836c7b7b3f39830623867ea9c0e007f23be47549243aa4ce36277271a70e527ed4241d8496a27925d45e6898914cf1fbd2d118751b5b6cd292adc18891ab78a9153cacda89d3019f1ca81569cc4570725c70fde9d293bea660ce216b720bf0d27bba7121f33bdb9fb450e385b2d68d890711e47e0e7c98f0e38e0a4cbbaf8137be683ec840c651f4051b00c3ed7e825a6a05fcd81b98502c8c0d280b33fef4c8fe2c0d8dc8bb3d31f96558004f6a437dcd9c4c784ec5f19f9718480de4cbd8c354687bf9c80f0056eca127a37a5a80cd2f268ad24e5e7506b627ef5989354ac09beb5e95e9e202b4c0675b6ea01843afd38ce840a0a97d3083771e956a1040fd7bb21ff1040cd3d025beb0e32ce10166acf3365941e7606b9d021df6ad162f58daa88591e69d94ae13330dafe128e8fc8098ec8011d88e40058cda8de05fc93cbf3b1e81abd599f3ba177f0908627e954fb703a027c72c0bcd4fb9085f59cd9bf2237fb7476e866cc5b9d52fa6269f912f49f7bec55000e8e9166c10f6b0a587b9075cbbb06e9129ee4cf2ad0d222bcff3d3db550ea803bd3d993feb24128f4f27081caf42c65f39bdba76215f32815ed5db1df935bad67101314cade9087e8db56416dda958e20cb1b3ddd5df3739d66d06564f1acf725706aedd6c2a9ec15ec1371690e9581a75f29ff5a09a629e45ae13e6d5d94a5bba0242a2bb4ddabda70b62a3717a942dab67d4112d8b7325aa5cf54233f8c686e9d6b275123dfd791f93db568f8cccb0bea7cf8deaee33696314f092b04b6c8ae4b26e64d3fa436bb49d87205719324c2196db84b758440ee9ebf8b38f60799c26ff8e6237d92f183d94e3213cbe5285b8a4deeb80bc450899c97a0c3383ad17bcdf337599a7461bc3cd7a7b82116d715109cf7564dedc7ff071d502452267776e485740886090a92ebbf802e37ac635966fd8ce8157b93a0fb442f73207996136049fdcd81be00bbb015918683d16427df44576b27ebef8471bbc5fd1bac62006314c1117086f3352c36a7c68abe2054f70d999155445a87431d0cc42df77a1a1d909b92e78cebe4260e86821b1015ab8072b72b255072a7b87b1afbb78f2604c783d84e4a586f248d26720f5402046a48aec8b8999fb958fcd5c1ce873a3b96c95c9508e349d0df5608404fb0a0a9e459c746eb9fb655f4a6d719d3117042c76e202cc3b1d530c8a6f313f2d160d193596244aacf5e704055004579c2a83c5963d8ab595604d10bf98e9d80681c698bf8b9bcde33df3bf3169ab754f5f1fd862adf9250aa422e1611ebac1eba3cc793662894288a724fbd791e89dcdd7a3bc74ff9aee5294be60e991748a2fa432205cf2226cb232967edb4999a3bf0643be318de1296f684b86cceb1dd56b2cdc486c8ec1a6d788568b835318fb355fdac8f9a34cd68725d7cd120372a7f636002fb17f1d10ddb0e674a7e86b98391a77d862325b5195109ebfdb3d8a1e27a7f79d0f7c567cad351826467a63ef149cf5dbfab8399a4012da12af9d4ff6598b0ae54c05775dcf85c0191d38e339e222ead9376c8ee915df379c5b1b42abe15048667290fe02ce9783cc46f10d35bd600d530292992b08ef959940dabc733c9e60fec0ccb388a427a3bf07ded414b478c91d2026df0a52cf2c25aeea9849c86480cfff3600c43174f5d4451df4962a4d9cebb9e2f11797679d4d28533baafa05c253c25a4f62ab3d0190278e8c20fd72662a023e60e0e3eab027090e07716dce1251cf730690b2bf033236d3fe537e153201455032f691e2516a02c1e0a999ceb02c5709dd672c70897838f57a4f6a854ae13c157d5b006677ca44af39133b0663ee2e2beb1fe4a68a846ce3cc2114bf80f91e187da1f0b250fddfdfde59eee0b656da275deb9490dbc7289643c91c94500771e373fdc338251f35567d0bf69a1281300e5f8f5f2df9c3ae790e5d8493c9176ae7048496383654e519118d249be0e47252aeeeec73ce3954722f4ecbfd6dd0a931e35970085692b3c1592022fa557733d6125793516ca85a7b27816862c7db7ecf74a5c01a50caa3997f023a836c410178c402c610b48b97854c8c3b2522a3c73a176c08f86a3d9bcd31c513d54107ed0e87ecbe6a8eef753d7ef748b49ce8e07f0240214d85463e7cb2cede2738ae1ae0ae628abe17f62b3e75cafa9b5065e5469a13bd04e1c7f96924d7fa6464191b9ead0479821d0114ba27f1cdbc1cb935e1fed086c8a53dc38245a373bc68cdd8f01a84d9c5fe093ff831c7ab0606d1179e52877caae79c9112cc5af723d1d6bfaa0515b8313eaa7c65daef498853daccf0345999610f6aa633d04805b14c1a5a34b9c3a75f758f3004e62464fe3f23255148ee02029002108728d91a4fb0f8af2a2c3a44f3d97c810e03a926116f125a42c9d8da8c93da016e97f3e1b50a2fb838b23eab8b279f95fcc21cedbf474b3457ac3a3d791f5d188b694285ec2d1169ef8015203e11a8f28ab520c57b37b9390cecb6d82a5829000182e20381e80220da354058660436f1563af7b6a0642c182b1a6fa48602b441b5b47c19c1b0c60fd82a5828000181e203922020185214c7c8cb8fa5f08e57348acba88012ee10f72b1fa2e048d495d152598b08000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2ebb8b6", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc181546e031e0c986a730e2567075d746fa9a148c12c16b15c36e689dba86a87", - "transactionPosition": 26 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce37d", - "to": "0xff000000000000000000000000000000002ce3ba", - "gas": "0x4af0e25", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000007878219656f590780970b4083f06e6772b24fde9600f285c9f542ffb9b93d15ac32a9cfaa002ed367b4d8ee029ac689e0b2a9f797a52bbdd1b01687a9ceb8c9790e00f14c1a9463b0f2caa20f079f8b4aaedd577aa0e21dd971bd3bc56a3eae10200b2e1c2270170508c50c10a32fccc9206146f1d57109fc723b6aef3a814c10ff21d9f9f57e30fb5c88eddaf18dc525847f6ac1519e200e8bfa5521554b9e6519e998b5c41b099e076efdef54142293a4164be42f89a6c006e9ca0a718ebf402aadb270d9b48bbaa30f8cc5d9558d81f61871b4fd006879593d13363fd3ef2d0a8589603037a53d315f2cadbba24eeceee4e6a5b43df62493eec2ba6c2da36e7ef21140e9d7b9f2623466cf2af2d3dac9370b4e7df3439beac4b58b9021ad7c3e8aa5fcd8d76eb21157ee86adaa6ca9fafaae6a986de700eb4446e710eb8a07d95b29f39cff7c35fa5d39f985203e26e0a739506473a53390d4a05c2a890168ae9dde4a0cfc34e7a215e16c83148c671d7f56105ad34ce38de2454a5c6807c422b7d640e4577b718e435b0ee978fdbff427bb8a9aece830bfa5146061849d05b881c9b7212fcdf7798f773347f8b9a0d7a70a2cb83902dd84f882f7245624ba8c44b1d06f98f5159e4f51494105c0b26830321f349f0dd436d1726d00041b0a490a404a689cd9c61949cb8c1624cb83b5ef7ccbbae5fea806b2ff6d9b32c3b4e48ae0046404f2c072676a7d7922467a625948821dbc2c81b8da97d3a187b329f5e86e899fa0a200fb5cb3e083dcf0010a77a3096e3b61afa89d10c834fbd8ec58cf4beef1d1f878aa3ddfdaf747853e9fc63f9f7f2e12e84bc85e453ef19d6b4ed18958fe3405aba887287def1c8dc15777b265c0a42780acf9897858c07cc9b2b9b02b6c90fc3b77a01dd974deb58864d6e31e0ebe2574c9a9283806ea6356b71bd4262fe608740cdfc4631ec56e1c09db0bc46c89698e2eb0e55e81ef73c3c8879b25a1f362a90543c74acd4cbb35cbe9d35765405457b626d5151ee48d91ec4e03ab0429dd9feab2c461f67dfc7ec78211aec693a2815fddd400c8846d224b869565060d2f2c87b753b8df856328e65ddcd3f60638749b6943989ac46cb8e85f33228497c5d2267c75282bc7d3fc69047799cd70f359964a95667d91e13f7d30daebe8bcae1024ace8f8e551f174538b37ca05841dd4a46063b760df8e6f3630953d716c5c410a4d573152fd5ad0360d81d57a051394c739b4e64c0f90679bb3ac490541788cdfc4f6bc54cb95114f05d625530420cdaca32574e660e7f0f9862b12e4ce7efb8543cc1dd745c2a7673f526fd938ab10b82b1a9c725ed7b83573b304ee6d2c13a782368d77f770fd62aec69bf981c3db02fd32b885661ad86de36b04daa7235ca2c7a0ac6f54a43a6af26add753ca2b6991bf2b4450645fff88b7dd39e73437bde00bad496297eb4d7b8522b8c2f2d5b375f43d8b145d5f1eb5c9525f15d8f91095bc1098415496f0570ddbc02173225aeb99cea9b0a4815f7f748043799771a1439be8fbc024c7931242d48262cc08aaa3b158d7fa8a1a53658d6ff4c902b272edcac0ea9650d2956725308dd3b45924f9ac61a1333a09d1ab64b9e3e0d7722b25d972258a46640ad2fd637d28880df353921218d8dc81615f8e43547b9c9759d3c2252e3aae6f07e001483bb5b5bd8a69c0ba553292ad1d8c14af76e7ced6b19a5d2457ae64bf6dba77b17a7bdcd130fdddf49f2f290b8c667b7f2badb370c1277166fb98f700cd6dd05dd895da055a09669e3467ac5d6cfcba2872f834c1834d610436fe6dfeb22f1026a113ea69aa671af6976925cbd5dd0f99babe9b950cf89a275ef1a28c541c2f342222326b9fce88df14f5e2cc597bd120cd518c7228d6c8a8c6f42db9078b54bfbdb3d6985a5fff3878608b15d4c5edeeff9657f7ae9bb515da83879a97566a1de5ea47c8f8db7fa1bdbb0ad3e8a89806e028a8a54c61ca914dc001ffa741069ac048a0c6afdf37b308c514bdc40556ba4351b1d340cb76431035265fd4bab3cf003b2fe67f019815f28d1402e4380a89dfab1e0b90274c861b288fd381ac83f48cdc4b5218d12dc7e41c6fcd86ab860d59c960db092d456e19d0d2ccfe7233fc7baea842a410c1bfaab6fb587d6fc26880ee860b6aef390991b1b242301e28680c6d65a39d49d7c7e25a8c1411571a66010a74f87851a7314b07731bbf30c6d786ecf42709360b3fc1b12c450115f35e707b41f258073d287a602b6fd57389348b8fec9366c80350ac96b03a10b0f8ad4c09455c906b681a9a1f9405d32fd8f08fd5fa9e9ff70b2b4a0fca4cb1dd659d39e9e99c630f2d3aa056f8f3acc239497f45643e0a7b6d86e7922129429caa5d71b00a88e3aefea943d8efc66f97ac160d570d2834c8114aeb1232010ca35826faa4cf786aeda3204f74c456407ae72a09bad8eb9261510bfabe03ef8c206645f36edd78572babe7ab11f7d26cd0bbd07c868eaad8e659ff329cfa502aa61081506313f76a4f81d100fd693b37c8877c9a6b543a8ee45e826669d7326b15681f5f83af7fb13b323cc649316cbccfa93e76756718bbb0a0e11ccfd5335af216c8ea21ba41976cfd65caa7c0072bd2f2df78fe32bbfa5612057cbf2c059d946ca4f5480043ca4d79ec3f1af8d36c47b0e1cb7a01e897485b1a76c00aad500e378da9ef789b500000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x661c29", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x2388a8d8981d196c52b6b731e06991d9a502910cbaeaa5396a156b882a6f6097", - "transactionPosition": 27 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3ba", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x479cbfe", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002ce3ba19656f811a045041325820cf92e6b2348466d49f180699709bf0afcc372ea7d8543074c0b5c724a7ecb6a7582030f7dea42fec628a29ba26a0dab17df120f5f9b2740e0ca4ba1c332d86c1eea2590780970b4083f06e6772b24fde9600f285c9f542ffb9b93d15ac32a9cfaa002ed367b4d8ee029ac689e0b2a9f797a52bbdd1b01687a9ceb8c9790e00f14c1a9463b0f2caa20f079f8b4aaedd577aa0e21dd971bd3bc56a3eae10200b2e1c2270170508c50c10a32fccc9206146f1d57109fc723b6aef3a814c10ff21d9f9f57e30fb5c88eddaf18dc525847f6ac1519e200e8bfa5521554b9e6519e998b5c41b099e076efdef54142293a4164be42f89a6c006e9ca0a718ebf402aadb270d9b48bbaa30f8cc5d9558d81f61871b4fd006879593d13363fd3ef2d0a8589603037a53d315f2cadbba24eeceee4e6a5b43df62493eec2ba6c2da36e7ef21140e9d7b9f2623466cf2af2d3dac9370b4e7df3439beac4b58b9021ad7c3e8aa5fcd8d76eb21157ee86adaa6ca9fafaae6a986de700eb4446e710eb8a07d95b29f39cff7c35fa5d39f985203e26e0a739506473a53390d4a05c2a890168ae9dde4a0cfc34e7a215e16c83148c671d7f56105ad34ce38de2454a5c6807c422b7d640e4577b718e435b0ee978fdbff427bb8a9aece830bfa5146061849d05b881c9b7212fcdf7798f773347f8b9a0d7a70a2cb83902dd84f882f7245624ba8c44b1d06f98f5159e4f51494105c0b26830321f349f0dd436d1726d00041b0a490a404a689cd9c61949cb8c1624cb83b5ef7ccbbae5fea806b2ff6d9b32c3b4e48ae0046404f2c072676a7d7922467a625948821dbc2c81b8da97d3a187b329f5e86e899fa0a200fb5cb3e083dcf0010a77a3096e3b61afa89d10c834fbd8ec58cf4beef1d1f878aa3ddfdaf747853e9fc63f9f7f2e12e84bc85e453ef19d6b4ed18958fe3405aba887287def1c8dc15777b265c0a42780acf9897858c07cc9b2b9b02b6c90fc3b77a01dd974deb58864d6e31e0ebe2574c9a9283806ea6356b71bd4262fe608740cdfc4631ec56e1c09db0bc46c89698e2eb0e55e81ef73c3c8879b25a1f362a90543c74acd4cbb35cbe9d35765405457b626d5151ee48d91ec4e03ab0429dd9feab2c461f67dfc7ec78211aec693a2815fddd400c8846d224b869565060d2f2c87b753b8df856328e65ddcd3f60638749b6943989ac46cb8e85f33228497c5d2267c75282bc7d3fc69047799cd70f359964a95667d91e13f7d30daebe8bcae1024ace8f8e551f174538b37ca05841dd4a46063b760df8e6f3630953d716c5c410a4d573152fd5ad0360d81d57a051394c739b4e64c0f90679bb3ac490541788cdfc4f6bc54cb95114f05d625530420cdaca32574e660e7f0f9862b12e4ce7efb8543cc1dd745c2a7673f526fd938ab10b82b1a9c725ed7b83573b304ee6d2c13a782368d77f770fd62aec69bf981c3db02fd32b885661ad86de36b04daa7235ca2c7a0ac6f54a43a6af26add753ca2b6991bf2b4450645fff88b7dd39e73437bde00bad496297eb4d7b8522b8c2f2d5b375f43d8b145d5f1eb5c9525f15d8f91095bc1098415496f0570ddbc02173225aeb99cea9b0a4815f7f748043799771a1439be8fbc024c7931242d48262cc08aaa3b158d7fa8a1a53658d6ff4c902b272edcac0ea9650d2956725308dd3b45924f9ac61a1333a09d1ab64b9e3e0d7722b25d972258a46640ad2fd637d28880df353921218d8dc81615f8e43547b9c9759d3c2252e3aae6f07e001483bb5b5bd8a69c0ba553292ad1d8c14af76e7ced6b19a5d2457ae64bf6dba77b17a7bdcd130fdddf49f2f290b8c667b7f2badb370c1277166fb98f700cd6dd05dd895da055a09669e3467ac5d6cfcba2872f834c1834d610436fe6dfeb22f1026a113ea69aa671af6976925cbd5dd0f99babe9b950cf89a275ef1a28c541c2f342222326b9fce88df14f5e2cc597bd120cd518c7228d6c8a8c6f42db9078b54bfbdb3d6985a5fff3878608b15d4c5edeeff9657f7ae9bb515da83879a97566a1de5ea47c8f8db7fa1bdbb0ad3e8a89806e028a8a54c61ca914dc001ffa741069ac048a0c6afdf37b308c514bdc40556ba4351b1d340cb76431035265fd4bab3cf003b2fe67f019815f28d1402e4380a89dfab1e0b90274c861b288fd381ac83f48cdc4b5218d12dc7e41c6fcd86ab860d59c960db092d456e19d0d2ccfe7233fc7baea842a410c1bfaab6fb587d6fc26880ee860b6aef390991b1b242301e28680c6d65a39d49d7c7e25a8c1411571a66010a74f87851a7314b07731bbf30c6d786ecf42709360b3fc1b12c450115f35e707b41f258073d287a602b6fd57389348b8fec9366c80350ac96b03a10b0f8ad4c09455c906b681a9a1f9405d32fd8f08fd5fa9e9ff70b2b4a0fca4cb1dd659d39e9e99c630f2d3aa056f8f3acc239497f45643e0a7b6d86e7922129429caa5d71b00a88e3aefea943d8efc66f97ac160d570d2834c8114aeb1232010ca35826faa4cf786aeda3204f74c456407ae72a09bad8eb9261510bfabe03ef8c206645f36edd78572babe7ab11f7d26cd0bbd07c868eaad8e659ff329cfa502aa61081506313f76a4f81d100fd693b37c8877c9a6b543a8ee45e826669d7326b15681f5f83af7fb13b323cc649316cbccfa93e76756718bbb0a0e11ccfd5335af216c8ea21ba41976cfd65caa7c0072bd2f2df78fe32bbfa5612057cbf2c059d946ca4f5480043ca4d79ec3f1af8d36c47b0e1cb7a01e897485b1a76c00aad500e378da9ef789b5d82a5829000182e20381e802209ce6925c121712eda1a12f4df52b5a3d3105f320dc6b45f24c785776a953de4cd82a5828000181e2039220207ff6f6be1300fb4908c0daf51e3d7d0425283612424a721db42365b52ea8cc06000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x361d537", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x2388a8d8981d196c52b6b731e06991d9a502910cbaeaa5396a156b882a6f6097", - "transactionPosition": 27 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff0000000000000000000000000000000021ef69", - "to": "0xff0000000000000000000000000000000021f07d", - "gas": "0x1afc9e2", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f285068182004081820e58c0b13a7cf6ffb355576e128fb8aee41b36616a208be66d052ac70553d944dbe0f1b0acc2540132e6e73d2768ef30811b87a0fa8aafd877070dba2c069689f4ee35e6bfbaa13cbfaabf1fb4993ce394f46d869176762f568fd424489bd4f30f1393039b7e812f687b929ad1239207ac71e60dbd48891e13f11eeaa0ca96e8552f2dd050d9717c63e3a1d9e101baf351083baf7e27633b6eab89e3fbeabd50cb6e355d36fc8a22137e9687355072fefc0d1c4291d9122bb90d719155ddf81db1dd2b1a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d690000000000000000000000000000" - }, - "result": { - "gasUsed": "0x1665f4e", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x3cf0131db87f6dfaeea3932633507f9dff31e2c8f7679e0eb5205035cf910373", - "transactionPosition": 28 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff0000000000000000000000000000000021ac60", - "to": "0xff000000000000000000000000000000002174cc", - "gas": "0x180d49d", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f285078182004081820d58c08b865932f85aa47714a2504c2449ae644e1e707b02cfa87f65050ead44fffdf335551663226b76a111eb07fa6f925cc1a2a3e2e0adc244bfe617b684dfa7e7e3b3b50156599a634395500da0cb12066f3376acd9ede2debe33e4c947a7c1aa710af15c15c5af34cddf7e3eb2dba604767d0ff52d42c2f96d16bb7ca5212ed5d43383279eea49778963a1fe2ef07ffd5fa66de8142d0af0bb17aaa80803d95dff6feff0b487353f7879bf9f090949d38260a4fbf6e5557bd5714ce8d4b0410bbd1a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d690000000000000000000000000000" - }, - "result": { - "gasUsed": "0x140ce4b", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x256951a7f0727529f3eac30506f6961b9bb393ef925d1f2d92ae118855bc62aa", - "transactionPosition": 29 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000129271", - "to": "0xff00000000000000000000000000000000129273", - "gas": "0x1c8bfc9", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f2850e8182004081820d58c08f79c809bb284bc44d5dbea91400a0fa579c80d3a67a42aa097fd6d87f87ad57618a343124258c40eb83694be6b6fd1c8fbb50bc744e3bb07263fcf016c2b01cca117a25eb6fe38e8502f6d4f5eeb61df3e96e085f2fdf8aa1b67cc6b28de7b7165f3bb0a76cd3f86416410c209b10c43eea9cefd8dcf6da457e6d51e3921920cefc97215219538eab13d665987d0bf0856d7030ab5b71078c7b3ee750aabfba4f8a95711aedde2e313594761bdcf6e44332b66beeb9f956386f9c820a1eed401a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d690000000000000000000000000000" - }, - "result": { - "gasUsed": "0x17a611b", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc6547d24edca37263cea22782639104718c6ab47802a579a7c593b50c03c5897", - "transactionPosition": 30 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff0000000000000000000000000000000024071d", - "to": "0xff00000000000000000000000000000000240720", - "gas": "0x4dea67f", - "value": "0x249707e1ba2b041", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000078782197b81590780b1f9d380ba870fa437208ac15878ab89d9f34543419b3e34eb4c0ffcab1dd0992538a2d9071e62187bb0ff89a3e2af7e80975593af8b408966223d147eedca680aebdd1e5bab0e28ef8339ffb595adbaf18b2d96f245274e15cfdd799db6e3a4034db1b7497bd552b7f38cd4b215e241c9988f3640325e3e4001cc21d2cd8bfcc05f2e21f8845d235d866232c134342f99eaf59c7ebc23005f57b27254bb8d7d7648a4364ad5521203158f4a2aca6046c19beb35782af2b2f3b05e4054cc5a5982fc8035f2b2e5bad7763f27a2ff9045af7149ec036d283304f6d2f24e63438b862089e498d000357170027730c997aa9882ac929ff2054cafece481a559624985d64edbf24eba8e157e7b98d14f2bf9a39a53e88862cdc4c613ccebcfb497600591a0a9018199adc75cd6d4b3c20e910d8e20aea5c1eb09609581e8ae4ab6fdeb211c974a031d6e54ce46df51beecbf879b41106c7414eb1cc5b04fda2c51a3531c7f04709c9e22f613c3d3b7ad4f5e7e781f3b42545baa78bc2493253841bcad24670ba011f5ba63fcc93e2d067f4fd0f1b614244aa5ef3841c10b7cc1a74cc81c9782d0ce1ac66177215374e7a1aea72a75e2e2bb9da9a6192b5e83f3bf67d6d965cc6a71aa5d830712576370ab75f0b7b6fc30ffb3cf538d1f9ee8447b1612b6a9c367ed3f20311c673c413c51a5a4b17bcabf9387231a5c2114b7b53f8aee48ac87a5ad6c6423c5ee5b14c54a4aa25edc9889e8246fc3b1d57dcbec40489d316220fc885f960f9a0f1b726b170a0bb338f6025eddd39e8dda03087a123f8932ffe6f36555ef77d5fac549d71a600f443ec3ad1f148037da3e1a8174cafe06935278299e757623eb47f0135f0b75b68438c64e93676fca8fdb911d7fa4768c03a37ad1301eccf1590446ab692118005081a29cfb985c9d924f5bd657bfac0e68a82a191a5ffc09122189493329c1ef4e363e9bce42884fac89c5b1e253d6ae3ca4d306ec9e2ec762950a9752d00291c5c57417006d9aba659a30d9b2b331ad8b860adc960e7597697e7c5bbf6995629d11e3c48b7b260af7a0505eada5628158b9e64913ad94aed332e8102eddad06c33064c68eb46662ea33655673f00ed13a5d5ee4f59943c4cfb1cbef7773ab83d50e98c6098e431b0a5ecd0e1a48d8740818809c0bda28094d6cd0999cf27c3166eba8f0842e81b4fc28f3e3340b3c0a321942f6d75cdd91ed3fba60b2cb7b2d5278c2f4e7a5ee24def2a35054b8d3091d554c985a2d53301b7558373ef8ce80bca45cd25e1a5357811d816a6ebc50fe673d5273453407f058311bfb9bcdd0d7d08d58a6da2ab1bcd5916ce7b2bb5ca34d875957c53712a9b1950220e8ce327d3c8a1d308dae2830620e9dc5e1f1d385fba934ff856b4627fa732ac1ddaa43b572089da4a6bee77be2cc1326fd910edc12ec05eeea0930b31806eb6143e89e024d51e1f90829f4d0189d29c414b84e0efc6a6cb9aef21257c6652c05ee743127c77d5946e48aedcb71a8ab3bb5ad4e815d882e9d397cc6d3b39644939d3085afb44969e77ff50629a356bf1caba25d9eca119afb0db5c351018d98b6b7de8d9ccea9fdd725295f738c2c8e9522d95ea2171db0c927976321c3651b67da188e509b4c64368ba8989efff9eecbaa2dd4b3e24fb0181adae49c248f6f523a5ff98a4c2d53ecc8042a8ff6863f41174cd04958fa488513cb38afb23a50f922cf247701b63ed1bf3b76d22eb64b38f39280195c59321bca0f4988251f886e16e9d6cb28eb13b1bcb33a5defe754ad4fa7554e54b9e3d1bb419f69b8e8aae47ed6e090541503f8c5ba540fc8eecd09755f1304266ebe5add110b0c10ecd0b6cc31313fb341c4beb9ff6b6801baa267f8be3f99545966ea7d36df8d78d8a7b764e2f142e7143043ba3a3fe3ad4e1172975e7e5a16920a21ed3e173b0fd4a2d178f124829c312bb34b8a086c661db0cf539643f06f9321f0ddab3443a54041dc26fef93072b60025182b4c319ceeb27490c3fb1895be83f081812a7e442f041c803636dc7bd7af7f91772485de16a3a70a6423441af2d578060152c6978b7f5eaf265f96aa593c6b8988ef6abd7f0c211685ebd15f4691d81a9b61458123e8f3222dae3d7eec64d3d793f8bcb89ff0957ca775a89e2143167d7cb39ebd4ced7f5ff6d5777e07feb6be110c0236f316ac919c2e4fc2bbab76ea38720a16a5e5b2d4bf40a8578daf93a7b78394ca172c71be8eb9d9f7872a31e25ce6ae6905b2761cc870eac7122db2f6a2462a0f6d230c3ea41b162f17567030cef113fdcde321e3b5d33a77caf6b4429ba578f598c7a76dc604661233e1d55b99359bfc5f19d67a64b6b04d36f99502fdb41c7946ac73b44c9ec05bbdcba1f76f1ecc660226fbdc1e225854866849e918e849736f04ac6c084294a822d73059f9fe80af1b41762b2fc84e140674c1864599dacf5ed6ad074e1095366a4918b0dad7abba9b24263bbf5e995ced17f759c6978d54af02a9b9c78d057896ee884d8df1fc163ef03b38d449e7f45cc41d831d132f75032c8665ce7b04b88c28b1ea61f1ace20ff87735ceac8ebff21ce2769aefd6e462c71a55150b775178016c4367d3a0772c6074ccababa6af341859176f0899d58047d4e95913a944acdca04f14c985d11118f0e9c07da419d22a1b3b249c00c61d0016ebd48c00000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x924748", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xbac6d7be578578bc26e152bd6d6daa4a9e87f38bdf6b42eb34a1fa0436188dbf", - "transactionPosition": 31 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000240720", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x47d5eee", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000082c8808821a00240720197b818058204fb7698ad0ac99ea2b79cfa2feaa9b606fb14a18877cfabe989d204b529fa8b958201e32feaddd3941fa6bc24f9608e4aaf72e91fb8c668ab31bd76c8a22f8c57243590780b1f9d380ba870fa437208ac15878ab89d9f34543419b3e34eb4c0ffcab1dd0992538a2d9071e62187bb0ff89a3e2af7e80975593af8b408966223d147eedca680aebdd1e5bab0e28ef8339ffb595adbaf18b2d96f245274e15cfdd799db6e3a4034db1b7497bd552b7f38cd4b215e241c9988f3640325e3e4001cc21d2cd8bfcc05f2e21f8845d235d866232c134342f99eaf59c7ebc23005f57b27254bb8d7d7648a4364ad5521203158f4a2aca6046c19beb35782af2b2f3b05e4054cc5a5982fc8035f2b2e5bad7763f27a2ff9045af7149ec036d283304f6d2f24e63438b862089e498d000357170027730c997aa9882ac929ff2054cafece481a559624985d64edbf24eba8e157e7b98d14f2bf9a39a53e88862cdc4c613ccebcfb497600591a0a9018199adc75cd6d4b3c20e910d8e20aea5c1eb09609581e8ae4ab6fdeb211c974a031d6e54ce46df51beecbf879b41106c7414eb1cc5b04fda2c51a3531c7f04709c9e22f613c3d3b7ad4f5e7e781f3b42545baa78bc2493253841bcad24670ba011f5ba63fcc93e2d067f4fd0f1b614244aa5ef3841c10b7cc1a74cc81c9782d0ce1ac66177215374e7a1aea72a75e2e2bb9da9a6192b5e83f3bf67d6d965cc6a71aa5d830712576370ab75f0b7b6fc30ffb3cf538d1f9ee8447b1612b6a9c367ed3f20311c673c413c51a5a4b17bcabf9387231a5c2114b7b53f8aee48ac87a5ad6c6423c5ee5b14c54a4aa25edc9889e8246fc3b1d57dcbec40489d316220fc885f960f9a0f1b726b170a0bb338f6025eddd39e8dda03087a123f8932ffe6f36555ef77d5fac549d71a600f443ec3ad1f148037da3e1a8174cafe06935278299e757623eb47f0135f0b75b68438c64e93676fca8fdb911d7fa4768c03a37ad1301eccf1590446ab692118005081a29cfb985c9d924f5bd657bfac0e68a82a191a5ffc09122189493329c1ef4e363e9bce42884fac89c5b1e253d6ae3ca4d306ec9e2ec762950a9752d00291c5c57417006d9aba659a30d9b2b331ad8b860adc960e7597697e7c5bbf6995629d11e3c48b7b260af7a0505eada5628158b9e64913ad94aed332e8102eddad06c33064c68eb46662ea33655673f00ed13a5d5ee4f59943c4cfb1cbef7773ab83d50e98c6098e431b0a5ecd0e1a48d8740818809c0bda28094d6cd0999cf27c3166eba8f0842e81b4fc28f3e3340b3c0a321942f6d75cdd91ed3fba60b2cb7b2d5278c2f4e7a5ee24def2a35054b8d3091d554c985a2d53301b7558373ef8ce80bca45cd25e1a5357811d816a6ebc50fe673d5273453407f058311bfb9bcdd0d7d08d58a6da2ab1bcd5916ce7b2bb5ca34d875957c53712a9b1950220e8ce327d3c8a1d308dae2830620e9dc5e1f1d385fba934ff856b4627fa732ac1ddaa43b572089da4a6bee77be2cc1326fd910edc12ec05eeea0930b31806eb6143e89e024d51e1f90829f4d0189d29c414b84e0efc6a6cb9aef21257c6652c05ee743127c77d5946e48aedcb71a8ab3bb5ad4e815d882e9d397cc6d3b39644939d3085afb44969e77ff50629a356bf1caba25d9eca119afb0db5c351018d98b6b7de8d9ccea9fdd725295f738c2c8e9522d95ea2171db0c927976321c3651b67da188e509b4c64368ba8989efff9eecbaa2dd4b3e24fb0181adae49c248f6f523a5ff98a4c2d53ecc8042a8ff6863f41174cd04958fa488513cb38afb23a50f922cf247701b63ed1bf3b76d22eb64b38f39280195c59321bca0f4988251f886e16e9d6cb28eb13b1bcb33a5defe754ad4fa7554e54b9e3d1bb419f69b8e8aae47ed6e090541503f8c5ba540fc8eecd09755f1304266ebe5add110b0c10ecd0b6cc31313fb341c4beb9ff6b6801baa267f8be3f99545966ea7d36df8d78d8a7b764e2f142e7143043ba3a3fe3ad4e1172975e7e5a16920a21ed3e173b0fd4a2d178f124829c312bb34b8a086c661db0cf539643f06f9321f0ddab3443a54041dc26fef93072b60025182b4c319ceeb27490c3fb1895be83f081812a7e442f041c803636dc7bd7af7f91772485de16a3a70a6423441af2d578060152c6978b7f5eaf265f96aa593c6b8988ef6abd7f0c211685ebd15f4691d81a9b61458123e8f3222dae3d7eec64d3d793f8bcb89ff0957ca775a89e2143167d7cb39ebd4ced7f5ff6d5777e07feb6be110c0236f316ac919c2e4fc2bbab76ea38720a16a5e5b2d4bf40a8578daf93a7b78394ca172c71be8eb9d9f7872a31e25ce6ae6905b2761cc870eac7122db2f6a2462a0f6d230c3ea41b162f17567030cef113fdcde321e3b5d33a77caf6b4429ba578f598c7a76dc604661233e1d55b99359bfc5f19d67a64b6b04d36f99502fdb41c7946ac73b44c9ec05bbdcba1f76f1ecc660226fbdc1e225854866849e918e849736f04ac6c084294a822d73059f9fe80af1b41762b2fc84e140674c1864599dacf5ed6ad074e1095366a4918b0dad7abba9b24263bbf5e995ced17f759c6978d54af02a9b9c78d057896ee884d8df1fc163ef03b38d449e7f45cc41d831d132f75032c8665ce7b04b88c28b1ea61f1ace20ff87735ceac8ebff21ce2769aefd6e462c71a55150b775178016c4367d3a0772c6074ccababa6af341859176f0899d58047d4e95913a944acdca04f14c985d11118f0e9c07da419d22a1b3b249c00c61d0016ebd48cd82a5829000182e20381e8022004dd1fca6c24f41664d592050ba2076cd6039cda423fda286e09f819521aab4dd82a5828000181e203922020077e5fde35c50a9303a55009e3498a4ebedff39c42b710b730d8ec7ac7afa63e0000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2ed1ebd", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xbac6d7be578578bc26e152bd6d6daa4a9e87f38bdf6b42eb34a1fa0436188dbf", - "transactionPosition": 31 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002d0792", - "to": "0xff000000000000000000000000000000002d0798", - "gas": "0x4925b8d", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000787821962a95907808265626909a4ea50de70b5e4dfaeeb5b403aab71f3409491deb25fb3c4449f50fe2bc1a5537e4b2608e03546eaa9d29687514caf1220dcb30c1e835079e713548f6740668c61e52d683dc8b67070abd42b15a531ad8b40175096f86a2c8b2ec702e738164a93493f95163c8c21e7e52868bf0fe9c5b00f8990f3f7ceada05f43383b91396b367551323e598bbdab72e18520442cea081a512f194206860173141bde8125f3e07bb1cf4d5fd549d25a0d4c88ef18b36556d3bc244e193bdd09d2875d98ede100bede4df18c21c96fb3cd453a1f3f93a0fdef627cca54d991ca62939ab2da07ae3aedc77fc02f7adf0bc2ab8fc7a2dbdbbe43822a6614121339caa742d899a8cec7b24bbecb74223453a797fb13d2a02fa5d9e64bea767015e9b204e07f623f6a3fa48d963e1862f6b3621aace85bcf136cfefbd90d7da18acd89a80095c8c5d69e81fb87509d591346c2b64a4b00034d547ae2a2434e2581d941e66b13ee33c24e4c6855b89e714903fa8144a973bb15845b611cc4b35a31b9af98a9dc2008efd63b66010cce307a8d0e0cb4ba546696799bc982100eb2a084d8ba0d2298be4031ee9be274ac56c4a54180db55f3c7d2a08ef23d62f32b77464cff82a20d207dc7d77088d48ac3be042b89b5474e5d3e01707df11c3fdace110f092303e87dbbf1fb68525eff8e9c6e3d2c707b1607df2825f9185347c8ea30bee636482051aac9a3186db603d47dc197b7f6b6641f8f04dc677a706ef7612b0319e64fc6054c264d352df72a8d0e5215e01b5e8df18a5c57f858ee2551ed283394b8a9ba8af2f2e5e2893af0b1eac66f7392bc5c03db6dd4ca92b79a14376f4708a9e02d2754869a5fad8c2053d04c23870d894b1f544bc24c52317b96c2cb3c48819dcef816df07139213c497e0ab81161b3edf503f2b0cadfff3a50ea9dc330014d31667bcaed035df0baf2e5f61d030c0990cb0632297193fd1388b3e1e92e14dfc50a3ba223014083e1b5b0dc441895eb44fb49fe1c05d0fae11eff6345c78a0f5ec196853cd23491bce4f19d4ca991243608a435e5237a4f42e2809912c934374d56394f5f43be0ac3849a53b5469ceed1885089c11cb32249426f9783e01a233d8bb9214b2156bd17e7e2bee7bb38520aad22aee9b7c1632703135717bba0f2f96df3af123c738c90bd19d8965bb21530f87ee8e8e0d3d1b080c456f910c499416cf83efa02a2f15f1a8fb49abc46f09456a809d1cb602dd97c45c8f35b1cd44c41a030d883d30764fd55953aa8c9d01a706cffea8a758bde4b13eb25338eafced8bfd2cdfe50d008a55849dc87554ad043e4598700c1c8909f1fe87ada76c4928a51c4a1cc4a70d4ffce0b1f0ab0aa1a71c8d8f59d3b1fb23a3293b55984d2f0bc0a9fbd1b5c2aecd8c50bd298d17f3940f287d128e62e09785a742f37ea7dca45bdb4e4aeb1885f4492f87799a30600051f8246b7b51fa9c67b2862019515e8bfc96177d185c55c69650e9fa4109ca4118f54cba2d2c425b4a59b60af44bcc0d183103185d7018f4e7835311a615e4162e6988e8722c752a221a87a4821fc2b7347cd18953e62a394972f913aea13a7af23a080800aee625613c3e82a45b1b788e023e6954c2f01d1cf0e6036c9a2ccf3e62c76eaf4795ff9f2546ae0347681aada949ac906bd3a1de1e07a9a62edba95110aeefbc36681894891a4a834826ff1a83cef05e707616fc89690fff9968798fbda3a53d84ef6c8a1a14e215ba888759a872c6ce00a5574c6de5eaabf613c83cf931a259230c371f0d99ec4716b8ef8901a254b12960c073f3dbdda8f9cab5a901cd8c0ee25127a43497f48e7d137d9113e3e3e6703db8aa3ddf0259121a747e3af4eaa99bd81bc89e8f01a9ec7ffef3d16003bfa686999882c2f04945dc1a23af1ee3454e20af05849310a51820069ad852446beb0315bc621a37a20db633b68659b4efc5e9dfcf9633ecc623d691dbccea398ec18581df9172eb1111f374974f1f455833dea61076684404d22b455a0cda3dd693170362e5709df55aabc08e74bd72bc667864f7d1e43d8ae4ba7131cc95584d0b0fd50e4468be82a9ecdbd22e196d47626ddc3aab7ed44f075e6b75123877609e5ccb0f06e880927d1d7ef767abb799e76335a6731719a0c45b970919652ec1345c6aeb3cba067490778aa13114dc3ace9e9c344059e00a41138130670c8aaa8e9caeb84bfd93ad31f17c8f8adf749f9952dc66992720ed9a7c418b7997c6b7333d17d625156c3401379625a57b221291f34b70d860cf19f7ab19fd6f368e5f8a5b6ae9cde87e77036e3a3419868e9da66c4586a15124dfb129a843552fb4ee6b547a330860e4853c1e6756166583bad1cbc0f1237b2cb83b3360b0e4cc99f573fa80b8ff45553e90ebf37e81d7e5e607e0588938e7688c35c074b450f658b799876b7e747f916e4b34e5c528ead2f62bd8b5fed53a3109018120998b9ed887ecff9f3b4f9bafb07aad375e2932572cc4dbfc74eb914a9312da02e733d82a0ee714c267539a6b098d2b4b3d5c72a78b94cdab5bfc68c211d416527daf7ed999906bb2ba6d6a12f721ac588d2ef19aa32ff39a3911e16934a67666bca132a0e2cef9145053becfa4cb0998ec5a101d15276d79029516d3091078d61fc8f4ac3a6329bf79474af2680c599507a9682f2416968e14b2d39b00000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x97deb7", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x1c497ac8ca436d3bdf10d78a0b639383046a81cbe079c6af5939c4e235a8cfa0", - "transactionPosition": 32 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002d0798", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x42b55c1", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002d07981962a9811a045b28e258205e3d639571a49f742b43f2f1c00218f4564577b58a3472c5e3d98a8e0d7838ec582019705c311b3d19f529f3c2161e7b3a08ffba8b20e0a434e7dcf4c04ea43f79345907808265626909a4ea50de70b5e4dfaeeb5b403aab71f3409491deb25fb3c4449f50fe2bc1a5537e4b2608e03546eaa9d29687514caf1220dcb30c1e835079e713548f6740668c61e52d683dc8b67070abd42b15a531ad8b40175096f86a2c8b2ec702e738164a93493f95163c8c21e7e52868bf0fe9c5b00f8990f3f7ceada05f43383b91396b367551323e598bbdab72e18520442cea081a512f194206860173141bde8125f3e07bb1cf4d5fd549d25a0d4c88ef18b36556d3bc244e193bdd09d2875d98ede100bede4df18c21c96fb3cd453a1f3f93a0fdef627cca54d991ca62939ab2da07ae3aedc77fc02f7adf0bc2ab8fc7a2dbdbbe43822a6614121339caa742d899a8cec7b24bbecb74223453a797fb13d2a02fa5d9e64bea767015e9b204e07f623f6a3fa48d963e1862f6b3621aace85bcf136cfefbd90d7da18acd89a80095c8c5d69e81fb87509d591346c2b64a4b00034d547ae2a2434e2581d941e66b13ee33c24e4c6855b89e714903fa8144a973bb15845b611cc4b35a31b9af98a9dc2008efd63b66010cce307a8d0e0cb4ba546696799bc982100eb2a084d8ba0d2298be4031ee9be274ac56c4a54180db55f3c7d2a08ef23d62f32b77464cff82a20d207dc7d77088d48ac3be042b89b5474e5d3e01707df11c3fdace110f092303e87dbbf1fb68525eff8e9c6e3d2c707b1607df2825f9185347c8ea30bee636482051aac9a3186db603d47dc197b7f6b6641f8f04dc677a706ef7612b0319e64fc6054c264d352df72a8d0e5215e01b5e8df18a5c57f858ee2551ed283394b8a9ba8af2f2e5e2893af0b1eac66f7392bc5c03db6dd4ca92b79a14376f4708a9e02d2754869a5fad8c2053d04c23870d894b1f544bc24c52317b96c2cb3c48819dcef816df07139213c497e0ab81161b3edf503f2b0cadfff3a50ea9dc330014d31667bcaed035df0baf2e5f61d030c0990cb0632297193fd1388b3e1e92e14dfc50a3ba223014083e1b5b0dc441895eb44fb49fe1c05d0fae11eff6345c78a0f5ec196853cd23491bce4f19d4ca991243608a435e5237a4f42e2809912c934374d56394f5f43be0ac3849a53b5469ceed1885089c11cb32249426f9783e01a233d8bb9214b2156bd17e7e2bee7bb38520aad22aee9b7c1632703135717bba0f2f96df3af123c738c90bd19d8965bb21530f87ee8e8e0d3d1b080c456f910c499416cf83efa02a2f15f1a8fb49abc46f09456a809d1cb602dd97c45c8f35b1cd44c41a030d883d30764fd55953aa8c9d01a706cffea8a758bde4b13eb25338eafced8bfd2cdfe50d008a55849dc87554ad043e4598700c1c8909f1fe87ada76c4928a51c4a1cc4a70d4ffce0b1f0ab0aa1a71c8d8f59d3b1fb23a3293b55984d2f0bc0a9fbd1b5c2aecd8c50bd298d17f3940f287d128e62e09785a742f37ea7dca45bdb4e4aeb1885f4492f87799a30600051f8246b7b51fa9c67b2862019515e8bfc96177d185c55c69650e9fa4109ca4118f54cba2d2c425b4a59b60af44bcc0d183103185d7018f4e7835311a615e4162e6988e8722c752a221a87a4821fc2b7347cd18953e62a394972f913aea13a7af23a080800aee625613c3e82a45b1b788e023e6954c2f01d1cf0e6036c9a2ccf3e62c76eaf4795ff9f2546ae0347681aada949ac906bd3a1de1e07a9a62edba95110aeefbc36681894891a4a834826ff1a83cef05e707616fc89690fff9968798fbda3a53d84ef6c8a1a14e215ba888759a872c6ce00a5574c6de5eaabf613c83cf931a259230c371f0d99ec4716b8ef8901a254b12960c073f3dbdda8f9cab5a901cd8c0ee25127a43497f48e7d137d9113e3e3e6703db8aa3ddf0259121a747e3af4eaa99bd81bc89e8f01a9ec7ffef3d16003bfa686999882c2f04945dc1a23af1ee3454e20af05849310a51820069ad852446beb0315bc621a37a20db633b68659b4efc5e9dfcf9633ecc623d691dbccea398ec18581df9172eb1111f374974f1f455833dea61076684404d22b455a0cda3dd693170362e5709df55aabc08e74bd72bc667864f7d1e43d8ae4ba7131cc95584d0b0fd50e4468be82a9ecdbd22e196d47626ddc3aab7ed44f075e6b75123877609e5ccb0f06e880927d1d7ef767abb799e76335a6731719a0c45b970919652ec1345c6aeb3cba067490778aa13114dc3ace9e9c344059e00a41138130670c8aaa8e9caeb84bfd93ad31f17c8f8adf749f9952dc66992720ed9a7c418b7997c6b7333d17d625156c3401379625a57b221291f34b70d860cf19f7ab19fd6f368e5f8a5b6ae9cde87e77036e3a3419868e9da66c4586a15124dfb129a843552fb4ee6b547a330860e4853c1e6756166583bad1cbc0f1237b2cb83b3360b0e4cc99f573fa80b8ff45553e90ebf37e81d7e5e607e0588938e7688c35c074b450f658b799876b7e747f916e4b34e5c528ead2f62bd8b5fed53a3109018120998b9ed887ecff9f3b4f9bafb07aad375e2932572cc4dbfc74eb914a9312da02e733d82a0ee714c267539a6b098d2b4b3d5c72a78b94cdab5bfc68c211d416527daf7ed999906bb2ba6d6a12f721ac588d2ef19aa32ff39a3911e16934a67666bca132a0e2cef9145053becfa4cb0998ec5a101d15276d79029516d3091078d61fc8f4ac3a6329bf79474af2680c599507a9682f2416968e14b2d39bd82a5829000182e20381e802204c7ab7b39b702b0f85ccccf1ab3be3cecfbcca6270f318e87139e54034536273d82a5828000181e2039220203e1051280d09281ef100474edd98804234de5c26e1f9b4f2fe44fc6b0f764b04000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2f02cf2", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x1c497ac8ca436d3bdf10d78a0b639383046a81cbe079c6af5939c4e235a8cfa0", - "transactionPosition": 32 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002d0792", - "to": "0xff000000000000000000000000000000002d0798", - "gas": "0x526a0ff", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000787821962ba590780ad996281f9ca4a29d9500e079f0250a570058f543c381fd8ff5207dec9eb29a21cf732e00a9eb5fc8ce8b92c68151f3ea5a8dcee1ce14acbc24ae6bf934d64e15815bc9f77f315e05c00432ebbadd65e756658ac51f723ab9fbc7f18d4ddef6b17d65d1d29f5c20bf130c8961f470eae522d4d7d4dc28fd743b26f887c9a9839228f2aa8617759e82bbf05b981dd0c2b8445502cb8b61250cb5b8d01c79b033712bd91da570437900ca7f3162e50b1761db4a01a33a1772e8e7650754c3f6a3ba979537cdf00b7754517d53fc5e18d882ae7117ba605ccf1d721e3ea5140a73690ee33308bf5a4fe2cd26c63ab034bfeb90f7df81b484f729a56493faf1040f97c2c85ec1020fd161cf9e0c8c1c065bfe31807b8784d5fd0f68c698f91229f700987a0ce6a73bd01fc4d460a72cfdd968493b612fa80e40a57b149e549d1efd97db9c8d1536af9b1e70fba2d395c7d50a514b2278efb333df538fe508f53d4cc292d2af34a41caf2eaa162222154d676730b10b10f9d118a064b8315418de4dfaaa8676c0d845e271c4f6d2f0a39db3f400434354a85d5fc8bc2bf7809da7623eef18779dfafa0e608c00871be55b43891059115ebbaddecb8b8d2862999e8b8803111718f8a23effe8ea2d666bed91ee98a3c36d6b89c9c90faf1576703b1e90b9b4164c1ab5c4daee51b26f1e556b4c2fac68125c293a77808e780c08bcfff3939ebd5eec5effa30f49d5860ca1bc890f610404430af070a43862d00ffad5de51d8701138d0cc46b856104fd792a08ba470d441992e282877b7ccf31844480ae2b679357e12c96b95620b64bcf38b27944f9ee4bf8768c9966973d3f40f6575e45a2dec3509de997e50445251a16608f299e2d8b3e71618942a15fc35ea5eb48da6a6600f1fffbf7e5b953b27f857e9773ca7ffc9dbc14ec275e60471c2bd60d07b74585bc5a34b93f36658600b57bcd4901d436374891e76826790619d555ca2dd736aedc7150f3f237345e77508e8dc3dc4310b36cad0af9e65a7a105c4b688eeca266d381d70f589348de350d0f19e7560a321282e17f954a6196322945b60efc03e30afef8d77d3fc1dd2b37f0d412d070edeed492eceabc80e650839f7b0e35ca378c689644dcc4245e08fc2fb1b6005fcce45165107ef7595ac4238af11527076d10e0fe0d0f09e7cacf37a60d9f7ed0259573911c85967b143e472b1079e14ce53f8bc32c7379a667792baca4475d11ffca642f2d780b1d14fef9e9334679d2a12e88b20870a1b7c6320ea9825b4e43cb92c048d3e2380f28232eaceac0872bc1f7a105fa8791d91832003b25e6cc7a9c7b4aeb3110a2a5ee2c973eb5a80b91a73fa6f229913b4105fba4130b66fee5180beca7baa641a2c4e5c2e6cf634bc48d90ac29df2255b199ed35668aaf0da3623882bc8205f2ca636ca57d421362ecb5ef30116a0116cd11ed69d8370738c0189ffdc4643ccda19121bd491092f5ae8a4fc41011c632a77ccd63ce37ce06ea0b6f1fe85e8708752916d3e4452933112c24c0cfdfd277d6f8380faba68b7895890a935554aa91a5b36e1530c9cf70dcece8ae88e0c5a6a57393b525df9776e959c1e2c3dde4a29bf9fb75fd97c09aeffbe6db0470e4a2643e67a3b853a8b316a486746132475b086318fb4544aa997ea1b029277e735e1b5594c143811c2b516042ac4dc03ca4fbe35b8ebaa09a29077e3781855a0c8d2145fc8265ace6acd485b54d45637233c4b802c20909601743f4ec91a511dd1c9a40151c1cee99ace0e96fc9899a95167bf257a296870409f20db7cf21d196c17d3b129a77ac87501ed12703bd1f3099f7c1a13522148184103faeac28ab678d0f0a147ecf4df1f500be9247273f7509964b9030579626584f64f121fa58c2553f3f9b4ce40f222fd6f0e6ce54474094db8dedd92f9fd120ab7e9ac7cd8b5c54380cecbf1aaed48e2168c1d6103c663abbadc136128e5ea5d92a9f20b9829c623d416a7bf3fafefacb698389f86beb29622dcca8a1196f72bfd67519413bbd81d5301435d8a591acbb2ad327ecec80ddda5c4a5ab366565db6b8c5fe50e2149a23d54c3969b397ca76981b100d57242378214addd615783e11d4be7c9581bdedafe3ea2d75960bb1f45337e4a8468ad1392e72b77bb73f9f72878441834c67488d6d094731df676f4f51ec0deed3c2d0669a02accd31a7bedd6fa9832cc62038aaf19253b18e9fbdacd86b15cfef36e9a6d23de4ff82b8ef728903b755295073b6c082b64793278b973a0c3059ace8e6e374fd0a4c0c81e8c40acfb96043d56e361c1c51542d0eb119cb806c19291c07e70e5ec730c251c32dfde23d16db87e8abc640fe068234cd9cc8327c68102028218fa8e804694dce870992a7a0b93763c76da0dc8973872c1bec2eacf36350d6fc6ce3e83380539c5c28ff24eb52827157e585468cc0a89a934dbb6b15c5bc889979f6ebddc9fb7e1fc68d5ddeacf15de8fa67f741acb10f452e47d04592810872d8d48ca85c4aea240fe825d6ccaec9faac8ba8a8b91d60a645e0656da4c0dc0cde1bcd1218c301e16881f782d91a57aa5e063c03762184110973924c9ecf4ce91c56691500f9251d3e528dd75bdde769d4f8e04aa7330362cf1bf2f1029ef25f8eb27059fb9d30d438d77cf63f364baf4744d243acb5966b79c5c35f74e8695c5b58469d00000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x97d42b", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x8ade285038462b2ece31e2ee0ff1af1448e9cf8e07520f04618b5b26436a73af", - "transactionPosition": 33 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002d0798", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x4bfa5bf", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002d07981962ba811a045b2bd658200614498463ce77b23bbf18bfd324b0fd4d5b102634806020ccff3f8c78f955b158208eb133c33fa726942d694d350b629cf534788e290162780485829b4a70160907590780ad996281f9ca4a29d9500e079f0250a570058f543c381fd8ff5207dec9eb29a21cf732e00a9eb5fc8ce8b92c68151f3ea5a8dcee1ce14acbc24ae6bf934d64e15815bc9f77f315e05c00432ebbadd65e756658ac51f723ab9fbc7f18d4ddef6b17d65d1d29f5c20bf130c8961f470eae522d4d7d4dc28fd743b26f887c9a9839228f2aa8617759e82bbf05b981dd0c2b8445502cb8b61250cb5b8d01c79b033712bd91da570437900ca7f3162e50b1761db4a01a33a1772e8e7650754c3f6a3ba979537cdf00b7754517d53fc5e18d882ae7117ba605ccf1d721e3ea5140a73690ee33308bf5a4fe2cd26c63ab034bfeb90f7df81b484f729a56493faf1040f97c2c85ec1020fd161cf9e0c8c1c065bfe31807b8784d5fd0f68c698f91229f700987a0ce6a73bd01fc4d460a72cfdd968493b612fa80e40a57b149e549d1efd97db9c8d1536af9b1e70fba2d395c7d50a514b2278efb333df538fe508f53d4cc292d2af34a41caf2eaa162222154d676730b10b10f9d118a064b8315418de4dfaaa8676c0d845e271c4f6d2f0a39db3f400434354a85d5fc8bc2bf7809da7623eef18779dfafa0e608c00871be55b43891059115ebbaddecb8b8d2862999e8b8803111718f8a23effe8ea2d666bed91ee98a3c36d6b89c9c90faf1576703b1e90b9b4164c1ab5c4daee51b26f1e556b4c2fac68125c293a77808e780c08bcfff3939ebd5eec5effa30f49d5860ca1bc890f610404430af070a43862d00ffad5de51d8701138d0cc46b856104fd792a08ba470d441992e282877b7ccf31844480ae2b679357e12c96b95620b64bcf38b27944f9ee4bf8768c9966973d3f40f6575e45a2dec3509de997e50445251a16608f299e2d8b3e71618942a15fc35ea5eb48da6a6600f1fffbf7e5b953b27f857e9773ca7ffc9dbc14ec275e60471c2bd60d07b74585bc5a34b93f36658600b57bcd4901d436374891e76826790619d555ca2dd736aedc7150f3f237345e77508e8dc3dc4310b36cad0af9e65a7a105c4b688eeca266d381d70f589348de350d0f19e7560a321282e17f954a6196322945b60efc03e30afef8d77d3fc1dd2b37f0d412d070edeed492eceabc80e650839f7b0e35ca378c689644dcc4245e08fc2fb1b6005fcce45165107ef7595ac4238af11527076d10e0fe0d0f09e7cacf37a60d9f7ed0259573911c85967b143e472b1079e14ce53f8bc32c7379a667792baca4475d11ffca642f2d780b1d14fef9e9334679d2a12e88b20870a1b7c6320ea9825b4e43cb92c048d3e2380f28232eaceac0872bc1f7a105fa8791d91832003b25e6cc7a9c7b4aeb3110a2a5ee2c973eb5a80b91a73fa6f229913b4105fba4130b66fee5180beca7baa641a2c4e5c2e6cf634bc48d90ac29df2255b199ed35668aaf0da3623882bc8205f2ca636ca57d421362ecb5ef30116a0116cd11ed69d8370738c0189ffdc4643ccda19121bd491092f5ae8a4fc41011c632a77ccd63ce37ce06ea0b6f1fe85e8708752916d3e4452933112c24c0cfdfd277d6f8380faba68b7895890a935554aa91a5b36e1530c9cf70dcece8ae88e0c5a6a57393b525df9776e959c1e2c3dde4a29bf9fb75fd97c09aeffbe6db0470e4a2643e67a3b853a8b316a486746132475b086318fb4544aa997ea1b029277e735e1b5594c143811c2b516042ac4dc03ca4fbe35b8ebaa09a29077e3781855a0c8d2145fc8265ace6acd485b54d45637233c4b802c20909601743f4ec91a511dd1c9a40151c1cee99ace0e96fc9899a95167bf257a296870409f20db7cf21d196c17d3b129a77ac87501ed12703bd1f3099f7c1a13522148184103faeac28ab678d0f0a147ecf4df1f500be9247273f7509964b9030579626584f64f121fa58c2553f3f9b4ce40f222fd6f0e6ce54474094db8dedd92f9fd120ab7e9ac7cd8b5c54380cecbf1aaed48e2168c1d6103c663abbadc136128e5ea5d92a9f20b9829c623d416a7bf3fafefacb698389f86beb29622dcca8a1196f72bfd67519413bbd81d5301435d8a591acbb2ad327ecec80ddda5c4a5ab366565db6b8c5fe50e2149a23d54c3969b397ca76981b100d57242378214addd615783e11d4be7c9581bdedafe3ea2d75960bb1f45337e4a8468ad1392e72b77bb73f9f72878441834c67488d6d094731df676f4f51ec0deed3c2d0669a02accd31a7bedd6fa9832cc62038aaf19253b18e9fbdacd86b15cfef36e9a6d23de4ff82b8ef728903b755295073b6c082b64793278b973a0c3059ace8e6e374fd0a4c0c81e8c40acfb96043d56e361c1c51542d0eb119cb806c19291c07e70e5ec730c251c32dfde23d16db87e8abc640fe068234cd9cc8327c68102028218fa8e804694dce870992a7a0b93763c76da0dc8973872c1bec2eacf36350d6fc6ce3e83380539c5c28ff24eb52827157e585468cc0a89a934dbb6b15c5bc889979f6ebddc9fb7e1fc68d5ddeacf15de8fa67f741acb10f452e47d04592810872d8d48ca85c4aea240fe825d6ccaec9faac8ba8a8b91d60a645e0656da4c0dc0cde1bcd1218c301e16881f782d91a57aa5e063c03762184110973924c9ecf4ce91c56691500f9251d3e528dd75bdde769d4f8e04aa7330362cf1bf2f1029ef25f8eb27059fb9d30d438d77cf63f364baf4744d243acb5966b79c5c35f74e8695c5b58469dd82a5829000182e20381e80220de75946bac8d0de4cc507110766a5f17cc19b30c8d0b0a55cf1b2b9afa65c939d82a5828000181e203922020d3ed4d594376bea8a01dd6414cca63e8f5165f21ae5d3ce86714ccca0f03f504000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x3662a3c", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x8ade285038462b2ece31e2ee0ff1af1448e9cf8e07520f04618b5b26436a73af", - "transactionPosition": 33 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce375", - "to": "0xff000000000000000000000000000000002ce3b6", - "gas": "0x32202fd", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007081818708195c92d82a5829000182e20381e802209d8f62a071341cd01d80033faa7c9ef07f7e1d8fbe534b0f811772b3fda0b23f1a0037e10b811a0454b28f1a0047eb4ad82a5828000181e2039220203c71b16224218e29bc29672e8db69ba0c012113a8b5d8cf2220c261f2a0b4a3d00000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x222e801", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x0ab1e62d5a67bd84621c1419f136c59115731ca47ba1da49e589760d38c95b84", - "transactionPosition": 34 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3b6", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x3169cd6", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x0ab1e62d5a67bd84621c1419f136c59115731ca47ba1da49e589760d38c95b84", - "transactionPosition": 34 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3b6", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x305d78e", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x0ab1e62d5a67bd84621c1419f136c59115731ca47ba1da49e589760d38c95b84", - "transactionPosition": 34 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3b6", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x2f3c21d", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a0047eb4a811a0454b28f0000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x489dcf", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e2039220203c71b16224218e29bc29672e8db69ba0c012113a8b5d8cf2220c261f2a0b4a3d000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x0ab1e62d5a67bd84621c1419f136c59115731ca47ba1da49e589760d38c95b84", - "transactionPosition": 34 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce375", - "to": "0xff000000000000000000000000000000002ce3b6", - "gas": "0x4abaf3a", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000078782195b555907809884d69eeaf1026b549be04d95a7ba703a48494ebdf41d17bc31dab9d7c1a4eb9b8cb0b88cd2e55617338b8c5a58d8feb896df6dfbac66c6fbc56fcc0f17a40fb03339bdbeaa70f0c1de65480e3ddddf1ae71d64dedfd200c263962df45b20761265f747786fac48c4e890695c371113ec4175b2234fc81296ac9116c9601332b99d0206321e58b16bb0d7a69e49a26a96ee1f0b811031d79fd51dd6d0e4498cc7f65e385df4287d72ed0a4a4dbe019a545abb5ebb5f170058a18293f41ed1228286e4110fdf37e6e04aaa156bcff40c7038ed380fd8eaddc864faf7813ea3349785566d1f911b529990df437c2a4f929622ce6571923fb7087485996562785a046a44d48847dd949522a274d3fbef4bf000b9c9979d64df5d77c0efa6c5e4680e457e02379f7d9da2a35de7dce57aecdbc696d4f2cc02d802117a371bd456af945be6d3e9e2146b25c30b7da50ce4dea9055fd1c429ed8ff3b6a7d14f16fe20d8f9ec8802abd3b8f61083c1f05cf3c31369d29938ab21e1a0aefc1f5f2eef4c8ccf3cd9d89a930697c9c9e0042ac63288dc70b9bd5d86e36fee93d5e2eef36a89e3db13cd6be53c37649a0e287ad71caaa0cd08dc46b5c93952d6a9540408017688facad9f4e7c94a24f80a7e8abe7352222a51a82c80a5cf3b0d11f1b071d20b5c67f2c120b8a7f722abc21fab82893fbc206501e4a5b8fd3e881ab39b89ec931bbf3dfc2f9ffa139c9a518552ffa4b9547d88a53cc7947f6c8145c33ae6feb9bfa3d7ed81a7df09827e16033ef88d6990fa158a56750d1f554de9786bb2fa83fb593f11b7747633ac9978c6991256e5fb0ca9a7a70b26fad4d27baa338e8eb3ef2cc3321361a7b44b84b5a5093dc6a04883c5843d2125797cf4be06297cf765eab5b11a2d2c5f3ade90a787ea41b236e1c4291bd547de7c16ed4046576ba515ba1786d16a9d7e55ba802c43702e1a7f4eb842b4a0a96772ed3ccf9a8d53283d5f2a31fde784532cfe8cff41802a72a3255f6998ab70c87afe02271b5ca1ac137350f68636af6a540f0bfaccf2074b0e3b562d7aa01af153d118c2375f053e916152723a1dc3bbc16c0d8fcf9cd2e494f71540f0610e12a6f871a3848b4b96fb73d259e4108ce519dc0fb10ebf6a83aa9fb407a9578cb12ae2eebc43362358255c093c66dfc22d6a2340d292b41a6a303c116579537134ee370eeec1ba522f131d702130b5f3a49defa3941176ad0789abeee3becc738ecea0140ef69f574cf0bfa214a9a4a9bae8fc78831207cd8784c917b967b502ab4a36fa814aa55e50915ce6adf77fba178c69d94ccd5b949a7af66e74de04232563e2fdef2338b83cad860e8b29259888c35159f8e64bfa34ddd87b32450c04770f20e6db09c137fb9016382987673cd00c284cffc207b47c83f6d6e290c287d4a1567d94a012043c4a51d3403467b4a21a7c6276f7239dde0966df4c194be5951b3c2f99480ab17f11463d9625bf27656d0b8285d3dadbdd73ecddaae62c825f621bdafcc7af4370d13be788e472b2975d7d55689341f9baa6e149e46b6826ca829a349172faa749f97f3d6ecbb7af40de6b28129aed0ff96febce04bb4ee7be532bda99fbede436a027b1c665bfa7675607fb57a5372ad9152ce057aa429ddf38fa3c9bdd8fd6206c49ffa21fe8f295eba5f54ab9fa4d609075877e8502fca77deaacc884564262bb091c2673a1ec8799887adcfb4e704127113eafa4c3d9ff3e65999049c92d2807c3799be3393ab73d781f5b8e7c20ec0c06428765120f54fd38e51b4d1e1c20db2e34f516ff69206330cb7acbca9eacb1ca8150b1cdc5389e70ffdc7d51957acef211a7eaa79fd9f77386681dc5e38f49dfc57f3825574b15b15656fb0945c7a681fa170a9710061fd7caf7abaa47cae606ac96cb7f1fbe0068c2e8f921be971d95596059a93fb8deeba5d4a62cb47e8dc1dce320e84038526c7c060e9d417f76f0f73bcb76f75c645230e5a90700c828274e2014c7518d2e1ddd46aa16c747153524b5e96cdebcb298c811e54753b3f43334537d058ae0dd56e20305133cfa46a5cc7006fbbea66a8b984cc8544ea5a76f0c2ed9791bd26ea64647d4eb00661f3e95693b61f2bc7ff947076ede2bb5091c147ab4909cb5621327875ba61e68975ee5e03d45e8ddbaae869db24ff4f70adda4dbe7172e4b36c6964a0ace19a86e08fb21568608cd852cab0449596ce4b9e49db0cd51a50f2b1ca224b9c96b8c1cd106aeb48c98ef00715f28fada0f7ba49177a3d335b7d009cbd88d6efefa71054423c1ac2c035ccea176b1042ee93168ca564503be614e44cabe8e122f183ee2cd6f49ad5413a4baa3213bc12c22518c6e87dcc93894b68ae158744f9f8e6d8e85a2bd79d8ffec811994db429726802997f02547c9adc375c68b458b373e9b8f78a7929fe22894ac92778dbc2efca07d79ff57b299a85fe25de4a6d01408bfe07dfef673cee97649565b439306e36bafc9baa89916b7ddb6dee4fc1912aae243fa0aef0c2b63e1850f22049737a5f8d2dfd273da4036dcfb1e9bcca5afa48401ac3823b00b4e5b16793a859abdf9da123b1ede0a42cb2c9005550a95039882814e58f6baa6311999faab45021217ab8b95e78306b6a172ec0c8ede1d7ce86879ff48851d8f8b03d000d04532155db191b8368833e7917bcb257ffcf64f821b00000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x699ac9", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x5391369997f0d1335d275d9f74b26e1b6633b7949375e065d75ecf3e9ee1b478", - "transactionPosition": 35 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3b6", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x472f623", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002ce3b6195b55811a0454b2585820ce4b423b5779ab9911bd426632a925b6a83466ce6e076590b106211eaffebb5a582047a51e4e53644247c4ad14e95dcf51070bc8748691de862db56543447a9993eb5907809884d69eeaf1026b549be04d95a7ba703a48494ebdf41d17bc31dab9d7c1a4eb9b8cb0b88cd2e55617338b8c5a58d8feb896df6dfbac66c6fbc56fcc0f17a40fb03339bdbeaa70f0c1de65480e3ddddf1ae71d64dedfd200c263962df45b20761265f747786fac48c4e890695c371113ec4175b2234fc81296ac9116c9601332b99d0206321e58b16bb0d7a69e49a26a96ee1f0b811031d79fd51dd6d0e4498cc7f65e385df4287d72ed0a4a4dbe019a545abb5ebb5f170058a18293f41ed1228286e4110fdf37e6e04aaa156bcff40c7038ed380fd8eaddc864faf7813ea3349785566d1f911b529990df437c2a4f929622ce6571923fb7087485996562785a046a44d48847dd949522a274d3fbef4bf000b9c9979d64df5d77c0efa6c5e4680e457e02379f7d9da2a35de7dce57aecdbc696d4f2cc02d802117a371bd456af945be6d3e9e2146b25c30b7da50ce4dea9055fd1c429ed8ff3b6a7d14f16fe20d8f9ec8802abd3b8f61083c1f05cf3c31369d29938ab21e1a0aefc1f5f2eef4c8ccf3cd9d89a930697c9c9e0042ac63288dc70b9bd5d86e36fee93d5e2eef36a89e3db13cd6be53c37649a0e287ad71caaa0cd08dc46b5c93952d6a9540408017688facad9f4e7c94a24f80a7e8abe7352222a51a82c80a5cf3b0d11f1b071d20b5c67f2c120b8a7f722abc21fab82893fbc206501e4a5b8fd3e881ab39b89ec931bbf3dfc2f9ffa139c9a518552ffa4b9547d88a53cc7947f6c8145c33ae6feb9bfa3d7ed81a7df09827e16033ef88d6990fa158a56750d1f554de9786bb2fa83fb593f11b7747633ac9978c6991256e5fb0ca9a7a70b26fad4d27baa338e8eb3ef2cc3321361a7b44b84b5a5093dc6a04883c5843d2125797cf4be06297cf765eab5b11a2d2c5f3ade90a787ea41b236e1c4291bd547de7c16ed4046576ba515ba1786d16a9d7e55ba802c43702e1a7f4eb842b4a0a96772ed3ccf9a8d53283d5f2a31fde784532cfe8cff41802a72a3255f6998ab70c87afe02271b5ca1ac137350f68636af6a540f0bfaccf2074b0e3b562d7aa01af153d118c2375f053e916152723a1dc3bbc16c0d8fcf9cd2e494f71540f0610e12a6f871a3848b4b96fb73d259e4108ce519dc0fb10ebf6a83aa9fb407a9578cb12ae2eebc43362358255c093c66dfc22d6a2340d292b41a6a303c116579537134ee370eeec1ba522f131d702130b5f3a49defa3941176ad0789abeee3becc738ecea0140ef69f574cf0bfa214a9a4a9bae8fc78831207cd8784c917b967b502ab4a36fa814aa55e50915ce6adf77fba178c69d94ccd5b949a7af66e74de04232563e2fdef2338b83cad860e8b29259888c35159f8e64bfa34ddd87b32450c04770f20e6db09c137fb9016382987673cd00c284cffc207b47c83f6d6e290c287d4a1567d94a012043c4a51d3403467b4a21a7c6276f7239dde0966df4c194be5951b3c2f99480ab17f11463d9625bf27656d0b8285d3dadbdd73ecddaae62c825f621bdafcc7af4370d13be788e472b2975d7d55689341f9baa6e149e46b6826ca829a349172faa749f97f3d6ecbb7af40de6b28129aed0ff96febce04bb4ee7be532bda99fbede436a027b1c665bfa7675607fb57a5372ad9152ce057aa429ddf38fa3c9bdd8fd6206c49ffa21fe8f295eba5f54ab9fa4d609075877e8502fca77deaacc884564262bb091c2673a1ec8799887adcfb4e704127113eafa4c3d9ff3e65999049c92d2807c3799be3393ab73d781f5b8e7c20ec0c06428765120f54fd38e51b4d1e1c20db2e34f516ff69206330cb7acbca9eacb1ca8150b1cdc5389e70ffdc7d51957acef211a7eaa79fd9f77386681dc5e38f49dfc57f3825574b15b15656fb0945c7a681fa170a9710061fd7caf7abaa47cae606ac96cb7f1fbe0068c2e8f921be971d95596059a93fb8deeba5d4a62cb47e8dc1dce320e84038526c7c060e9d417f76f0f73bcb76f75c645230e5a90700c828274e2014c7518d2e1ddd46aa16c747153524b5e96cdebcb298c811e54753b3f43334537d058ae0dd56e20305133cfa46a5cc7006fbbea66a8b984cc8544ea5a76f0c2ed9791bd26ea64647d4eb00661f3e95693b61f2bc7ff947076ede2bb5091c147ab4909cb5621327875ba61e68975ee5e03d45e8ddbaae869db24ff4f70adda4dbe7172e4b36c6964a0ace19a86e08fb21568608cd852cab0449596ce4b9e49db0cd51a50f2b1ca224b9c96b8c1cd106aeb48c98ef00715f28fada0f7ba49177a3d335b7d009cbd88d6efefa71054423c1ac2c035ccea176b1042ee93168ca564503be614e44cabe8e122f183ee2cd6f49ad5413a4baa3213bc12c22518c6e87dcc93894b68ae158744f9f8e6d8e85a2bd79d8ffec811994db429726802997f02547c9adc375c68b458b373e9b8f78a7929fe22894ac92778dbc2efca07d79ff57b299a85fe25de4a6d01408bfe07dfef673cee97649565b439306e36bafc9baa89916b7ddb6dee4fc1912aae243fa0aef0c2b63e1850f22049737a5f8d2dfd273da4036dcfb1e9bcca5afa48401ac3823b00b4e5b16793a859abdf9da123b1ede0a42cb2c9005550a95039882814e58f6baa6311999faab45021217ab8b95e78306b6a172ec0c8ede1d7ce86879ff48851d8f8b03d000d04532155db191b8368833e7917bcb257ffcf64f821bd82a5829000182e20381e8022034ec77824ca12b84726221d0f555f3f2259556b034481de7444a5c4aa8bf1c50d82a5828000181e2039220206228c3dd993b0b2ae7a15517e751ab3c3eccbdbf66fae8352ca91edbd307b200000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2f32441", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x5391369997f0d1335d275d9f74b26e1b6633b7949375e065d75ecf3e9ee1b478", - "transactionPosition": 35 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002d0717", - "to": "0xff000000000000000000000000000000002d071d", - "gas": "0x465036e", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000787821915ed590780855ab01ab68d7f7911622a692d9f5e153cd7fa0c876e34bdc0e919b9dad348d36a5ca7da172d7861dc9d374691ff78598f681fb630f618d5ec6dd97e4d8a4e0fdf3f470e0a270b498470e6077d62ee727c3befcea4bd75f78fd5e2baaef1154100a718f8cdf3f51606b032345be315e76db25515c551449554df8986c672cc635509291c62e177d32302c0e4786c892890efc83438b702777075fbefdf555bed7bb2247ccdbf5180e39269a95e03be6be89dc54665ef8739adbd1deb19b654a2a38cbfbe57591f81c1270883e1cf67fb9aa629166cc561ba617833a693a205500021a0cd11a4819df33dbab56d16b9c78963c2673b2c7a4ac8de6d05ef318cf273434c9a2abe2e0f052e1862d2fc1e6e61f3edb2402094f81449ce1dc7be7e320a53970a2da7e93ce09803d02b5d281d2a903e95d032a668d2b1f841f6ba1f6929e8ba6c1bc2b5529fa2ae454358fd6a8ae66ebcfae4fad3caf51d07ccac6b7eb6e0cce37e5f33b934b9f3804572d36e8890793e24685e0824732a9303cae07aa3cc5acf931554fa5a27cbad77cbd038aa052d7f480450aefd205ced4eb9061e7284158d0885a359c90ec391a13b7dcdaf1813dbfdd47dc891733aba9cac4783f3ddcdc48cb6b1921273cd611b6fad160a3fe55116777fcc891e6889bfe15dbc00a040dfd74a3d22cb5e3be517f90ff919d15788b29c50d80bf410e92c7ec237f0612f93184c4e0da45f0b200ab37719b02a6d62d04d153c2db8a667dd2dedfc82351d9337db03a47cc8725600b06867a98a469798098624a596ceedc82cd48f86a62dc8eb7f7e99826b9631a85d6fd16d82cd6d71a4688eee0423c4bb7b3edf2486896cff4d406aa42fcc63f0c43aafa8ec1861ba4d5e518d898f9952017faa76b718ae54e2a09bd55e3ddc6330d3e01496b8e16284d5af1c22adb0869974620dc3e4391b2fb94e86c25a724d51fea1fd2eaddcdb455eabfd8243e1e5433802bc68b8aa42651290ede7c143442f2dda975d26c0d4826866e1087526d18a250b297cea211cb48f13ad95b7d2d2f8828bdd07ca84124c296536e2cd5cb4dd61a1ac2f544e079c716bf706216cb708dbc5fb8230e8898b1bf17e822e198a78d7ea9d7d704bf8f92c0190075422deb51286b5f282571527ba28e18abe3ccc5bebccba440c9fb1de24b5c1104584082f2109f5e499cba9d3dae4f57c0302d38b41d10f6f0f108678011f025affb7a754c400b85cc54aace92ae21baea706d8d76580c57199f7a64cfdf087a38982597b3199af6ca2900ee6652fc62340cc1d60157e7fc31b2c474ae9938c0db0c31a3edb7ad52f086c299551fd5c60b1b6231281eda99598c5a8a9fab4c6b523c25d03415c071d9cfc252129d2fa2319cb9af8525aeff2501cfb25d443952f1f3147bfc1fda017550abb58ebeb17b9327c2c86d059cf04cb2d29966fa689d9e871c45e32f16f322655f5f5556e55bc1a4ae33d68ce15235816886fe7ce919935dd497bfb829c8fef3b22c8e5a1e0bb3e446bff9d0b458dd7531778d144dfd38de1a220e6cda4da4708930f8265d4db9a3fb8a73ae369fa128d49181a04908fc9aed5cb5c6e55caf4786d878ba601cbf32820939679af0c8a99b4fc33d5b343dc3d428258e9a802ed92d6ae7bd6b874198cfbe168738536975ed78666b6cdf8d3155990c913aa782e39d9d6feb1229b4b743e32a2ba22cbaf681945b9eaf70e3e49ec02a3caaf478c3cee83a96c775823d02976408c0633c100b27f8cca832ba01e4b8f9a50d3eb8295abdec2d1a06e4aac519e0ba2c5c16779f01c4dbf6217a55ac9125b4fa5e940fb50eff805cf6aeca0435a52fdf674808e088670c7efbd9c8cf59480dad35b7279c25e0b4a7441e169d78ed79aae7a48f29c98669cdefc6ebb71f66fcdbe1f1132adb2ae7dc3a714a2cb4a4f5865d1ec9ae4c2930511b9f9a16e220ee2afc51d056c7b69e8bcdb3a7ef8e9c264a499c6d1839f4c6161859ab00e06795b03d33a13cbe6e025a7c5ed1b3d6044b714b25ef4228841cb1326e9a198fec9eaae849f1571d7a988f832ab45d6a05106344eb0529b07b4649bdd0592e602e1b2a263ef675e2fb5250d0fc12bcc1482a9102568c9379611cf7ced996cd9a575836ead5474162a2a93c23888edde35d8fe83d4d4c1306feee83f8c39c4f9ad788e5687f67228715d99f512b40ded4a799d620d81c858dd5126a69dfac4864496cc980004071a578e9004bdce44ea1371f6acfadd0b3e1afa6a293e81eb3ab08a58958a5be5d0d5a9347e0b7968fb694d200078e991c2666bc35c2d27000427c6d9d3390ab4c1db1a92554f2891a06e196438556a18b8e861d815a8f1c1bb5d854fb8a06b983efff0ce867571d9d6d2c57315407adbd16ef8874b27f8813a3f8f8729705fce0084eaf6387ecc87a23bac38b25eabdd4067558fa08305e4a349aa2549f25217d5aa7f4246ca354196e56d51032b673f351121a217fbea67c47215c397ad20729eb402e188e616eecfbd8397f67a33c14eaa247741331d54e4d3de3e8755a09ae65f1e1b5e83779f03ade05a0076fa1272324c4ddd2caa3ef289038d11e55d5d6f8f47262d5b441c21930e5d718556d13350cb0153688ca40083ce5d8a6cfbda9f072f0a096806d16c89e721b86e7f8b0a7a26b91aef4164a2581db740dd88536c967ed71cf12f22794ad66e00000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xa3a5a2", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xe01589cddd43f6bceff20e5ae7715922ddb41b97e88b5e4520fa355684aea943", - "transactionPosition": 36 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002d071d", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x3f23511", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002d071d1915ed811a045aacad5820daf497f015b8511702381fed6091635b036cdaf22a594e43cd2d97f95eb90f6b5820cdfd7ccb0815a58ba76c8db18a2801a5fdf6e4f7bffebc8a48086b5769d838f4590780855ab01ab68d7f7911622a692d9f5e153cd7fa0c876e34bdc0e919b9dad348d36a5ca7da172d7861dc9d374691ff78598f681fb630f618d5ec6dd97e4d8a4e0fdf3f470e0a270b498470e6077d62ee727c3befcea4bd75f78fd5e2baaef1154100a718f8cdf3f51606b032345be315e76db25515c551449554df8986c672cc635509291c62e177d32302c0e4786c892890efc83438b702777075fbefdf555bed7bb2247ccdbf5180e39269a95e03be6be89dc54665ef8739adbd1deb19b654a2a38cbfbe57591f81c1270883e1cf67fb9aa629166cc561ba617833a693a205500021a0cd11a4819df33dbab56d16b9c78963c2673b2c7a4ac8de6d05ef318cf273434c9a2abe2e0f052e1862d2fc1e6e61f3edb2402094f81449ce1dc7be7e320a53970a2da7e93ce09803d02b5d281d2a903e95d032a668d2b1f841f6ba1f6929e8ba6c1bc2b5529fa2ae454358fd6a8ae66ebcfae4fad3caf51d07ccac6b7eb6e0cce37e5f33b934b9f3804572d36e8890793e24685e0824732a9303cae07aa3cc5acf931554fa5a27cbad77cbd038aa052d7f480450aefd205ced4eb9061e7284158d0885a359c90ec391a13b7dcdaf1813dbfdd47dc891733aba9cac4783f3ddcdc48cb6b1921273cd611b6fad160a3fe55116777fcc891e6889bfe15dbc00a040dfd74a3d22cb5e3be517f90ff919d15788b29c50d80bf410e92c7ec237f0612f93184c4e0da45f0b200ab37719b02a6d62d04d153c2db8a667dd2dedfc82351d9337db03a47cc8725600b06867a98a469798098624a596ceedc82cd48f86a62dc8eb7f7e99826b9631a85d6fd16d82cd6d71a4688eee0423c4bb7b3edf2486896cff4d406aa42fcc63f0c43aafa8ec1861ba4d5e518d898f9952017faa76b718ae54e2a09bd55e3ddc6330d3e01496b8e16284d5af1c22adb0869974620dc3e4391b2fb94e86c25a724d51fea1fd2eaddcdb455eabfd8243e1e5433802bc68b8aa42651290ede7c143442f2dda975d26c0d4826866e1087526d18a250b297cea211cb48f13ad95b7d2d2f8828bdd07ca84124c296536e2cd5cb4dd61a1ac2f544e079c716bf706216cb708dbc5fb8230e8898b1bf17e822e198a78d7ea9d7d704bf8f92c0190075422deb51286b5f282571527ba28e18abe3ccc5bebccba440c9fb1de24b5c1104584082f2109f5e499cba9d3dae4f57c0302d38b41d10f6f0f108678011f025affb7a754c400b85cc54aace92ae21baea706d8d76580c57199f7a64cfdf087a38982597b3199af6ca2900ee6652fc62340cc1d60157e7fc31b2c474ae9938c0db0c31a3edb7ad52f086c299551fd5c60b1b6231281eda99598c5a8a9fab4c6b523c25d03415c071d9cfc252129d2fa2319cb9af8525aeff2501cfb25d443952f1f3147bfc1fda017550abb58ebeb17b9327c2c86d059cf04cb2d29966fa689d9e871c45e32f16f322655f5f5556e55bc1a4ae33d68ce15235816886fe7ce919935dd497bfb829c8fef3b22c8e5a1e0bb3e446bff9d0b458dd7531778d144dfd38de1a220e6cda4da4708930f8265d4db9a3fb8a73ae369fa128d49181a04908fc9aed5cb5c6e55caf4786d878ba601cbf32820939679af0c8a99b4fc33d5b343dc3d428258e9a802ed92d6ae7bd6b874198cfbe168738536975ed78666b6cdf8d3155990c913aa782e39d9d6feb1229b4b743e32a2ba22cbaf681945b9eaf70e3e49ec02a3caaf478c3cee83a96c775823d02976408c0633c100b27f8cca832ba01e4b8f9a50d3eb8295abdec2d1a06e4aac519e0ba2c5c16779f01c4dbf6217a55ac9125b4fa5e940fb50eff805cf6aeca0435a52fdf674808e088670c7efbd9c8cf59480dad35b7279c25e0b4a7441e169d78ed79aae7a48f29c98669cdefc6ebb71f66fcdbe1f1132adb2ae7dc3a714a2cb4a4f5865d1ec9ae4c2930511b9f9a16e220ee2afc51d056c7b69e8bcdb3a7ef8e9c264a499c6d1839f4c6161859ab00e06795b03d33a13cbe6e025a7c5ed1b3d6044b714b25ef4228841cb1326e9a198fec9eaae849f1571d7a988f832ab45d6a05106344eb0529b07b4649bdd0592e602e1b2a263ef675e2fb5250d0fc12bcc1482a9102568c9379611cf7ced996cd9a575836ead5474162a2a93c23888edde35d8fe83d4d4c1306feee83f8c39c4f9ad788e5687f67228715d99f512b40ded4a799d620d81c858dd5126a69dfac4864496cc980004071a578e9004bdce44ea1371f6acfadd0b3e1afa6a293e81eb3ab08a58958a5be5d0d5a9347e0b7968fb694d200078e991c2666bc35c2d27000427c6d9d3390ab4c1db1a92554f2891a06e196438556a18b8e861d815a8f1c1bb5d854fb8a06b983efff0ce867571d9d6d2c57315407adbd16ef8874b27f8813a3f8f8729705fce0084eaf6387ecc87a23bac38b25eabdd4067558fa08305e4a349aa2549f25217d5aa7f4246ca354196e56d51032b673f351121a217fbea67c47215c397ad20729eb402e188e616eecfbd8397f67a33c14eaa247741331d54e4d3de3e8755a09ae65f1e1b5e83779f03ade05a0076fa1272324c4ddd2caa3ef289038d11e55d5d6f8f47262d5b441c21930e5d718556d13350cb0153688ca40083ce5d8a6cfbda9f072f0a096806d16c89e721b86e7f8b0a7a26b91aef4164a2581db740dd88536c967ed71cf12f22794ad66ed82a5829000182e20381e8022007bba645c0115a8f7d6f67c1f9d2460f73d641ceb84997bb54afe36ed11a9c72d82a5828000181e203922020756cc556bd2c6db9ffad0a86b29297860d63869337c1d5fd0921e71afa592a29000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2f75775", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xe01589cddd43f6bceff20e5ae7715922ddb41b97e88b5e4520fa355684aea943", - "transactionPosition": 36 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff0000000000000000000000000000000015c8bc", - "to": "0xff00000000000000000000000000000000018a9c", - "gas": "0x3603c9c", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001b685168282044082054081820d590180b9e0650e2791bd2e265cf4ff157de041c454b9813e09d21651b6b132609604b77b8a54feec5a9596f79a7aaa2e96fb50ade24f30190a0cb5fb7fa0d77780812aff6c95f4d15990f648a57fc4b3af6ca33ce17b1b0ba17c59198975f7dc79c231180918651145f631f00234c7697c8ad40d293cb34edd09af17f4f2a1f950aed915d6e16a18540542154198516872226599ef6d287379e87c82cfcc40bb6f1c4226d1876235d0e4c2cf7e5c830e7ecc81df37f42fbf714e393a501b80496549d1b52ce5ca02ba411c32ae4b89878363c6434ef47dc39a36027e01f3ad4c60427cd9e9d65595421a557624fb76b236702e94d4400f3bfe6814a14a0fdf34e98ccce46c551a77eea8722ff0b61baa3de7566e17d752e01d4c6ba769be27e9d4dbbf0364002ac4a50774c06353273215ba88c4a820cb9e4a2e70e7f0f4f12f95977b66fba1bc3d5a9a1549514fc23d4d39d9a6233d894761862d0763d7602eb764f05818d872e2ed5c10f0b9dac5e2540c825b6beef6049f00056ee5eaa72bca70be1a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d6900000000000000000000" - }, - "result": { - "gasUsed": "0x2c387bc", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf62bf0708f9536497bc516be3b6d547abc74b04e46598b8d428d1bb989175294", - "transactionPosition": 37 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b3964", - "to": "0xff000000000000000000000000000000001db56f", - "gas": "0x5d6ade4", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000010185181a8182004e40587e20e402c98ae04030a2159e81820d58c0810a1d1d92f527d5c666cacddb7c8da0c4319d0b04c56b232ffaf060c49cba4ca620bb1159347958d2681a5727efd7e090ed851ae5a9236758cd90dbacd940cc7ee8e431727a501fdf14809cb9d343520a11b67feb31f49da7cf1401fff4a04c10fb9d02be4807a8c83a812bf690ea6a3e7bf377884f8ac908c8e02f4d10bd7adc51480159b52ca97c5e9b9d3ee9c4e98d2b48aba3ea7de3710ef8f20a85aa25feff154a5d62baa62f446ddeebb8ca723c9df71e7b8f4021c48871459b65cc721a0037e5f75820a3a81d9f7df6ebf387d44874769074545c6eaafa2e7d3e3728a8b70a048a411800000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x35c5604", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x36071df471f65c45aa13469b5e27b464ca5467f7a0c32a24b7e35e02ea17953e", - "transactionPosition": 38 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001db56f", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x28f29a3", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f8246013800000000460138000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x15c9767", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x36071df471f65c45aa13469b5e27b464ca5467f7a0c32a24b7e35e02ea17953e", - "transactionPosition": 38 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce379", - "to": "0xff000000000000000000000000000000002ce3c0", - "gas": "0x326ffc0", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000708181870819436cd82a5829000182e20381e8022028d8fa5716e16415633900cf944c3a7c13c89b4b7998357f96b93e52033ab7101a0037e107811a0454802e1a00480c92d82a5828000181e20392202026901d7150173a8f624326ea93f6aa265f7d4bea28071817876b420b6bb1763d00000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2270b3e", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xa105b44c0b659b3818b548210b3ac8c78f9d1320c94d359df1a499b8e92b1d69", - "transactionPosition": 39 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3c0", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x31b9999", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xa105b44c0b659b3818b548210b3ac8c78f9d1320c94d359df1a499b8e92b1d69", - "transactionPosition": 39 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3c0", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x30ad451", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xa105b44c0b659b3818b548210b3ac8c78f9d1320c94d359df1a499b8e92b1d69", - "transactionPosition": 39 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3c0", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x2f8bee0", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a00480c92811a0454802e0000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x488c45", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e20392202026901d7150173a8f624326ea93f6aa265f7d4bea28071817876b420b6bb1763d000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xa105b44c0b659b3818b548210b3ac8c78f9d1320c94d359df1a499b8e92b1d69", - "transactionPosition": 39 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001614ac", - "to": "0xff00000000000000000000000000000000018a9d", - "gas": "0x290f9d0", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f285028182024081820d58c0a59157072c783a23fdad72543a912757d0234de28719fa60979b8cfbf9cf4eb5f7819b9e24f2aad13efb0e64f69f20b0b1fb96008c44c1ee0695eb0740aee9ba1106fb832481216b53b6929e9da8f3ece77b6685bedd0ac314a12dc9838f7b4612c874b708ca9d190d31d22f5c362625a6993f9055ca9078f040aedbd1195823a13b92e4f83234cdbd69f7df84741fd89704df8fb5d8455f4c1590aa377ebf35ea6918e47951d1ad4c22f64cec35a81bda8e2063148a98259e3689b711747ff81a0037e5f6582080334418ee2e59dadfdb8d33bf996c9c35f36dbe740e214e308a34ebd796ddc90000000000000000000000000000" - }, - "result": { - "gasUsed": "0x21a9811", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x5baf436ea76a5a6938726489a2b0ac90d8a62225ee0a67bf9440d9b66c8ab007", - "transactionPosition": 40 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001c3bf7", - "to": "0xff000000000000000000000000000000001c3c0e", - "gas": "0x6a72102", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000027a8518258382004082014082024081820d590240915b93366d1917d73ed68ec773c15b05dcfabe838b6aef263760ca3e6db2c6173be57b35130f6fbe36edc2f923b97f74a48c1b20703f76fece172c2f10ee57dcbc723f46b02d971a1407358f025066b292140a32797174b57406603f80bb85f70ab3669c9a27f96f03808f47de7fe08a12149ca62ee727ebe4de79e3f7e547781fc5b6a86d3974c842f134f388418f39b7f528104398df97d94c20d49793e55105399d0e591cab05e828f98ef04f48796e367696e4ecd7c9cf48fd989199905d891f08abd8c6ab58bae7d76cb0a142e875e259f7cfeca88ca0c1cc42798c516abfefd08366536984294f0463ddcb287bac2b2305440a2d11b80bcde36942dbc0a900e8a8edd3f74bd8d63479a011a9cbffbe20eae183469f8a093bcec950ff0a0dffe0a0d2039dd24fbdef242e0d7a81e5d375443ca995272da2733c8f1087552a2572aad7049c15d007e59a5f19befab15b2c01a196d4c131ebf118c4beb33bf1ae866b5f1d89906de32cad27e4c9c9e824108d07fee9d5938c62214a10fbfcad0efef90ae507b4b9b8f20786dfe26ebdb215adda0d3530bef4619c99011340a3f6e294391e5a70cd8f3507e046bf54a27b703a49354359363d301723d6eceb1760bf220ecd15753058e87d3b489fdcf6bdf391a6ceb5c932e9cf9e3e83fe53087f8c9ba3f369567dbe5d597c133534dc3d5961f71fa1cca16614e2871a102aa59af9bc65a9f69e6babb703e41fa3cdb2599140ebd81769ae17febc5700d59e556be7bcc6ab0f245dac5cab7d98f0e0565d1b4d91505ff1f147f1a91aa80d9d1a0037e5de58201bc00903e90ca71cf8a383db0b4d3a038e6c65f02639a026af83a66ff71862a5000000000000" - }, - "result": { - "gasUsed": "0x565a543", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xd88734b87797aaa8445444670a2d6bdd3bbd84d4ec90d3b7b78acae141f7c2a1", - "transactionPosition": 41 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce375", - "to": "0xff000000000000000000000000000000002ce3b6", - "gas": "0x538030d", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000078782195b765907808776753106795c7ae26573ae19505c7fc8a145840257c67736fc00ed90d79d770f0bcbb1d914234ffc6f522a4aba7ae8893e8fb235797e8c7258a8fcbfedf14a90fe28bbfa9834fdc4e5030caaf9ef6313a756804a9c01e2a4ef6cbee65d16281897d1285160d0504c5e42d119880b6c1967694b390b219079086f2ebb8012e17f3866f16f7be2224bceb0327cf80150ae72b36802a974b70541a9613da97ff18342f052a8a505b89b83f948fc6ae6e467bfdb19b5c9ef2c392bd54052bb1f13877b77cb9ad7cdc31bdfbd2ea960a82b6196e6c6d4fe8dddc50fe40c6f2bb4ceb0ebb1802548bf4f1f16ad3890285c00b89aa1d36745ab91d5ef87d043f15a1d8d1dc392832c578b40f456e6e1e4adbf21363cf9f0905ef9f32a1cb10ba0ac9010acdde70778af71b6054348355503a303951d23be9f4c559390da5e1708098e34f37bb0dcd3033549e902c4c1f3d747b2212f19d3c0f10a10e7176022832c521afc8f6b58407de713dc3cc35940762cfabf4ae2cb11b6d730529f4f37dcf94faac4bf6007443e3d7fa2fc3ed9207926345d2135759822096874eed1156a4040244ca30c55e64954531d1e0ac583b6128ec62a2d5adc76dca441a36884bafc29937bd304187e2c75f3ffc04b522a86e381fe672dc5299f9e53632f01d52c78a81495daee861015440697d3d49cfb4ec14c4371b5931e550168703a4e0cb329cfbf0bfccddb6d4a686b961a5910176a3ab16e4b57edfd8dfc7217bd51836bc9fba749534a635d2ce58b50563087e3a59a0a7e0cc897bfe3444792f04a3f52e375a4c7de5cf5eb1077b9aa09fd2ad2adec73036686e29548174b6515e0845d5c5a32635c25253ee3a4afb699f00ab6c720a3f82349679c5ed59ab01a70c68e4ddf395cc14cb70ed8a1645b9007226c4b733e6136d8493430420a155caaaef3e9f40869873f0d1816baa43388948f8ce129405a8ff08d514ebfd190c519871ee66af8f599911f6f10d0b727a09ad16621d1b804c32e6d906152e8651e754d58dcd2ec572696175f743b426bf5f9bd96996f53083f871fa9628eb93fe223438e39448bf72ad9facc6c2649e29345f3aaa6a26140bed5c6f524137e9789ed17efde3b814e2b16eafe8785fe83fa7d66e53c76932f20cf89053e4651060552134686a70a8faa289a0baab5d8bdf5903621347c6b9d30bf6d6d1c227148cbd69da134d20baf7597b0314737a8c30a00e13362f12a7d49b2b8c673346ed47d70e0c11423cc97f7b62af88afccc4afeaeed0f8059b726785b7e459c4a620145ab96b6ee7d683420761b1f5a96757676fc33b528d5182f7b8e84fb84ab36eba1f39db0d551b59af3258e48317d70a612e79afa2302cdfbc89d99905aa6026d24cdee78ea2d5f3145e9cad4531b2550472914cc5ff89780669bf5b7b90db9f518801c3fc317721469dcdb5ab2aa22df8a55f3db7e7f62119252fda66bb5d1201088b68faccc1403ae074c6f8085f436eacedd09a9286396ad0779ac01c50dfb60ded4892fde0ec55c56b702ec5ec3c886f9ff62e84b88f0252460118d411ca853216ee009b948b07a60cd3f4689135bc857663fe4ed71309e7babe6d0750f15e9225045b506a8dd78ba11138d131c30e7175c9bcf461c1ae51cb232d1ee4361757d729d0cea9676f1e6de815fb305e390c0cc402f1481fffbd8bcf49ae6ab8965941bcad082a712c97975217f189c434cbadd60bce79381f9f1b42de417d9e0ab162027f4c00c09bf0d01d84fbc28a74079d94462bead8c877a09bf978b8e8ec49816123cc244613c0fe60426977348bbe2d75b18f59840e4cba2688992e5713493514c6f2ebbecd2c3650eed7c3018614bec7030211b9638bf78d1b74f2adc51d3c588eb05a58f6faeab355e620c0091ad67fa3b5a861ec0fb700c637680fff5468e99bea0257f74e38ba8fbcdd08ddea960403db1af19ba1df53e371b0ae6af65fc3996effe4e8451b49fdc81c873ba134d06d38ccfc19a589d135ac1501cf693fdcc897b15b044736a99a47f8f69a48d8f58297ab20bb43f986209c0c3d8862b055c66611d040c50a281bb349c82884f39f3d9258febfcfcd6b359a03dcc0455ee5e9fe97e4c89350871a21cb0564fb0ecfb45a3263f10fb0d74bcf8c5920dd677472c45a519f63ef0b1c71344d6f2f4a95c2be6a344b20a8470a0b4eb3b2aea6a1c94c9ebc0fa8ac142432f81361b87e2d73e768b6d636fedf68e02a0481173987dd0f3d0e6707cb517c8e9364bfe2b24521459e1e9ab2fa00932c0ea67ab512532b3870e6c7f5b885911ad4d96308568461d5386d370d9a081573585105bcff4b83867b466b8248a626d41c8dc0e88345145ae881c0a03b9a157e002e822ec3b6ae93e1e35dc7e40a5e9f4f7b828eded0a715ae79d61941cff6bd265efd6cedac3c5db83c50a59ce24ac3779a5055627bc52d2ff33b335cc0e47d517e06f3861bb08d10135516097ec0cb573f3299b58e37cf3a431627b8ddcf3b0e894cab48bc3d16ac21b17ad6488977a2991c31936b6ae1d83b2988de15eb4996850978dd2853d9608b4d7d90e6d3b6198fbd064403012f7918829b806669194b55eead50487d9a89ab7c3e808cb6b6f04613df480eea6bb837b8e94790d5af979fad39284d609c1fc40bcf347466bc24434f617465178d7f89998da9da550cc49d54b18807f6eff00000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x6badd2", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xa96e51f481ad7fa03bf8bb21077c475f6104053a8d553f7be0c7ec6bdbc5f4ac", - "transactionPosition": 42 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3b6", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x4fd333a", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002ce3b6195b76811a045356e5582091f30bc594d5d33c5fdead7467a5c9a5f5d3493a04d21054be0fd86e552edb33582059f127e49b8051719946f330b970a0c841fa53270c5b6e5453ddfa01e9db20155907808776753106795c7ae26573ae19505c7fc8a145840257c67736fc00ed90d79d770f0bcbb1d914234ffc6f522a4aba7ae8893e8fb235797e8c7258a8fcbfedf14a90fe28bbfa9834fdc4e5030caaf9ef6313a756804a9c01e2a4ef6cbee65d16281897d1285160d0504c5e42d119880b6c1967694b390b219079086f2ebb8012e17f3866f16f7be2224bceb0327cf80150ae72b36802a974b70541a9613da97ff18342f052a8a505b89b83f948fc6ae6e467bfdb19b5c9ef2c392bd54052bb1f13877b77cb9ad7cdc31bdfbd2ea960a82b6196e6c6d4fe8dddc50fe40c6f2bb4ceb0ebb1802548bf4f1f16ad3890285c00b89aa1d36745ab91d5ef87d043f15a1d8d1dc392832c578b40f456e6e1e4adbf21363cf9f0905ef9f32a1cb10ba0ac9010acdde70778af71b6054348355503a303951d23be9f4c559390da5e1708098e34f37bb0dcd3033549e902c4c1f3d747b2212f19d3c0f10a10e7176022832c521afc8f6b58407de713dc3cc35940762cfabf4ae2cb11b6d730529f4f37dcf94faac4bf6007443e3d7fa2fc3ed9207926345d2135759822096874eed1156a4040244ca30c55e64954531d1e0ac583b6128ec62a2d5adc76dca441a36884bafc29937bd304187e2c75f3ffc04b522a86e381fe672dc5299f9e53632f01d52c78a81495daee861015440697d3d49cfb4ec14c4371b5931e550168703a4e0cb329cfbf0bfccddb6d4a686b961a5910176a3ab16e4b57edfd8dfc7217bd51836bc9fba749534a635d2ce58b50563087e3a59a0a7e0cc897bfe3444792f04a3f52e375a4c7de5cf5eb1077b9aa09fd2ad2adec73036686e29548174b6515e0845d5c5a32635c25253ee3a4afb699f00ab6c720a3f82349679c5ed59ab01a70c68e4ddf395cc14cb70ed8a1645b9007226c4b733e6136d8493430420a155caaaef3e9f40869873f0d1816baa43388948f8ce129405a8ff08d514ebfd190c519871ee66af8f599911f6f10d0b727a09ad16621d1b804c32e6d906152e8651e754d58dcd2ec572696175f743b426bf5f9bd96996f53083f871fa9628eb93fe223438e39448bf72ad9facc6c2649e29345f3aaa6a26140bed5c6f524137e9789ed17efde3b814e2b16eafe8785fe83fa7d66e53c76932f20cf89053e4651060552134686a70a8faa289a0baab5d8bdf5903621347c6b9d30bf6d6d1c227148cbd69da134d20baf7597b0314737a8c30a00e13362f12a7d49b2b8c673346ed47d70e0c11423cc97f7b62af88afccc4afeaeed0f8059b726785b7e459c4a620145ab96b6ee7d683420761b1f5a96757676fc33b528d5182f7b8e84fb84ab36eba1f39db0d551b59af3258e48317d70a612e79afa2302cdfbc89d99905aa6026d24cdee78ea2d5f3145e9cad4531b2550472914cc5ff89780669bf5b7b90db9f518801c3fc317721469dcdb5ab2aa22df8a55f3db7e7f62119252fda66bb5d1201088b68faccc1403ae074c6f8085f436eacedd09a9286396ad0779ac01c50dfb60ded4892fde0ec55c56b702ec5ec3c886f9ff62e84b88f0252460118d411ca853216ee009b948b07a60cd3f4689135bc857663fe4ed71309e7babe6d0750f15e9225045b506a8dd78ba11138d131c30e7175c9bcf461c1ae51cb232d1ee4361757d729d0cea9676f1e6de815fb305e390c0cc402f1481fffbd8bcf49ae6ab8965941bcad082a712c97975217f189c434cbadd60bce79381f9f1b42de417d9e0ab162027f4c00c09bf0d01d84fbc28a74079d94462bead8c877a09bf978b8e8ec49816123cc244613c0fe60426977348bbe2d75b18f59840e4cba2688992e5713493514c6f2ebbecd2c3650eed7c3018614bec7030211b9638bf78d1b74f2adc51d3c588eb05a58f6faeab355e620c0091ad67fa3b5a861ec0fb700c637680fff5468e99bea0257f74e38ba8fbcdd08ddea960403db1af19ba1df53e371b0ae6af65fc3996effe4e8451b49fdc81c873ba134d06d38ccfc19a589d135ac1501cf693fdcc897b15b044736a99a47f8f69a48d8f58297ab20bb43f986209c0c3d8862b055c66611d040c50a281bb349c82884f39f3d9258febfcfcd6b359a03dcc0455ee5e9fe97e4c89350871a21cb0564fb0ecfb45a3263f10fb0d74bcf8c5920dd677472c45a519f63ef0b1c71344d6f2f4a95c2be6a344b20a8470a0b4eb3b2aea6a1c94c9ebc0fa8ac142432f81361b87e2d73e768b6d636fedf68e02a0481173987dd0f3d0e6707cb517c8e9364bfe2b24521459e1e9ab2fa00932c0ea67ab512532b3870e6c7f5b885911ad4d96308568461d5386d370d9a081573585105bcff4b83867b466b8248a626d41c8dc0e88345145ae881c0a03b9a157e002e822ec3b6ae93e1e35dc7e40a5e9f4f7b828eded0a715ae79d61941cff6bd265efd6cedac3c5db83c50a59ce24ac3779a5055627bc52d2ff33b335cc0e47d517e06f3861bb08d10135516097ec0cb573f3299b58e37cf3a431627b8ddcf3b0e894cab48bc3d16ac21b17ad6488977a2991c31936b6ae1d83b2988de15eb4996850978dd2853d9608b4d7d90e6d3b6198fbd064403012f7918829b806669194b55eead50487d9a89ab7c3e808cb6b6f04613df480eea6bb837b8e94790d5af979fad39284d609c1fc40bcf347466bc24434f617465178d7f89998da9da550cc49d54b18807f6effd82a5829000182e20381e80220653bb701762a54aec57374c36979ae603e219bc00ef3be63554bf4a537c9be5fd82a5828000181e203922020dedeefe8f2842d894b8ef09f16bdb01583ef59df9249aeec829366d436be382e000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x36c73ec", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xa96e51f481ad7fa03bf8bb21077c475f6104053a8d553f7be0c7ec6bdbc5f4ac", - "transactionPosition": 42 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce375", - "to": "0xff000000000000000000000000000000002ce3b6", - "gas": "0x5b8f940", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000078782195b65590780b0d5b0ae31a98ddcc416abef382cebb9497b1f392ad01eea0da893b1830817c4363a3d8553f7f49433d65d2a2b36460ab3e02973695e6a0dcf8a7226c2de837a3c7780f2cba244e1eca00be2d1e4aaa1be39b9bb68dbaf151ee90bc7f5a499310f690858ed287ee53d27ce21a485938f847db28229e6c4c078d5a0f8dea6bc7303c891bd668e6208356efa1c527805399282f916b8ba3082034aa4b89be8eff9007efa934a7302aea6d85c2008276e7fd39ead1fe418e5671db68e328182462baa5f085e99ea93a2081f63683629c8451beb4a55fde9204d19dcfcda05b9024bfbe4c99cf42bbf21a61ab5a89a431256a122169cf3ea980326ccd701dbb37a04c6d97a850e0d7da22b08a7e4f6ccc717ba53b5950b95abdf294ffbccada1068611c9cf16516df5df034b397b8f55c3411024b16cf1f6a83ff6e1ba079c2aa300dc232c96d2bca93e3c75bf0db07e6bb8886c95035566b4c35e8ffb0730ff4a3f42d38dde63aaa711cced92c376d56f2d848c29b0a979a83acf3c46bb5d123c6cac402b87a4c24d59310f44af1bcc61316c8d778a2da26a6e0cc3c1f0e3053431dcc3bb29021e11ce9bd74bf44676734c8ab00ac0156ae09e3ab82f82f2ca467d3d5ac6153dd388faa6ea8384692559f65ed9ef1948e400c8908f335d494aa8f611fd52acdb72c913f7b0ab498b5dcf487e666499c3507b4a418934b4c26c60da8d2d53b4d17a1497db33596126ff1bc2ab4fccbba06ce6c2b83027d82ef31a7f796464d996d4085bf7da53a2e260c8dca9024f28ae70cce80a23482f4aa19aff838a77344229c7b6884ca2d319e6bfe987e8c025c1c71d892d480c7a55bb46e5c0498fcea2e7f5d06f5ad4caee57fd3da417ee1cdeb660a00519fd939feedf6eef6a2ae2cf57ec6a588dd0415debd6edf03ff259b70b7217c394e0c0b1504eb819c1731560cc7011367e650862035c1eff36d61358aef1cd66bfb345c5c349d986afa0bc31ce62f53d7746b5bbdb027db28352f745320768b01619a152b9f766689c7573bd1770fe7704faa8eff9b8c83c064681e7bf2b345d9def6b7d57a219841a2ed3b613adb9c13ac6ec9595f59691ed05db2274062868c42451cb669e048b3cb9a5929448822ada2029ffa2a42ca2a7b97e7e1cf652314ef151242d982d0204ab49bb8900d45fe57e07484cb008454aa8cf1bbef3b797c6c8bb100c7d7b000f3c347bd0c6ee5f225ab5c579c24ca23d4358f5349423c93e9c6f9056d3faf80241b9c0a3c8b77f18bd364f400a08a50c55cc989646d1de3b8b0c2b243f4529b72c0d1d5cb316b2068588848aa784c2c1b047b63d30507e9cd58cbcd0034390e5ec61ff2990b0b07867011387e9aad8e6b38c236bb93bd7eefd961d696bd64169784ad55e9c5196a0792e070a33dd95fee21939f59b7fd4a7bdb0c3c8c56135e7f8e621ba578e59eb328426864527394bfd080e8e2c06e317693bfb7f9c970dbeddcb5e6ea07f44c1cc364a0d864a645fab0422c0e27363dbc7256ad44eb265b00c671a7c0f20b0bf8904b008bc2ea07490db2fd954cfbe48d137963c1e286d79a8e60f1285003f476ab16b5b28a79ebfa78fd689c276203f4aea3b3f43f9999c81e03a13e9c8faede90f991440dd39a34fe4763db1652a4b7dc274412ee9b836f8aab5c7e02328a2897f8f331a378324e1d2a84cf998435fd6ad631f16b1ff3566def97b6b319c95038d95135c8f1edddd7ceee4083cd9fbfd48539db28007ae00c350bae09355a3d3cd68bc7c91ec4616dff3dd300e8da3861c78cd67b58a49bb4a5c98c4d29b72f3e0fe32193e9047ff4f62b207623f0da9eb315415bb153347b4d7e7989107a2fe1f2c25572e6e7d757b09c5e382d046e5a72934d378ac9298db613281a15611b522725f058106276dd70689b65ccb27b0fbccda99c7b502af389fcc4c441738462efd87439cafb0aab34b7f33d0f44da59b90ea03d7d8e7b7a96b8b78f34ba126936a08a5c915ea8c679aadf08a72b1ac79738ca72f157cadc8246b3332d20bf1c4319c4d0b4a0fc643710084c05a44ee3c6d6316f26b3d5c05afde5b660d6534dc0bb51647a9b980dca0aa2b9430ff689dd3b9fa871d5cf594df3b25776cf0bbfd9a4644bb53c083c7b3845064ae1046b0420475b0a5cd3d316ff15ed581d62456d117021a52e8adacda6ab8365c94578a84139adcdabe3d32694258b687202452117ba61bacab047517a323b2f36607052fa48ffd55dd3ef4a78c65a1441b4f628666b623f76ef4576136ac1ef16d6f98bbaeb14d1428790969175eee0968e9c76aaedacdb1b9d77f898e5aa8bfbf4d053a7cba468090a749f80ed668cb7f4dc7af33af98935de85ea462451634378eebb58acb78f6a1021a4b023ce212d83d60402f28dde712722f72d6438142148231e69e55a1ab5fa192f5a2e068a40d60327422e302b8a560b66f8eb0eb0a94c36bbaba86f38310bc2e8a8c06d9ab87c8af8edb971581de9ac84b8c7371560c9d4471fec715f622b92bc0439c1a3b2ff4a50e2e5db260beda585142eb60851c9b31e0e4254d010fecbf7af7ce9921e5dad628acb23f5406a2f3e9b647944de96e8d6b2e91aadbdf8cd27af2a8800af5f450015eaf88b47a858b5123f718c6378c9c085da31519fc41dfcaca195a0b2de33dfd216dac4b348e4cb4b2811853cdfab8e11ed55a00000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x64fb6e", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x8de36a2a721ac62d59185fc78daabfe4f045335c963be30edd33170cc7d2ca47", - "transactionPosition": 43 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3b6", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x584d9b1", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002ce3b6195b65811a045353365820e8191611dfc1e40b36fb948cae0d899ee686b3cd8900eab1e5d4c9f8502a022a582047a51e4e53644247c4ad14e95dcf51070bc8748691de862db56543447a9993eb590780b0d5b0ae31a98ddcc416abef382cebb9497b1f392ad01eea0da893b1830817c4363a3d8553f7f49433d65d2a2b36460ab3e02973695e6a0dcf8a7226c2de837a3c7780f2cba244e1eca00be2d1e4aaa1be39b9bb68dbaf151ee90bc7f5a499310f690858ed287ee53d27ce21a485938f847db28229e6c4c078d5a0f8dea6bc7303c891bd668e6208356efa1c527805399282f916b8ba3082034aa4b89be8eff9007efa934a7302aea6d85c2008276e7fd39ead1fe418e5671db68e328182462baa5f085e99ea93a2081f63683629c8451beb4a55fde9204d19dcfcda05b9024bfbe4c99cf42bbf21a61ab5a89a431256a122169cf3ea980326ccd701dbb37a04c6d97a850e0d7da22b08a7e4f6ccc717ba53b5950b95abdf294ffbccada1068611c9cf16516df5df034b397b8f55c3411024b16cf1f6a83ff6e1ba079c2aa300dc232c96d2bca93e3c75bf0db07e6bb8886c95035566b4c35e8ffb0730ff4a3f42d38dde63aaa711cced92c376d56f2d848c29b0a979a83acf3c46bb5d123c6cac402b87a4c24d59310f44af1bcc61316c8d778a2da26a6e0cc3c1f0e3053431dcc3bb29021e11ce9bd74bf44676734c8ab00ac0156ae09e3ab82f82f2ca467d3d5ac6153dd388faa6ea8384692559f65ed9ef1948e400c8908f335d494aa8f611fd52acdb72c913f7b0ab498b5dcf487e666499c3507b4a418934b4c26c60da8d2d53b4d17a1497db33596126ff1bc2ab4fccbba06ce6c2b83027d82ef31a7f796464d996d4085bf7da53a2e260c8dca9024f28ae70cce80a23482f4aa19aff838a77344229c7b6884ca2d319e6bfe987e8c025c1c71d892d480c7a55bb46e5c0498fcea2e7f5d06f5ad4caee57fd3da417ee1cdeb660a00519fd939feedf6eef6a2ae2cf57ec6a588dd0415debd6edf03ff259b70b7217c394e0c0b1504eb819c1731560cc7011367e650862035c1eff36d61358aef1cd66bfb345c5c349d986afa0bc31ce62f53d7746b5bbdb027db28352f745320768b01619a152b9f766689c7573bd1770fe7704faa8eff9b8c83c064681e7bf2b345d9def6b7d57a219841a2ed3b613adb9c13ac6ec9595f59691ed05db2274062868c42451cb669e048b3cb9a5929448822ada2029ffa2a42ca2a7b97e7e1cf652314ef151242d982d0204ab49bb8900d45fe57e07484cb008454aa8cf1bbef3b797c6c8bb100c7d7b000f3c347bd0c6ee5f225ab5c579c24ca23d4358f5349423c93e9c6f9056d3faf80241b9c0a3c8b77f18bd364f400a08a50c55cc989646d1de3b8b0c2b243f4529b72c0d1d5cb316b2068588848aa784c2c1b047b63d30507e9cd58cbcd0034390e5ec61ff2990b0b07867011387e9aad8e6b38c236bb93bd7eefd961d696bd64169784ad55e9c5196a0792e070a33dd95fee21939f59b7fd4a7bdb0c3c8c56135e7f8e621ba578e59eb328426864527394bfd080e8e2c06e317693bfb7f9c970dbeddcb5e6ea07f44c1cc364a0d864a645fab0422c0e27363dbc7256ad44eb265b00c671a7c0f20b0bf8904b008bc2ea07490db2fd954cfbe48d137963c1e286d79a8e60f1285003f476ab16b5b28a79ebfa78fd689c276203f4aea3b3f43f9999c81e03a13e9c8faede90f991440dd39a34fe4763db1652a4b7dc274412ee9b836f8aab5c7e02328a2897f8f331a378324e1d2a84cf998435fd6ad631f16b1ff3566def97b6b319c95038d95135c8f1edddd7ceee4083cd9fbfd48539db28007ae00c350bae09355a3d3cd68bc7c91ec4616dff3dd300e8da3861c78cd67b58a49bb4a5c98c4d29b72f3e0fe32193e9047ff4f62b207623f0da9eb315415bb153347b4d7e7989107a2fe1f2c25572e6e7d757b09c5e382d046e5a72934d378ac9298db613281a15611b522725f058106276dd70689b65ccb27b0fbccda99c7b502af389fcc4c441738462efd87439cafb0aab34b7f33d0f44da59b90ea03d7d8e7b7a96b8b78f34ba126936a08a5c915ea8c679aadf08a72b1ac79738ca72f157cadc8246b3332d20bf1c4319c4d0b4a0fc643710084c05a44ee3c6d6316f26b3d5c05afde5b660d6534dc0bb51647a9b980dca0aa2b9430ff689dd3b9fa871d5cf594df3b25776cf0bbfd9a4644bb53c083c7b3845064ae1046b0420475b0a5cd3d316ff15ed581d62456d117021a52e8adacda6ab8365c94578a84139adcdabe3d32694258b687202452117ba61bacab047517a323b2f36607052fa48ffd55dd3ef4a78c65a1441b4f628666b623f76ef4576136ac1ef16d6f98bbaeb14d1428790969175eee0968e9c76aaedacdb1b9d77f898e5aa8bfbf4d053a7cba468090a749f80ed668cb7f4dc7af33af98935de85ea462451634378eebb58acb78f6a1021a4b023ce212d83d60402f28dde712722f72d6438142148231e69e55a1ab5fa192f5a2e068a40d60327422e302b8a560b66f8eb0eb0a94c36bbaba86f38310bc2e8a8c06d9ab87c8af8edb971581de9ac84b8c7371560c9d4471fec715f622b92bc0439c1a3b2ff4a50e2e5db260beda585142eb60851c9b31e0e4254d010fecbf7af7ce9921e5dad628acb23f5406a2f3e9b647944de96e8d6b2e91aadbdf8cd27af2a8800af5f450015eaf88b47a858b5123f718c6378c9c085da31519fc41dfcaca195a0b2de33dfd216dac4b348e4cb4b2811853cdfab8e11ed55ad82a5829000182e20381e802204d0dbb2063ca111c6cf0067d447609ce7c03d44c821d8fbb2c417e96ea7b5a18d82a5828000181e203922020c5455567d46fbaa53337932443d470d07b100bed66689795774541eac6bfd92e000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x3dab44b", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x8de36a2a721ac62d59185fc78daabfe4f045335c963be30edd33170cc7d2ca47", - "transactionPosition": 43 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002af885", - "to": "0xff000000000000000000000000000000002afa9a", - "gas": "0x4ffdb6b", - "value": "0x1a7f287357826fcb", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000789821a000190f1590780aa6dd1b6cde27d943f35448a588dda962b90bb5c2d83fb2fc6b05c8879362c7e817ed132379a5d26eedc593a3c606d21b1ad6dce5e114d940ed6339ad15a75f332154afdfcf7b12d95582ee4a14202d965a951d4188d7c229f08116f19110bf10ffe6f3131684f4b81a83d8f134ba8b934c9fbba9de485da6314a6e2b4506ce66faabd643095fc29368353eccc4666ccabd8aa5e770a7a983fc357e401b33944c70ad9ca78bc9cfc28c89557f36c9211906843f318dc25c061094b004bd12cba8e086520ffd326f3770543b454bc2dde31928e03115ae3ec7827456b9d1686d4221d1bbd908594ae4765699cd49f5d35b683c356421c3b3d84831c9f848ede29dd011f0b7b2b19cd4247ba549fc81ba77a935573ecb2a8e51fe4e0cbcf360a3a19cbca7006ede49b7b6520c744849e6b4daa1f79d178a9247fce67547d3b07d2e19762f3bf18dba2a6f5889aa872a843948cd7ef996705951974d2335c4b8f5162f93695a1babd67b5452e4bc9ec673031a23f3e19238e1ce953df0d6932a2b5a9a4cfdf9ec7023cb3a3532211daee7979736978bf3dfaba75b44e9890fc1e324a6ff5740bb5d5a91b57d21795a62843af5e1fdc25d90e2b087d36c3d646f526a2e9336f937d80f9e03a855a08878f80da10ea8167836870d3b8abf9ebf2b5b60f001ad41bf0e78dd27b48569c587335e59482402570c61740c114af91f1b163699498bf6b0cefd1d1d211328ce8f0cd831770938798626b188d47787e3169c103fd8acf60aafbad26c69259c269e55aef8ef27ce606e69cace5bed0e8570864881b582727c096fab89d33c0e6d2d22ac2b3d02974110bd104a36be0f1d20bf3ed4d3c4c3fbdbb4165f534eb2adf466587effe414558bd05d694c75eb071cacaf21d67d7782d4bc7b42deb129286a96d00de11b5ffbe2f07502224b99541637018c19fd37d110a3636addac50bc20bfd8c408d014e4f41c3f6caaccdeb47057447de642bfd04b7a240d526879cd484ecafb45f07e4c4728a6df049534316e76ab3b751101a606b8f7741c30508c86d68075d24bde13a68276dd9c64f2bdc20798f8d3ff9052273f3407b8a255a5de6303c2a2a2501a1f0271a3996dbad35959ce3f69169329593faea319dd23d1aaa66adb32fc8be99377176e47de9538a55b5fc6f02ed74a494dd4546953b7cc772b649ed17a4c728ecb4d63ea885293926751387f8e6f1779acbc0c8a4aebbea14b51dd4e651f8b2055c6b307c041fca08e590f50bfd1066f7e074b7f98fd4000fc8a10872fd115e4c74a21c9129fb0230ae27f4af10bdbcd02cfe96792a10628b0018ef8cf7984ab0b2fa34a9464404500c8e2d6945935a22a534962ea8ea448da54d79b8e53d56b9c2c7ad7de5b9003476d530f1bc1f7a3371a4e1dddffe93974eb03383e315632977426589e4a13022b55ad361150a48c42c35ba475848dd8a6ec957c7c1a6525830eea4ca05175c3cfb0a5c1faff0338023961101aa2cf5458b1ec14e1c958ee0a8048acac6cdde743043847c2c8951d708a00247e19a8fcc1f90325533a773ffbe795e78498ffc3d81267c2ca13eb7b9fe362ac30518537653ec47bf3bf172ec0acd9a65d9e8c16022a6ced9030033964a3505894f0621c9be0268328a4826c7596ea3f21a6f404126dcdfda7063e9e70175d012424b890043a619d74f99734052b262f7983646b7c273061c898341b4ad3a46e0c825790b4f8b43181669311936f36b7595c13f46f111c1b49bc1b18c9fc55bedd45667483a7effc18e808f5159a09530f142669318d5ad90c9c2c768351373af5cb789cd938c40735aee33285036aadc2f692dc762caeedf5f870d215c7c1b5fecd512b83dce65badc32776ee0ae289eef405ad38683de41cf605690a078d504cea2c2e7436e3b87f2bf9a89f06ca8a9a79b91cc449dabb10818c178334db4ca84d41a17d390d07ac237423365864ca394acd3a1c17dec6bea0542ef51aca5929e4ce6cc4a422d79008b060ee30734f93bfd0d2b5114f4a49c2479f2d2a2761bd7f60fa028a66c3206a393a6025cb26c546c1244b6ddb13e7bfde0de9441da50655ba2d00085ec2d9ccdb2d58068b8e55d04aa4c4283b7b8626ea6e7458cdcf6ba6100be0dda7d2eb19d2b9afc2bb6ad54a1f444e388e5e43d5160fd81ac0fd1235f8479fdc8594e65b5e45e2227348206fa42fa56fe1449825b054c1ad68f2b8420b5e669af6f79d3d9f6c36e5d1d7e9dd9a04377fc49a38e8dbc54d5a45e50a9f5504911666d08c6720967d47e27b6b680731a22100bd337afccac15368ae597a8ac55a8c548fe999c5c2c1767d04c91e547032e4fdcefde964a4901c34e6f374c0a77d1afe3a5ba1bc85a66ca17f3bb12192f0029ca4e402547b2f9b60f67925f14c7ea6f30eab4d4a75110969326dc7631ae6090e665147ff29b6b0aaa472ec0fd57937813cb55ff2dc4b47d73775022ed80e10e2aac5bbd886f1c91a09dad3ae74f3aa71db848302c08145f59d5e56a476f9ad63326fd249bb7083b4a77e5e576685ef161c65bfc3c2beb424ecf1b320c2471171cec1f8c27b04c1f685823d84a3aaf4f74b2f1a613064a75500b84f7c3635836b17380ef09f24ab3dd708af2705201af775ea0422215d6ff0a616ed0e6d31a94aad6f55cc65fd60336380f28870b93cc861d9b2ee43293063c27620892065a0000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x7544fb", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc0408073d9e8c991bfcb7a2e0205c700a68359ed40dcf6912fbc02d2623971ce", - "transactionPosition": 44 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002afa9a", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x4bbae48", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008338808821a002afa9a1a000190f1811a045acda85820b24b3e1b897f42b136eab69c226a2ae7647adf103dc5fe05e67ef22daa0e65f8582074fea458846979d6f5d8383b77701ee74e32ded56a348a283c139f3019f64153590780aa6dd1b6cde27d943f35448a588dda962b90bb5c2d83fb2fc6b05c8879362c7e817ed132379a5d26eedc593a3c606d21b1ad6dce5e114d940ed6339ad15a75f332154afdfcf7b12d95582ee4a14202d965a951d4188d7c229f08116f19110bf10ffe6f3131684f4b81a83d8f134ba8b934c9fbba9de485da6314a6e2b4506ce66faabd643095fc29368353eccc4666ccabd8aa5e770a7a983fc357e401b33944c70ad9ca78bc9cfc28c89557f36c9211906843f318dc25c061094b004bd12cba8e086520ffd326f3770543b454bc2dde31928e03115ae3ec7827456b9d1686d4221d1bbd908594ae4765699cd49f5d35b683c356421c3b3d84831c9f848ede29dd011f0b7b2b19cd4247ba549fc81ba77a935573ecb2a8e51fe4e0cbcf360a3a19cbca7006ede49b7b6520c744849e6b4daa1f79d178a9247fce67547d3b07d2e19762f3bf18dba2a6f5889aa872a843948cd7ef996705951974d2335c4b8f5162f93695a1babd67b5452e4bc9ec673031a23f3e19238e1ce953df0d6932a2b5a9a4cfdf9ec7023cb3a3532211daee7979736978bf3dfaba75b44e9890fc1e324a6ff5740bb5d5a91b57d21795a62843af5e1fdc25d90e2b087d36c3d646f526a2e9336f937d80f9e03a855a08878f80da10ea8167836870d3b8abf9ebf2b5b60f001ad41bf0e78dd27b48569c587335e59482402570c61740c114af91f1b163699498bf6b0cefd1d1d211328ce8f0cd831770938798626b188d47787e3169c103fd8acf60aafbad26c69259c269e55aef8ef27ce606e69cace5bed0e8570864881b582727c096fab89d33c0e6d2d22ac2b3d02974110bd104a36be0f1d20bf3ed4d3c4c3fbdbb4165f534eb2adf466587effe414558bd05d694c75eb071cacaf21d67d7782d4bc7b42deb129286a96d00de11b5ffbe2f07502224b99541637018c19fd37d110a3636addac50bc20bfd8c408d014e4f41c3f6caaccdeb47057447de642bfd04b7a240d526879cd484ecafb45f07e4c4728a6df049534316e76ab3b751101a606b8f7741c30508c86d68075d24bde13a68276dd9c64f2bdc20798f8d3ff9052273f3407b8a255a5de6303c2a2a2501a1f0271a3996dbad35959ce3f69169329593faea319dd23d1aaa66adb32fc8be99377176e47de9538a55b5fc6f02ed74a494dd4546953b7cc772b649ed17a4c728ecb4d63ea885293926751387f8e6f1779acbc0c8a4aebbea14b51dd4e651f8b2055c6b307c041fca08e590f50bfd1066f7e074b7f98fd4000fc8a10872fd115e4c74a21c9129fb0230ae27f4af10bdbcd02cfe96792a10628b0018ef8cf7984ab0b2fa34a9464404500c8e2d6945935a22a534962ea8ea448da54d79b8e53d56b9c2c7ad7de5b9003476d530f1bc1f7a3371a4e1dddffe93974eb03383e315632977426589e4a13022b55ad361150a48c42c35ba475848dd8a6ec957c7c1a6525830eea4ca05175c3cfb0a5c1faff0338023961101aa2cf5458b1ec14e1c958ee0a8048acac6cdde743043847c2c8951d708a00247e19a8fcc1f90325533a773ffbe795e78498ffc3d81267c2ca13eb7b9fe362ac30518537653ec47bf3bf172ec0acd9a65d9e8c16022a6ced9030033964a3505894f0621c9be0268328a4826c7596ea3f21a6f404126dcdfda7063e9e70175d012424b890043a619d74f99734052b262f7983646b7c273061c898341b4ad3a46e0c825790b4f8b43181669311936f36b7595c13f46f111c1b49bc1b18c9fc55bedd45667483a7effc18e808f5159a09530f142669318d5ad90c9c2c768351373af5cb789cd938c40735aee33285036aadc2f692dc762caeedf5f870d215c7c1b5fecd512b83dce65badc32776ee0ae289eef405ad38683de41cf605690a078d504cea2c2e7436e3b87f2bf9a89f06ca8a9a79b91cc449dabb10818c178334db4ca84d41a17d390d07ac237423365864ca394acd3a1c17dec6bea0542ef51aca5929e4ce6cc4a422d79008b060ee30734f93bfd0d2b5114f4a49c2479f2d2a2761bd7f60fa028a66c3206a393a6025cb26c546c1244b6ddb13e7bfde0de9441da50655ba2d00085ec2d9ccdb2d58068b8e55d04aa4c4283b7b8626ea6e7458cdcf6ba6100be0dda7d2eb19d2b9afc2bb6ad54a1f444e388e5e43d5160fd81ac0fd1235f8479fdc8594e65b5e45e2227348206fa42fa56fe1449825b054c1ad68f2b8420b5e669af6f79d3d9f6c36e5d1d7e9dd9a04377fc49a38e8dbc54d5a45e50a9f5504911666d08c6720967d47e27b6b680731a22100bd337afccac15368ae597a8ac55a8c548fe999c5c2c1767d04c91e547032e4fdcefde964a4901c34e6f374c0a77d1afe3a5ba1bc85a66ca17f3bb12192f0029ca4e402547b2f9b60f67925f14c7ea6f30eab4d4a75110969326dc7631ae6090e665147ff29b6b0aaa472ec0fd57937813cb55ff2dc4b47d73775022ed80e10e2aac5bbd886f1c91a09dad3ae74f3aa71db848302c08145f59d5e56a476f9ad63326fd249bb7083b4a77e5e576685ef161c65bfc3c2beb424ecf1b320c2471171cec1f8c27b04c1f685823d84a3aaf4f74b2f1a613064a75500b84f7c3635836b17380ef09f24ab3dd708af2705201af775ea0422215d6ff0a616ed0e6d31a94aad6f55cc65fd60336380f28870b93cc861d9b2ee43293063c27620892065ad82a5829000182e20381e802206742e56094a25e9cb8ccfd548669001e885fdb05e5a3a32e414f33401d1ece41d82a5828000181e20392202072f2ba4a22ea83fd98e49f73658227ca7d4026d6c093aefc824f23e5a5acc71c00000000000000000000000000" - }, - "result": { - "gasUsed": "0x371397e", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc0408073d9e8c991bfcb7a2e0205c700a68359ed40dcf6912fbc02d2623971ce", - "transactionPosition": 44 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001282b2", - "to": "0xff00000000000000000000000000000000127dd3", - "gas": "0x1d3a22b", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f285078182004081820d58c0941484e4cc646ac06322666cfc99a0ed9ad905db567a1cc54211cdce6f42ee3dee60352b2be6a23aba9bba8e794ff0698928e0a8c7f0bd2ad8eadff2ce5e9750579598a700988bd54af7e4667fdd1ae926725280acf36e1ab80cc1de1601e8da010c876138e492f250edc21b8b59635fe8a8f7c7337b6c142ef29b76757b5145e1e9882e664b43e8a10ec295667d9cb2a6b2de7f8c3b15035cf8c7865767b31d6e2f2dd243b61e8abbc333640b050bb8524f7c22950fcf01306c1fdcef61e1541a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d690000000000000000000000000000" - }, - "result": { - "gasUsed": "0x183032f", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x8643bb35c2d74917f7c4ccc8b797edc5c71e1d27e5cf731220ffe82a3533e984", - "transactionPosition": 45 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff0000000000000000000000000000000015c8bc", - "to": "0xff00000000000000000000000000000000018a9c", - "gas": "0x2eafd6d", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001b6851682820a40820b4081820d590180b2eb828eedbe33cad6f19452c4a64757c82c436f25cd9eaf8e04e32b77ad8322faf0b5c2faa9b927cb1f9c8e0bf7e943883c08d938f92ab436ad40321f8cea79f55f2011801a09c8728506c0d208f370617e3559354b5cbccc6e9e37493999a604e8c9020ac8f8ae7ec6318c8f907748b717f990f97a5f3bdefb656bd8e1ab058e90e1ae54d200c60da283573c6462db83164589345e177a4f12c229c60965ce0345ca9f4f375464dd4c4b22e5fe3c4b93c4a566e55f4ae5d2f888c22cbdfb3aac072a09b8d53f522a24aaef5d50a5cd405d81980c744454908cc3198eae1549d77e8f2f038dbc7600bae3709738870985f222ce07642e324042fd34bac9d89c17e327a10f5e6f1b1e15af4e85682eb5737255793ac3502890b72403550aa5440c8560e2634fc9d63ed0dac44bf3d1162666cc01adea437adc6ad2b5bf16c3e16c850c6d914054f5eff597510445ccd98600f5f6e75e683e13a942bc4c3ff3d26f7dce9ea5ad9281809f01018ea7c7a93991336e2358ad44f5094686cb6ea4441a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d6900000000000000000000" - }, - "result": { - "gasUsed": "0x27952ea", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x0cfb366083cce141ec4cbcb7fbc844164a6d689001b4fa1a5233ba49ac2a60b8", - "transactionPosition": 46 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff0000000000000000000000000000000015c8bc", - "to": "0xff00000000000000000000000000000000018a9c", - "gas": "0x367a5bf", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001b685168282004082014081820d59018083cfb7899f78e9abf3b68fb49960e041030f4a1111d343aaf21b64aff5cb100e76bd22d19c9b785283ebfe8678bc4df1b758afeba700e80c3821f5cdcbcdcb4203a09c02283c38eed8602a4fd85cf2b7ace368e0870aae6f09269d4e2f84861b1737f0af1660fd760a0bccf250bc131510d151ee15ce9ca7949bea9f981dca6bfb9f1c1201a464982345ea31d5188afa8eea3801c8edacef9734027b0f51aeb2abb58399a43c27189a368f4baf04c151cec24704723ef65ec928f465d7673501b5e895e3968a207457fa85d74a28b0254d13d8ac435c1413fba0e64155ab4344403690d1191e9240699cd5798d27d7cd95e88ad5077892b556df1102216b13fcbd8b10a2225da004b1fd134292441bf70a721f2a846a15d6850866f59017271e016aa391968b25a1ba3ad81fd844601bfd2b57d7a40a2fc036f457dba80b4ebb2399677fe3419026d308eb9ab3e49ba08e0087e610c6f3f02f14f180549c697687f4bd3f0ea6d51ad0f238a320adc310b8a86f661a3ec01b30664a771471383d1a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d6900000000000000000000" - }, - "result": { - "gasUsed": "0x2f3564d", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x6e808c2937deb1c22c515ef2023ee58c101a4576db5d5e6ac159ac61ba036e6d", - "transactionPosition": 47 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff0000000000000000000000000000000015c8bc", - "to": "0xff00000000000000000000000000000000018a9c", - "gas": "0x39ef4a2", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001b685168282064082074081820d590180afa14917d4144b56496b6cc74319d2098d173c50b0da49bb9512bed6dd7595601cfa80897e804817c0d2478207f71d2587dd46bfcf6b13cf6521add3b7652f47e4639b19027f26aeda2f1b3722110d54cd360c30150a3bbee48dc463e3e663cd0c21b12ee7ca34be107976d7b795d7dd4112d82dc88b762fe79698d0125e512e1a44c16134cc62e91459213c2fd61bad9562f6f79e1886637d4643ffdb1c6b73d81fa1c9340db4bd820e616bb6f6b95ab0a3ef8ad4a66493ee9a309a9e0a7809a0f7cbf76c61839805069b4e4877f198f05b1635a20c99068ab282c61cc8f3bc39f7ee82a63bf4b31edee162fd63aa4491f4a1f7ca6315e8d9821e782ce077ebc413911b951bf6d428541ac0953fa6ad6b0b6ea69baf7385e9c6c3aa4a649d4d09cd4b65224f6e84399d0833ecd50cc64e2ce13f38eb38e9988d7f1191f67e85e80460abb16b217cd2d086157df72c71875a36560a72f5fa0e8bf4575d9de99122fb62eefa0dcfaaedbff79ef30a2a9d9406468359ee9481cd403de8fd3de2321a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d6900000000000000000000" - }, - "result": { - "gasUsed": "0x333798d", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x3208378ba92f068351f798d2e18b7515acc2cb3706ad08828a25fa04bd8d5176", - "transactionPosition": 48 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff0000000000000000000000000000000015c8bc", - "to": "0xff00000000000000000000000000000000018a9c", - "gas": "0x2df8316", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001b6851682820c40820d4081820d5901808b127976370cee8e21810fef9e01d393ed9157f129f195e89183a3b3986dd47d6b1fc50411f55fa62e3e8a21b87432f5b4083668c65d107e89a712a69228bc8f9b8434cfb6cce55292911f7259ad33dc27b52ce79130347f460b6cd9af4a1fc201fa009bfdec83897255da344d1f87b4549066a6d1480d2e45d78817a48496d9170fea27163562954584ad441d094b90b70ad4eda0a538df1aa2c98ad11c53df1527b388ba6d985f184749d68c6755f589aa44158cb62b25a216a65a6d34707c8e1964f519655f563b7d8176595a9962081e4080e7cc1222eb61b8fbbef1148bea9dc54cfccf8ad8a8219e4743f42d1cb8d068aee2c128a2e7078d273ceb80b1b5d0be48e05c89220f166e09f24a92d4fc1653d899044f27801cd7be7b60516b082a25bf8e584af5d2ab269526b640e2a9b15d8bef2746851aa32ff49440a5d422503b60e4891e4406dca42d2df507f6a69222360c3a8d4974bd664bbb84996665e0e006c354d5270219b23a91b13f61b74352e5c977ef9a334ca6e8dfef7c681a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d6900000000000000000000" - }, - "result": { - "gasUsed": "0x2c63cfa", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xa92fbc3e483686a903d03dad832850c63d219c087a9560d8dd11846aba05d051", - "transactionPosition": 49 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff0000000000000000000000000000000015c8bc", - "to": "0xff00000000000000000000000000000000018a9c", - "gas": "0x277b6fa", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f285168182084081820d58c0a00fc058f003e9ce71e5c7f0110c27df98ac84029077d1a9b2744f7e0151cf7f37003c1eecdaca3938bdbd868a115882aff1ebd7e304411ef8bae7829e72f002b092325c39e5b2e81b27daa449d1e68e653f07b8bedfe8280056265a1f69de540f5bbb84169d1bc2066279ff20e66708357d446408e3da7c4b690b0f87ab40dce7a3f32066eccc99e74612d6ece3570caef37ce3559cfdde7e2415c47ee44bcc21d4f14974bbb91207ee9bc4597be61fea4b52f2f210f58bd1ee7ca856fc56461a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d690000000000000000000000000000" - }, - "result": { - "gasUsed": "0x22bd49a", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xac6b89882cdc6aee6b38cd3e2c2ddaa63c2d4f48c431cf8298e24c5d95af081a", - "transactionPosition": 50 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff0000000000000000000000000000000015c8bc", - "to": "0xff00000000000000000000000000000000018a9c", - "gas": "0x279471a", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f285168182094081820d58c0a16986848d181bf1de1caebee43c1eccd53a589850e2a21b63e3fd4c7157be80c013abb17ffeb957d5064bb729f24be8a8a1f9bc526100fde11a14cb850d2773204d90747c7eeec3943bb2ab043b1c7427245198329a88920423807b1d378a1d05ecc5761e6fe291dc6f1a826f8af0d213406ffa54176aa3954fbdc6875bf14dab7c3b3e37973d09e7c2501bd3b8a3dc8a9f1a14e911342e61f958afb3765d537f4442254d53cf384e210b8f22e897d2fb2da104c1cb7167f1b1dcddc216105b1a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d690000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2381302", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x66969ead995791added4e5ea61637c9f338e94808ab77f703ff227e63cd158a9", - "transactionPosition": 51 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000000c4d75", - "to": "0xff000000000000000000000000000000001536f3", - "gas": "0x210e371", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001b78518228282004082014081820d590180ab8892f0b4c2c9054daf7a9ed12d1b5a3a216bf4c540c2d12ff2f19b40e14a2aaee5a22012b97888bf5e150a69e6561f850dbdc6a8bce7a292800a139bddff40a363999f600294b6b7490984d83af7f92bb98c27df4490661b5bfb662ace46f20c62a9fc0db2401dc84f0548f8cb6c2bb8d57ec3d520d49aea2dc2a6416dff3072b2b901948d6474c794484e2e0fc90b95856412e33b02c965f56065eaf18a042545cf1094f16c16f0a70f671d11276a73633fd4aa4e0ff4d261d07298950c25b3b9dd698ea90ef14758e2d760a8339b6e893c97bb5a0797c9b0a199bb6dc7fd82dcdb58c3f4e8e7b525823cc28430d280d85fd1c4cf59019d8387b0edaf3c136d04bdceba76e8942efb9ec1f642d2db2f61e50e5c6917ef2485e272d4ef047712b5b7e6ae6c4621ea2cff3dabab4cd71c0bd559460e39d5f6e571006c3286bf72ec724e9bd5d9f8ccb0abd1557ef7408c3cc690e1e7ba8f4b0fb87d447b6e66cb34b90c3150ba3a2bd623fcc09ebefcabf62aa3bd01c63308c2428272ace8b01a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d69000000000000000000" - }, - "result": { - "gasUsed": "0x1b7284d", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x957227ab77c764b14f4df2b5f2a60da060bd4da8f8f9f8ae87a67f2188f134a6", - "transactionPosition": 52 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002d0792", - "to": "0xff000000000000000000000000000000002d0798", - "gas": "0x5a9136f", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000787821962b1590780a3e5852016727b45737aa29963044c46e1db6470429aacf34f13901c0f14737eefdbd5397827482d83c172d144687b9a88ea422d78dbbd983dfc149f18e56512cc94e94c81b34d5dab65edf4f2162c96a23f9a7e64389a6d5687930180c5777116996e4921375c5c20f43a201b95a204a461e4641476649e5cca5e164c74a8bd4a6598ded80a57ef8b1fba959de531e8a27a73a56ac06909e5a71852b04441cabb5675d2012b93285232c8e631d8bd76659134bcceabedadeca603df88a21c3db6640598a40b0df2473d6fc3869229a85efc0636dfedb4d34eca5b74904bbd5ed3af1cbb1f1a87643ed8414835ee9eb8a4825f62c74d7e750e5ec9fc5578548b78103536969c97d1f58ddf1acf39527e335827f8a78dd2d0a25419032819f99b03b66aa0fb3abb3f03068f34f03f0a8659bb6f1c16b80534eeacd0eaf88493a0ae91dfb25e5f06a4dd152b29a2c6498fb22755da0e514ab417656a676ca94d3b621810634ac162decd849c9488542d6d2fc73f293cd670884391b243947c42e6a310fca6b6f11683aea97b10d7040cbd42cd18c3c7b1f74ea9465037a82639898baf55c881719afd3b515668eaa6d42b8cf88178ec5656af49760c56f3384ca50305479afeb610aa3c73637f1b5f454a8eb775bee088a34fbf1008a303afd43d114b09ae243b41895bd07cba783646fc19a54f9e1026fc5ec214c6147795dcc334b6836bed237f75c03f70f81bea01c386742a25f408582d0cd4feee4b1d842aa45169e841cf4047bbb3774cf4e94989255905aad75a10ed978c9643994b39deaebb37a3a9d085a5161d592c536992b66e7ed7652b41676254a339e4cf630f7c1f8d067d558ea51a712e19f02829003589c84b321cd305f2b22dbf4777c6a04df233f59e49794948b97539b73bb1e801cd4c6d55b2462cee225939279de6b5d509a6c1ae5eec57cd9ef5eae92ecb9a6d83c1afce2371e1bb740975f4aeb53b57e6c8c3306d35541cd8bdb0e4ded06ff1aafa1fda68a352fa08ac3f2d3d878030af72503dc5edafa040e3f86d4dd393c84b75622c2579867f2aacf2b0fcc871d6a29ea710940a052fa9fcd7b88e61b629b41569616cf37756928a313e6c6e22e4e49ee9d5ff55bdae517a645d5e4ae3258b97737f906aa1f033d50e76e7d7994e9e2472ee6bfdb2b87ba54d0d85d81417674264deb6d747a2de5d3e8719ce7e6f0c510da24e1e3bb2c83810e9dfe19c5d69908183132dc368ecf12f57515b980bbf22bcf06fb059167910897ab200517795dcb31ffdb04466e78d000310859edb2e5cc76e85f6bbf79f1aba4667429b24b25e698ac37f20e24ac9fea4e7ebac408106c36d4939a70a027a838f3f561e41f3b084a773980f2803943d3145a5a5742fb373dd991e3b29f2746ee4820e19a1b52dbe9dac79b36204f52a85368cb507d93b7ec6a4935258ffacd2111459d57ed8b959e19627453384708da76ec1176404ad527184daee6523f76681a6958aa405e33d7335204474a9cc5ba2a7dbf1d556629c54ef419af663dc6c87b3166b1db21db1a772a94e119da2d83a9923bf6d08e50660df19f2ea19ba1408458ad5163911d0d5053993569f7c2feb04379c4db9cb6dc5bc2304138b9aafb4ae1165651a181bf252936af0c1450d9906bac56a379f27153b12424a645b5dc22f646e9483d5e9212adc5ef5755c491ee76be107d2cd0b0945a7d10aa5433be44fc36d806a83e064d88f9b1ffa85ca9428f1483c08ee4519fc35692df29f69fead901ba40966c150d96c54e7bfac3bd02a543d881d3609fb7d3956b942df9da3fe417ae9a6dcf2454eb7987ff5437f12cd8d77e15cdd292bd02d7480edf5b28dd6667ab3b0fbad18f5081b4171b6b3c92d8c3090b6d7f244272825fef7d2c3ba44c68ede30cdf681567a43a18d24d65948f09884933aa0eef904bb940b3dd1a92bfd67e6abc7eeb148aad962836b622007653043443474e44c2042c2e353bf99b556853d26a4d314e715bbbdc9073ffb98ec6cc40fa010c562c6b123ffff8cabfb1038a29a489899ef8d6dbe2031f073ab9f4d82582df6660dacd347dd6d207174f18db4977c669d3ae04f6d9fc5e1b190358c7155c1afb1f18e022d0207aaa724aaafc2484478ba1cd7abfb18046cf04a59eb36a313ab0e6cc23799943432d828ff486adcd3a6cd078cc8a931fcd0bf2c392e70daa6ac9c1def1e3a249fc6db7c451beab38b40738eba8e40bfc26b0b9580aacce29745135024154074bcd74774c8173f388b9a41ea5bbf36f8aec182dff1506f097f5a7aeb71749767b80b1ad9898d977c9f250439c8baf8114f94a7ec61f4ccd958f4680144ad1558c0e075d3b0d8a4aa38307191765bdadf1c62fb19b0e5b995bed7898195d30ddc393b2cd516976ea358e10882af8013947be9d0a82803e18d12c5f3d90e382a0d336413382e47014735c63bc04063941fdf5a8a5d1b57738a501ee1b50b185033926bc90480e0b8a991a693a610fa869245301c023fd291efb61b60d1af0f0f2e2080e1c17ee21ed1a4c6aa53c232949a1a0e2d325d0d3309c7b7de30811c7b1e2872918b3c2cc4f055e2b9afa7ade34b7462b95a8ed657248bd379407c57e0c0243aefdaa54b0597cc4206a85b6db894d534e00b91c07d7942e29107bb4dd5db5749e790e7a57bd23d50af35116d1c15f38e7ade6baa7000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x91d33d", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x4de7b42d21f0a27c46254aa5536ddc1ba165322f56ad47655d256a8fdbcc0839", - "transactionPosition": 53 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002d0798", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x54822a8", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002d07981962b1811a045b2a6358203fd3e68cc4519573cd488191e2be8fb213cc957966f7ab315c24afd373103850582058eff1bd5d4366dbe11ce747891d3e24826b623b4459903b6ab586fda7f31209590780a3e5852016727b45737aa29963044c46e1db6470429aacf34f13901c0f14737eefdbd5397827482d83c172d144687b9a88ea422d78dbbd983dfc149f18e56512cc94e94c81b34d5dab65edf4f2162c96a23f9a7e64389a6d5687930180c5777116996e4921375c5c20f43a201b95a204a461e4641476649e5cca5e164c74a8bd4a6598ded80a57ef8b1fba959de531e8a27a73a56ac06909e5a71852b04441cabb5675d2012b93285232c8e631d8bd76659134bcceabedadeca603df88a21c3db6640598a40b0df2473d6fc3869229a85efc0636dfedb4d34eca5b74904bbd5ed3af1cbb1f1a87643ed8414835ee9eb8a4825f62c74d7e750e5ec9fc5578548b78103536969c97d1f58ddf1acf39527e335827f8a78dd2d0a25419032819f99b03b66aa0fb3abb3f03068f34f03f0a8659bb6f1c16b80534eeacd0eaf88493a0ae91dfb25e5f06a4dd152b29a2c6498fb22755da0e514ab417656a676ca94d3b621810634ac162decd849c9488542d6d2fc73f293cd670884391b243947c42e6a310fca6b6f11683aea97b10d7040cbd42cd18c3c7b1f74ea9465037a82639898baf55c881719afd3b515668eaa6d42b8cf88178ec5656af49760c56f3384ca50305479afeb610aa3c73637f1b5f454a8eb775bee088a34fbf1008a303afd43d114b09ae243b41895bd07cba783646fc19a54f9e1026fc5ec214c6147795dcc334b6836bed237f75c03f70f81bea01c386742a25f408582d0cd4feee4b1d842aa45169e841cf4047bbb3774cf4e94989255905aad75a10ed978c9643994b39deaebb37a3a9d085a5161d592c536992b66e7ed7652b41676254a339e4cf630f7c1f8d067d558ea51a712e19f02829003589c84b321cd305f2b22dbf4777c6a04df233f59e49794948b97539b73bb1e801cd4c6d55b2462cee225939279de6b5d509a6c1ae5eec57cd9ef5eae92ecb9a6d83c1afce2371e1bb740975f4aeb53b57e6c8c3306d35541cd8bdb0e4ded06ff1aafa1fda68a352fa08ac3f2d3d878030af72503dc5edafa040e3f86d4dd393c84b75622c2579867f2aacf2b0fcc871d6a29ea710940a052fa9fcd7b88e61b629b41569616cf37756928a313e6c6e22e4e49ee9d5ff55bdae517a645d5e4ae3258b97737f906aa1f033d50e76e7d7994e9e2472ee6bfdb2b87ba54d0d85d81417674264deb6d747a2de5d3e8719ce7e6f0c510da24e1e3bb2c83810e9dfe19c5d69908183132dc368ecf12f57515b980bbf22bcf06fb059167910897ab200517795dcb31ffdb04466e78d000310859edb2e5cc76e85f6bbf79f1aba4667429b24b25e698ac37f20e24ac9fea4e7ebac408106c36d4939a70a027a838f3f561e41f3b084a773980f2803943d3145a5a5742fb373dd991e3b29f2746ee4820e19a1b52dbe9dac79b36204f52a85368cb507d93b7ec6a4935258ffacd2111459d57ed8b959e19627453384708da76ec1176404ad527184daee6523f76681a6958aa405e33d7335204474a9cc5ba2a7dbf1d556629c54ef419af663dc6c87b3166b1db21db1a772a94e119da2d83a9923bf6d08e50660df19f2ea19ba1408458ad5163911d0d5053993569f7c2feb04379c4db9cb6dc5bc2304138b9aafb4ae1165651a181bf252936af0c1450d9906bac56a379f27153b12424a645b5dc22f646e9483d5e9212adc5ef5755c491ee76be107d2cd0b0945a7d10aa5433be44fc36d806a83e064d88f9b1ffa85ca9428f1483c08ee4519fc35692df29f69fead901ba40966c150d96c54e7bfac3bd02a543d881d3609fb7d3956b942df9da3fe417ae9a6dcf2454eb7987ff5437f12cd8d77e15cdd292bd02d7480edf5b28dd6667ab3b0fbad18f5081b4171b6b3c92d8c3090b6d7f244272825fef7d2c3ba44c68ede30cdf681567a43a18d24d65948f09884933aa0eef904bb940b3dd1a92bfd67e6abc7eeb148aad962836b622007653043443474e44c2042c2e353bf99b556853d26a4d314e715bbbdc9073ffb98ec6cc40fa010c562c6b123ffff8cabfb1038a29a489899ef8d6dbe2031f073ab9f4d82582df6660dacd347dd6d207174f18db4977c669d3ae04f6d9fc5e1b190358c7155c1afb1f18e022d0207aaa724aaafc2484478ba1cd7abfb18046cf04a59eb36a313ab0e6cc23799943432d828ff486adcd3a6cd078cc8a931fcd0bf2c392e70daa6ac9c1def1e3a249fc6db7c451beab38b40738eba8e40bfc26b0b9580aacce29745135024154074bcd74774c8173f388b9a41ea5bbf36f8aec182dff1506f097f5a7aeb71749767b80b1ad9898d977c9f250439c8baf8114f94a7ec61f4ccd958f4680144ad1558c0e075d3b0d8a4aa38307191765bdadf1c62fb19b0e5b995bed7898195d30ddc393b2cd516976ea358e10882af8013947be9d0a82803e18d12c5f3d90e382a0d336413382e47014735c63bc04063941fdf5a8a5d1b57738a501ee1b50b185033926bc90480e0b8a991a693a610fa869245301c023fd291efb61b60d1af0f0f2e2080e1c17ee21ed1a4c6aa53c232949a1a0e2d325d0d3309c7b7de30811c7b1e2872918b3c2cc4f055e2b9afa7ade34b7462b95a8ed657248bd379407c57e0c0243aefdaa54b0597cc4206a85b6db894d534e00b91c07d7942e29107bb4dd5db5749e790e7a57bd23d50af35116d1c15f38e7ade6baa70d82a5829000182e20381e802200c82b78d2956e18f54a7f16aeb3aad9d317d478d5b3212fefcc83d184999b859d82a5828000181e20392202096242ea129b2d38f1a44eb5dda71f9cd9f697abd137478959f807b834680c202000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x3db1b4c", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x4de7b42d21f0a27c46254aa5536ddc1ba165322f56ad47655d256a8fdbcc0839", - "transactionPosition": 53 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce389", - "to": "0xff000000000000000000000000000000002ce3be", - "gas": "0x2fb7db3", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007081818708193efdd82a5829000182e20381e802207f4d07b116b6300ab7afb497429e9b845c6b81d1a4aa8b3f7dee0fb9c2d5ad5a1a0037e10e811a045365671a00480bc5d82a5828000181e203922020a2e68e2e80ebeaca7ed536bb450bb1c4a8577e2209135546f2287009cc45671b00000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2042bc7", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x8912c515d71922148eec6cd24d28673f3920b413bf2ced0ae6847f79c001d58e", - "transactionPosition": 54 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3be", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x2f0178c", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x8912c515d71922148eec6cd24d28673f3920b413bf2ced0ae6847f79c001d58e", - "transactionPosition": 54 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3be", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x2df5244", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x8912c515d71922148eec6cd24d28673f3920b413bf2ced0ae6847f79c001d58e", - "transactionPosition": 54 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3be", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x2cd3cd3", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a00480bc5811a045365670000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x4888fe", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e203922020a2e68e2e80ebeaca7ed536bb450bb1c4a8577e2209135546f2287009cc45671b000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x8912c515d71922148eec6cd24d28673f3920b413bf2ced0ae6847f79c001d58e", - "transactionPosition": 54 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cf0e9", - "to": "0xff000000000000000000000000000000002cf0eb", - "gas": "0x3f8e7b6", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007081818708194760d82a5829000182e20381e802205bd2105f384ad53a57500cfda364fb6d48557fd75a9da12488bd97950b8e22031a0037e0db811a0453be491a00480bc7d82a5828000181e203922020133508dc0c6fc1cdcb063ef40e4cb11f4165e5d52f9bf41ca2f9ceb1f1e5701800000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2cee852", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf698e990a7ea335ddf046c92759be8d87ced39bae42e1295308137ce727281d5", - "transactionPosition": 55 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cf0eb", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x3ed818f", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf698e990a7ea335ddf046c92759be8d87ced39bae42e1295308137ce727281d5", - "transactionPosition": 55 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cf0eb", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x3dcbc47", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf698e990a7ea335ddf046c92759be8d87ced39bae42e1295308137ce727281d5", - "transactionPosition": 55 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cf0eb", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x3caa6d6", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a00480bc7811a0453be490000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x4887a6", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e203922020133508dc0c6fc1cdcb063ef40e4cb11f4165e5d52f9bf41ca2f9ceb1f1e57018000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf698e990a7ea335ddf046c92759be8d87ced39bae42e1295308137ce727281d5", - "transactionPosition": 55 - }, - { - "type": "call", - "subtraces": 7, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001bac42", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x13c70235", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000eb8181828bd82a5828000181e203922020edaa33053d8a5f916356a3ff18ad24fbda73e61e7d6bdd4486427e9bb61f180e1b0000000800000000f555010f29c20971f6e29cead00b57277fb5975fd83f914400a29f74783b6261666b7265696234616d7a696b6578787072753432747579626c7a35777736776636776a6236737935367a747863636b62696d647374627966711a003814d61a004f81164048001b6a51c29fe8e040584201667d08b321901df7ed569ed7f84c82d3b69c25b4bdee7b916eb19319500c778b06b89684eefd98e18bf8f5086c272b4c5fc53ac344a1b17e9d226612077b346f00000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x8c4173b", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000982811a045b5762410c0000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xe62a29767a0e1030fd633df1090900a6ca3b2ccfde69682b8b4850a4faa385ad", - "transactionPosition": 56 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff000000000000000000000000000000001d0fa2", - "gas": "0x13b36e85", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000014c1cb970000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000054400c2d86e000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x13301e", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xe62a29767a0e1030fd633df1090900a6ca3b2ccfde69682b8b4850a4faa385ad", - "transactionPosition": 56 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x139f99ae", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xe62a29767a0e1030fd633df1090900a6ca3b2ccfde69682b8b4850a4faa385ad", - "transactionPosition": 56 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x138ecdce", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xe62a29767a0e1030fd633df1090900a6ca3b2ccfde69682b8b4850a4faa385ad", - "transactionPosition": 56 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 3 - ], - "action": { - "callType": "staticcall", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000021f2a6", - "gas": "0x137bc7fd", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000009d8b06780000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000ea825841667d08b321901df7ed569ed7f84c82d3b69c25b4bdee7b916eb19319500c778b06b89684eefd98e18bf8f5086c272b4c5fc53ac344a1b17e9d226612077b346f0058a48bd82a5828000181e203922020edaa33053d8a5f916356a3ff18ad24fbda73e61e7d6bdd4486427e9bb61f180e1b0000000800000000f555010f29c20971f6e29cead00b57277fb5975fd83f914400a29f74783b6261666b7265696234616d7a696b6578787072753432747579626c7a35777736776636776a6236737935367a747863636b62696d647374627966711a003814d61a004f81164048001b6a51c29fe8e04000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2e9853", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xe62a29767a0e1030fd633df1090900a6ca3b2ccfde69682b8b4850a4faa385ad", - "transactionPosition": 56 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 4 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff00000000000000000000000000000000000007", - "gas": "0x126d96d9", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000c26ddbd50000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000064500a6e587010000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2ce23f", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000104f001206c3fe6bb46656ea1e89d8000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xe62a29767a0e1030fd633df1090900a6ca3b2ccfde69682b8b4850a4faa385ad", - "transactionPosition": 56 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [ - 5 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff00000000000000000000000000000000000007", - "gas": "0x123e221e", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000d7d4deed000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000067844500a6e587014200064d006f05b59d3b20000000000000584d8281861a001d0fa2d82a5828000181e203922020edaa33053d8a5f916356a3ff18ad24fbda73e61e7d6bdd4486427e9bb61f180e1b00000008000000001a00176c401a001b60c01a003814d68000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x378d0ea", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000043844f00120654f8b6172b36ea1e89d80000500006a8d15c1acd58b4e5110000000000520002f050b9c93842f9b9dcb15680000000004d83820180820080811a034d18b40000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xe62a29767a0e1030fd633df1090900a6ca3b2ccfde69682b8b4850a4faa385ad", - "transactionPosition": 56 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 5, - 0 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000007", - "to": "0xff00000000000000000000000000000000000006", - "gas": "0xf2d40e0", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000de180de300000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000006e821a85223bdf5866861a0021f2a606054d006f05b59d3b20000000000000584d8281861a001d0fa2d82a5828000181e203922020edaa33053d8a5f916356a3ff18ad24fbda73e61e7d6bdd4486427e9bb61f180e1b00000008000000001a00176c401a001b60c01a003814d68040000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2439f4c", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000d83820180820080811a034d18b400000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xe62a29767a0e1030fd633df1090900a6ca3b2ccfde69682b8b4850a4faa385ad", - "transactionPosition": 56 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 6 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000021f2a6", - "gas": "0x4c7ce06", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000f98c996600000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000009c8258948bd82a5828000181e203922020edaa33053d8a5f916356a3ff18ad24fbda73e61e7d6bdd4486427e9bb61f180e1b0000000800000000f54500a6e587014400a29f74783b6261666b7265696234616d7a696b6578787072753432747579626c7a35777736776636776a6236737935367a747863636b62696d647374627966711a003814d61a004f81164048001b6a51c29fe8e0401a045b576200000000" - }, - "result": { - "gasUsed": "0x9858a", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xe62a29767a0e1030fd633df1090900a6ca3b2ccfde69682b8b4850a4faa385ad", - "transactionPosition": 56 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cbdce", - "to": "0xff000000000000000000000000000000002cbe59", - "gas": "0x5b6dcdd", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000078782196cb85907808da266f0d295889a05be62ca726ac93cae2bf1976516514387a04f6c161b95f727f97fe04494e7eed2533dfa309ff933835dfcf8a37564e3ebf5e4f2a7c0048aa707c303814028b15d44a34184d6e88f422defb35d333642a4592eee09398954097c2bb5290d2d989947f61ffcfbb57ff4efd12c5f0bdbe44e97487e45aeca92cdfd5678303f0b20617dc9fb65ecc734986207f6e60aeb1426702cebcba9b1e3e99b1f05d922b877818dcea601b115ee1794c04f76b666de9bf218f78f3bcb44b1c2f38368f86d53ce36b72bf6517d1a5f289da6799cbc2619403ea09b6f2fe13d598586938debb8a2e28762e65540b2b5401175c6e0533f20065b308444fcdd2462733270fba4adff7c4f1e9ac2c311b0c4e2fc6304f26ad4f1700a849eecaa0778bc7133031c1de707450c8fb1dc14f13789ed53844ca17939d2bc948ec550d0c18282b578e1228c7fa698c47611c28933cc0ae919ea14bb1aa6f75a01e7c474660eb686047eba0742f543acce9640bb67b163874d4528c7ea10b78fbd2a9180d69cb3e6477e8076fbfc1331194b04165be5e67f5161067e3b462edae40b9696d0d5217a1c20752df202384733c238a2a0b1ed16038f3cc7cbe51848d47d9031302870450538e8dd0022e41fb115ef3a4d9c4581a9229ab5385dce0753fcde17e8ed5bd77e3d3f865bc9f2da86ddff535786f72c93627459d5e4953019b9e71a0a13ff2fb7745b9a770c2ff2147edc8bb388730ff2c7b60645eb02a3dd96c6987d5cfe6f2fc254916d37c4589807faccdeed6595c5ed36d3bee947f5600f74b7737eaf6da28e3ba27ef9bd8b2075819620049af7b0034a55355a032fd807838dde66f7e9fee2aafa916f8c481bc8fca7fd868e1b39433a55bb96819eade540d63a2541f6de1ec9bca1c9f5545ab9b87481738c6868c3173daa50e42d6ca2c70172e512f1a7716ad8fcaa273d14d22466de3b18d9ac596b99fbfe1b50dc78a43042057415f19d79a9fb0243639303eeaccd138b5e8ffd0eaa1afa6418b7acab26e09458079a866b5cb655e0e2c9193a3be567ebdb8be7aa7b7ec4ca5726490798ff39a69bc1d15c8b4db2258173bb90c15ccdd3a38f0761a67a2893b7a176478345af99a4f151d89bf47addc9c19dc0a1fc7b8cfc2777cf6f98690feb3024ee5e393f63430d96d88efd01077fb4793b1a1369c529f05ccb8631a86eda1496c700095d52127504428f372d5dfd5b0c62aeed47698c64685eabd5ca056c3cd59f1c352de9b6aaa4ad2e3cfc3ea8ca119db8b24685432c4b2e6904c8b2d9ebc40585f8e37d77d585bc45b24448685f85b9061254e159b4d19ea81a33a3ee180e1ead0492e74fc2f4223ab2fda859b7c5e5abeb763194281c9b80ec89d0e1ef85bb4cbeb34dd238cc85f4f4b916ff8a3b47a9a18925f84adbf8b425970f6b2fa58789473fa2092f619365699266bdf2c84d80fa2f580f2135ea744abe1d6522a98b018b0c48e390f635a4e53c8df2c17b981d8e9b9ecf20681cbe9b727ec422a107486effcc2eb1255b4b16dd9ffda0d317834a3dcaba0647ffff274bbafb78225ed55d8a91e5fd5593425ec96db174708fae8ef62c34bc529fbe0ce13b52fb99d09288a63914fa099634fcc6af402b29dec3579afaa5136ae438ad32576e0a401d9e9da4dd79dd76992b02dc924d7b83c8b1f01ca4409fe937525b7772962430e8169a122afe5535e4c05af5001c96c548ddcdfbf2e313846f4422efc1b7d6399f022926f0a45475d6a1fc2e8dc9e1da4ceeb78ad725f324a5a495e4af104b244d3aecad990a3bf0538bc13afd284af720833bf6e0be96192104f181450c28bb8da27e81dda480c36dc01f13a33989f2cbeb16b295fe2b2fcc6f964fec34622e66a1ce4cd850d06e17ba099287e812603fdc936768fc2f0d13256fb56cd98b546906dd67c8f9c3724429cd8df899588c71a08404ad8e7443b3d19d3614fc7be29a6c7ed83efbf70d3d642c4673ef2622f45fa6ca7cfba8d0c1c49b40caf850a218158cceea53ed7379476f03114197df49e352e97677764c0d050c24155edfc4d1089bafcd0666161acc6f2b53bad7648f9564a9cdc969b22d769aa037c00b4b27aae14db8d6085c48ec89f16ef310c532d483aa1754e0e4b1d3f50cc91ef92bb8af19f71eeead2c2a1b32f5f8554204d22d23c1e861b8d3b72b38391abbd4c9a468c75f9f55425b1ccdc7d75fcd8f0730aa27d634fb3347a5a79bd73bbc4d8f0c3b2ad4900a618b54a9fe84738413ae34ccadd19059d68c869976f3b1342b6d53197b798f65977ad905386fca03705d04e6e826b343229a267164b2068d851c69f560abbdbb426aa9ebfd2e79d1e5ea05b8b78869b7722b5245a91021cec3673336befd9074f10aa72cb164cc46be41573d5f482e0495a8287951c9e85dde7e9494044ad93f6981796257b8b6f4095dbd21d9dd853506dd43bc225116cdb4b019d74a49ccd953656d12162103019cdc4ab77f2248b477c0584848da3e7f70c61eb4e66e01f49b97ec92190ca44cf1f45753ddb1d135f00074b70ca728b939379102b396420ce758f0bbae79b2809293d9161621b3a3ead8574652a49f857aedc874aafa48269d666f46efd63c17100812b9a79a0500309e82d3bbe467996dda0aac3afc8d391c6cd3a55d7cf2c7daad4da2072c6800b7d1fc9273921bb6ca97a400000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x9e78d2", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x19d4af3d4de075052a7e46d1e79cdac48c06b20a637af57edfaa39add1b7ebbf", - "transactionPosition": 57 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cbe59", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x5493bc3", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002cbe59196cb8811a0458d4515820c733511a1ee67c4b6354648a0aad557a7d6103db91ba7ec3605d0e0d0a7dd727582070588192c2cfcb22bd6934b66eb4b782332859eca0353d7a6a6de5ae787e23ff5907808da266f0d295889a05be62ca726ac93cae2bf1976516514387a04f6c161b95f727f97fe04494e7eed2533dfa309ff933835dfcf8a37564e3ebf5e4f2a7c0048aa707c303814028b15d44a34184d6e88f422defb35d333642a4592eee09398954097c2bb5290d2d989947f61ffcfbb57ff4efd12c5f0bdbe44e97487e45aeca92cdfd5678303f0b20617dc9fb65ecc734986207f6e60aeb1426702cebcba9b1e3e99b1f05d922b877818dcea601b115ee1794c04f76b666de9bf218f78f3bcb44b1c2f38368f86d53ce36b72bf6517d1a5f289da6799cbc2619403ea09b6f2fe13d598586938debb8a2e28762e65540b2b5401175c6e0533f20065b308444fcdd2462733270fba4adff7c4f1e9ac2c311b0c4e2fc6304f26ad4f1700a849eecaa0778bc7133031c1de707450c8fb1dc14f13789ed53844ca17939d2bc948ec550d0c18282b578e1228c7fa698c47611c28933cc0ae919ea14bb1aa6f75a01e7c474660eb686047eba0742f543acce9640bb67b163874d4528c7ea10b78fbd2a9180d69cb3e6477e8076fbfc1331194b04165be5e67f5161067e3b462edae40b9696d0d5217a1c20752df202384733c238a2a0b1ed16038f3cc7cbe51848d47d9031302870450538e8dd0022e41fb115ef3a4d9c4581a9229ab5385dce0753fcde17e8ed5bd77e3d3f865bc9f2da86ddff535786f72c93627459d5e4953019b9e71a0a13ff2fb7745b9a770c2ff2147edc8bb388730ff2c7b60645eb02a3dd96c6987d5cfe6f2fc254916d37c4589807faccdeed6595c5ed36d3bee947f5600f74b7737eaf6da28e3ba27ef9bd8b2075819620049af7b0034a55355a032fd807838dde66f7e9fee2aafa916f8c481bc8fca7fd868e1b39433a55bb96819eade540d63a2541f6de1ec9bca1c9f5545ab9b87481738c6868c3173daa50e42d6ca2c70172e512f1a7716ad8fcaa273d14d22466de3b18d9ac596b99fbfe1b50dc78a43042057415f19d79a9fb0243639303eeaccd138b5e8ffd0eaa1afa6418b7acab26e09458079a866b5cb655e0e2c9193a3be567ebdb8be7aa7b7ec4ca5726490798ff39a69bc1d15c8b4db2258173bb90c15ccdd3a38f0761a67a2893b7a176478345af99a4f151d89bf47addc9c19dc0a1fc7b8cfc2777cf6f98690feb3024ee5e393f63430d96d88efd01077fb4793b1a1369c529f05ccb8631a86eda1496c700095d52127504428f372d5dfd5b0c62aeed47698c64685eabd5ca056c3cd59f1c352de9b6aaa4ad2e3cfc3ea8ca119db8b24685432c4b2e6904c8b2d9ebc40585f8e37d77d585bc45b24448685f85b9061254e159b4d19ea81a33a3ee180e1ead0492e74fc2f4223ab2fda859b7c5e5abeb763194281c9b80ec89d0e1ef85bb4cbeb34dd238cc85f4f4b916ff8a3b47a9a18925f84adbf8b425970f6b2fa58789473fa2092f619365699266bdf2c84d80fa2f580f2135ea744abe1d6522a98b018b0c48e390f635a4e53c8df2c17b981d8e9b9ecf20681cbe9b727ec422a107486effcc2eb1255b4b16dd9ffda0d317834a3dcaba0647ffff274bbafb78225ed55d8a91e5fd5593425ec96db174708fae8ef62c34bc529fbe0ce13b52fb99d09288a63914fa099634fcc6af402b29dec3579afaa5136ae438ad32576e0a401d9e9da4dd79dd76992b02dc924d7b83c8b1f01ca4409fe937525b7772962430e8169a122afe5535e4c05af5001c96c548ddcdfbf2e313846f4422efc1b7d6399f022926f0a45475d6a1fc2e8dc9e1da4ceeb78ad725f324a5a495e4af104b244d3aecad990a3bf0538bc13afd284af720833bf6e0be96192104f181450c28bb8da27e81dda480c36dc01f13a33989f2cbeb16b295fe2b2fcc6f964fec34622e66a1ce4cd850d06e17ba099287e812603fdc936768fc2f0d13256fb56cd98b546906dd67c8f9c3724429cd8df899588c71a08404ad8e7443b3d19d3614fc7be29a6c7ed83efbf70d3d642c4673ef2622f45fa6ca7cfba8d0c1c49b40caf850a218158cceea53ed7379476f03114197df49e352e97677764c0d050c24155edfc4d1089bafcd0666161acc6f2b53bad7648f9564a9cdc969b22d769aa037c00b4b27aae14db8d6085c48ec89f16ef310c532d483aa1754e0e4b1d3f50cc91ef92bb8af19f71eeead2c2a1b32f5f8554204d22d23c1e861b8d3b72b38391abbd4c9a468c75f9f55425b1ccdc7d75fcd8f0730aa27d634fb3347a5a79bd73bbc4d8f0c3b2ad4900a618b54a9fe84738413ae34ccadd19059d68c869976f3b1342b6d53197b798f65977ad905386fca03705d04e6e826b343229a267164b2068d851c69f560abbdbb426aa9ebfd2e79d1e5ea05b8b78869b7722b5245a91021cec3673336befd9074f10aa72cb164cc46be41573d5f482e0495a8287951c9e85dde7e9494044ad93f6981796257b8b6f4095dbd21d9dd853506dd43bc225116cdb4b019d74a49ccd953656d12162103019cdc4ab77f2248b477c0584848da3e7f70c61eb4e66e01f49b97ec92190ca44cf1f45753ddb1d135f00074b70ca728b939379102b396420ce758f0bbae79b2809293d9161621b3a3ead8574652a49f857aedc874aafa48269d666f46efd63c17100812b9a79a0500309e82d3bbe467996dda0aac3afc8d391c6cd3a55d7cf2c7daad4da2072c6800b7d1fc9273921bb6ca97a4d82a5829000182e20381e80220c655d56cf0ef0742b187ef633a957ffbd32c3defade8f2594ec34910ae8ef12cd82a5828000181e20392202097b123d3710a5dd973f857a5dc4d0514f0a97e1d975eb01e42aaa1cd87b10e3f000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2fbb841", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x19d4af3d4de075052a7e46d1e79cdac48c06b20a637af57edfaa39add1b7ebbf", - "transactionPosition": 57 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001a0f6b", - "to": "0xff000000000000000000000000000000001a0aaa", - "gas": "0x1c23858", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f285098182004081820d58c095c1f3fcb3c1579274a763a69efe68df562832d6d07902f5bd0d92df6c5f3633242ab04b3bf33cd6c1dc4e53e6348e57b64a9c8af60ecc295357de84447d6eec698cf27d62e260272bb587d433ca022f29db4399a700813df8341aafefde3eac07783ba253600998029028668b87cd91de868c4a1e0485ecdce958493fb7b03de06060e93b360705af0f49e300d06c90acdf3e6e8d6de2933463909e42a9f1b6a96a465cab34d14c762e113b2ab20797a9101fa6321983e962c74fdab9fe9eda1a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d690000000000000000000000000000" - }, - "result": { - "gasUsed": "0x17514ec", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xe85fd747a88049a644b187321afe6fc108862c99c3e42fb076fe35e266905bcf", - "transactionPosition": 58 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001c3bf7", - "to": "0xff000000000000000000000000000000001c3c0e", - "gas": "0x6e1b2cc", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000027a8518258382034082044082054081820d590240a06d5c12f5ddfcf4805175f47f5711e5e0eda838d6ccc4423f9426fa01451a3037af6100b3ea8913e16caffeac1f8308805c1898f39f306d98d01c0cde75312cd842c524eabd33bd58aa15b50f9d0b1f2e068c36ce8fb4db6cd66e4f11ebb93c01d973c053d13e1b070b9ffe554b0ff62e602e7940bd9904618ab4bdac3dadcccd10183165128f991bc97d20924c719e8b483140672040df267d9c5ec364f04c5df0b2134c18673cb86feeae16bad31fc27d68282f9a2158b0d91718020cf98383682293553b37a85872eaff3c7b34cd107853c65f24bcbbee7c0962080abd615c7568f4f84a795e9c43e21bfd10d3008efcab45e9f96671fa38198fbd4d4f9e50e9285ab59092b4c9f7f34936dab4996949b05ecc76a489b2e2cc02943f12fb143cbbfada5ddb42821e823f5c8acb9686de0e3ef85fc41d39719dbe8702ae9a3662a834f68c200c79f6d9e7c7032124b438ab8660ac3de175f80c0c7fefd992e83534f7b1f7e7c3afb4974cebde8238ffd9f3a738047ee515d483b2f91fd8fc8630777082c23784590b3d8a817a963f10a39e83a5740f7b6954a8930dd713c6fa332ba0da90a8e2b3f71e645f6876ac8a2b980be79ec5fc0d39fec3ac0a6cbcb99e661b4d62d5a46d6feebe18280aeb0e2ff21001b3bdcf4faaa677f16dbd9a03d0dd3fbec38d38403f21fe7795a4612bfec76bb2c945456637b84d2929e0d962a79a161de51d0ff523b91c342c37e5b170338c7678717aecc762d5935ffd168257763650220d5f38f6a269d2c39160a243cd163f8446607bbba2f2d5e61aa91a0037e5de58201bc00903e90ca71cf8a383db0b4d3a038e6c65f02639a026af83a66ff71862a5000000000000" - }, - "result": { - "gasUsed": "0x5b37ee5", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x468720813c31079df1c1d52f6bdc98c5b55dd3c3cf0c0f5377d692e2030c0ed8", - "transactionPosition": 59 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001c3bf7", - "to": "0xff000000000000000000000000000000001c3c0e", - "gas": "0x548fbe8", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f38518258182064081820d58c0b4a54e9a1ab64f94e7977377bea362e540a83376e284bf86dc426fdde3652582267f7508e553394e0317f636155ad659b04b297544b50bc0ddb85e23b7657fb6851233d15ed8c422341192b767e9627396f6b4c1380db51647bda0570fc0c8f51016b9cf8092e073beb73a451a93e08c309762bdd09ab92af5c738661bfdd8e86eb24c5ddbe399bf534221d26bd3ac0aa247b059202ec208dc4b57488ed12e0144b3ab60c54912333580b0b5e2c20cf0f14908dfb11b7514cfd0624a6ccb4c591a0037e5de58201bc00903e90ca71cf8a383db0b4d3a038e6c65f02639a026af83a66ff71862a500000000000000000000000000" - }, - "result": { - "gasUsed": "0x4840f87", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x6ae23d16858a9de909457b27a4a7272b0b7bee42f8d5f12c10f0cdc55118b459", - "transactionPosition": 60 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b3338", - "to": "0xff000000000000000000000000000000002b3363", - "gas": "0x4ae7ef5", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000072818187081a00011b51d82a5829000182e20381e80220f29dcf0ce726c36a3a9dec02c7975bc7d4fbe9058f088a5f66d827231404b12d1a0037de15811a045af8041a004f7082d82a5828000181e20392202091de727d20058c198e55b1a08967352ff72168b2b70f0c597e7e907b3778df3a0000000000000000000000000000" - }, - "result": { - "gasUsed": "0x3613dfc", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x558e1f235e7b5c9ee2373bb7c39b2c236f584979cb6d3b0898e1e283e3c05e07", - "transactionPosition": 61 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b3363", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x4a318a5", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x558e1f235e7b5c9ee2373bb7c39b2c236f584979cb6d3b0898e1e283e3c05e07", - "transactionPosition": 61 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b3363", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x492535d", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x558e1f235e7b5c9ee2373bb7c39b2c236f584979cb6d3b0898e1e283e3c05e07", - "transactionPosition": 61 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b3363", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x4803dec", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a004f7082811a045af8040000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x489183", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e20392202091de727d20058c198e55b1a08967352ff72168b2b70f0c597e7e907b3778df3a000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x558e1f235e7b5c9ee2373bb7c39b2c236f584979cb6d3b0898e1e283e3c05e07", - "transactionPosition": 61 - }, - { - "type": "call", - "subtraces": 4, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b199b", - "to": "0xff000000000000000000000000000000002b19a2", - "gas": "0x4ed0b83", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022881858708195f5bd82a5829000182e20381e80220f9acb798b6fe4fb2b334af3d3e0ee522004ea693556ed6e7ead3d68306d75b151a0037e089811a0457ca271a004f598cd82a5828000181e2039220202e5970c0e581497fbb90cc3f526c0d9d553c5fbcdb2aaadcb64149ad5f22b0098708195f37d82a5829000182e20381e802208fe0b57c9c0dff7538fdeedef16dca53661ce854dc7a06a296a6adc3491ce7361a0037ddee811a0457c65a1a004f598dd82a5828000181e203922020607d2d181747f8a1df3b139e06f52588adf196097d38f13f8160aed6d437e1188708195f5cd82a5829000182e20381e80220c31fbd6bb96206aaf825a12337ddcac48808b5b835ef8d2b1c6b82255f1b0e081a0037e0a6811a0457f0331a004f5983d82a5828000181e20392202036078ad115da0b077798bbffb9abdf4756c5fb3eba49d917f608f4e93791b13b8708195f38d82a5829000182e20381e802206a13d744c58c3c3194185fc76b711ddd793451a6bc1c1b0d2593391fa4b9aa271a0037ddf7811a045921771a004f5980d82a5828000181e20392202085574c49ce6d1f8149b4631ce8d502e8764c0e1a949f05287f34553420e6ab228708195f5dd82a5829000182e20381e8022037ea5178bc9933cd3e272574beb4420d23ba7fd1141d27ef728aa2a03cb78d1d1a0037e0aa811a0457e4e41a004f5987d82a5828000181e203922020f4abd76e214c8125bd6b5470b3d111cc049db132b3e80cf0d9e64beb13cd4129000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x307c5da", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xef58a04d45dda0091572a6ae13c1c043e2397ab851d94fb07f108d5bc34beac5", - "transactionPosition": 62 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b19a2", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x4e01c98", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xef58a04d45dda0091572a6ae13c1c043e2397ab851d94fb07f108d5bc34beac5", - "transactionPosition": 62 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b19a2", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x4cf5750", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xef58a04d45dda0091572a6ae13c1c043e2397ab851d94fb07f108d5bc34beac5", - "transactionPosition": 62 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b19a2", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x4bce48f", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000043818583081a004f598c811a0457ca2783081a004f598d811a0457c65a83081a004f5983811a0457f03383081a004f5980811a0459217783081a004f5987811a0457e4e40000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xdad3b5", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000de8185d82a5828000181e2039220202e5970c0e581497fbb90cc3f526c0d9d553c5fbcdb2aaadcb64149ad5f22b009d82a5828000181e203922020607d2d181747f8a1df3b139e06f52588adf196097d38f13f8160aed6d437e118d82a5828000181e20392202036078ad115da0b077798bbffb9abdf4756c5fb3eba49d917f608f4e93791b13bd82a5828000181e20392202085574c49ce6d1f8149b4631ce8d502e8764c0e1a949f05287f34553420e6ab22d82a5828000181e203922020f4abd76e214c8125bd6b5470b3d111cc049db132b3e80cf0d9e64beb13cd41290000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xef58a04d45dda0091572a6ae13c1c043e2397ab851d94fb07f108d5bc34beac5", - "transactionPosition": 62 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 3 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b19a2", - "to": "0xff00000000000000000000000000000000000063", - "gas": "0x1053f8b", - "value": "0x48fa86c15fa600", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x1770", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xef58a04d45dda0091572a6ae13c1c043e2397ab851d94fb07f108d5bc34beac5", - "transactionPosition": 62 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001614ac", - "to": "0xff00000000000000000000000000000000018a9d", - "gas": "0x2950360", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f285028182034081820d58c0a24c0e37761bfe50b11dd0fd147cc58de9c706966e58ebf1c59bc68d826c3aa1c8ce21e214b65146eebb6599c7e9b13e9771b9703259e98da1c6be2581c7a3d32de9391b77cba94683e4a488a85b7677f7c4b24ee043e5b74328c64b2ab3548e168389d0eae2771b996bd858db8331aea66eacc1dab69bd7488c341a69d1a77c19373ab2cd2e0a213a4dd87e3a65b47ab6ebe365a6b834dd6d0f508e3cf61f4d534aafcc397017d2858c44e26b493131f8db7feb49cdc4df3f916b2681d5a8701a0037e5f6582080334418ee2e59dadfdb8d33bf996c9c35f36dbe740e214e308a34ebd796ddc90000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2288efe", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x9821a2e7145a9a0635ef1ef3b683392817d43c61caafbfbff0ec5422c3ada2bd", - "transactionPosition": 63 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b199b", - "to": "0xff000000000000000000000000000000002b19a2", - "gas": "0x43cf604", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000078782195f2a590780af0a4181a3dd50f9927739353d495ca29562a915a1ac79b0a82ac9fc0a2f6cf9a54d632c8c9f0198f2837f66507ce46eadce1f4c166c6256647d8973789a927937e5e74d9a27305ce146b549af4be391a9ae468e2a579b2864a66e7f641ffa38019d7736b70736af2d3747d2b5baef4a5d594ff32fec3d286dc115f7af556287d19df302659af6dbc84444e43eb9afe1a42de2f425ff8d15f75809e36e3d559197df381cbaf4cab36ac23c51ae9fc19c0e9dd92ce497c3757784cdde3d11c766a8a3142e9a8ddf1c5c03f6cb797b60a407d6b70395c40be27bca492c50f56b990468a1a436d4d84109afed2b7a8ace86b7c4376ac7e99bf30ef9360b4f84cfe243c8808e68fdc48d6a4780c225d78fe033eadfd2194bff4b72dd07e0c763ce8d13687ceddb0fe5ddcbf00a8d3ef2795a04928c1cd2c8ab9883d8b027d6b9d2e7130732f84d2e7677e8a66fc09bf7677fa8e5a83b4318d7991fa6e9c7e4ed85d92f4d0aa0421ca497c5248e7740b133558b3f348254b784271ea1cba40c570c6ba6dffaf4a6aa9c7f7f0c32d3175cd4b3ecacc8ec238a0b2dcd12decb7d6915f04cd722fe3c39385a51e4fd5a62663027a8e0e9d833d18e95f375aaecac522193cd514123eb9e351fc001127196a777d6cf7a47e490d38bcc435539a21bb781770ebef5972953b9793169212f89ebf0e717d9e37985df6313884a24442391e52cec8e668ffa90cc2f87e4a0ed3d4358d3b8d1ab4702b58df778b1710de8d416f6249f10524144721304e415dcb2b41599553e111f80262229c3e3ef6d2cd9c0a0a34efc99920676c84381dcc23180cc9094bbd90c29caa08077a6dca013e081c38db3db3d69dc436fe177d940ae47f566a2326d7b901bf823aae9ada3d119291b1d75eb7e327b19b4bec1217b732fad8c4c0a68bb8238fc3b9ee2ebc78d295773030cb0c8526ed6426f0765b5b1f1e89d0d80dc399f99ad18b4fb29e1236babcf994e75f7f60a68cd364bada4a5765af2b50f4b3a4695b3fdae7c48133f80fa6148b12478a1b46810d31258b50e3a694d6817548a39d664ba0d5fdda0d32f458b91119ff404818d4163345ac7fcb4dbff87b2c20d407c829c200236f9453a43a8bc9cde4da3d3a6763d306e86f97cf996b875e446091b221926e6a3574a64e8eed09a03e8450707c097c5059b357df5d2625446ebb495637f2137565d97a6e919006a576f55c4eccae9d0919dcc15984d908f970d6988fdd9df8c505c0f1c078069a659c07c420063bd90c71d4b9b4d16908921063c78e8b88fcd606b8505e4cb4c0a23983090df8fa3b14176c08c293c9404a2f3844a16eb4c3d2542398cf90db898fdf234259a06f52e445e2ef8755db263a09c97abb53b6c9044fb2214fbbeeaff34c0888b8ae6977a082b521a106aacaab8acd1faa0422a83a4fd799d3f3bd384bc017b5c2e2d00cd16ebdc2b41dcf1f5f41886ae1455fe0cb8769066a4f114205ed50579936c6ad6ee817a47080865f04c33bf029acbedeedf5474f90e9e556cfebb52bb44b745ac3d1119980a2998282e3384b4b4b72e44a2ce7715853f9f8b4289d83c5a524d0fde04ae762a1e6b9a25bf1a6ea57eded7737afca6b61e83639136b89ce030c6c54cedb8392db55895af9ad866bde97db08fb12f9f235e296c86fa007954cce91f615ff01ad1d7aa0ec788362313392bd4d47921872d821571d42689c2d10e9f27e30e065b0f1c03643f419b5ca7aa5157373e6c0b8ae60b5a56daa8932782bcb72bd7da154719fa551a84af6e3f7b5e30f263944c6c1f47fa510d037b90e01b1095df28baf2ef9885b7af50aaab01ccd913b9f1dccfec40b7fe6c4aefd442654aaa5442abd774d41d55583656a53d5bc9f3ff08d87478894a9ddecd39efa5f2119bfb25415970408fcf3265a8faf706bf6841953e44371630048ab9b604354fa279314cd9dd19af0822942179f32bcd9f9d1b19e4ec3ad7308372d8b5bf03908178e5d4c5e87a3eb40c0120fccad46d4d0f91c484416e0132a4cc9dde03bd5d4c492aaa06967014c5b414c8fedb95c4553f69643e6bdcf3076a67a85b8c32ae6a0e72ce8d2737a8724688121662677175c2ac618a2c481cd4043ce6c0fcd6a1d9bb39e1ac0caf63d27b1a01366de0f5f93d007f480efc95b00263a391ba807aed88a3e421ab2d62b9e604456026434da79a0949a7b12f9c791ddf565ea9f107b4c1b8050d563ba5a633528f068dc30cf7a440cfe418de1f54c571ac670cc2a2fb17ee44e9d57f9c7cdadf0b7772bba6edb8a6b260631a01532da47bf3fc27fd5b137a4a449e0a25a4adb6b60eadd2ec7abec7e32f970fe50696932a324eb8a81297a23f6e27d082d3e7bb299625f591c887bcef5587fdfaad00d5e1faf2c1c17dd48b0504993f224285f3c40e8965bc39422455ff1e248a1ba8859079c361dd332b2246b733cf51cb21b38f704127a36cf08f92b9200a3f951b90979e061b5b3b3c7163fbb03eab9ec644a1f14b05c3e4d90ec541006d90c315a69a4bcbebba3bfb5a1be8814bf3006cdbfc7436673e967e1d2d0be00f12c2ec5b7fc3d05ae6d23cd318c77f9ba86f31dafd6c886a3bd0e05dbbaf6dd052c29e10c13355768314c14c1077f3e8ada461e49d3bb8e0d9270b25f1e35bc638e1c732188ff5a7e082cfd91183afdc79ecc78878e1e0122abf3b767051b93600000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x841db2", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x352d011f8bc53595968e076db6f8bd657486a8fea38439ae699980096c2dc7e9", - "transactionPosition": 64 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b19a2", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x3e9b138", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002b19a2195f2a811a0458bf95582019d87ad3ccfc90bcec5bdd6e8270ed89d169ef967b5f727bae65de6ef176ae9b582045d1743b2baf96952374e2de1c0f2ef15e7cf0e678ebb9d158d2572d304a8530590780af0a4181a3dd50f9927739353d495ca29562a915a1ac79b0a82ac9fc0a2f6cf9a54d632c8c9f0198f2837f66507ce46eadce1f4c166c6256647d8973789a927937e5e74d9a27305ce146b549af4be391a9ae468e2a579b2864a66e7f641ffa38019d7736b70736af2d3747d2b5baef4a5d594ff32fec3d286dc115f7af556287d19df302659af6dbc84444e43eb9afe1a42de2f425ff8d15f75809e36e3d559197df381cbaf4cab36ac23c51ae9fc19c0e9dd92ce497c3757784cdde3d11c766a8a3142e9a8ddf1c5c03f6cb797b60a407d6b70395c40be27bca492c50f56b990468a1a436d4d84109afed2b7a8ace86b7c4376ac7e99bf30ef9360b4f84cfe243c8808e68fdc48d6a4780c225d78fe033eadfd2194bff4b72dd07e0c763ce8d13687ceddb0fe5ddcbf00a8d3ef2795a04928c1cd2c8ab9883d8b027d6b9d2e7130732f84d2e7677e8a66fc09bf7677fa8e5a83b4318d7991fa6e9c7e4ed85d92f4d0aa0421ca497c5248e7740b133558b3f348254b784271ea1cba40c570c6ba6dffaf4a6aa9c7f7f0c32d3175cd4b3ecacc8ec238a0b2dcd12decb7d6915f04cd722fe3c39385a51e4fd5a62663027a8e0e9d833d18e95f375aaecac522193cd514123eb9e351fc001127196a777d6cf7a47e490d38bcc435539a21bb781770ebef5972953b9793169212f89ebf0e717d9e37985df6313884a24442391e52cec8e668ffa90cc2f87e4a0ed3d4358d3b8d1ab4702b58df778b1710de8d416f6249f10524144721304e415dcb2b41599553e111f80262229c3e3ef6d2cd9c0a0a34efc99920676c84381dcc23180cc9094bbd90c29caa08077a6dca013e081c38db3db3d69dc436fe177d940ae47f566a2326d7b901bf823aae9ada3d119291b1d75eb7e327b19b4bec1217b732fad8c4c0a68bb8238fc3b9ee2ebc78d295773030cb0c8526ed6426f0765b5b1f1e89d0d80dc399f99ad18b4fb29e1236babcf994e75f7f60a68cd364bada4a5765af2b50f4b3a4695b3fdae7c48133f80fa6148b12478a1b46810d31258b50e3a694d6817548a39d664ba0d5fdda0d32f458b91119ff404818d4163345ac7fcb4dbff87b2c20d407c829c200236f9453a43a8bc9cde4da3d3a6763d306e86f97cf996b875e446091b221926e6a3574a64e8eed09a03e8450707c097c5059b357df5d2625446ebb495637f2137565d97a6e919006a576f55c4eccae9d0919dcc15984d908f970d6988fdd9df8c505c0f1c078069a659c07c420063bd90c71d4b9b4d16908921063c78e8b88fcd606b8505e4cb4c0a23983090df8fa3b14176c08c293c9404a2f3844a16eb4c3d2542398cf90db898fdf234259a06f52e445e2ef8755db263a09c97abb53b6c9044fb2214fbbeeaff34c0888b8ae6977a082b521a106aacaab8acd1faa0422a83a4fd799d3f3bd384bc017b5c2e2d00cd16ebdc2b41dcf1f5f41886ae1455fe0cb8769066a4f114205ed50579936c6ad6ee817a47080865f04c33bf029acbedeedf5474f90e9e556cfebb52bb44b745ac3d1119980a2998282e3384b4b4b72e44a2ce7715853f9f8b4289d83c5a524d0fde04ae762a1e6b9a25bf1a6ea57eded7737afca6b61e83639136b89ce030c6c54cedb8392db55895af9ad866bde97db08fb12f9f235e296c86fa007954cce91f615ff01ad1d7aa0ec788362313392bd4d47921872d821571d42689c2d10e9f27e30e065b0f1c03643f419b5ca7aa5157373e6c0b8ae60b5a56daa8932782bcb72bd7da154719fa551a84af6e3f7b5e30f263944c6c1f47fa510d037b90e01b1095df28baf2ef9885b7af50aaab01ccd913b9f1dccfec40b7fe6c4aefd442654aaa5442abd774d41d55583656a53d5bc9f3ff08d87478894a9ddecd39efa5f2119bfb25415970408fcf3265a8faf706bf6841953e44371630048ab9b604354fa279314cd9dd19af0822942179f32bcd9f9d1b19e4ec3ad7308372d8b5bf03908178e5d4c5e87a3eb40c0120fccad46d4d0f91c484416e0132a4cc9dde03bd5d4c492aaa06967014c5b414c8fedb95c4553f69643e6bdcf3076a67a85b8c32ae6a0e72ce8d2737a8724688121662677175c2ac618a2c481cd4043ce6c0fcd6a1d9bb39e1ac0caf63d27b1a01366de0f5f93d007f480efc95b00263a391ba807aed88a3e421ab2d62b9e604456026434da79a0949a7b12f9c791ddf565ea9f107b4c1b8050d563ba5a633528f068dc30cf7a440cfe418de1f54c571ac670cc2a2fb17ee44e9d57f9c7cdadf0b7772bba6edb8a6b260631a01532da47bf3fc27fd5b137a4a449e0a25a4adb6b60eadd2ec7abec7e32f970fe50696932a324eb8a81297a23f6e27d082d3e7bb299625f591c887bcef5587fdfaad00d5e1faf2c1c17dd48b0504993f224285f3c40e8965bc39422455ff1e248a1ba8859079c361dd332b2246b733cf51cb21b38f704127a36cf08f92b9200a3f951b90979e061b5b3b3c7163fbb03eab9ec644a1f14b05c3e4d90ec541006d90c315a69a4bcbebba3bfb5a1be8814bf3006cdbfc7436673e967e1d2d0be00f12c2ec5b7fc3d05ae6d23cd318c77f9ba86f31dafd6c886a3bd0e05dbbaf6dd052c29e10c13355768314c14c1077f3e8ada461e49d3bb8e0d9270b25f1e35bc638e1c732188ff5a7e082cfd91183afdc79ecc78878e1e0122abf3b767051b936d82a5829000182e20381e802205236012662fbb977c807728310b40aebc827a02db9ee6a6eb618093047f3f416d82a5828000181e20392202003feb391e43dda2b8d156216efff3abad9143e71882e38ff2f78ed23db51e537000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2fdd661", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x352d011f8bc53595968e076db6f8bd657486a8fea38439ae699980096c2dc7e9", - "transactionPosition": 64 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002d1d1d", - "to": "0xff000000000000000000000000000000002b1e7f", - "gas": "0x3735056", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000708181870819599dd82a5829000182e20381e80220d2d632c58c38d9ec51c905873d86022179e01c1508c4cc851f5f6a1ab3e4c4241a0037e0fc811a045b33ab1a004f64ead82a5828000181e2039220202f50056598e93a1acef58e7f51d3ea8bb0c7a50758cda43c77f43cd2eb09140100000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2660a8d", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x905d04dc590112e6e1c646b036395ef60c40a0dd3d1692e5d7aa645b72206993", - "transactionPosition": 65 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b1e7f", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x367ea2f", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x905d04dc590112e6e1c646b036395ef60c40a0dd3d1692e5d7aa645b72206993", - "transactionPosition": 65 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b1e7f", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x35724e7", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x905d04dc590112e6e1c646b036395ef60c40a0dd3d1692e5d7aa645b72206993", - "transactionPosition": 65 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b1e7f", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x3450f76", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a004f64ea811a045b33ab0000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x478f65", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e2039220202f50056598e93a1acef58e7f51d3ea8bb0c7a50758cda43c77f43cd2eb091401000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x905d04dc590112e6e1c646b036395ef60c40a0dd3d1692e5d7aa645b72206993", - "transactionPosition": 65 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001db1ef", - "to": "0xff000000000000000000000000000000001db1f8", - "gas": "0x4ff0eb8", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000789821a0001933e59078085a6a05ba075038c36680da91e35dc5e0342a4b0321542423416ef2cd460c0c4ff58ffa9ef1fa9cc25b2552a34c615dba6b9e7239402bf8e7c1d6c5c4310df78676f565d7a5cc7d86d9b5167dd1ab39f5ebae6bfdd2ce39f0121e4da37372620167a8403f8b0559db674be819fd9fd0fbf56c3d2380e69d7b24ac4ec5f15a984f2a347f7ba4369e2b4138a929933721085746f24dde157e3505c368b5f2da8f0f771be99312f7036e1104a8be9110ea577633f13ed70b3af39063c3421cbd673b3794651781e4b02612ac51ad3d045241c3f529ad25440301aea3e523b862ae800ae314e9a7d3c56c10df3febe2c2745a8431b0e400b9e78a151d50861417006dc356daa9dad6de15e4f62c1f3755e326fa69c95ef5bb94447912b0420eaf16b1034069ea566340dcaaad6b3c0e3230f0e24e829deba4880f6b14b069bc1fad638fda5443c98c7a23d733f50316b26ee887cda3dc12b890ebbfe8cd06f0a5e932937e994d3632086e26068050e0eb5e38788eb9917f96d995f9378d0705a71a682a4ea8657a5dc5b301e650f0ec27a5061b72f1b0378c5ccad2af6f73c802f87c56497ca82134b0b33ff48c211664128b2568daed030ab8a9395c7aa3bcd9e4f45570cfadcead43c36274ec16ceaf030b79cc110bb4526ebf51872b2420c025b0dd6eea34b9433bba6343fc6423ef42f6ff8ffd66e1f221d3385138a03155dad6d88e341d059a211606a7b23626046a5b11dcb4b1a36f783f863fe0d98a152c6a776498bb1ce5c9d36782f7ef973439585c50410ae820cdde1e5e78b7ed8f503b499e35f83958098c0d48896c0dc5e8fefe05a8cf42a28bec48f80dd2ac9de1fc09b409bb326b90a84b935c3867530b08df919bee3190d349e9416a8da77a23769d41f94990c342a042024389c6d6cd76994836fa3460ced9122476ec30848370c21e632ceef9ec9c06809562c90d008591f3005d92638efd1c3a4f6f8d86df6e0d39b30cf8f6ec91afc479db4e73016818d72c72e7ea7269eb2baa704b384e39e852103d92696cb10223e1262b3bcfb2f555ed19fbe9adc4cb16e50964be2f9aebcc0cc146466f72ec0c226e4d4e0d9846b99c646bfd5113884ae519ce08dfd6b08eb22a1af25b6d1815c7758bea333b9a410db11c75981e3c8fd36c663ee2fe62da024f01d684333317f1c8c075af0baff1dd9e1e647a295679900455cb16601e70b453351eda37402cfd61566ff38ae99f209e94a03976744a02b3a65f7b09d39bea0437cabc80d3971d0846d565d8bb1534add4e34f906b971460c23fd49f7ceb43db3647e8b4fc88417462477782a5dad0e67a4784c54490c5b21f971d192cb642a66c41d3c68a10b3655552b007fda2576a8cbb01ee3ff85021d26ddfd652a04942a586794ea3ba963ac24509ba0e8ee686f62f67c7d15d0efe99f86500f7f1c109939619a594b7ac7465179d1aebe00ea6a4fcb81ff9d3df670995d2202214db0a3d5eb371dc45fe23c3e82e7ec22f0d61b0d79714dcec70249fb447cbc96e50c387210bae5a4307d24dde932906bc825c16499f98c1837d6cf3561dd5fdb49618f6cfb5a1ca730566923a7836b769548ef077db369969121768624f1a7504590fe55a2dae2053b7b3f3f5301934abe9e804eddd301547c8d3aaa947caf2d21053d659786fbd959e2658777b9aa2808f3319a1189bad15b5f735bb6bc8e068acddb64e9131733ba193b38d9039f5646024ee3eeb5b952191dde3fd98019471737f6e6dc983d57a9fdee96aaf60598335fab83da3328b7f208d0731458e59377832573e74e6de7dc2337eb381fa37e4a08c79a65c754655d6af439d8ad54b347b98323dfe758e27178a40d63208fc1fcd3df61b2d7160757947d46c696a8843f7ce187c6656ac0008407a7b5f634405ea440bc3447ec91910599f4edd8def67045b4200778e4c9757c8e868bc08366fffedc3ce00dbff563b173552ae31d197a949f3d18bb143d460a0e02d3b4aaad2779c0820b2853477964deff42a9067033402aafefbd28dcae30cadf7e68a92b5499c47faf0a6cb25d3225dbbe1debb6a7f8be1692e1ed61ca929fa7b6d88a13905e6992dbf0e9a2102c50b8e346c9fa52e67516a226b4a6ed0a5175c2a4432c22882acb727a284191fb0207329a873381bdbfae72dc2a2f1e6be1c0a8bf77e090d056a7689b5b6763f128f8b76639404cc14d396d6604d2a8c5145ccb1bb72a3d899550b93b132cfff03f3b941c92677f1bc1d0d0bc9819ca0467643eaacd6eb7923124b15cd5cbefd24647152e11e3865c1e17bb1a35f7cd7a57d50998cd91f03efc0f5dc9b5658b8cc739f46745801fd9dedf3829a7ce44ce25ef3a0cb8b49b8c18c89247f4d5095c877d71302e66ccc2ac9debf3c6fb511bad40eae508965dfdf66ed8d45e218f8eb3fc13e98644b50ddd91dbc11d6944ec32801ce9761e585a69930468de3b262956f72e250eb0bb4711b0387509df05732feced968a04b24fe6805ecf93ea6d85d178f4af97ccc05811f56288f28972b3fb873a1b626056be4f4aabe5b3b8bcbfdcdc277e0854c1c3eace993ef36304bb9453cd91e76b60bfbca1bf1613426cdddabd05e689ba64cdbc8c4ffaf177e1a9731b38dca1fb28934b8140d54f6e0a636cdee02e18c31b79871bb61d4a1d171798460859b76e358af75eac0ea8c55d3dd93b584a0000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x7adabe", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x6393b0c1a03fcddab9d197626ee380c8dce03ac552781061a25ce03ed4a4b293", - "transactionPosition": 66 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001db1f8", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x4b51aed", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008338808821a001db1f81a0001933e811a045b2300582097527d534b9bb0b5c38ed86fd4baf2c362b69fc2cb99f053439fd0d75ef7fa5e5820064b2b8946c028299181336eb551f58ccaa48f3f51029555218e5b6eb443910b59078085a6a05ba075038c36680da91e35dc5e0342a4b0321542423416ef2cd460c0c4ff58ffa9ef1fa9cc25b2552a34c615dba6b9e7239402bf8e7c1d6c5c4310df78676f565d7a5cc7d86d9b5167dd1ab39f5ebae6bfdd2ce39f0121e4da37372620167a8403f8b0559db674be819fd9fd0fbf56c3d2380e69d7b24ac4ec5f15a984f2a347f7ba4369e2b4138a929933721085746f24dde157e3505c368b5f2da8f0f771be99312f7036e1104a8be9110ea577633f13ed70b3af39063c3421cbd673b3794651781e4b02612ac51ad3d045241c3f529ad25440301aea3e523b862ae800ae314e9a7d3c56c10df3febe2c2745a8431b0e400b9e78a151d50861417006dc356daa9dad6de15e4f62c1f3755e326fa69c95ef5bb94447912b0420eaf16b1034069ea566340dcaaad6b3c0e3230f0e24e829deba4880f6b14b069bc1fad638fda5443c98c7a23d733f50316b26ee887cda3dc12b890ebbfe8cd06f0a5e932937e994d3632086e26068050e0eb5e38788eb9917f96d995f9378d0705a71a682a4ea8657a5dc5b301e650f0ec27a5061b72f1b0378c5ccad2af6f73c802f87c56497ca82134b0b33ff48c211664128b2568daed030ab8a9395c7aa3bcd9e4f45570cfadcead43c36274ec16ceaf030b79cc110bb4526ebf51872b2420c025b0dd6eea34b9433bba6343fc6423ef42f6ff8ffd66e1f221d3385138a03155dad6d88e341d059a211606a7b23626046a5b11dcb4b1a36f783f863fe0d98a152c6a776498bb1ce5c9d36782f7ef973439585c50410ae820cdde1e5e78b7ed8f503b499e35f83958098c0d48896c0dc5e8fefe05a8cf42a28bec48f80dd2ac9de1fc09b409bb326b90a84b935c3867530b08df919bee3190d349e9416a8da77a23769d41f94990c342a042024389c6d6cd76994836fa3460ced9122476ec30848370c21e632ceef9ec9c06809562c90d008591f3005d92638efd1c3a4f6f8d86df6e0d39b30cf8f6ec91afc479db4e73016818d72c72e7ea7269eb2baa704b384e39e852103d92696cb10223e1262b3bcfb2f555ed19fbe9adc4cb16e50964be2f9aebcc0cc146466f72ec0c226e4d4e0d9846b99c646bfd5113884ae519ce08dfd6b08eb22a1af25b6d1815c7758bea333b9a410db11c75981e3c8fd36c663ee2fe62da024f01d684333317f1c8c075af0baff1dd9e1e647a295679900455cb16601e70b453351eda37402cfd61566ff38ae99f209e94a03976744a02b3a65f7b09d39bea0437cabc80d3971d0846d565d8bb1534add4e34f906b971460c23fd49f7ceb43db3647e8b4fc88417462477782a5dad0e67a4784c54490c5b21f971d192cb642a66c41d3c68a10b3655552b007fda2576a8cbb01ee3ff85021d26ddfd652a04942a586794ea3ba963ac24509ba0e8ee686f62f67c7d15d0efe99f86500f7f1c109939619a594b7ac7465179d1aebe00ea6a4fcb81ff9d3df670995d2202214db0a3d5eb371dc45fe23c3e82e7ec22f0d61b0d79714dcec70249fb447cbc96e50c387210bae5a4307d24dde932906bc825c16499f98c1837d6cf3561dd5fdb49618f6cfb5a1ca730566923a7836b769548ef077db369969121768624f1a7504590fe55a2dae2053b7b3f3f5301934abe9e804eddd301547c8d3aaa947caf2d21053d659786fbd959e2658777b9aa2808f3319a1189bad15b5f735bb6bc8e068acddb64e9131733ba193b38d9039f5646024ee3eeb5b952191dde3fd98019471737f6e6dc983d57a9fdee96aaf60598335fab83da3328b7f208d0731458e59377832573e74e6de7dc2337eb381fa37e4a08c79a65c754655d6af439d8ad54b347b98323dfe758e27178a40d63208fc1fcd3df61b2d7160757947d46c696a8843f7ce187c6656ac0008407a7b5f634405ea440bc3447ec91910599f4edd8def67045b4200778e4c9757c8e868bc08366fffedc3ce00dbff563b173552ae31d197a949f3d18bb143d460a0e02d3b4aaad2779c0820b2853477964deff42a9067033402aafefbd28dcae30cadf7e68a92b5499c47faf0a6cb25d3225dbbe1debb6a7f8be1692e1ed61ca929fa7b6d88a13905e6992dbf0e9a2102c50b8e346c9fa52e67516a226b4a6ed0a5175c2a4432c22882acb727a284191fb0207329a873381bdbfae72dc2a2f1e6be1c0a8bf77e090d056a7689b5b6763f128f8b76639404cc14d396d6604d2a8c5145ccb1bb72a3d899550b93b132cfff03f3b941c92677f1bc1d0d0bc9819ca0467643eaacd6eb7923124b15cd5cbefd24647152e11e3865c1e17bb1a35f7cd7a57d50998cd91f03efc0f5dc9b5658b8cc739f46745801fd9dedf3829a7ce44ce25ef3a0cb8b49b8c18c89247f4d5095c877d71302e66ccc2ac9debf3c6fb511bad40eae508965dfdf66ed8d45e218f8eb3fc13e98644b50ddd91dbc11d6944ec32801ce9761e585a69930468de3b262956f72e250eb0bb4711b0387509df05732feced968a04b24fe6805ecf93ea6d85d178f4af97ccc05811f56288f28972b3fb873a1b626056be4f4aabe5b3b8bcbfdcdc277e0854c1c3eace993ef36304bb9453cd91e76b60bfbca1bf1613426cdddabd05e689ba64cdbc8c4ffaf177e1a9731b38dca1fb28934b8140d54f6e0a636cdee02e18c31b79871bb61d4a1d171798460859b76e358af75eac0ea8c55d3dd93b584ad82a5829000182e20381e802201c82068b925940ebe16ec679b86840d59d53a5afbc0ac8a51cf35e448f994a68d82a5828000181e203922020f96346a454a264d426a574e670033337c65e38a464a9fe95b5347d845167f73200000000000000000000000000" - }, - "result": { - "gasUsed": "0x3016322", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x6393b0c1a03fcddab9d197626ee380c8dce03ac552781061a25ce03ed4a4b293", - "transactionPosition": 66 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001db1ef", - "to": "0xff000000000000000000000000000000001db1f8", - "gas": "0x4c53635", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000789821a0001936a590780979cc13480c6881871acf163c67f7a1a2bc1f03cc354f55dcf9dde15e48cc8a68e98e105f7e7616ad71c627e3d8f6e9b8943c90960eefd4a81a64f2fbdff01e1898df27da876dbda7c1e563a46fbfd254c52605b2add4b45236708ffa56c2fbc07908bb0d02575f826cdcb31c359c02aabad55583196cb94a22d4f17192f8b154a0e1a1f82153db370e15254e9f2976fadeb82b9f9dabed2109febad359bf7f04ea2468d908f72fd558a3e6f38c71e7e85bc1ba5111b542f05ccedd6e05b5599b348dcc5e5c24397b8bca23768f81ce95793fc3bc58d35ea8a77edcdf8dd3bb8bb527033e09f1f013bf3a4c14e050d4892ce22664802477bb67714e5ab3554f6f533905dcc74a467a55daad996dc9e7ba973238b0bc61cc5fc0dcf6f8867746b07a3026ffd5871c015495177899006daebe855e8b582b4141f55a94b06c07535589c5b736aef5faee110569bdebc43f5a97fb56e9378ada1f40298d28f98b3601e22b8029a62d1787652dada0e8b2fd1be40f8371bf9f2622ac367f91e3a4ff6a5d2b2c648eb2265eafc4e1c7a970ca09cfe7b3155bf10b42596bd5ff02f7d4e89010e9c4ac3a27aef7e8a543ca01310a89951f79a2f2429618dae9ae72baf2ebd4252816841e095b84d0d7abc399ef3d9435803749fa2bdf5ba8da6091e0e6c03b171affa5d8a3994ba6e269e2626318fa75808e6962ec2ce525b6ba5c3a395c90698aba9dd1f775527109dda8f3923a1b29dc929d02c9944f123f6b5a406b981ac7d94fdae623b963dabb44965a2ec39dbc3c350e060859f09b33c33b7183ca3507d467b152b9adefffb424b0411309bdb55882d0ed0284bd5891e81a2cf43243ea28da1815900beae3b1241952ff0989730da43746acef8bd5f5180291fdd707b206b7c5e3db17434cd1cf0c9951b147d9de68552128857e9867410bf03a50399230d371fcad03c528ddd8820305db233fb5f807f7268621f43b7bee3d44435ceb00b959a9298343ab73af240d2c6adc91fd617ee83aab7d65cf53248f30699745db31f4727a5a8f4137efc258d63621fd8326c90c26195e85686bea4c9eb8694df56f88732df7e7112b27259326c8d151410b922a23103b10ee6d81d325be256a6760c5b700fc4c21a99ae270f1fb4b6d0cc60948801c9d408bc6223eafcbe52dce0cc9f1ed2f2f2f52b3ba691418fda181a27417fbf753e14a8680b3ff4000579547b6c37c0d72e6d2e8b48280f96da26b838373d5a242342a9a46daefd8341852f2f53f5dfac1d8d40a3402ab891a0a8da280978d7803d88f4c52ab376d6db65c3cf315ee5f185e0f8b30d72f7da427654ead6a1790ff750bb41113313a32d9e81be3849240ec12417e7f16eca03a9a381b90352649b5ccfbd8a1ccaedaf858ef2cb1b31f85bce89d86e471984b30e029c13859ecc88301ade00414c5140b0a1ba262677a9d1f082287db7bdf4ab1f5d6ff313dbdbe62ac8faa283471019bcdb8670033166fc082d37812abe3a30284a67f7bf95c2ed53b1b521baa8d1069578cf5c01c8d09351fb27fe25a8d7b542582846c6b4c8c7f3decd48c0e21d6c00f6106e27a64e8b8a0cb6acf08c1d3fc566229e9317b81c372c53e0b4d61fb477b6c786b0bc58ee7a794b988fe8de466d93665535e19b98b909537d133aa4f4a995a89631da6007e4f698a0a2e529a0ece6534782c158195fa292657956c07d76c1668deb0ed453603c35a3bda1da85b5e29d766a17d87f1f995367f3801c066428d52d2c18a9dbb3934b69aa3c5cd6b20987df92d5cd1bbac10c805e33a9c6ec48162385eb4d2d9cd56dee21dc48ae2bb998b343be328590e079d8bf64b367c8169543df713d89ab398b9856c4c7437abd4ecb799cc0f5e56c7a165d1e8dababa0b8ee63474c0d31fa50e59274bdd73db1d0a6ba2fb590a523234fe5026bd276526293fe12ef6ead189f7d967a858a3243befb772036d5763730b6fd1d7d2515630653a23d63558ec0ef68428987bb3c6beb925703ca08c8333cc5aa742d0afc2a7fe498d6cc5f22f13d993a0b5023e6712c547c825a7b0adf77e9a88d93fc539e4f708e5c0fb9f6f44925ba5e2e97ebba350c59965d74652b17c3707440c8c7bb0604f4a0623a34425f37f8e03f50a8bfba1054ad50942a3f4e9c76486ca9a92219cbea8de506ca908a751ad9c51c1e9296f809b2b02f2f66a5659f5d8bd30426c484507723380cc1ab250482eda674e329f191abc9caa647f6ba3d54449e9969fc2a42f340b5440660da775488e5364d49de6a8a4fa8c81b836abc90ba13a01dedc03e28113d7f5d655b7071e665b9f13772632a5cc8df38593b76b6afe348878d07ef2cfb78c1210c26fbe21ea60564376f1b1770a732dd01a39af2f9ebd3b802686dc271b6046bcf3b1c0c8468506cfc654bb04aebe9d67d8814d0eeb9b1aab31c1f67ca73528ca48743992ac0ed70b4536b46ca73bfeb84e6678f5c730a05e6b02be30a8620830554179018980dff44d9bed0578b162191608205f506d2f427edd72d38e0a3b750aab0a7dc896a5cf8339f685ab8573e94728ced5a0a738844c3d8d64750c547383f99a227dd13138db4712808f367392979cd6227a3ce5930eeb8b2ec388b6fb7c0a2fd9da86ddbc94f7f71e87694781c69f428158a923c3c7b5199f7f9dd6d71eb50b8035b53e8bfeaa2ca0c7aaea9930c2df83b0000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x7ac08c", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x49ef42aace56fbd0d5779635f363a2b37eb171aa85aefcf5218da7d495abf901", - "transactionPosition": 67 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001db1f8", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x47b5c9c", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008338808821a001db1f81a0001936a811a045b2a7758203e20b2105a6aae7da18e80f56751382eb6c4750b41c516ded263e3cd722d81f65820096c96c4cac9b18cb607419f842911557776091216b2c3a38014e9484ff0bb3a590780979cc13480c6881871acf163c67f7a1a2bc1f03cc354f55dcf9dde15e48cc8a68e98e105f7e7616ad71c627e3d8f6e9b8943c90960eefd4a81a64f2fbdff01e1898df27da876dbda7c1e563a46fbfd254c52605b2add4b45236708ffa56c2fbc07908bb0d02575f826cdcb31c359c02aabad55583196cb94a22d4f17192f8b154a0e1a1f82153db370e15254e9f2976fadeb82b9f9dabed2109febad359bf7f04ea2468d908f72fd558a3e6f38c71e7e85bc1ba5111b542f05ccedd6e05b5599b348dcc5e5c24397b8bca23768f81ce95793fc3bc58d35ea8a77edcdf8dd3bb8bb527033e09f1f013bf3a4c14e050d4892ce22664802477bb67714e5ab3554f6f533905dcc74a467a55daad996dc9e7ba973238b0bc61cc5fc0dcf6f8867746b07a3026ffd5871c015495177899006daebe855e8b582b4141f55a94b06c07535589c5b736aef5faee110569bdebc43f5a97fb56e9378ada1f40298d28f98b3601e22b8029a62d1787652dada0e8b2fd1be40f8371bf9f2622ac367f91e3a4ff6a5d2b2c648eb2265eafc4e1c7a970ca09cfe7b3155bf10b42596bd5ff02f7d4e89010e9c4ac3a27aef7e8a543ca01310a89951f79a2f2429618dae9ae72baf2ebd4252816841e095b84d0d7abc399ef3d9435803749fa2bdf5ba8da6091e0e6c03b171affa5d8a3994ba6e269e2626318fa75808e6962ec2ce525b6ba5c3a395c90698aba9dd1f775527109dda8f3923a1b29dc929d02c9944f123f6b5a406b981ac7d94fdae623b963dabb44965a2ec39dbc3c350e060859f09b33c33b7183ca3507d467b152b9adefffb424b0411309bdb55882d0ed0284bd5891e81a2cf43243ea28da1815900beae3b1241952ff0989730da43746acef8bd5f5180291fdd707b206b7c5e3db17434cd1cf0c9951b147d9de68552128857e9867410bf03a50399230d371fcad03c528ddd8820305db233fb5f807f7268621f43b7bee3d44435ceb00b959a9298343ab73af240d2c6adc91fd617ee83aab7d65cf53248f30699745db31f4727a5a8f4137efc258d63621fd8326c90c26195e85686bea4c9eb8694df56f88732df7e7112b27259326c8d151410b922a23103b10ee6d81d325be256a6760c5b700fc4c21a99ae270f1fb4b6d0cc60948801c9d408bc6223eafcbe52dce0cc9f1ed2f2f2f52b3ba691418fda181a27417fbf753e14a8680b3ff4000579547b6c37c0d72e6d2e8b48280f96da26b838373d5a242342a9a46daefd8341852f2f53f5dfac1d8d40a3402ab891a0a8da280978d7803d88f4c52ab376d6db65c3cf315ee5f185e0f8b30d72f7da427654ead6a1790ff750bb41113313a32d9e81be3849240ec12417e7f16eca03a9a381b90352649b5ccfbd8a1ccaedaf858ef2cb1b31f85bce89d86e471984b30e029c13859ecc88301ade00414c5140b0a1ba262677a9d1f082287db7bdf4ab1f5d6ff313dbdbe62ac8faa283471019bcdb8670033166fc082d37812abe3a30284a67f7bf95c2ed53b1b521baa8d1069578cf5c01c8d09351fb27fe25a8d7b542582846c6b4c8c7f3decd48c0e21d6c00f6106e27a64e8b8a0cb6acf08c1d3fc566229e9317b81c372c53e0b4d61fb477b6c786b0bc58ee7a794b988fe8de466d93665535e19b98b909537d133aa4f4a995a89631da6007e4f698a0a2e529a0ece6534782c158195fa292657956c07d76c1668deb0ed453603c35a3bda1da85b5e29d766a17d87f1f995367f3801c066428d52d2c18a9dbb3934b69aa3c5cd6b20987df92d5cd1bbac10c805e33a9c6ec48162385eb4d2d9cd56dee21dc48ae2bb998b343be328590e079d8bf64b367c8169543df713d89ab398b9856c4c7437abd4ecb799cc0f5e56c7a165d1e8dababa0b8ee63474c0d31fa50e59274bdd73db1d0a6ba2fb590a523234fe5026bd276526293fe12ef6ead189f7d967a858a3243befb772036d5763730b6fd1d7d2515630653a23d63558ec0ef68428987bb3c6beb925703ca08c8333cc5aa742d0afc2a7fe498d6cc5f22f13d993a0b5023e6712c547c825a7b0adf77e9a88d93fc539e4f708e5c0fb9f6f44925ba5e2e97ebba350c59965d74652b17c3707440c8c7bb0604f4a0623a34425f37f8e03f50a8bfba1054ad50942a3f4e9c76486ca9a92219cbea8de506ca908a751ad9c51c1e9296f809b2b02f2f66a5659f5d8bd30426c484507723380cc1ab250482eda674e329f191abc9caa647f6ba3d54449e9969fc2a42f340b5440660da775488e5364d49de6a8a4fa8c81b836abc90ba13a01dedc03e28113d7f5d655b7071e665b9f13772632a5cc8df38593b76b6afe348878d07ef2cfb78c1210c26fbe21ea60564376f1b1770a732dd01a39af2f9ebd3b802686dc271b6046bcf3b1c0c8468506cfc654bb04aebe9d67d8814d0eeb9b1aab31c1f67ca73528ca48743992ac0ed70b4536b46ca73bfeb84e6678f5c730a05e6b02be30a8620830554179018980dff44d9bed0578b162191608205f506d2f427edd72d38e0a3b750aab0a7dc896a5cf8339f685ab8573e94728ced5a0a738844c3d8d64750c547383f99a227dd13138db4712808f367392979cd6227a3ce5930eeb8b2ec388b6fb7c0a2fd9da86ddbc94f7f71e87694781c69f428158a923c3c7b5199f7f9dd6d71eb50b8035b53e8bfeaa2ca0c7aaea9930c2df83bd82a5829000182e20381e802209731f18aa4fce0cef187b30f84c05ce42eb42b7321ead72441898d5abdeea412d82a5828000181e20392202039f254d4d3235a7d50f05f37d5d73cdc32d12a9c989840c1108a54fb9a15523500000000000000000000000000" - }, - "result": { - "gasUsed": "0x3778f53", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x49ef42aace56fbd0d5779635f363a2b37eb171aa85aefcf5218da7d495abf901", - "transactionPosition": 67 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cf0e5", - "to": "0xff000000000000000000000000000000002cf0ea", - "gas": "0x338cc13", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007081818708192d05d82a5829000182e20381e80220351469a2b481b840bd9743a6fcf7513753a3937e572c7cb25433031de7ec15651a0037e103811a045669031a004808a5d82a5828000181e20392202046998118b3148e63f207a911c0224a4eac12b07b6743415b6bff7524aac8023500000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2354b00", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x0f12d60f9268de78ac12e64be7f99d6dd88c7d865d8ce91f32c11652baf62c16", - "transactionPosition": 68 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cf0ea", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x32d65ec", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x0f12d60f9268de78ac12e64be7f99d6dd88c7d865d8ce91f32c11652baf62c16", - "transactionPosition": 68 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cf0ea", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x31ca0a4", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x0f12d60f9268de78ac12e64be7f99d6dd88c7d865d8ce91f32c11652baf62c16", - "transactionPosition": 68 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cf0ea", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x30a8b33", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a004808a5811a045669030000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x488993", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e20392202046998118b3148e63f207a911c0224a4eac12b07b6743415b6bff7524aac80235000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x0f12d60f9268de78ac12e64be7f99d6dd88c7d865d8ce91f32c11652baf62c16", - "transactionPosition": 68 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce379", - "to": "0xff000000000000000000000000000000002ce3c0", - "gas": "0x305c21c", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007081818708194375d82a5829000182e20381e8022019ea299b91070fce9a4ed47462469afe6593ac2a70357ce4d26758a7334278591a0037e112811a04548e851a00480c92d82a5828000181e203922020690da56aaad92166ebff80ea0daf666c3c7e1a59eb690b95367f47097cd4363c00000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x20c763a", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x949c7c1e151376037350ef882355c9b80173632c806d5ca6164148b7f51ea7cf", - "transactionPosition": 69 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3c0", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x2fa5bf5", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x949c7c1e151376037350ef882355c9b80173632c806d5ca6164148b7f51ea7cf", - "transactionPosition": 69 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3c0", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x2e996ad", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x949c7c1e151376037350ef882355c9b80173632c806d5ca6164148b7f51ea7cf", - "transactionPosition": 69 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3c0", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x2d7813c", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a00480c92811a04548e850000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x488993", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e203922020690da56aaad92166ebff80ea0daf666c3c7e1a59eb690b95367f47097cd4363c000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x949c7c1e151376037350ef882355c9b80173632c806d5ca6164148b7f51ea7cf", - "transactionPosition": 69 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b3543", - "to": "0xff0000000000000000000000000000000021b478", - "gas": "0x1995446", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f285068182004081820d58c08a3c01bd1c9be515f6217c646d9fec13e541dc1445178d0fee2bc33183b23dc4dc5b7d7d06493bb131a44566a5e1eecf8fb68474b5448d132996e4cc35db93b6e13b32f59f2d6a02eccc2f128271e171c09ce20cedfa3b7a57f6905d553af8e403305f072dadfa782b2e673feb8bebe6e75d8875a0af330a3ba0b404463b46ada804bde0677c9e44659a1973e82a243e88d1f18d823d6849eff98fab0e9c585246f8fc4a897916d823c91780159e530fd732352ad88e2474f2cf815c388dc3111a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d690000000000000000000000000000" - }, - "result": { - "gasUsed": "0x1545d77", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x7d27a0315e51d96e06bc4699d5b5efcebf60f838a9db79f1c1ca6183ae3b86c5", - "transactionPosition": 70 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cbdce", - "to": "0xff000000000000000000000000000000002cbe59", - "gas": "0x4f47d1a", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000078782196c66590780906c8270f7a49125a567510c97404d726700444a974c52abaa369eacad17de494b817f70ae9f60f7e4b4fd7bb7325a17b8124630857a51652e94ee9a590f76dd479998cfaabbb74fc4dbdebe8be928083719ba2bc514dbf3723335072095ac5110206d71e43f6acb4c8d4a5f5c4489c2adfbacf8b54f44eeb958ccee349a7bd587c8ad64af6cbe885df7a04af2a4e1cda51388e3d459cb9efcc9905ffc53d4de4a7aed05a0537de2ce8594c672b5c3876bba195e095302b88d73f91b4d1716fab0eca1e729706fee665d539960ef9a4719ec4edc6bc0a4e6b52bb0e76ad484d5eeb2c1650ef9bc821d67524aa82f3bf4825548eb5d9b8a7f1638c747f219649c75241dbc9d1b47edf6baa29e7d85e667f9c96703e9ff09e3e5ee2be72a10a3e30f2e506c74bff2b581521fceb697c2a7b83db32f3c665e213c0eba5a8505d2415fb60ddebf509bbfd1a1dec3df01d480b23bdc16c8268bc0462d477101742b11bb5f31dfc037608610ad3489b78bd776e3e66e7655f5b55f98ef01edbe1c1ad0938b6883764bca24d33e2dae96c467ddb242b153f1bbb41df5d0842d496f1271e4cc9e212ab58369126cd56c30c602708f6b62ce8c1dd20b7eb5de15447c683536faee20399a2f23a619b2069546b279077a5b73c050962447c05997bb9c5831146230ac4f09cbb01eb7dc5abc87e185eb44da96695fd18f6047831d0276710e7aac3e850507ca142bbf4b036b72b872a3c351eff4e017b664d965c4c3a5b3745fb4dbd401f57ec16b0fcff536d8b705757f369e16ae1ea5b59e4f37d45b15d699257bdf5e01ea1be77e3dbb45a5204108e0009e93197e601b7da1d6bc29dab0c8126b99aef1ada965ea0a4c999b335c81095313b3d576be3ae66e4157a86a985285b64eff9380da6f2b4fcb5f6966ade1c97b4c293efc753316ad19d3e542600ef0539c6b1fa0ebed10edd8b6da5f276fc7d07075e1953120543573a4263641e9cf2c1e73df2d803339b29f7d9b14e08811c3f0d631c08f61853a59cffb9f585e2f409b1149602f4861bfafcbeabb4a8f6a0095e5b5cc20c3c7080e93563d4988839181355d52ec6c8e141890a662460b5d8e4b7a53f2abd3922221b848943b6aa79b10421431d8cf5e5973e7b28f218ab7578011d031c9f1ad86fba0f4915aa01e1dc34465c8e39caadf80d8e312e96b4e1ee885bafa1f2d0346461ea8939f08cd15cfa035611642aff82dede8f823d84aa267db9e9a439dddc9eb81ac4855a4746e844aa385b797c674f952b56ecc8a31180a1478fcb46486892bcd7f546b03546a6a2beb93c5412814a1ec8bf9c8cb8c098afbaac58bbfc45e1db9df070188c93ab52ec845d019ec1fc01f4a2e4efeb9930b71905de02d44736a3d07a7b7c34c4f4480dc8f8b974532afb2bb06ea8165a113710d5e429ba435c552fe7aa34183643a0e1b716447f71dc12ac4668f9c67a3f43195f9f4f9ee28451ac2445403c208be6a02cc5ac06012fd1209663aae19460d2afdff338e18f77a3cc12d9439ba513ddfe94c2f15754f39def5eec8b731642c48e08b4edb7c015578f02f69c4d8ba79b746a0caaee744715d94c139a6c9aa489a3ce3a343edac841181136c833f65a9dad3b1dca25f31f729fcb7dbebb47a11eaaf5a0f7904690b5b31a0bdcb88559d34ea252862b0a399dfcc57e283c0ea11412ab19438e439926f6dc9db6086c68ea329984dff59b636137eb83eb0a0c43b4e3d81c5e0e45bf305f0dc720d092086254dfc294477e5e5ff47c793feaa1866c94e251d5511229f1e95c05540b591984e73a1959b4094e62e71affd916daa99eabffc7ce1881e081be632ae2c09d61ad1b69e1b2bf064eae889182e86e0d65ec4667f75e2d277ee17b0cb85aabe7c6b7169011ed82f831758553ae49d4ea6e41b7e578c6e354c33fe528217a0f331ea1ba1350165b3098015cb58d38340811ff27e5e20a1368081fe140bd1ebc77dfdcdb28bc567e28b6584a7c68b68f6dec93e9cc385135038ee735322260f7c90044be8a19bf18d99e09f70607ff2a6a3a4ad29e0a291202eb601186b75e69835d2258e74bdbd04ec8f9c75981eb6f4dc7f09c01763aa3a3de91dd16c0b3f61b97802a4dd6dfccbc1da28fa09a5f8ee64b41ba12580d4a4163f84ec9f81a4edc5fba7fec105aeeb32dcb68d757cbd276c13a5e09af8302a11e5b503e7911d058ae7c1dcaa44b5927c9ab4f02aa796a478b2442b91f21877c46d91b41b07ba9b2b8514bd1adb787a9a3c27efa27fdeb8371e1d27c66ab80da9ec7c2c1f43145e73dd2483e3a93ea363c28131b8aadcbe8c7fd536caa63a79df8cd3a2e432fb449c6df33bc07a4a85328c5f0d7f1c89d5920bb7272f5b320df888a23877838b9bfb9a53217b09c71b5c5e0ccf18684ecfe43093a8586c2ca5567b99b52dde92398b19af9eaa852639e726d14c261610f64bb225b4d598b3e99f27fd427ccff640bf57173cd0ea640c4fd4662d9c14914d93457b202f3c020c4aec749ee965d824ce864b837797fdb365c5c20bfce3d5f3410308901bb2e6bbd2979af999c909d9f6b240a50559868ade0f8d1b22b8cf1fed7e084c000e3229b36b06c12f9eef090281bbc125f78415f6a9382b6646b5f3741ee2c39ca4a070e3fa322f138ae27f56f7bfecff510cd82b170b2d9c20bcf4133ab2cfb82278268f6a998e07cd00000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x9f6264", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x09256a67c71f386327c50728db231ac787c819340a27da9c52bf5338efdc1b8c", - "transactionPosition": 71 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cbe59", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x485fdf9", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002cbe59196c66811a0458d8675820c733511a1ee67c4b6354648a0aad557a7d6103db91ba7ec3605d0e0d0a7dd727582070588192c2cfcb22bd6934b66eb4b782332859eca0353d7a6a6de5ae787e23ff590780906c8270f7a49125a567510c97404d726700444a974c52abaa369eacad17de494b817f70ae9f60f7e4b4fd7bb7325a17b8124630857a51652e94ee9a590f76dd479998cfaabbb74fc4dbdebe8be928083719ba2bc514dbf3723335072095ac5110206d71e43f6acb4c8d4a5f5c4489c2adfbacf8b54f44eeb958ccee349a7bd587c8ad64af6cbe885df7a04af2a4e1cda51388e3d459cb9efcc9905ffc53d4de4a7aed05a0537de2ce8594c672b5c3876bba195e095302b88d73f91b4d1716fab0eca1e729706fee665d539960ef9a4719ec4edc6bc0a4e6b52bb0e76ad484d5eeb2c1650ef9bc821d67524aa82f3bf4825548eb5d9b8a7f1638c747f219649c75241dbc9d1b47edf6baa29e7d85e667f9c96703e9ff09e3e5ee2be72a10a3e30f2e506c74bff2b581521fceb697c2a7b83db32f3c665e213c0eba5a8505d2415fb60ddebf509bbfd1a1dec3df01d480b23bdc16c8268bc0462d477101742b11bb5f31dfc037608610ad3489b78bd776e3e66e7655f5b55f98ef01edbe1c1ad0938b6883764bca24d33e2dae96c467ddb242b153f1bbb41df5d0842d496f1271e4cc9e212ab58369126cd56c30c602708f6b62ce8c1dd20b7eb5de15447c683536faee20399a2f23a619b2069546b279077a5b73c050962447c05997bb9c5831146230ac4f09cbb01eb7dc5abc87e185eb44da96695fd18f6047831d0276710e7aac3e850507ca142bbf4b036b72b872a3c351eff4e017b664d965c4c3a5b3745fb4dbd401f57ec16b0fcff536d8b705757f369e16ae1ea5b59e4f37d45b15d699257bdf5e01ea1be77e3dbb45a5204108e0009e93197e601b7da1d6bc29dab0c8126b99aef1ada965ea0a4c999b335c81095313b3d576be3ae66e4157a86a985285b64eff9380da6f2b4fcb5f6966ade1c97b4c293efc753316ad19d3e542600ef0539c6b1fa0ebed10edd8b6da5f276fc7d07075e1953120543573a4263641e9cf2c1e73df2d803339b29f7d9b14e08811c3f0d631c08f61853a59cffb9f585e2f409b1149602f4861bfafcbeabb4a8f6a0095e5b5cc20c3c7080e93563d4988839181355d52ec6c8e141890a662460b5d8e4b7a53f2abd3922221b848943b6aa79b10421431d8cf5e5973e7b28f218ab7578011d031c9f1ad86fba0f4915aa01e1dc34465c8e39caadf80d8e312e96b4e1ee885bafa1f2d0346461ea8939f08cd15cfa035611642aff82dede8f823d84aa267db9e9a439dddc9eb81ac4855a4746e844aa385b797c674f952b56ecc8a31180a1478fcb46486892bcd7f546b03546a6a2beb93c5412814a1ec8bf9c8cb8c098afbaac58bbfc45e1db9df070188c93ab52ec845d019ec1fc01f4a2e4efeb9930b71905de02d44736a3d07a7b7c34c4f4480dc8f8b974532afb2bb06ea8165a113710d5e429ba435c552fe7aa34183643a0e1b716447f71dc12ac4668f9c67a3f43195f9f4f9ee28451ac2445403c208be6a02cc5ac06012fd1209663aae19460d2afdff338e18f77a3cc12d9439ba513ddfe94c2f15754f39def5eec8b731642c48e08b4edb7c015578f02f69c4d8ba79b746a0caaee744715d94c139a6c9aa489a3ce3a343edac841181136c833f65a9dad3b1dca25f31f729fcb7dbebb47a11eaaf5a0f7904690b5b31a0bdcb88559d34ea252862b0a399dfcc57e283c0ea11412ab19438e439926f6dc9db6086c68ea329984dff59b636137eb83eb0a0c43b4e3d81c5e0e45bf305f0dc720d092086254dfc294477e5e5ff47c793feaa1866c94e251d5511229f1e95c05540b591984e73a1959b4094e62e71affd916daa99eabffc7ce1881e081be632ae2c09d61ad1b69e1b2bf064eae889182e86e0d65ec4667f75e2d277ee17b0cb85aabe7c6b7169011ed82f831758553ae49d4ea6e41b7e578c6e354c33fe528217a0f331ea1ba1350165b3098015cb58d38340811ff27e5e20a1368081fe140bd1ebc77dfdcdb28bc567e28b6584a7c68b68f6dec93e9cc385135038ee735322260f7c90044be8a19bf18d99e09f70607ff2a6a3a4ad29e0a291202eb601186b75e69835d2258e74bdbd04ec8f9c75981eb6f4dc7f09c01763aa3a3de91dd16c0b3f61b97802a4dd6dfccbc1da28fa09a5f8ee64b41ba12580d4a4163f84ec9f81a4edc5fba7fec105aeeb32dcb68d757cbd276c13a5e09af8302a11e5b503e7911d058ae7c1dcaa44b5927c9ab4f02aa796a478b2442b91f21877c46d91b41b07ba9b2b8514bd1adb787a9a3c27efa27fdeb8371e1d27c66ab80da9ec7c2c1f43145e73dd2483e3a93ea363c28131b8aadcbe8c7fd536caa63a79df8cd3a2e432fb449c6df33bc07a4a85328c5f0d7f1c89d5920bb7272f5b320df888a23877838b9bfb9a53217b09c71b5c5e0ccf18684ecfe43093a8586c2ca5567b99b52dde92398b19af9eaa852639e726d14c261610f64bb225b4d598b3e99f27fd427ccff640bf57173cd0ea640c4fd4662d9c14914d93457b202f3c020c4aec749ee965d824ce864b837797fdb365c5c20bfce3d5f3410308901bb2e6bbd2979af999c909d9f6b240a50559868ade0f8d1b22b8cf1fed7e084c000e3229b36b06c12f9eef090281bbc125f78415f6a9382b6646b5f3741ee2c39ca4a070e3fa322f138ae27f56f7bfecff510cd82b170b2d9c20bcf4133ab2cfb82278268f6a998e07cdd82a5829000182e20381e80220650c58df573197bd182a131c412403032df10dd4be914cad3fee6a8f9f50490dd82a5828000181e203922020bbfd25d9bb672844a5597ab0c099ddd592766e9cc6bfbc1aabcbdeb78b5af603000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x378821c", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x09256a67c71f386327c50728db231ac787c819340a27da9c52bf5338efdc1b8c", - "transactionPosition": 71 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cbdce", - "to": "0xff000000000000000000000000000000002cbe59", - "gas": "0x5842604", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000078782196cb759078088616c745421a5e0810320c2d4127fb35257616beecae3a3857f10eef0cbe560c70555cf106e7df9ff45475bd7e7f097ada94a97f56afacb8367bae0a7c9b9e07bd27f4372024646cb53abdc2f6be656423de773c1a708381a3424184cf315e80ea091cecc1005df11c33d569f0d275d97d25892f931e33d3817f556f964d44bf425022a7d06d67442c2ad9304f5e6b9b42497880067eaf6df1d5b6d405f1a7296142ea97427040300ece471973832fa6b0dc8cfbd4dbb4b747475ac001497f980a801e5d9ed67095e8253cd38643181f78ed6d1fd39f9d5969faa493b5136c40536e7f464cf02d8b67b682e34825f8d9755f05de55886dc90e1058deab0272e02ecfbbca1f12e12e56123cc2c5ea67f8631520d5691b5a78ddfe1af7e40bebd163fe9a61c4f8a9d02a3939bd06bc3665a97fc75ba9bb0febcfff1b0339d6a88df855e5a15c9a6693164bd5d3e9220869867de3399f97ab90ae31232176481decab82cae43a9c82944ce462cc38e68eb7a112acd831286494b5842f2afd857dda77c58d2134652c533d730e6b5ae06f7d3437c4489c590215f5c8371b355ff2d934d166a48a586e02e8e7f48730810d4b591b5cfad7ebdb03553a70af3c23a214b7b06392f902cbbc6766ca703c25e9122e46cf6b698c6b7b615f8cc1cae3a3f1253f5fadca7be1d9548af2ea031ee8aea3cea3be303178c1d01c9b3b2d5e99daf1546921fb56aaeda89be4f70a11343a4c6a8459454783024ab4aeabba105a969b453cb83ad6dd19bbd8bee6dfe04de7bac58fb49abc7ab6f63831a904a0b66b209f3326c4e21854aff6c2be4f393a145290a9574ae7ab0d6c90ab17da9c5e1c027f06ad6c19797e8f411f5499a6c62b1d8cfa70b6f61fd3f6100b93d6b53141ac5598568e9357449ee5c2b1d444ea251a41956b52d35e2b7cd26dc33c3eaa5067ff1da021e9901f02fa9e7a5a5a55b67c336655a31407ba2899156fc7dc35f221422cdf332c3fd52978b644c6e73a2aff102ff518f8d3ecacdbc64cc07a70f4d62fd677c740f3470e41bf0e28288f3bf8c615054fd2a853c0f80fd284b979999db697097029be9b7a5a0ef6c8abc89ac176180a208cd524679f10c6a9403472f9ff960cebec3e33b9912d62f455ef0adb936e5f88e24127e4f4dd46fe7ff9ae1317c217a630a0760bc84ff5fba0b48b047e5eb25975e00572c6b6dbe561ade01ea50f345d95c91c866537d86c0720e7396dff985a191fd554f838289ae148fb45a6db5ebb752f2690ffcb42e75c29aa74b3f566789ad96d7eda31320391c160dbe295d4a8102d4b331d8a1e1290e41bff958c5e0155f333e1846b21d1a14e0871bacecd05b4a16370aacf1319016359ac2d1d9d3911dd41b1936cc9f0d8d0a33196d4805e869e801df13e1046487c5a0fe53944aa55a6737cc6e0a84fce49f514d76c68e9be3c782c64b788d92c6c04f4c158d5c63670ca19e1a85024017d2160f591c8ac226ffdf96733ccb6bda368b266f6b326ff8b01227d1f4fcbe2850a57c9b71ea234817a198d111f8f81b59b12a4bc48614ec5364db7ef0205315c3530e418f5a039e05883b1401c9d9de917e25a1324bdd5f693cb93a8bd86db1d797bd6be5f9a4078c6710690da48a2b5058939427c91b9184bfba9199a5ce398f3e851e0b2299a1138e3ac216f64a9d8981f312c74419106595e549d9dfcbe7bd6f803995f9f36d20685e0c771c6b73e73c602bb0e18c7c1b71a2b18cababbd1a1891bb3b4b927e58bc8a7b5d6c3433640985609b3e3baf4c178f3a469a52a2697a185fa6b57c2a6d6371cf02f18cdba2ae3b7467a3663962e333726df013118dd4e5f91b258df39a3870e252712e87cc44f2e9e3a7b6e4c503088efa5ce363e7b10d2ce33a87a85629482cb5d64c01ec4f97d0b7f704a8fd30590bcefa2b3699a133a39e0a383cf5db3fcaa299b0154eb3dccdecbd83c3daf38216cd7ce9c9b436ef33decba8a32a2f1a64e5539ebe5b5432d5a438a24d2d6e8ca07f33519cc601dfe0551faeb8514b005c79ad7ed2c7a1f8c9cc2804ca4170bd1ab04e00c0d07e5bef9895515ad0485dcc4b00cdba458655f3818dcdf7757ba76fff722b9856c3925ff17e0213075adcccd677fc0794c92930da09419fbe0fc78c98d466c550a6541aa4e5a01689d9634d384d8b7f77e678676555b342d1f54ab3f7719191c4bc98577a0ead9b05b86e16bd1bdc018a98f467472d8b8e54ca29642b2020d11595a282b3cabd7689bc79d0c5822fa6d3b539a962bdd17b3eb3adf7c34ca9eb2c00a7e18d11e5290d11878e2437876e4d306e2fab457fab2c52deccf7219830e2308d87441b33906d7334feab7c6e9259b8136502f237344b954ab861132dc40769b10ac6f4e4933fa08422e2f499a7e588caec50bb4912e85df69e97c6a148a6961d683b631c82a5e96080368d6800c9908f5e36f93d2d4b3ebcbdeff3e492b26faec713df3f5e74536a313effcb12e1b0c3d2c3e290d34ec267b895f23ca7511cdc0be7dfb6648163f8731e9833b73b77697ee24bb2012cf36986470798b1d00fa384d2e0eeb172ea82d705c6b55785c0c1fc64b0f85081dea8f9b9242961e1251f0d3bc474b6db1977efd1c0d217fbaa15682c62fd9d386fe37b710fb7a3e86018b629cf0c15cb32509633f555f7750c4f553798b97b008b8347c3dedc9d4900000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xa41eb9", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xfe2a899422ce49a8aa92413b3b30dfeef07f4601e1f817a9be1e2681e0c6a3c5", - "transactionPosition": 72 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cbe59", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x510df01", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002cbe59196cb7811a0458d1025820bdda037c22b1041a1ab65a83f366c6dda203b1a83f8a11f7e6d76b019411a960582070588192c2cfcb22bd6934b66eb4b782332859eca0353d7a6a6de5ae787e23ff59078088616c745421a5e0810320c2d4127fb35257616beecae3a3857f10eef0cbe560c70555cf106e7df9ff45475bd7e7f097ada94a97f56afacb8367bae0a7c9b9e07bd27f4372024646cb53abdc2f6be656423de773c1a708381a3424184cf315e80ea091cecc1005df11c33d569f0d275d97d25892f931e33d3817f556f964d44bf425022a7d06d67442c2ad9304f5e6b9b42497880067eaf6df1d5b6d405f1a7296142ea97427040300ece471973832fa6b0dc8cfbd4dbb4b747475ac001497f980a801e5d9ed67095e8253cd38643181f78ed6d1fd39f9d5969faa493b5136c40536e7f464cf02d8b67b682e34825f8d9755f05de55886dc90e1058deab0272e02ecfbbca1f12e12e56123cc2c5ea67f8631520d5691b5a78ddfe1af7e40bebd163fe9a61c4f8a9d02a3939bd06bc3665a97fc75ba9bb0febcfff1b0339d6a88df855e5a15c9a6693164bd5d3e9220869867de3399f97ab90ae31232176481decab82cae43a9c82944ce462cc38e68eb7a112acd831286494b5842f2afd857dda77c58d2134652c533d730e6b5ae06f7d3437c4489c590215f5c8371b355ff2d934d166a48a586e02e8e7f48730810d4b591b5cfad7ebdb03553a70af3c23a214b7b06392f902cbbc6766ca703c25e9122e46cf6b698c6b7b615f8cc1cae3a3f1253f5fadca7be1d9548af2ea031ee8aea3cea3be303178c1d01c9b3b2d5e99daf1546921fb56aaeda89be4f70a11343a4c6a8459454783024ab4aeabba105a969b453cb83ad6dd19bbd8bee6dfe04de7bac58fb49abc7ab6f63831a904a0b66b209f3326c4e21854aff6c2be4f393a145290a9574ae7ab0d6c90ab17da9c5e1c027f06ad6c19797e8f411f5499a6c62b1d8cfa70b6f61fd3f6100b93d6b53141ac5598568e9357449ee5c2b1d444ea251a41956b52d35e2b7cd26dc33c3eaa5067ff1da021e9901f02fa9e7a5a5a55b67c336655a31407ba2899156fc7dc35f221422cdf332c3fd52978b644c6e73a2aff102ff518f8d3ecacdbc64cc07a70f4d62fd677c740f3470e41bf0e28288f3bf8c615054fd2a853c0f80fd284b979999db697097029be9b7a5a0ef6c8abc89ac176180a208cd524679f10c6a9403472f9ff960cebec3e33b9912d62f455ef0adb936e5f88e24127e4f4dd46fe7ff9ae1317c217a630a0760bc84ff5fba0b48b047e5eb25975e00572c6b6dbe561ade01ea50f345d95c91c866537d86c0720e7396dff985a191fd554f838289ae148fb45a6db5ebb752f2690ffcb42e75c29aa74b3f566789ad96d7eda31320391c160dbe295d4a8102d4b331d8a1e1290e41bff958c5e0155f333e1846b21d1a14e0871bacecd05b4a16370aacf1319016359ac2d1d9d3911dd41b1936cc9f0d8d0a33196d4805e869e801df13e1046487c5a0fe53944aa55a6737cc6e0a84fce49f514d76c68e9be3c782c64b788d92c6c04f4c158d5c63670ca19e1a85024017d2160f591c8ac226ffdf96733ccb6bda368b266f6b326ff8b01227d1f4fcbe2850a57c9b71ea234817a198d111f8f81b59b12a4bc48614ec5364db7ef0205315c3530e418f5a039e05883b1401c9d9de917e25a1324bdd5f693cb93a8bd86db1d797bd6be5f9a4078c6710690da48a2b5058939427c91b9184bfba9199a5ce398f3e851e0b2299a1138e3ac216f64a9d8981f312c74419106595e549d9dfcbe7bd6f803995f9f36d20685e0c771c6b73e73c602bb0e18c7c1b71a2b18cababbd1a1891bb3b4b927e58bc8a7b5d6c3433640985609b3e3baf4c178f3a469a52a2697a185fa6b57c2a6d6371cf02f18cdba2ae3b7467a3663962e333726df013118dd4e5f91b258df39a3870e252712e87cc44f2e9e3a7b6e4c503088efa5ce363e7b10d2ce33a87a85629482cb5d64c01ec4f97d0b7f704a8fd30590bcefa2b3699a133a39e0a383cf5db3fcaa299b0154eb3dccdecbd83c3daf38216cd7ce9c9b436ef33decba8a32a2f1a64e5539ebe5b5432d5a438a24d2d6e8ca07f33519cc601dfe0551faeb8514b005c79ad7ed2c7a1f8c9cc2804ca4170bd1ab04e00c0d07e5bef9895515ad0485dcc4b00cdba458655f3818dcdf7757ba76fff722b9856c3925ff17e0213075adcccd677fc0794c92930da09419fbe0fc78c98d466c550a6541aa4e5a01689d9634d384d8b7f77e678676555b342d1f54ab3f7719191c4bc98577a0ead9b05b86e16bd1bdc018a98f467472d8b8e54ca29642b2020d11595a282b3cabd7689bc79d0c5822fa6d3b539a962bdd17b3eb3adf7c34ca9eb2c00a7e18d11e5290d11878e2437876e4d306e2fab457fab2c52deccf7219830e2308d87441b33906d7334feab7c6e9259b8136502f237344b954ab861132dc40769b10ac6f4e4933fa08422e2f499a7e588caec50bb4912e85df69e97c6a148a6961d683b631c82a5e96080368d6800c9908f5e36f93d2d4b3ebcbdeff3e492b26faec713df3f5e74536a313effcb12e1b0c3d2c3e290d34ec267b895f23ca7511cdc0be7dfb6648163f8731e9833b73b77697ee24bb2012cf36986470798b1d00fa384d2e0eeb172ea82d705c6b55785c0c1fc64b0f85081dea8f9b9242961e1251f0d3bc474b6db1977efd1c0d217fbaa15682c62fd9d386fe37b710fb7a3e86018b629cf0c15cb32509633f555f7750c4f553798b97b008b8347c3dedc9d49d82a5829000182e20381e80220ad21a8b5617018e5872ad54d2fb83e094fb5178f7ac7530bc2a82c91c9b0e473d82a5828000181e203922020bc3b85c5479f349cc048347aa46fcd4a1364eb6ec3b191d86bfd70ae490cdc32000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x3e69aeb", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xfe2a899422ce49a8aa92413b3b30dfeef07f4601e1f817a9be1e2681e0c6a3c5", - "transactionPosition": 72 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000181d8a", - "to": "0xff00000000000000000000000000000000181e6b", - "gas": "0x1959fb3", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f385181a8182004081820e58c0a829718db09f68797f2d9be15809d2e1387a94bd8b1a51d53013dab9b9611d70aece45b4771bd878d93db7f151e76e9a812d513d34dd30afd572af6adb1763bf9583245961b2e14558086e2eb0da8a1fbf572d042dc1f0d6f0ac2bdab7ed340306aa8db0e432b57aca8579449eab439fc87f84c441133383e504824927027f70f722acfaebc2ebe1ab004f5989794a51b59d9f1ac7749ae9d4cf9b0860b9b1f8b9bd11b0e7f183bf20e9d1ad2278791c6c438c3c768c6e9395786a463bd9ba4f1a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d6900000000000000000000000000" - }, - "result": { - "gasUsed": "0x1517eb4", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x1b93e4163b1bd35cb6342233c84dcda34d3347e5f5ea996f90e63b2d0e71f98e", - "transactionPosition": 73 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002c2abc", - "to": "0xff000000000000000000000000000000002c2ade", - "gas": "0x4dea6fd", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000007878219911e590780960f552b98a30ad3840c6da73d540213f5723c320b231419709ba5b7381feff81e38de449fdf783e55f3f411c1baa728854228f264cb115325348d007cb5922c59e926caf1a0d78fe1b4ec9841883cfd34d7a6b2eaf574097ca32f079d4a862a12245f8aec0237e66ee5b29da0ab67f6d5da06f5311dc40f205aef2c09826d84283d6c951d802f3a4dbba31b38abf15fa07625c908e2c2797578c1431ee2894e7d954bf134439530ae612ebcf87deb1f354bc3bb96bfa4f4ca53c79112772671891803ee5c6dc2bebf2beea9bed50c947b731c5faf9369b3c42fb5cfe9d0236f4f559a05a7a8a584cf26637e6713ad1a80520fb7f0d3e6f3c70866b6371c17ee1ae76dbbcc809a0c2a70a2e6241ed2b04d9f35f5e9d215e346266321856099c81550a8d7b7e6a82bb31f5002c5545e9e853ae79cbe26d5d9fbdca504de06b48dbec1614875ead88b1aacc0a76a6e1123b91092b53b92d6507d2c4b90b950406e3706e22fd16ba83ef4474f93edf8e908ca824aa875175425b757365e0d94af4d8f054278185210db77b78c66d44ead9ad621b2080b8a96d41aece81e05638984440954d538f31a8d90847550cfc56846a51c83ec9630241066785b926985a90ab0d30f815a1a365adadf5560b911a33336e278dcd77eceef98eaaa2acc96c03806004221de8e1e43c097d2ddd72870cf493361094483976e1cf852362c17559004265230b9f6582e572b3f359a2242948c49b4273efce8cf7e2ae233ab96db76f22b2214c2c2b58edc3faa2fc67e589bad0a2eb05e0830be889f439d27a7c1dab781d301ac88790943ade6fd61679b08f70bfdde55c4f98f8d41a9ff2ad730a8221eacb29d4b26ddcf813bffa913bf5ab88e3f73a9a3b6b8b8e433ecb6ba009384e088519a4422bb250aacf4cc81d39be173eb79b52b2991171310067a240b6507a0b344cecd6896062ed7dc28022567c5236f2ad5e97fbd6c37621fbfe727b11536ee5e6ba31030f252ce8a05224bc69617ba5aa7a5a8561d68c97b7532181c77df9804feb4853fdd3efbff953eb9d8e826ea01b27919b33911bbec2f9041b2aa05c84419df21b1f91515ec4467a3abcb30aab03f04018b351ec6684a1fb67e7f67f356ac375c34f2a181d718318f92b97acd7328907279031a4b93eebc88b4fc70b4e831be82deef3e724b3af5608f1f311723a3a3c7dfa2be97407358469e04304456ee864fc2b212eaa3adff6d89c1991ca2fa1e988eb2769178e935a996ac0c1efa44000328ff4667474f79536499392534a06cd4638cfabbddaab5028f202a3c7364fc01f147902c9bdbc2bfd750498927e9a0769330daada7cb605619ad35a6f016f2405e9978b6df94c5226f1c796328027b9be8f87dfd536ffd4a3cba032084902817ef3b5a014a29d81db98cf0f0ffcc696a4408a40097d0e809237cab607f0ec005572c41cc3a7829a2ecbd9df4280d283f23882fbd6d159e208f0447ef60c9c20ed40115ca2631ebac7a8b5920dfeb64c5354fc1a6a3e489b33e9fe5c7dbf8139d06ed75b648aeaa7995b013265228524981c8f09cc1268bb0a626ab89d3d829fefafddd2a7734787f1e691f862ded29aec3478520af69fda96493b04ba75b38ed974f0ed859bf8703f7b9579c2bc9358994a71481b1450f791996ddd13b9faee4684551059290a96485a537d58087d1e857eb09bf9e2896542631756c5bab31ee5c4a2b4bee767d62945ae42aed7edb1d294df7698b64f1b603179a12f1efd28980a98ba42890c2afcafebf1a970f660ab38ec4120192e3087a438c1860274a65e5928726c4e42bfd07a8adf53878b8aef598229d957591aa63b0e9e3e21a1ad7df441c5292a24f4e6f47e6dd12310a9037da22169cdcb1e46fb20f63afc4133ccb82a4ec7e21af6670e565090ab805d9a93b30648b57e472f24deb499426392b6134b2de005119751aadbdb7535e60042f0f8d549522bacdaa1c55572d267294fd3f2c98230004b05d2f2b2315acf1e4e9d46d71e3811b25690382e88dedf7046af4d4439aaedc1e8bdbed282aeed7d88e451e393bcabb3e8c7e6343aceddaf51dceaaa4041231b5008bdee7e20d551bc2003d54518a240afc80bee8c709256971ce9ae1ed882c2f080ffcc7e9cf0b5a15a678e81ed782fe8ab12df8aac05305bac6864eeb2112012703cc57fa1c7c09845cab94dad1eb3562aeeb75dee6b56fb23f915865f0fa577bae17fbba3d8dab489dc750e20d4bade21b94fa009563993932a5f67cadd5904177a457ede06a0bb3e41f64af699d4b2e0f11368d7c9bd7a490a34b334766df92964dec91fd5cc7e1768319c3b761a16ce91b980ec58ae990b2d2828a0e780a8e84967fa7c03cc052d842cdc645b47f3b000286cc8f28fee0542bd0a715c04816572412a78e07a595f646354de4e8959a80713401b9bae9624f6fd9828d1f36c440417ab6c6e4f7d3ce8f165529983ab31f1bf09eb67f7b19d6f192548cdb5f34accfdfd6a69f081cf9ffa9994cb5824ff1f89bd9915ceb08eb935c7d1181aba7da21f540b7351098fcfb708e91d65d7b091cb0b17dd60f241e87cd8e6635b0eba3b015185d25de049b63a7c32ce752989dac83f03a2af6664f6eb072202100a5a6d10a8ea84d2cc10872acafd6e2989c32baf41e3c4f3d5d55ec352f30517ebd74b96a10b80454e52009898eab5a699900000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x96b2d7", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf14f04259ac556d1381965bd9d12c803e7e1ec83656ef48e3d57130963a8fd10", - "transactionPosition": 74 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002c2ade", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x478d47b", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002c2ade19911e811a0459c3f65820be2d62bd4039006ec2601e9b85f158e3facb09f64592cf07d68ade65df8ce7345820a821855a854abe1c7ada3b128358010c99e1da5bea3dd573733e8978edfe3483590780960f552b98a30ad3840c6da73d540213f5723c320b231419709ba5b7381feff81e38de449fdf783e55f3f411c1baa728854228f264cb115325348d007cb5922c59e926caf1a0d78fe1b4ec9841883cfd34d7a6b2eaf574097ca32f079d4a862a12245f8aec0237e66ee5b29da0ab67f6d5da06f5311dc40f205aef2c09826d84283d6c951d802f3a4dbba31b38abf15fa07625c908e2c2797578c1431ee2894e7d954bf134439530ae612ebcf87deb1f354bc3bb96bfa4f4ca53c79112772671891803ee5c6dc2bebf2beea9bed50c947b731c5faf9369b3c42fb5cfe9d0236f4f559a05a7a8a584cf26637e6713ad1a80520fb7f0d3e6f3c70866b6371c17ee1ae76dbbcc809a0c2a70a2e6241ed2b04d9f35f5e9d215e346266321856099c81550a8d7b7e6a82bb31f5002c5545e9e853ae79cbe26d5d9fbdca504de06b48dbec1614875ead88b1aacc0a76a6e1123b91092b53b92d6507d2c4b90b950406e3706e22fd16ba83ef4474f93edf8e908ca824aa875175425b757365e0d94af4d8f054278185210db77b78c66d44ead9ad621b2080b8a96d41aece81e05638984440954d538f31a8d90847550cfc56846a51c83ec9630241066785b926985a90ab0d30f815a1a365adadf5560b911a33336e278dcd77eceef98eaaa2acc96c03806004221de8e1e43c097d2ddd72870cf493361094483976e1cf852362c17559004265230b9f6582e572b3f359a2242948c49b4273efce8cf7e2ae233ab96db76f22b2214c2c2b58edc3faa2fc67e589bad0a2eb05e0830be889f439d27a7c1dab781d301ac88790943ade6fd61679b08f70bfdde55c4f98f8d41a9ff2ad730a8221eacb29d4b26ddcf813bffa913bf5ab88e3f73a9a3b6b8b8e433ecb6ba009384e088519a4422bb250aacf4cc81d39be173eb79b52b2991171310067a240b6507a0b344cecd6896062ed7dc28022567c5236f2ad5e97fbd6c37621fbfe727b11536ee5e6ba31030f252ce8a05224bc69617ba5aa7a5a8561d68c97b7532181c77df9804feb4853fdd3efbff953eb9d8e826ea01b27919b33911bbec2f9041b2aa05c84419df21b1f91515ec4467a3abcb30aab03f04018b351ec6684a1fb67e7f67f356ac375c34f2a181d718318f92b97acd7328907279031a4b93eebc88b4fc70b4e831be82deef3e724b3af5608f1f311723a3a3c7dfa2be97407358469e04304456ee864fc2b212eaa3adff6d89c1991ca2fa1e988eb2769178e935a996ac0c1efa44000328ff4667474f79536499392534a06cd4638cfabbddaab5028f202a3c7364fc01f147902c9bdbc2bfd750498927e9a0769330daada7cb605619ad35a6f016f2405e9978b6df94c5226f1c796328027b9be8f87dfd536ffd4a3cba032084902817ef3b5a014a29d81db98cf0f0ffcc696a4408a40097d0e809237cab607f0ec005572c41cc3a7829a2ecbd9df4280d283f23882fbd6d159e208f0447ef60c9c20ed40115ca2631ebac7a8b5920dfeb64c5354fc1a6a3e489b33e9fe5c7dbf8139d06ed75b648aeaa7995b013265228524981c8f09cc1268bb0a626ab89d3d829fefafddd2a7734787f1e691f862ded29aec3478520af69fda96493b04ba75b38ed974f0ed859bf8703f7b9579c2bc9358994a71481b1450f791996ddd13b9faee4684551059290a96485a537d58087d1e857eb09bf9e2896542631756c5bab31ee5c4a2b4bee767d62945ae42aed7edb1d294df7698b64f1b603179a12f1efd28980a98ba42890c2afcafebf1a970f660ab38ec4120192e3087a438c1860274a65e5928726c4e42bfd07a8adf53878b8aef598229d957591aa63b0e9e3e21a1ad7df441c5292a24f4e6f47e6dd12310a9037da22169cdcb1e46fb20f63afc4133ccb82a4ec7e21af6670e565090ab805d9a93b30648b57e472f24deb499426392b6134b2de005119751aadbdb7535e60042f0f8d549522bacdaa1c55572d267294fd3f2c98230004b05d2f2b2315acf1e4e9d46d71e3811b25690382e88dedf7046af4d4439aaedc1e8bdbed282aeed7d88e451e393bcabb3e8c7e6343aceddaf51dceaaa4041231b5008bdee7e20d551bc2003d54518a240afc80bee8c709256971ce9ae1ed882c2f080ffcc7e9cf0b5a15a678e81ed782fe8ab12df8aac05305bac6864eeb2112012703cc57fa1c7c09845cab94dad1eb3562aeeb75dee6b56fb23f915865f0fa577bae17fbba3d8dab489dc750e20d4bade21b94fa009563993932a5f67cadd5904177a457ede06a0bb3e41f64af699d4b2e0f11368d7c9bd7a490a34b334766df92964dec91fd5cc7e1768319c3b761a16ce91b980ec58ae990b2d2828a0e780a8e84967fa7c03cc052d842cdc645b47f3b000286cc8f28fee0542bd0a715c04816572412a78e07a595f646354de4e8959a80713401b9bae9624f6fd9828d1f36c440417ab6c6e4f7d3ce8f165529983ab31f1bf09eb67f7b19d6f192548cdb5f34accfdfd6a69f081cf9ffa9994cb5824ff1f89bd9915ceb08eb935c7d1181aba7da21f540b7351098fcfb708e91d65d7b091cb0b17dd60f241e87cd8e6635b0eba3b015185d25de049b63a7c32ce752989dac83f03a2af6664f6eb072202100a5a6d10a8ea84d2cc10872acafd6e2989c32baf41e3c4f3d5d55ec352f30517ebd74b96a10b80454e52009898eab5a6999d82a5829000182e20381e80220c128d6724fd7c641a959983b978d9fa693eb45ebfa44d0befd58193ac5f96c55d82a5828000181e2039220201026e6f2fadbf4f6d050d669a0d7171f9b602d906e9c51a6a311c86b5273dd2b000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x30520d4", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf14f04259ac556d1381965bd9d12c803e7e1ec83656ef48e3d57130963a8fd10", - "transactionPosition": 74 - }, - { - "type": "call", - "subtraces": 21, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cffc4", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x530c26f6", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000007128188828bd82a5828000181e203922020638379e47a3916bae849a7d2eb688c4c79f2a16dc32813f0fbc2799237183c251b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d41584367354149677a69414f5733616d6d6f2f2f342b6f4e77425546714c787a57712b5365565143506f79524842494f4c46451a00381e4f1a004fa10f40480016de9bafa1a688405842018c87b3a0c61f501ebe01004d2c3989e74e8d3409e66ea7cf0485433f61ea84b92cab8863c916181ce0de0547b883855699afb28d70d8ed96c4fa49608949373100828bd82a5828000181e203922020a646954f2e700a51f149491da320e5065d7266a505b4013f3fe2b95a410487341b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d415843673541496777613542697561305a34474d65625947757336787a484e5a6e47726c744b456d7535744b3536676e366e551a00381e4f1a004fa10f40480016de9bafa1a6884058420144ad58edfe5d5d30d7b9789535d6c44c6f0014a05b3cb8e1141c1bf9a506d34c68d42e1f2b90ad6b13e231d84358535d36eb329f4e126dc5667eeebe8f87d40701828bd82a5828000181e20392202019553461c49b73c9ac6661d0bcbad1abd0b5747cb3b2c2cdbaaf2f0ee22e4f381b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d41584367354149676d43454d37766c6163766b78657351395a6e785554564433696473695259644c613641636b4d6266514b381a00381e4f1a004fa10f40480016de9bafa1a6884058420111c340ca79b4ec152f975556bdca273716cd512588b630a12ac48109c4719e00406e504a97193fd228a1fa0492381acf41c3929fdc8f5a2d0cec1eef7216c79d01828bd82a5828000181e20392202078ef0796f6fb842665ab9c1896535d700bea5b1f70189272d8bea7a61b36f43b1b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d415843673541496749617553335659766c5578394e7575384c42776d507a55326f6d6e6a4c724e2b534474686439535656776b1a00381e4f1a004fa10f40480016de9bafa1a68840584201634845b6b07346e86e066a38f58506581f2c0edc279607b5d55f0245a2b0713046385efd92bd31fc2553d484d6067a699f1b9997dac16cd7f6467904f029f25501828bd82a5828000181e203922020997081e8865104540ec72c6f436a983e9633c5e5098d7ab01edf1a381c9495201b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d415843673541496742437463616277537077566247476239436d3068686a2b5075756e485471597a52694d41433676563879771a00381e4f1a004fa10f40480016de9bafa1a688405842013f72930bc2558ba9617bd708bd4abf74e197ba4483a97ced4bd8029bc0c5be26696583bf0c963021d7009827f800494e13e8cb5c0e17a44955cfe2739d8ed5c301828bd82a5828000181e20392202058a6d1ab9902fade4bf717e7331aee4b46fc210ba1b972dcf3ea2048a541463e1b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d41584367354149674a4b56744f37483938726c477248634d5738466a5764644d6d532f54356a34334b4c4944563575713037491a00381e501a004fa11040480016de9ad5b2b21640584201df7baeaa70111f1bf9cabbebb63a42ed93369fdbc655810c47539e2109e457984c9dfb31ebbb414bf6bde203b8b16895df94722e92d76f49d075deb1528e4e1d01828bd82a5828000181e2039220204792c6cd5e8d978b7093e5d9a26a72c45fe7c815fad2bced0745522d5b2004261b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d41584367354149677567622f52586644466330763238562b304e4d684c5263723376596f70682f455246325435742f354970731a00381e501a004fa11040480016de9ad5b2b2164058420119c8ffdccfa265a45e1f93b09d158cb6f6d9313a9acb81dbd40ac6006d25003e29d4bcc093e3cc3de10fb0a3f4221c8e98a8785e3c63fc8843786af1f975cd3001828bd82a5828000181e203922020ffcfabfe3a003640cba142966a19f85d1028b10edb5b0d647273c55857af431f1b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d4158436735414967764c754c47416773594e6a3972372f734b67656433356f6d6d31545141614830716d6b6d307a54796774411a00381e501a004fa11040480016de9ad5b2b21640584201b639096d1da0d389c6fd4d4104be639dbb22c591fe7bdebb2907aee734a9b73e3cb9f9262905b81f92997b92e23616cf3f635eef8405e4d794f80f4fa431c2e7010000000000000000000000000000" - }, - "result": { - "gasUsed": "0x3374f7cd", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002d82881a045b57631a045b57641a045b57651a045b57661a045b57671a045b57681a045b57691a045b576a42140100000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff00000000000000000000000000000000108a0a", - "gas": "0x52f45b15", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000014c1cb970000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000064500c4ffb3010000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x133019", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x52e08644", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x52cfba64", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 3 - ], - "action": { - "callType": "staticcall", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x52bcb4e2", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000009d8b06780000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e38258418c87b3a0c61f501ebe01004d2c3989e74e8d3409e66ea7cf0485433f61ea84b92cab8863c916181ce0de0547b883855699afb28d70d8ed96c4fa49608949373100589d8bd82a5828000181e203922020638379e47a3916bae849a7d2eb688c4c79f2a16dc32813f0fbc2799237183c251b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d41584367354149677a69414f5733616d6d6f2f2f342b6f4e77425546714c787a57712b5365565143506f79524842494f4c46451a00381e4f1a004fa10f40480016de9bafa1a688400000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2e9807", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 4 - ], - "action": { - "callType": "staticcall", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x528ae02a", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000009d8b06780000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e382584144ad58edfe5d5d30d7b9789535d6c44c6f0014a05b3cb8e1141c1bf9a506d34c68d42e1f2b90ad6b13e231d84358535d36eb329f4e126dc5667eeebe8f87d40701589d8bd82a5828000181e203922020a646954f2e700a51f149491da320e5065d7266a505b4013f3fe2b95a410487341b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d415843673541496777613542697561305a34474d65625947757336787a484e5a6e47726c744b456d7535744b3536676e366e551a00381e4f1a004fa10f40480016de9bafa1a688400000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x26f6e7", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 5 - ], - "action": { - "callType": "staticcall", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x526100b3", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000009d8b06780000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e382584111c340ca79b4ec152f975556bdca273716cd512588b630a12ac48109c4719e00406e504a97193fd228a1fa0492381acf41c3929fdc8f5a2d0cec1eef7216c79d01589d8bd82a5828000181e20392202019553461c49b73c9ac6661d0bcbad1abd0b5747cb3b2c2cdbaaf2f0ee22e4f381b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d41584367354149676d43454d37766c6163766b78657351395a6e785554564433696473695259644c613641636b4d6266514b381a00381e4f1a004fa10f40480016de9bafa1a688400000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x26f6e7", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 6 - ], - "action": { - "callType": "staticcall", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x5237213c", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000009d8b06780000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e3825841634845b6b07346e86e066a38f58506581f2c0edc279607b5d55f0245a2b0713046385efd92bd31fc2553d484d6067a699f1b9997dac16cd7f6467904f029f25501589d8bd82a5828000181e20392202078ef0796f6fb842665ab9c1896535d700bea5b1f70189272d8bea7a61b36f43b1b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d415843673541496749617553335659766c5578394e7575384c42776d507a55326f6d6e6a4c724e2b534474686439535656776b1a00381e4f1a004fa10f40480016de9bafa1a688400000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x26f6e7", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 7 - ], - "action": { - "callType": "staticcall", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x520d41c5", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000009d8b06780000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e38258413f72930bc2558ba9617bd708bd4abf74e197ba4483a97ced4bd8029bc0c5be26696583bf0c963021d7009827f800494e13e8cb5c0e17a44955cfe2739d8ed5c301589d8bd82a5828000181e203922020997081e8865104540ec72c6f436a983e9633c5e5098d7ab01edf1a381c9495201b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d415843673541496742437463616277537077566247476239436d3068686a2b5075756e485471597a52694d41433676563879771a00381e4f1a004fa10f40480016de9bafa1a688400000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x26f6e7", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 8 - ], - "action": { - "callType": "staticcall", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x51e3624e", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000009d8b06780000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e3825841df7baeaa70111f1bf9cabbebb63a42ed93369fdbc655810c47539e2109e457984c9dfb31ebbb414bf6bde203b8b16895df94722e92d76f49d075deb1528e4e1d01589d8bd82a5828000181e20392202058a6d1ab9902fade4bf717e7331aee4b46fc210ba1b972dcf3ea2048a541463e1b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d41584367354149674a4b56744f37483938726c477248634d5738466a5764644d6d532f54356a34334b4c4944563575713037491a00381e501a004fa11040480016de9ad5b2b216400000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x26f6e7", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 9 - ], - "action": { - "callType": "staticcall", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x51b982d6", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000009d8b06780000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e382584119c8ffdccfa265a45e1f93b09d158cb6f6d9313a9acb81dbd40ac6006d25003e29d4bcc093e3cc3de10fb0a3f4221c8e98a8785e3c63fc8843786af1f975cd3001589d8bd82a5828000181e2039220204792c6cd5e8d978b7093e5d9a26a72c45fe7c815fad2bced0745522d5b2004261b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d41584367354149677567622f52586644466330763238562b304e4d684c5263723376596f70682f455246325435742f354970731a00381e501a004fa11040480016de9ad5b2b216400000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x26f6e7", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 10 - ], - "action": { - "callType": "staticcall", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x518fa35f", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000009d8b06780000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e3825841b639096d1da0d389c6fd4d4104be639dbb22c591fe7bdebb2907aee734a9b73e3cb9f9262905b81f92997b92e23616cf3f635eef8405e4d794f80f4fa431c2e701589d8bd82a5828000181e203922020ffcfabfe3a003640cba142966a19f85d1028b10edb5b0d647273c55857af431f1b0000000800000000f55501d886fa8ef922a4be477ef30d932184be05323f2244008a944278346d4158436735414967764c754c47416773594e6a3972372f734b67656433356f6d6d31545141614830716d6b6d307a54796774411a00381e501a004fa11040480016de9ad5b2b216400000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x26f6e7", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 11 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff00000000000000000000000000000000000007", - "gas": "0x508a6662", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000c26ddbd50000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000064500aa9b8b010000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2edc57", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000104f002b5ff708418d6c8000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [ - 12 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff00000000000000000000000000000000000007", - "gas": "0x4a86bae6", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000d7d4deed00000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000026f844500aa9b8b014200064e0003782dace9d9000000000000005902538288861a00108a0ad82a5828000181e203922020638379e47a3916bae849a7d2eb688c4c79f2a16dc32813f0fbc2799237183c251b00000008000000001a001782c01a001b77401a00381e4f861a00108a0ad82a5828000181e203922020a646954f2e700a51f149491da320e5065d7266a505b4013f3fe2b95a410487341b00000008000000001a001782c01a001b77401a00381e4f861a00108a0ad82a5828000181e20392202019553461c49b73c9ac6661d0bcbad1abd0b5747cb3b2c2cdbaaf2f0ee22e4f381b00000008000000001a001782c01a001b77401a00381e4f861a00108a0ad82a5828000181e20392202078ef0796f6fb842665ab9c1896535d700bea5b1f70189272d8bea7a61b36f43b1b00000008000000001a001782c01a001b77401a00381e4f861a00108a0ad82a5828000181e203922020997081e8865104540ec72c6f436a983e9633c5e5098d7ab01edf1a381c9495201b00000008000000001a001782c01a001b77401a00381e4f861a00108a0ad82a5828000181e20392202058a6d1ab9902fade4bf717e7331aee4b46fc210ba1b972dcf3ea2048a541463e1b00000008000000001a001782c01a001b77401a00381e50861a00108a0ad82a5828000181e2039220204792c6cd5e8d978b7093e5d9a26a72c45fe7c815fad2bced0745522d5b2004261b00000008000000001a001782c01a001b77401a00381e50861a00108a0ad82a5828000181e203922020ffcfabfe3a003640cba142966a19f85d1028b10edb5b0d647273c55857af431f1b00000008000000001a001782c01a001b77401a00381e50800000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x397cbb2", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000067844f002b5c7eda94a39380000000000000500006a8d4d4487a428de5110000000000520002f0503706f730bd424045568000000000583083820880820080881a034d18b51a034d18b61a034d18b71a034d18b81a034d18b91a034d18ba1a034d18bb1a034d18bc00000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 12, - 0 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000007", - "to": "0xff00000000000000000000000000000000000006", - "gas": "0x475d9536", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000de180de3000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000277821a85223bdf59026e861a0022cdaa06054e0003782dace9d9000000000000005902538288861a00108a0ad82a5828000181e203922020638379e47a3916bae849a7d2eb688c4c79f2a16dc32813f0fbc2799237183c251b00000008000000001a001782c01a001b77401a00381e4f861a00108a0ad82a5828000181e203922020a646954f2e700a51f149491da320e5065d7266a505b4013f3fe2b95a410487341b00000008000000001a001782c01a001b77401a00381e4f861a00108a0ad82a5828000181e20392202019553461c49b73c9ac6661d0bcbad1abd0b5747cb3b2c2cdbaaf2f0ee22e4f381b00000008000000001a001782c01a001b77401a00381e4f861a00108a0ad82a5828000181e20392202078ef0796f6fb842665ab9c1896535d700bea5b1f70189272d8bea7a61b36f43b1b00000008000000001a001782c01a001b77401a00381e4f861a00108a0ad82a5828000181e203922020997081e8865104540ec72c6f436a983e9633c5e5098d7ab01edf1a381c9495201b00000008000000001a001782c01a001b77401a00381e4f861a00108a0ad82a5828000181e20392202058a6d1ab9902fade4bf717e7331aee4b46fc210ba1b972dcf3ea2048a541463e1b00000008000000001a001782c01a001b77401a00381e50861a00108a0ad82a5828000181e2039220204792c6cd5e8d978b7093e5d9a26a72c45fe7c815fad2bced0745522d5b2004261b00000008000000001a001782c01a001b77401a00381e50861a00108a0ad82a5828000181e203922020ffcfabfe3a003640cba142966a19f85d1028b10edb5b0d647273c55857af431f1b00000008000000001a001782c01a001b77401a00381e508040000000000000000000" - }, - "result": { - "gasUsed": "0xa1c4027", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000003083820880820080881a034d18b51a034d18b61a034d18b71a034d18b81a034d18b91a034d18ba1a034d18bb1a034d18bc00000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 13 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x10787f37", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000f98c996600000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000009582588d8bd82a5828000181e203922020638379e47a3916bae849a7d2eb688c4c79f2a16dc32813f0fbc2799237183c251b0000000800000000f54500aa9b8b0144008a944278346d41584367354149677a69414f5733616d6d6f2f2f342b6f4e77425546714c787a57712b5365565143506f79524842494f4c46451a00381e4f1a004fa10f40480016de9bafa1a688401a045b57630000000000000000000000" - }, - "result": { - "gasUsed": "0x98587", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 14 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x106e1d88", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000f98c996600000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000009582588d8bd82a5828000181e203922020a646954f2e700a51f149491da320e5065d7266a505b4013f3fe2b95a410487341b0000000800000000f54500aa9b8b0144008a944278346d415843673541496777613542697561305a34474d65625947757336787a484e5a6e47726c744b456d7535744b3536676e366e551a00381e4f1a004fa10f40480016de9bafa1a688401a045b57640000000000000000000000" - }, - "result": { - "gasUsed": "0x98587", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 15 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x1063bbd9", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000f98c996600000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000009582588d8bd82a5828000181e20392202019553461c49b73c9ac6661d0bcbad1abd0b5747cb3b2c2cdbaaf2f0ee22e4f381b0000000800000000f54500aa9b8b0144008a944278346d41584367354149676d43454d37766c6163766b78657351395a6e785554564433696473695259644c613641636b4d6266514b381a00381e4f1a004fa10f40480016de9bafa1a688401a045b57650000000000000000000000" - }, - "result": { - "gasUsed": "0x98587", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 16 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x10595a2a", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000f98c996600000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000009582588d8bd82a5828000181e20392202078ef0796f6fb842665ab9c1896535d700bea5b1f70189272d8bea7a61b36f43b1b0000000800000000f54500aa9b8b0144008a944278346d415843673541496749617553335659766c5578394e7575384c42776d507a55326f6d6e6a4c724e2b534474686439535656776b1a00381e4f1a004fa10f40480016de9bafa1a688401a045b57660000000000000000000000" - }, - "result": { - "gasUsed": "0x98587", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 17 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x104ef87c", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000f98c996600000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000009582588d8bd82a5828000181e203922020997081e8865104540ec72c6f436a983e9633c5e5098d7ab01edf1a381c9495201b0000000800000000f54500aa9b8b0144008a944278346d415843673541496742437463616277537077566247476239436d3068686a2b5075756e485471597a52694d41433676563879771a00381e4f1a004fa10f40480016de9bafa1a688401a045b57670000000000000000000000" - }, - "result": { - "gasUsed": "0x98587", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 18 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x104496cd", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000f98c996600000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000009582588d8bd82a5828000181e20392202058a6d1ab9902fade4bf717e7331aee4b46fc210ba1b972dcf3ea2048a541463e1b0000000800000000f54500aa9b8b0144008a944278346d41584367354149674a4b56744f37483938726c477248634d5738466a5764644d6d532f54356a34334b4c4944563575713037491a00381e501a004fa11040480016de9ad5b2b216401a045b57680000000000000000000000" - }, - "result": { - "gasUsed": "0x98587", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 19 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x103a351e", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000f98c996600000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000009582588d8bd82a5828000181e2039220204792c6cd5e8d978b7093e5d9a26a72c45fe7c815fad2bced0745522d5b2004261b0000000800000000f54500aa9b8b0144008a944278346d41584367354149677567622f52586644466330763238562b304e4d684c5263723376596f70682f455246325435742f354970731a00381e501a004fa11040480016de9ad5b2b216401a045b57690000000000000000000000" - }, - "result": { - "gasUsed": "0x98587", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 20 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000022cdaa", - "gas": "0x102fd36f", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000f98c996600000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000009582588d8bd82a5828000181e203922020ffcfabfe3a003640cba142966a19f85d1028b10edb5b0d647273c55857af431f1b0000000800000000f54500aa9b8b0144008a944278346d4158436735414967764c754c47416773594e6a3972372f734b67656433356f6d6d31545141614830716d6b6d307a54796774411a00381e501a004fa11040480016de9ad5b2b216401a045b576a0000000000000000000000" - }, - "result": { - "gasUsed": "0x98587", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc72dce371722de2e44a8b2f9b33551bd2dd5ae8595caafe3d34b9f15b01734ad", - "transactionPosition": 75 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001c17d8", - "to": "0xff000000000000000000000000000000001c17eb", - "gas": "0x4af5d01", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000072818187081a000122f0d82a5829000182e20381e802203050bd59cec207bc1408562d631cd13b7a00056b34e3dc612288cec5950573111a0037e0c0811a045b35581a00412a9ad82a5828000181e203922020e296585450fb35885860e0c4ac16cc5a7b1013b0f76d736b884763912a3e7e080000000000000000000000000000" - }, - "result": { - "gasUsed": "0x362e335", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x78132bb8f711bfc5da6a26980693e7256b6ba22e5d829b3d2e8614ba26b0a953", - "transactionPosition": 76 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001c17eb", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x4a3f6b1", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x78132bb8f711bfc5da6a26980693e7256b6ba22e5d829b3d2e8614ba26b0a953", - "transactionPosition": 76 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001c17eb", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x4933169", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x78132bb8f711bfc5da6a26980693e7256b6ba22e5d829b3d2e8614ba26b0a953", - "transactionPosition": 76 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001c17eb", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x4811bf8", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a00412a9a811a045b35580000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x478528", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e203922020e296585450fb35885860e0c4ac16cc5a7b1013b0f76d736b884763912a3e7e08000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x78132bb8f711bfc5da6a26980693e7256b6ba22e5d829b3d2e8614ba26b0a953", - "transactionPosition": 76 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002d0815", - "to": "0xff000000000000000000000000000000002d082d", - "gas": "0x512a1bc", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000078782194e995907808c80a1a7f1aef23e9c5a72f013f261279d9c83df068122083bfc73c928562b99d7e142e8323c3b1ba398efc3fddd17d686367ba915da2e8f05f5605d58f2957927a51de60a07ab0dbd24ac1e8a52b6af11b95df831d14dbac25d750fd4a45dca0837df29befcba63b8178ea8f02340b6d7d6bdb058bfc565ded5260ada6394f7c68c9a4d8494e84eeac2866efd78a5ab8995d7fa578bdc9df4b54ede4212535f7f890a681a729126bc2012fbb57b1cb3b59a93bec4617ee15ad138e9985b14b4955cd39414d99e873f5f4647c36d32362ee8d01c892a0117abed4d76e5f4f8fffbef81f01b535662b42ce3ccf5b415a2b8a671847288a295efc0f210cd27009e8084a633405e7f458cf404909035e07384d4f599bc4d7a2de45c9630b2b12c33072bff14f9e8dc7cbefc0f2573c2227e89711e63ae803944745a28949dc0ac962d4b8faccb5730b1a5e95e59c269dc1fb1a45fe8186d56382cfcc8c2cb70e93f01a16f059d08f5cde17a7fc62454ec77182142a08e69d2b2b4ff48333542077db708bd647e0ac8f4d1db47b183c91f6084957b28fb75778288f8b83975bc630c1ee44dcd9a9b95c3e440aac0bfe88683986dcca45b0e4c16286c3a2aeb9f7e1d43b914cd0b3f629ee426feff417c321ddb9b798070e040a269c9e22b1e1a81e20931d105241068cf3ae4792bda0f15593e4482dfeeae4e2f0386f08eb5cf7f1bbec416fa4378dfd75924a56d04610790aeb39aaaa8c2840f7c52fcb8f89a8b2d7cc4336ecac6d9e40223d45b31f2926dd0e45a1bfb6f0b6cfb5f4d0f0d8f9255aa0021ea31b75191ea0159be650d495e4dbe608db1ecfba2da7de60a321e5f7da691ad4db7db3b5c7885ad51cea72d38ad5c0eb37188c86cbe01c938896c0b6893c8698a5f7f1eb5cea5cdbcd39f718098086ea5be03d25c5d9ab5a30d96b78308b14a7ca1b098b8045c4d90a78206fc18fe6e1cf8c4cca2cda8dcd6b345211e4d8841a90978a7a21c58474c06aa5e568adaaa9fa880ac84956894d2e976d459bed01056a00e69bfccfe9d47f65fdef85720468801603ba01a63865c99acba06990df8d8ada941b6ebedc7484a9a574a18b16cea4683a8bfd6a90e1d845fa4ca568f3e4ea10818a8b551ee7defa34f7d95a71ed3bd40ceb688ec6c00cc207ce8305060b96a5acfb86c38a514bc3223a21e36a2212bf47bef2c2e6b3a93ad463501f46a336efed31b3d9e9998dbb10362e86135838c62a8fbb0489c18f72110dd2fc63428d2dd7e4351b2e075ffc89d9f99a1c2e049c1b5c7ddcebf1972cc4208122a1c981f44bd5c4810a2233922ceadebff26e745d4dba5cd698659399f6eb4b80e14b0f5b2d3604c971c4d6d5a2a3aad696a5660ceb110379fad11e21bcbcf82cc7c9de0ea2afbc0a90c4e22b1562cb1f436a758f2550d8ec0f06ea21e2a230e94e2b1eb5f3dba6341a01dcb08c9c9e19a68e1302b6ec8d9453214ec9340261329fc6389bbe2ee256e72c6ee1440ec2cd7d6e9a0ccaa1676826978e9763fdb9883efd5856573bffda76b38e20a84a48ef7aa8f17163962498dad8493ee48f8f0aca45098fc026df4b3dc20231bb647d894380c97bdedcd652bce638f65cb1b82c672e1aba05a9220b3540cbfed4bc74626309f44fd91d4d666349fd06ac0af5eaa45e37913a9ac44edd81d852c06f2a4fc567dc4c88164be2e004c7c4adc45223a4f6c0e34056c15393ef77c4a6f7f8ebd40774402335828324485c93f37cc01627a3234c1544fbda48a4d48be691d60055a3dbc38890cc0a5c9c89b4487f9865c0279f8898b77f66ee7c0b5f60b7f93cff14cffbd5465f61130bcee8107b17b6f761697f9008048dee7d0d3ae8519d43e3769b6c954fbbde729cd7c2fb86f97a78a947df5709f793c3d532e1dc4f81943b2f4e3502ffa30bd2d0da55c7661d6e779218c9b5708687671a0d2c7aa11962792e3ceb0cd8fd43824d6076c9566cc9fcf53af9c5ca560d6c2f73032ad1bc2a6b7667a4d925604e8b0ae8b55152a14ca4945e04f764f32c499d127884d6aa62fcea45b9c8d4a4f92cef803c55f66794f15cd5408dd57b802f4b95076a42eaf8bc88cae56becbdbb80130d2a52271d56c12876c2468726b439ea6e3558513790133e1dabb8574bb9cb99e70062f8cb206a536d8531deef4622096152e2e11d781b076e340edead07f4d34716bb6a5e6f0f26d3db1ba392c423857d9a2415eb660aae7b35aea3690a916e06ca2f927c5746b850f48239eec274963a123579ff3c33cdd40c83f04c6bfb307827953be12769e909af08c922f5872b8091629d637234884ed042edcca7c763e92163bc5e1ef1dfa28bd61ff429eb805a50e5cd798e723a051b57f08f9d4cebb4c5b5d78c8941e7a506d48fbf39b62c0a9255ed8a21170fecc1749cbb3b3ce64becf159388dc98ef1ce0163e12083ebafe678083245e05fb0fe66eefde1a1af1cb5529efe6897d7982a0a52fe1a6eb7c2d0df9568427e81a5c2dc38229752c83511345b03aaf28e40f40d6437410404158149cc36906067c6228cf679e1edc64b37abab0026c2fbf628c75c140466df6dd059ed0f4ab80b87bd7b107d8d5a1d40238c594468b699a34606c6a45aab2b7e834df48b7b2956fd06ccbffaded621041a9d8f78061daba66f2f943910c69ff71169a5a2464e05895a10ed0c38973d188afbd1500000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x88fc3f", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf9b0bea08c4a70da033330b656816716dd0bb2907a20fbb247e02bd6f5a7506c", - "transactionPosition": 77 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002d082d", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x4ba7b2f", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002d082d194e99811a045b25435820e1d4ff2fffdc2d955643405d87b009d9b4e4416564a87718a503d569c42a9c13582079dec049eddef67a2d8acaafb048cce7ccea8cd69df4b68830bb33041f5914c25907808c80a1a7f1aef23e9c5a72f013f261279d9c83df068122083bfc73c928562b99d7e142e8323c3b1ba398efc3fddd17d686367ba915da2e8f05f5605d58f2957927a51de60a07ab0dbd24ac1e8a52b6af11b95df831d14dbac25d750fd4a45dca0837df29befcba63b8178ea8f02340b6d7d6bdb058bfc565ded5260ada6394f7c68c9a4d8494e84eeac2866efd78a5ab8995d7fa578bdc9df4b54ede4212535f7f890a681a729126bc2012fbb57b1cb3b59a93bec4617ee15ad138e9985b14b4955cd39414d99e873f5f4647c36d32362ee8d01c892a0117abed4d76e5f4f8fffbef81f01b535662b42ce3ccf5b415a2b8a671847288a295efc0f210cd27009e8084a633405e7f458cf404909035e07384d4f599bc4d7a2de45c9630b2b12c33072bff14f9e8dc7cbefc0f2573c2227e89711e63ae803944745a28949dc0ac962d4b8faccb5730b1a5e95e59c269dc1fb1a45fe8186d56382cfcc8c2cb70e93f01a16f059d08f5cde17a7fc62454ec77182142a08e69d2b2b4ff48333542077db708bd647e0ac8f4d1db47b183c91f6084957b28fb75778288f8b83975bc630c1ee44dcd9a9b95c3e440aac0bfe88683986dcca45b0e4c16286c3a2aeb9f7e1d43b914cd0b3f629ee426feff417c321ddb9b798070e040a269c9e22b1e1a81e20931d105241068cf3ae4792bda0f15593e4482dfeeae4e2f0386f08eb5cf7f1bbec416fa4378dfd75924a56d04610790aeb39aaaa8c2840f7c52fcb8f89a8b2d7cc4336ecac6d9e40223d45b31f2926dd0e45a1bfb6f0b6cfb5f4d0f0d8f9255aa0021ea31b75191ea0159be650d495e4dbe608db1ecfba2da7de60a321e5f7da691ad4db7db3b5c7885ad51cea72d38ad5c0eb37188c86cbe01c938896c0b6893c8698a5f7f1eb5cea5cdbcd39f718098086ea5be03d25c5d9ab5a30d96b78308b14a7ca1b098b8045c4d90a78206fc18fe6e1cf8c4cca2cda8dcd6b345211e4d8841a90978a7a21c58474c06aa5e568adaaa9fa880ac84956894d2e976d459bed01056a00e69bfccfe9d47f65fdef85720468801603ba01a63865c99acba06990df8d8ada941b6ebedc7484a9a574a18b16cea4683a8bfd6a90e1d845fa4ca568f3e4ea10818a8b551ee7defa34f7d95a71ed3bd40ceb688ec6c00cc207ce8305060b96a5acfb86c38a514bc3223a21e36a2212bf47bef2c2e6b3a93ad463501f46a336efed31b3d9e9998dbb10362e86135838c62a8fbb0489c18f72110dd2fc63428d2dd7e4351b2e075ffc89d9f99a1c2e049c1b5c7ddcebf1972cc4208122a1c981f44bd5c4810a2233922ceadebff26e745d4dba5cd698659399f6eb4b80e14b0f5b2d3604c971c4d6d5a2a3aad696a5660ceb110379fad11e21bcbcf82cc7c9de0ea2afbc0a90c4e22b1562cb1f436a758f2550d8ec0f06ea21e2a230e94e2b1eb5f3dba6341a01dcb08c9c9e19a68e1302b6ec8d9453214ec9340261329fc6389bbe2ee256e72c6ee1440ec2cd7d6e9a0ccaa1676826978e9763fdb9883efd5856573bffda76b38e20a84a48ef7aa8f17163962498dad8493ee48f8f0aca45098fc026df4b3dc20231bb647d894380c97bdedcd652bce638f65cb1b82c672e1aba05a9220b3540cbfed4bc74626309f44fd91d4d666349fd06ac0af5eaa45e37913a9ac44edd81d852c06f2a4fc567dc4c88164be2e004c7c4adc45223a4f6c0e34056c15393ef77c4a6f7f8ebd40774402335828324485c93f37cc01627a3234c1544fbda48a4d48be691d60055a3dbc38890cc0a5c9c89b4487f9865c0279f8898b77f66ee7c0b5f60b7f93cff14cffbd5465f61130bcee8107b17b6f761697f9008048dee7d0d3ae8519d43e3769b6c954fbbde729cd7c2fb86f97a78a947df5709f793c3d532e1dc4f81943b2f4e3502ffa30bd2d0da55c7661d6e779218c9b5708687671a0d2c7aa11962792e3ceb0cd8fd43824d6076c9566cc9fcf53af9c5ca560d6c2f73032ad1bc2a6b7667a4d925604e8b0ae8b55152a14ca4945e04f764f32c499d127884d6aa62fcea45b9c8d4a4f92cef803c55f66794f15cd5408dd57b802f4b95076a42eaf8bc88cae56becbdbb80130d2a52271d56c12876c2468726b439ea6e3558513790133e1dabb8574bb9cb99e70062f8cb206a536d8531deef4622096152e2e11d781b076e340edead07f4d34716bb6a5e6f0f26d3db1ba392c423857d9a2415eb660aae7b35aea3690a916e06ca2f927c5746b850f48239eec274963a123579ff3c33cdd40c83f04c6bfb307827953be12769e909af08c922f5872b8091629d637234884ed042edcca7c763e92163bc5e1ef1dfa28bd61ff429eb805a50e5cd798e723a051b57f08f9d4cebb4c5b5d78c8941e7a506d48fbf39b62c0a9255ed8a21170fecc1749cbb3b3ce64becf159388dc98ef1ce0163e12083ebafe678083245e05fb0fe66eefde1a1af1cb5529efe6897d7982a0a52fe1a6eb7c2d0df9568427e81a5c2dc38229752c83511345b03aaf28e40f40d6437410404158149cc36906067c6228cf679e1edc64b37abab0026c2fbf628c75c140466df6dd059ed0f4ab80b87bd7b107d8d5a1d40238c594468b699a34606c6a45aab2b7e834df48b7b2956fd06ccbffaded621041a9d8f78061daba66f2f943910c69ff71169a5a2464e05895a10ed0c38973d188afbd15d82a5829000182e20381e8022046a040e4fb486d77bd3aefb25a4eb3670e46e3f935fade483164764113c9092ad82a5828000181e2039220208e04de2905b7795a206f4ca5423c6a65dabfd8304b05b92aeb298af986ff1c01000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x309b9c7", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf9b0bea08c4a70da033330b656816716dd0bb2907a20fbb247e02bd6f5a7506c", - "transactionPosition": 77 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002d0815", - "to": "0xff000000000000000000000000000000002d082d", - "gas": "0x4d995a0", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000078782194e7659078099831f9576b59ca994613a166b25313bd47dfea452d5fca7152208025b41569c4b746255e1f9b84c29ae1a2fd7b51c41b8ef90add5e5507af6fc9477acf8a4e467d92a6f8b9192187d58a63cfebbbb58db31e89a5cb0cec05afa21126a9f1f4d12a51c2bf2630a8f35de9f5b5917dbbd53fb2a80b940dba66b544572dcc9442f0b504c48d4af9930d653ad9e5de6d407b58f2ea14e8c4d0daa6b1ba3a9ee6aa6336bfe6a6d62b9b76bd8ce9a3cb83b67c98695a0d83762582a3c0a0f9ee2e4f081b5f7573a0879bef50e2833c42862f1c18a880b9caa2a55f0eb415e8394fef0c5e681cb33cb06532f49658ffe675138b16d27817e745e0b0cc3928eaf99644f8eaebb6f1679a0a9fb3814a3342004b5885dc1a14a27b4fa64286dd0a076e3b60276a16e86e2736bd0a808734caede2fb2f09db852720f3e531d3226ef7c42dab93285600995614ce01dbdf40c0a2450ae04149d63878dee9f00c37980523cadd7570c2e9fe5ed4b2b33a16f510e5cdfe2add1e44df129097b71a25f918bc5ed80278fa2c13977ce824c8772f285c8ff2cae6502d59f46965a18169fae74f22801c0360ee99355f2d05e8cae2635e9148d397467af6b2288b27f2104fce3a1de18900fe71808c5d30b050f2a6842794dc547b49ca9c8d9eafd6dc5e40f9999ad0d06dfdf3a3148d6504816215035bf4ef67f767196837fd6332179e426838a1a6015d1ed9eecce775bb1cf805beeb22f96ccbed2588efcee9e0900ca3d1dcda9cd9fb1b74d1421f69e9f64116ca7f847824983039a2cf530ab96016bb625df50b880b01ea628f7bac496a4884a2be94a8bae8d344f0e4ebcb1910cf9d4547df86ba460645e4803f4a9f94b1ab3385552872be6f3a8dfb1993843ff36dd9f84f8bea6dafdf293a110d6617c14d2f9baed553c8a13fc1b711f1eeb45eb24a4ec0407f15431b667d49129837af9ed1dd815feb000650de073f80802572806947ae12feb9068dab35f2b3ba34a2fa9a4086187a81a66b249c8bee5b6ceb4bd8e453db444ef365eeea12e1e2ad3b656a2071a21512e972a99b7578898f82e458a6dcb91f2fe327f52b66d72167780f91bf3087924285f659f13900e441d56720ad2706460b970a36a67ec7d4ae41dbb0c63e490fc93ce81e6462b22842cc6fcdcda160f534531f6142ec8b6c4a46b506e1098e19994af6317be2aaa17f028adc4c43a18a2b77b16b1504c73782e5a9d0cdae8ad687921e1e5a1994c1cf0a46cd6cb78b318e72fdcd162adf463642e7bc48a6c92028af24770f3456e2dec29a3f3dc5b310183734a2c2f736a28807168b68e1985d39c39586eab9ded0df6d5643cd41eaee50789eda6cee7540552c9ec67094db48f7d7053605b266747545e72283d5640548aac9fce739dd4004d4ef869a72480a04609bd8f87054815b3cdfb18929784b1c12d8d6aa81bb417625924df826dcc025a1cce3c7203d906c6f25d7a863b1831bfb61737060a63707c5308f125fde71c50686e8813373aea7451e15e1579508a43b7bf9ac23546bfe1e5d80de563979d455615f690a07a44b2e7f4fa8460f1a1ce0a428495e3746cca143099ea00351ab4e001eeb3a8f2f8dc7ceaa06e9680424438aa2401867f354e33808fee11abc04240d19eeb91fd9551fb828a32cbd7e3e1b924247bd77f37335c957be1af84013c9ed4ad5beaee30bf3838c9bdaa4b4c9c6ef4b838360983426cde4454d1220f8677da23a117844bb82e168c51781222bc620ad9e8341141391b7f1e74ba73c751b47a8c2a44bbf90f2926a19fef298afdf0ed34c7f34e8c3f3339c653a7adb80caf0d61be88fb521e254a313d3118f0d59d9a77b5b29d321c08c739fd7256bc4cf42fa1b3c200bddfeea074843a99e8b14bc42ec667fc1a46356e3e18aea5bb5cbb981e867e2a9f26e4c00e4f922741e132900115257ece0265cdc485a9a6b1e2141fb5048e05e911a34b14eccb6c9cc5ad8af7be6cb6c5d9159832640d0773f5b3cb57cb4f85d9673db29632bc0c5bc7d26db2d396c8d74932fc3a7670440b76ea62b96e69dca52efccfebc9ae594b3a0e26143124d8a84ac427cd974889dd6b74405dd8d2367a7ab0e4aeec9b51b1e96f4819f48deb0afb32d9b0b9bbc4701c9d60da394dcea1d86c98edb8b6a065bc0c19840d2e6f454459f9aeedb422addd265807259f0dfceb1321cb3acd3b40584bf8b3340066580a27ce0c9b289959fce200924097e34d457096a4c5e3c3da0e644d665c071d75b3f7828361f7d801e48527af973586fbd0c07de1925a07388f606844c7c379a347b3439648a6752e95a4e89fd64823c8ac619459b93cf7716c1dcea6ddd460f4c42eb9994ff98a8b00cf9a74b13152e9fb1bfcf0b00d45869bdfd88355ec1d473a8752bf1a2a04640ab3918bc4f0bef6b9187b74c414b0d1ee19ff3a1aeee50b73300d3010bd2698ca444c629509acce503d59248b475602fe48abea5a4b8da9886e71d00f4ba62142c8790b630b357014be80b83aadd3784423b90d54cce1ac94297a694e0da2f284a0af093c1a73557ff126cfd29e035d59c283c826d17da2af6d967936735edd32c4e3dbaddc3b113a2a03c2b7ee953b194f05f2b17e54fa36fb3b30396fb858ef99398e82a3ea62113cac8f1a216017083de9d1c5524b777dfba1051c3bc2d510cfde0fb762cea5f226d8b6e64b00000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x89592a", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x11af98bb4b63e184700045f41d8370daa555dc189c7670a92b59c4fc7c47ff06", - "transactionPosition": 78 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002d082d", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x48112a4", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002d082d194e76811a045b225a58208e503e3d366299b9095156df2ccafbc49fbe238c3592a879c7cbc3fa095d26545820f7f9630e9e1ff4c01611a3dd41958c27ef95804e97da3fafe1e0c27da131a91459078099831f9576b59ca994613a166b25313bd47dfea452d5fca7152208025b41569c4b746255e1f9b84c29ae1a2fd7b51c41b8ef90add5e5507af6fc9477acf8a4e467d92a6f8b9192187d58a63cfebbbb58db31e89a5cb0cec05afa21126a9f1f4d12a51c2bf2630a8f35de9f5b5917dbbd53fb2a80b940dba66b544572dcc9442f0b504c48d4af9930d653ad9e5de6d407b58f2ea14e8c4d0daa6b1ba3a9ee6aa6336bfe6a6d62b9b76bd8ce9a3cb83b67c98695a0d83762582a3c0a0f9ee2e4f081b5f7573a0879bef50e2833c42862f1c18a880b9caa2a55f0eb415e8394fef0c5e681cb33cb06532f49658ffe675138b16d27817e745e0b0cc3928eaf99644f8eaebb6f1679a0a9fb3814a3342004b5885dc1a14a27b4fa64286dd0a076e3b60276a16e86e2736bd0a808734caede2fb2f09db852720f3e531d3226ef7c42dab93285600995614ce01dbdf40c0a2450ae04149d63878dee9f00c37980523cadd7570c2e9fe5ed4b2b33a16f510e5cdfe2add1e44df129097b71a25f918bc5ed80278fa2c13977ce824c8772f285c8ff2cae6502d59f46965a18169fae74f22801c0360ee99355f2d05e8cae2635e9148d397467af6b2288b27f2104fce3a1de18900fe71808c5d30b050f2a6842794dc547b49ca9c8d9eafd6dc5e40f9999ad0d06dfdf3a3148d6504816215035bf4ef67f767196837fd6332179e426838a1a6015d1ed9eecce775bb1cf805beeb22f96ccbed2588efcee9e0900ca3d1dcda9cd9fb1b74d1421f69e9f64116ca7f847824983039a2cf530ab96016bb625df50b880b01ea628f7bac496a4884a2be94a8bae8d344f0e4ebcb1910cf9d4547df86ba460645e4803f4a9f94b1ab3385552872be6f3a8dfb1993843ff36dd9f84f8bea6dafdf293a110d6617c14d2f9baed553c8a13fc1b711f1eeb45eb24a4ec0407f15431b667d49129837af9ed1dd815feb000650de073f80802572806947ae12feb9068dab35f2b3ba34a2fa9a4086187a81a66b249c8bee5b6ceb4bd8e453db444ef365eeea12e1e2ad3b656a2071a21512e972a99b7578898f82e458a6dcb91f2fe327f52b66d72167780f91bf3087924285f659f13900e441d56720ad2706460b970a36a67ec7d4ae41dbb0c63e490fc93ce81e6462b22842cc6fcdcda160f534531f6142ec8b6c4a46b506e1098e19994af6317be2aaa17f028adc4c43a18a2b77b16b1504c73782e5a9d0cdae8ad687921e1e5a1994c1cf0a46cd6cb78b318e72fdcd162adf463642e7bc48a6c92028af24770f3456e2dec29a3f3dc5b310183734a2c2f736a28807168b68e1985d39c39586eab9ded0df6d5643cd41eaee50789eda6cee7540552c9ec67094db48f7d7053605b266747545e72283d5640548aac9fce739dd4004d4ef869a72480a04609bd8f87054815b3cdfb18929784b1c12d8d6aa81bb417625924df826dcc025a1cce3c7203d906c6f25d7a863b1831bfb61737060a63707c5308f125fde71c50686e8813373aea7451e15e1579508a43b7bf9ac23546bfe1e5d80de563979d455615f690a07a44b2e7f4fa8460f1a1ce0a428495e3746cca143099ea00351ab4e001eeb3a8f2f8dc7ceaa06e9680424438aa2401867f354e33808fee11abc04240d19eeb91fd9551fb828a32cbd7e3e1b924247bd77f37335c957be1af84013c9ed4ad5beaee30bf3838c9bdaa4b4c9c6ef4b838360983426cde4454d1220f8677da23a117844bb82e168c51781222bc620ad9e8341141391b7f1e74ba73c751b47a8c2a44bbf90f2926a19fef298afdf0ed34c7f34e8c3f3339c653a7adb80caf0d61be88fb521e254a313d3118f0d59d9a77b5b29d321c08c739fd7256bc4cf42fa1b3c200bddfeea074843a99e8b14bc42ec667fc1a46356e3e18aea5bb5cbb981e867e2a9f26e4c00e4f922741e132900115257ece0265cdc485a9a6b1e2141fb5048e05e911a34b14eccb6c9cc5ad8af7be6cb6c5d9159832640d0773f5b3cb57cb4f85d9673db29632bc0c5bc7d26db2d396c8d74932fc3a7670440b76ea62b96e69dca52efccfebc9ae594b3a0e26143124d8a84ac427cd974889dd6b74405dd8d2367a7ab0e4aeec9b51b1e96f4819f48deb0afb32d9b0b9bbc4701c9d60da394dcea1d86c98edb8b6a065bc0c19840d2e6f454459f9aeedb422addd265807259f0dfceb1321cb3acd3b40584bf8b3340066580a27ce0c9b289959fce200924097e34d457096a4c5e3c3da0e644d665c071d75b3f7828361f7d801e48527af973586fbd0c07de1925a07388f606844c7c379a347b3439648a6752e95a4e89fd64823c8ac619459b93cf7716c1dcea6ddd460f4c42eb9994ff98a8b00cf9a74b13152e9fb1bfcf0b00d45869bdfd88355ec1d473a8752bf1a2a04640ab3918bc4f0bef6b9187b74c414b0d1ee19ff3a1aeee50b73300d3010bd2698ca444c629509acce503d59248b475602fe48abea5a4b8da9886e71d00f4ba62142c8790b630b357014be80b83aadd3784423b90d54cce1ac94297a694e0da2f284a0af093c1a73557ff126cfd29e035d59c283c826d17da2af6d967936735edd32c4e3dbaddc3b113a2a03c2b7ee953b194f05f2b17e54fa36fb3b30396fb858ef99398e82a3ea62113cac8f1a216017083de9d1c5524b777dfba1051c3bc2d510cfde0fb762cea5f226d8b6e64bd82a5829000182e20381e80220f35099649c579a0471c666d7319476b5bdc31aa0d651e214daae463001788769d82a5828000181e2039220200eaa7afc2292c2eb6a2db7208cd738b6b5b7fb69577b134cf1817c6f4d39dc0d000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x37f99a8", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x11af98bb4b63e184700045f41d8370daa555dc189c7670a92b59c4fc7c47ff06", - "transactionPosition": 78 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001ca66f", - "to": "0xff000000000000000000000000000000001ca698", - "gas": "0x336d63a", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000027a8518228382004082014082024081820d590240a08ff3b7b6fac42168d1bb9fe5ec46d2a1f0f7393abe61635e7cd7abb92358fc5b06b05b5598363c685a19a2eb0a789a974237d01d4e9abab7fd26c5f5b961c3c2e7e7a02f811aef4e9017183f01fbf2fcec48c11c66b04ec88c6205326d942c0481a6cd1f2a9ce933999b9f1ca17e7e250dace30151e38d334fd375c80942e881b4b75905866110ff9a8767765908bea19acda6d804c59114151663ef6aed4a26048afbc1b3541673ee9b223dec1adbc7e0317d90c570b2aa0ef83ca19fdc649143af73cce6bc1b5a2710b6da1abc090b08f26d9f14d4db79460f04803bfd718988bc44a81936da65d2631296f56489b8cce5f35c108aaf789d8f51cbccb80974d86b100876b421af58cc973a25363ad0e465c91a650677fd76e3d82d028dd910925f07bb1fe8e81bb8bbea9661f6bab48cd6a72c0fd8a70edb644cb4f1cb5c51be9168b28a2eba6e429f0fd69657fd88e05ef08bb018310481be280d48d435cf22bc452d61a846bae765092fd51625d5cea1a4d2b1fc2064b7b682393d35348c988668c6d985754deaa21b4ec3e7411268453a8f118295888def479a01e0c3a8fa274ce6c75cfe7ddd1f32d033a43080a25e8579a26b7dc65c1de7e88f22234842ce224f4f946717a7ada60282758ee63a6a08fb59f73ee5419aaeceeaac6511e0b58e0e14906e2e9343e52d9e6d05d4149ee4777bbfadb41f1d822dc68c5d5db3e1cc7d1cf8af15a7f2f874bbd867b13c74e229c51a1fe2e34d3e141592e5d16406695de5c9d4044770ccc776e49db6fdd3ac048ecf32d813e0e3fb883dc61a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d69000000000000" - }, - "result": { - "gasUsed": "0x2a5696d", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xab7a2853fd0c3c51361087f07a575acf5ac0930968bef1ac73bf360275a52281", - "transactionPosition": 79 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff0000000000000000000000000000000014b4b4", - "to": "0xff0000000000000000000000000000000014b4ca", - "gas": "0x27c657d", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001b785182f8282004082014081820d590180a6333476a9dc6aaee94d32d85f613057168b6aca7e43fcd54ee38b03aae16a0917c867e0339d5f492cd987024f4141a0966b843654b2e9e7af330fe6d83fa05630c3aff5417eeb00e5e3aafce6d12ffdcd18b68cea29003838faee385a3626330a8e3c20ec69cdb8e3b5ce8594c9c9eaf68996771fc6b8399edd629b97856d7611475591ba61b86aafba95ebb482ec5498d88485f2cb9d6920fd45dd8a234091a31014aa836496dced595b0f5f89aa7abfa5c5eae63a11e93c0a3b241cf42901b931570b6fe9f665a4e62be9d3b1cfc5c3d915edc6b899b28f9e49ed67157375d74210aa54d116b9af6d1da31095d2eaa8a5d81d48da35aa43436e12c6d1a539c691aaf6a3575dcf80219a0214b5babfb9b8811e55ec3afd1b9fd73f1b82dff707ea2962acc711288fa110af7e7671ec90b1a7f0f40534c7fc9b538a09fd3e89a5e38de44b12b279f952775aa0075d8c9840f0bbb2c40c0a5c4b999329da775933d297d4c97101cb13ed3192edac7596df7ac4533cf1a38b07fa1e18b58b11eb1a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d69000000000000000000" - }, - "result": { - "gasUsed": "0x20d2a3e", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x395ddc88f44eadde7eab48be036779baba3f53083167c7517a713f15c96bc804", - "transactionPosition": 80 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000112c55", - "to": "0xff00000000000000000000000000000000112c87", - "gas": "0x184c153", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f385181e8182004081820d58c0b6ea07c16c33360054815486dabadebe23a05ea409bd99d4fbf7ee738771daa5b76f68d46c3a59f42766ef147e7ed9a299dfe9d4990acbcd4aa5c21ba31e98b599d8f904c11c3a312877e1ed3e9d76581bf5b1b6c37872136a084bf5e7be564a150ed4a52079e13f4fa2a95946893e961ed7ff6753f8412494ba67faca84a2d912a51d9388bbdee6338a6b58bf7283ae85f28878222de7ceea33fa13d229b5cb1ba5a74186feabf2752d0329b868f9e92f5cc92bc1f120bd2d1f8eccabb8ef0d1a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d6900000000000000000000000000" - }, - "result": { - "gasUsed": "0x143ecfa", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x865c98e58b41449ae2484a8b15d7a5fa601779e0a55194cc8ccf5276556c532c", - "transactionPosition": 81 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ae495", - "to": "0xff000000000000000000000000000000002b1e7f", - "gas": "0x526070a", - "value": "0xf2ac3304b3a9ee7", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000787821958b3590780b104649bcc2962b63068e99c9027750b8c0624579c9782df07b725bbf8fd863d32bfb7e038c5ee49a53a243b7c4b619f8f01f2528b63d3d30ee42353e63991460d3017d690d5d1d4061308bcc06d6df4b637605a12c71871455de0ec0ca9d4bc0618c739d4a1d2840b40e941da2f979eecb008f4d42936fd507dd2ae990e56a60d0eb73c942acc11a32316852505bc07ab49bfdfbdee30b32738060a00deeb7a72fcecaca2c2fb0c78d2572b23d95a996fbae088c4089a1c7d78a6879e40b1d8a08912c83a221c96cdcf3c27a041039e0c082b49aa120a8612ebbb8b212643e5f919598c6d783c944ae489e397c5dd3ea0afd730a6bd1c439380d86e05420a08145ffff0b1c76da1f920b39ff18cb04a3dc17c98b841c7bbff185d647e3b73f411f897ba7012863e6c3f58d734e3e105ff9e66cf6fa967763ff752823c056262bac4ca4ec4cdea379a6021f4674f16e4897b09752a737058c6539bb958782e5e2a9c87fa86b281e4fcc52acb75d568f6bcaff64d47e2cd235efdf5d1587ba860af4a62d1b04809f8fb934412ae24a9ac4dae0f8b9b6984b08e016c7053aa46fb312790aa0aaf31e7996fbae29c724027a0309818b8abc4baf5c5d96a582f55b79cccea28bcdf5ee03001dfff5cd945dfa476cd9008aea7eea2362719bb57342e15719a80d95056f0db56af34639b334309caddc833513c772ba05b7a365d6e91141112e6570296fce32640717602308ca37497d9efbdf284d2f4e7502ef3072a9e01f4a9a055457ce9db2b66c6e20aac758aaa1d085a1143bafe1ca3bb961379a02718f0fbbc7394f89b6ed096317866c50eb17fbb56ef9e0ef4ba22eed07a2cfa955669b7861ff662fe5698eb2d6e608a209c2286f0a6ab0cea4db0a567747498be3aac058521cb00e1ad7c6b6223f01f755c9ea146ec5c36562116172684cb16ce4479da96c83f6f509002ce583dfaa127db607a494aa3676db46be3e5faf22a990280626e7e39de54b6437302e194aa3ccbda92483130976b902cb621226639664deb1564336ae7f7d60d8c81ede66a154b38c147ce6c8ddfc93e8c20116085603c2d8cbeb6eed491e82d1838809bba6ff51bf56155c1e3e5eb4811bfeb2f68c17e4830a4fdaae17bdc1ff6869aee810941f3a7d3e4b01b47a118bf44f98d0acc3580390a05a4597f3a055498485d7850ef0a30cc412156e872a8ac1525f00c09113370c97ee408b6669998c6bd4513dc1ed023093e93354650c5c9be1c302a3e6f4ea612c6f24b2f1d31344d0f8ba7715ce0d0ca3ec816938fc2b7355c3424a2d2af92c6ed135bb8a0f2e692dfac2a25ca123dac083ad7f379d9316d320da4d4f6df987064e5918b5b55d0e39d97a1e972097f3cc790edf8479aa57d1dc911cb1c584aabef898354e0858b7c869ea0f3b4a827bc681be89ede0af01dcd2856595eaccb0d6ffd4aefaf3c1d406e39faba62ea6d46d24000097db353f506c4054e9309827a32246d6db154689bd3d164e9ad0ee9e662cb4f66e4f191d2adab221a004f49a035ab639d3a7a9044a148b12c4d0809dbe51231938026da42e017e475eca0d6f6f584dae896b73c5c1048ef04d42389728b8ad82479de84600161a731949cf60a2b18093ce01b865c31cb192dcfa58942f15ce2396893408b81b69b02d146412f8c5c57962aef640b774098da0b21442042325efa87ec757e45b40d0b01555296a39172f3f72098c6564a7a40b5526b63208f411e01fe949af04904c39b78775cffca74cd36af636971621ea74c83ad29f2cc55974c5ba7fdfc4e3b743f906e9d739a545f5974bf44e2f695326a2296fc4eba138971507b5c208e78fbaa94a1f0f6332ff6f242679dca0034472a9ac61f993a8f09dae64b860a44aa5e61c527122e4074a24fae53f58b713df614d5a308c91299f13f34ccefb9c4aab4f75baec8d0ac16d8e79115ebb45499a39d5ff53b40eb21496ebbb37c9a691748b80dd24b9d6f869300799059f284c39b97f1f75b773d78793de665ad8af00dddbad2208b6b3add604e1fa2d567f44be4661d4e74d72fb2018dc31a6542e856c9ca57804cbc9b5af7e14ffbc1fc59a65e845ecbdbb8cf1a8797224dfa0c76e8f995f899d541d50dd45cab0f05312ab8ea7a8522a1756d6d2e770d93500f7782670068603ab43ad972b4a55b42e5e0932790eba71343d1da3dfe2490959a3c11c2a1e859134047d18fbf75445d34cbaf139550fe0d9a72f80e13310255cc82b955156746e04272811b74a0b3ca3c2866b59b12dda698d562a619238965ed1200b8b2f41d3e06318067a0c248112a3aaf3aa30a396be7422986e35b41e8d375f76010785ff3eb09489b70e5a2c82984a10812bf9b1c7f4f877df41bab0d7b01e685670c56f3e545329a4a882fb0a7264343d54597346b15df13f25d7a0931a887a7be4465b717ee4552a0192534a10b2d19d086d577d1adb8154787e3af1050370e60599d3636e0d5081e357c5483e2a5eafb2f71c6db5b1213f4a07ffbe2a86eec3960d242fe2106ffbd9865882ae4dfc2d9542cda9a26a3320e72db1fe68c1235c468bffdb20a26791df145dac5a0d2c9527ea841961c45fda95d3d0267955dfef6bd960231c1a8dfd36583533836a1dd94d4b456a97ef41237fd6d8d5b898aa21eed123218ef4b3aedd3e645c9c0166c26e652699bf951f8f6f0af9ae11500000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x7dd123", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xd5c156a48ee21fc31fd16f0102ba7f09ae50f5f040c95fec880ebd46b96c2506", - "transactionPosition": 82 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b1e7f", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x4d9396e", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002b1e7f1958b3811a045b26ab5820f8ccb64bff08817a3cb554d2b97d7ad9fb9428ac6bde0bd9144cfdffec760af75820749b5789aaaaa013a8a90db28d47429b37b0c322abf2f2af8eb0261f3fd9f133590780b104649bcc2962b63068e99c9027750b8c0624579c9782df07b725bbf8fd863d32bfb7e038c5ee49a53a243b7c4b619f8f01f2528b63d3d30ee42353e63991460d3017d690d5d1d4061308bcc06d6df4b637605a12c71871455de0ec0ca9d4bc0618c739d4a1d2840b40e941da2f979eecb008f4d42936fd507dd2ae990e56a60d0eb73c942acc11a32316852505bc07ab49bfdfbdee30b32738060a00deeb7a72fcecaca2c2fb0c78d2572b23d95a996fbae088c4089a1c7d78a6879e40b1d8a08912c83a221c96cdcf3c27a041039e0c082b49aa120a8612ebbb8b212643e5f919598c6d783c944ae489e397c5dd3ea0afd730a6bd1c439380d86e05420a08145ffff0b1c76da1f920b39ff18cb04a3dc17c98b841c7bbff185d647e3b73f411f897ba7012863e6c3f58d734e3e105ff9e66cf6fa967763ff752823c056262bac4ca4ec4cdea379a6021f4674f16e4897b09752a737058c6539bb958782e5e2a9c87fa86b281e4fcc52acb75d568f6bcaff64d47e2cd235efdf5d1587ba860af4a62d1b04809f8fb934412ae24a9ac4dae0f8b9b6984b08e016c7053aa46fb312790aa0aaf31e7996fbae29c724027a0309818b8abc4baf5c5d96a582f55b79cccea28bcdf5ee03001dfff5cd945dfa476cd9008aea7eea2362719bb57342e15719a80d95056f0db56af34639b334309caddc833513c772ba05b7a365d6e91141112e6570296fce32640717602308ca37497d9efbdf284d2f4e7502ef3072a9e01f4a9a055457ce9db2b66c6e20aac758aaa1d085a1143bafe1ca3bb961379a02718f0fbbc7394f89b6ed096317866c50eb17fbb56ef9e0ef4ba22eed07a2cfa955669b7861ff662fe5698eb2d6e608a209c2286f0a6ab0cea4db0a567747498be3aac058521cb00e1ad7c6b6223f01f755c9ea146ec5c36562116172684cb16ce4479da96c83f6f509002ce583dfaa127db607a494aa3676db46be3e5faf22a990280626e7e39de54b6437302e194aa3ccbda92483130976b902cb621226639664deb1564336ae7f7d60d8c81ede66a154b38c147ce6c8ddfc93e8c20116085603c2d8cbeb6eed491e82d1838809bba6ff51bf56155c1e3e5eb4811bfeb2f68c17e4830a4fdaae17bdc1ff6869aee810941f3a7d3e4b01b47a118bf44f98d0acc3580390a05a4597f3a055498485d7850ef0a30cc412156e872a8ac1525f00c09113370c97ee408b6669998c6bd4513dc1ed023093e93354650c5c9be1c302a3e6f4ea612c6f24b2f1d31344d0f8ba7715ce0d0ca3ec816938fc2b7355c3424a2d2af92c6ed135bb8a0f2e692dfac2a25ca123dac083ad7f379d9316d320da4d4f6df987064e5918b5b55d0e39d97a1e972097f3cc790edf8479aa57d1dc911cb1c584aabef898354e0858b7c869ea0f3b4a827bc681be89ede0af01dcd2856595eaccb0d6ffd4aefaf3c1d406e39faba62ea6d46d24000097db353f506c4054e9309827a32246d6db154689bd3d164e9ad0ee9e662cb4f66e4f191d2adab221a004f49a035ab639d3a7a9044a148b12c4d0809dbe51231938026da42e017e475eca0d6f6f584dae896b73c5c1048ef04d42389728b8ad82479de84600161a731949cf60a2b18093ce01b865c31cb192dcfa58942f15ce2396893408b81b69b02d146412f8c5c57962aef640b774098da0b21442042325efa87ec757e45b40d0b01555296a39172f3f72098c6564a7a40b5526b63208f411e01fe949af04904c39b78775cffca74cd36af636971621ea74c83ad29f2cc55974c5ba7fdfc4e3b743f906e9d739a545f5974bf44e2f695326a2296fc4eba138971507b5c208e78fbaa94a1f0f6332ff6f242679dca0034472a9ac61f993a8f09dae64b860a44aa5e61c527122e4074a24fae53f58b713df614d5a308c91299f13f34ccefb9c4aab4f75baec8d0ac16d8e79115ebb45499a39d5ff53b40eb21496ebbb37c9a691748b80dd24b9d6f869300799059f284c39b97f1f75b773d78793de665ad8af00dddbad2208b6b3add604e1fa2d567f44be4661d4e74d72fb2018dc31a6542e856c9ca57804cbc9b5af7e14ffbc1fc59a65e845ecbdbb8cf1a8797224dfa0c76e8f995f899d541d50dd45cab0f05312ab8ea7a8522a1756d6d2e770d93500f7782670068603ab43ad972b4a55b42e5e0932790eba71343d1da3dfe2490959a3c11c2a1e859134047d18fbf75445d34cbaf139550fe0d9a72f80e13310255cc82b955156746e04272811b74a0b3ca3c2866b59b12dda698d562a619238965ed1200b8b2f41d3e06318067a0c248112a3aaf3aa30a396be7422986e35b41e8d375f76010785ff3eb09489b70e5a2c82984a10812bf9b1c7f4f877df41bab0d7b01e685670c56f3e545329a4a882fb0a7264343d54597346b15df13f25d7a0931a887a7be4465b717ee4552a0192534a10b2d19d086d577d1adb8154787e3af1050370e60599d3636e0d5081e357c5483e2a5eafb2f71c6db5b1213f4a07ffbe2a86eec3960d242fe2106ffbd9865882ae4dfc2d9542cda9a26a3320e72db1fe68c1235c468bffdb20a26791df145dac5a0d2c9527ea841961c45fda95d3d0267955dfef6bd960231c1a8dfd36583533836a1dd94d4b456a97ef41237fd6d8d5b898aa21eed123218ef4b3aedd3e645c9c0166c26e652699bf951f8f6f0af9ae115d82a5829000182e20381e80220f8558d739d8803bc6cebfb9d848e53a6dad4c37fab064e8c705621641b35074ed82a5828000181e20392202061158a40f8ea8f3f04148354f737e70549998404cdc66a5583c2f4e3cd0a0a1d000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x30cf1b6", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xd5c156a48ee21fc31fd16f0102ba7f09ae50f5f040c95fec880ebd46b96c2506", - "transactionPosition": 82 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ae495", - "to": "0xff000000000000000000000000000000002b1e7f", - "gas": "0x402499b", - "value": "0x105e0cd9863c06ef", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000787821958cd590780adcd547b9f1054f3eb179481e1437f981389c937872090f003ddb6e08d753861e6f05e90b9990d98c754dee7d6dddcf1941a48d0ee5bc3570fd52520a5ce3d9da7b40f68761b3cf3a7633dde498283d95055e3e32521d8a720d41321026044280910a2478ff76ce23192d695ab518438f967da0da1912608ad89ea0a50d2702b5032f026c62cce894480256c74c639bab56435da3aefad1ad28659d132ecbce88d3f80b0cc4a3048e94f13803b84404f17dc0a2d57b81ccb1255eadf2a55d5fa892eb861314ca95a1d516f63df0bc93c77529573929e10aa35c8e0ba9c225df897ff0d005be902a2d74c4a693893015f8f14311232d816d1fad5a4e005e685d426dd78c531e9563dd8785ac95e44970fac7f3b7d8d6603329604061b626cae7502fbac2ef0c460af26be45e113b2b7a43d935116511231a1c33ac5d769ef3b0c9c3fc626fbec2f8cfb87572e68c2d617897a9fe18d703b26f4253e931ea3e9c1a4b5077fc7656797b8df389b14bb5e2888a7dcdfdf89dcbc81c34d236d31f021a7a207dc806af6f3c89889efedd24f511562a2d01bea7b55afdea26e486bb729cab9b33a63c3360f8d9f267673eee7aab384b4d6e92d99955b0f1d58dce11f6e41dfe0ad020db66d34b9809c01d2f652a567b0ee46bc2ea4743a3f570d96acbc1525a2691d8bc7e5a1abd34f91189f9aacf36cb833f75be11cf12d47a985667f8b65c5444b1995495ea618484846e5038a8bff82cdb012ffb11bfb14934cc26e4e00331511d49ab2634d2d521a0989aca51ba718f0247dbdb88693f094bbb045a4d9f586340fc388fb9b4e0c65b52a38287cb8614bdad6271c3122e845ce8e74fef8bf6a5764d4384c1273fe5ce3037a951b46409d1518c256e26776e58f7f54f710ee3f190b4706de47e1c4dc0f427fc026a55c4b78deda2aebc381b98de5e200a765da1713999ca2a0d4de1ae76f12442e43e367055cb854ca45aed1115a0e9b20aa1fd188613f7c75beb1a2d26617890e786f2bdbab208b9dad19c1e6ec5dd69b6bc65657fbe561f9efae97eda5d4d855841f450d60fdceeddb45f371b5ed86ddf2cd367ad9a977eb4d9161ee549f5ab243513b70fe3f5708d4f13a47731db9898777079601f103ad01acb74075eda60036ae233fc572e41e546081f852496dad5e90ee06d722cd3835294cda3a31b61a3a167138d00aed08ebbe66a970e80334142cfdc870855c9b6d32e5942d526ed4762a13f9372572cb2555bb3aef5428906719050e1aa6a16ebecd5b35a38f940b3d4b12308a155e5bf035484bc1c2955ca84c23c25801065f7bd907b43e98116262a29c52d313f5355adf15b98f7e9038d780a2ef90049625360d7aa4ad4db577bfd9258d5627932993250fbc0b10b037a899612b74ac70c044044ca1e2c4a4cabe88260646811723ec43609158519e563e44a34ac6ca462c1cc2e6d79b48ed604e5fb890f41f0239ee947dcf6daa11da281778f48e78f9280f54d54c728c2ec8299e710c6c3acda600ec4b55e421c4d77376d8da43f1eb65c473c00f08c3ae15348d1049f75449826c0f9ee69361241491d8261d82daded638e80e042cea44cd702a529329aa603704231b24e933aa6d700662ec989717c7bd430cd3023a53221052ec6c3539c2e15518dc75f6118da892225026bc05c0802b106e6240d28505cd2a4ba725484ca570ed17c7a254e7a338aefd50ae4f45e3bf2d8ffd4695ac84bda612cd4fbc6dd857735594ac3e0457fd9c7e650c66fc77b562c31ef1ce72e50efc6c2ff3fabfd59c601e98a35fbc269d02a7c274f6d814b167217d3053a50fb5181bfe154ccef4a6f83cdffc84d6483072335302dc9210f0370fb49d01fa9675c9952c660f53b0def14bb37043a979df12da6ee45e3ee721b8364c64f9bf80d3b53c67f4888d1b22defc537c0d50cb0fa1c0074ed63333d8e1095b945f902f303a784d070ab17b68765f138598501387130ab052f0056f3881d376b6eef1b256af53d985ae9e8c2613d6859acb171bd1101c29a5b3319eb0b97459948ff358d596fb1a08388368bc9174756855b3b1a629706bf31d16e9dccb3e05b57581da42995a170f8b77103c24ab1e11f287c7dd1c089238da7c24411148130654996d194c5bcab051a092be26332a16b195248002370eb07a43411aad2128e64c0ca0bbda8bb6b25e87628d8bd55c28666bfb999afa7e0dda689c8fb0d7059f418b2af12a70d310c2bb564551159e52f34ccf38f40ec3d9661b57a91c6f8d84a11f86058b4ca466ec86ccd32ac4d69aa0057d65f04dc6f6da6daf63db98605f948b9b9f191eb9a2fb7b4c6c669f446f7fd47300257a50cdfe05030011def8e0d493254dedb9e343a6e0209b91cd3237e2c6d2ae7f36ecfd5aea18d3a6f65568875e0f05b678701f09de308cdc203112568204e2bfe4dbaac2b2b6fd636c96230c2748e8d111252ce1dd1f208c26528d7df70c37546bbb173a609e591782252adc8245049da8326c3d4c5bb9fd4956de0c1204a9c88987c1a3df6973fb1aa9efdb3d9d6ec7aa69e066487eb29edf789e510e3d454eee28598dee5a505644cbc372d039c5ee473eab761ae5ad64a40e389bd65433d15e42de02d7f1bfb64f5d392999f48c0eb5a1d16a0e31453fe75db5aa2992f395d24f0364653bf9c19dedf45ddc3fd0f13454973785990d18c1d6ae2000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x7bf31d", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xfd6e6033273714f103cb791d301d0cc728ec726a9ed10ac961ea47833131ab2b", - "transactionPosition": 83 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b1e7f", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x3b7567a", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002b1e7f1958cd811a045b259d582042b3e721f3f06ce0795d00e70534f16e2e39d65a22e99601ea49474aee1c6ff65820b9d0d8ad40b13f20a3f521674d65d23f718195e3009e84f1a2b06f61fe851092590780adcd547b9f1054f3eb179481e1437f981389c937872090f003ddb6e08d753861e6f05e90b9990d98c754dee7d6dddcf1941a48d0ee5bc3570fd52520a5ce3d9da7b40f68761b3cf3a7633dde498283d95055e3e32521d8a720d41321026044280910a2478ff76ce23192d695ab518438f967da0da1912608ad89ea0a50d2702b5032f026c62cce894480256c74c639bab56435da3aefad1ad28659d132ecbce88d3f80b0cc4a3048e94f13803b84404f17dc0a2d57b81ccb1255eadf2a55d5fa892eb861314ca95a1d516f63df0bc93c77529573929e10aa35c8e0ba9c225df897ff0d005be902a2d74c4a693893015f8f14311232d816d1fad5a4e005e685d426dd78c531e9563dd8785ac95e44970fac7f3b7d8d6603329604061b626cae7502fbac2ef0c460af26be45e113b2b7a43d935116511231a1c33ac5d769ef3b0c9c3fc626fbec2f8cfb87572e68c2d617897a9fe18d703b26f4253e931ea3e9c1a4b5077fc7656797b8df389b14bb5e2888a7dcdfdf89dcbc81c34d236d31f021a7a207dc806af6f3c89889efedd24f511562a2d01bea7b55afdea26e486bb729cab9b33a63c3360f8d9f267673eee7aab384b4d6e92d99955b0f1d58dce11f6e41dfe0ad020db66d34b9809c01d2f652a567b0ee46bc2ea4743a3f570d96acbc1525a2691d8bc7e5a1abd34f91189f9aacf36cb833f75be11cf12d47a985667f8b65c5444b1995495ea618484846e5038a8bff82cdb012ffb11bfb14934cc26e4e00331511d49ab2634d2d521a0989aca51ba718f0247dbdb88693f094bbb045a4d9f586340fc388fb9b4e0c65b52a38287cb8614bdad6271c3122e845ce8e74fef8bf6a5764d4384c1273fe5ce3037a951b46409d1518c256e26776e58f7f54f710ee3f190b4706de47e1c4dc0f427fc026a55c4b78deda2aebc381b98de5e200a765da1713999ca2a0d4de1ae76f12442e43e367055cb854ca45aed1115a0e9b20aa1fd188613f7c75beb1a2d26617890e786f2bdbab208b9dad19c1e6ec5dd69b6bc65657fbe561f9efae97eda5d4d855841f450d60fdceeddb45f371b5ed86ddf2cd367ad9a977eb4d9161ee549f5ab243513b70fe3f5708d4f13a47731db9898777079601f103ad01acb74075eda60036ae233fc572e41e546081f852496dad5e90ee06d722cd3835294cda3a31b61a3a167138d00aed08ebbe66a970e80334142cfdc870855c9b6d32e5942d526ed4762a13f9372572cb2555bb3aef5428906719050e1aa6a16ebecd5b35a38f940b3d4b12308a155e5bf035484bc1c2955ca84c23c25801065f7bd907b43e98116262a29c52d313f5355adf15b98f7e9038d780a2ef90049625360d7aa4ad4db577bfd9258d5627932993250fbc0b10b037a899612b74ac70c044044ca1e2c4a4cabe88260646811723ec43609158519e563e44a34ac6ca462c1cc2e6d79b48ed604e5fb890f41f0239ee947dcf6daa11da281778f48e78f9280f54d54c728c2ec8299e710c6c3acda600ec4b55e421c4d77376d8da43f1eb65c473c00f08c3ae15348d1049f75449826c0f9ee69361241491d8261d82daded638e80e042cea44cd702a529329aa603704231b24e933aa6d700662ec989717c7bd430cd3023a53221052ec6c3539c2e15518dc75f6118da892225026bc05c0802b106e6240d28505cd2a4ba725484ca570ed17c7a254e7a338aefd50ae4f45e3bf2d8ffd4695ac84bda612cd4fbc6dd857735594ac3e0457fd9c7e650c66fc77b562c31ef1ce72e50efc6c2ff3fabfd59c601e98a35fbc269d02a7c274f6d814b167217d3053a50fb5181bfe154ccef4a6f83cdffc84d6483072335302dc9210f0370fb49d01fa9675c9952c660f53b0def14bb37043a979df12da6ee45e3ee721b8364c64f9bf80d3b53c67f4888d1b22defc537c0d50cb0fa1c0074ed63333d8e1095b945f902f303a784d070ab17b68765f138598501387130ab052f0056f3881d376b6eef1b256af53d985ae9e8c2613d6859acb171bd1101c29a5b3319eb0b97459948ff358d596fb1a08388368bc9174756855b3b1a629706bf31d16e9dccb3e05b57581da42995a170f8b77103c24ab1e11f287c7dd1c089238da7c24411148130654996d194c5bcab051a092be26332a16b195248002370eb07a43411aad2128e64c0ca0bbda8bb6b25e87628d8bd55c28666bfb999afa7e0dda689c8fb0d7059f418b2af12a70d310c2bb564551159e52f34ccf38f40ec3d9661b57a91c6f8d84a11f86058b4ca466ec86ccd32ac4d69aa0057d65f04dc6f6da6daf63db98605f948b9b9f191eb9a2fb7b4c6c669f446f7fd47300257a50cdfe05030011def8e0d493254dedb9e343a6e0209b91cd3237e2c6d2ae7f36ecfd5aea18d3a6f65568875e0f05b678701f09de308cdc203112568204e2bfe4dbaac2b2b6fd636c96230c2748e8d111252ce1dd1f208c26528d7df70c37546bbb173a609e591782252adc8245049da8326c3d4c5bb9fd4956de0c1204a9c88987c1a3df6973fb1aa9efdb3d9d6ec7aa69e066487eb29edf789e510e3d454eee28598dee5a505644cbc372d039c5ee473eab761ae5ad64a40e389bd65433d15e42de02d7f1bfb64f5d392999f48c0eb5a1d16a0e31453fe75db5aa2992f395d24f0364653bf9c19dedf45ddc3fd0f13454973785990d18c1d6ae20d82a5829000182e20381e80220a73bc120d1ef9c13aff71a3ca6e094ceab44768e7463c06c5411d342fa01ea2fd82a5828000181e203922020fcf4f8a87325eae4550a7b7833830414cfa062bec0a61e0de1981315771db603000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x382d430", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xfd6e6033273714f103cb791d301d0cc728ec726a9ed10ac961ea47833131ab2b", - "transactionPosition": 83 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cc31b", - "to": "0xff000000000000000000000000000000002cc320", - "gas": "0x30439df", - "value": "0x8abb7a55e7bee0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007081818708199e96d82a5829000182e20381e8022012bebfcae441d607d18402467f62c2ab7f7817897031757fc1e3804dc04442311a0037df6b811a045b14c01a004f9a76d82a5828000181e20392202055b250e600ac801d6356d82b0df8a453360fb0ec7084b1910e2759234e15902600000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x20d62cd", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf56db0f8b6c0adf7d4b97adb155081e3d4159fb405b85419eb6d94d7d2452c78", - "transactionPosition": 84 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cc320", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x2f17cd0", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf56db0f8b6c0adf7d4b97adb155081e3d4159fb405b85419eb6d94d7d2452c78", - "transactionPosition": 84 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cc320", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x2e0b788", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf56db0f8b6c0adf7d4b97adb155081e3d4159fb405b85419eb6d94d7d2452c78", - "transactionPosition": 84 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cc320", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x2cea217", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a004f9a76811a045b14c00000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x476739", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e20392202055b250e600ac801d6356d82b0df8a453360fb0ec7084b1910e2759234e159026000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf56db0f8b6c0adf7d4b97adb155081e3d4159fb405b85419eb6d94d7d2452c78", - "transactionPosition": 84 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000241c2e", - "to": "0xff00000000000000000000000000000000241c3f", - "gas": "0x48d6a79", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000072818187081a00010a76d82a5829000182e20381e80220c68a4c0a1f9bbacc1b43b94dd70a03f0360cf525da916df0d7021b78895cf1501a0037e0a1811a045b34251a004f93c3d82a5828000181e203922020ee6562b75f62cccc594244cb2f9eb26aca725a060f7eba8ee3c4b32e418c2b230000000000000000000000000000" - }, - "result": { - "gasUsed": "0x347e2a4", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xd8ee13b065e0b4ca8217629d5ad2cce9dd7000642534213e4531e1b3f8a1da29", - "transactionPosition": 85 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000241c3f", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x4820429", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xd8ee13b065e0b4ca8217629d5ad2cce9dd7000642534213e4531e1b3f8a1da29", - "transactionPosition": 85 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000241c3f", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x4713ee1", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xd8ee13b065e0b4ca8217629d5ad2cce9dd7000642534213e4531e1b3f8a1da29", - "transactionPosition": 85 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000241c3f", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x45f2970", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a004f93c3811a045b34250000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x477613", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e203922020ee6562b75f62cccc594244cb2f9eb26aca725a060f7eba8ee3c4b32e418c2b23000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xd8ee13b065e0b4ca8217629d5ad2cce9dd7000642534213e4531e1b3f8a1da29", - "transactionPosition": 85 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce389", - "to": "0xff000000000000000000000000000000002ce3be", - "gas": "0x31bd06d", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007081818708193f04d82a5829000182e20381e80220ee335a22ee6fe624ad6988dc7323bee4e562b9be560510700bf6d6a46914ca021a0037e0f1811a045361b11a00480bc5d82a5828000181e20392202088a1d5a6621a6fe38348e63ea64d091d8ab3060ee1769ad08c5e608db0ce4b3200000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x21e08e5", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x29a86bf568ea52bc778da4c2f1882f2b2459e8d5e638294f5b3c7341f39452a6", - "transactionPosition": 86 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3be", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x3106a46", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x29a86bf568ea52bc778da4c2f1882f2b2459e8d5e638294f5b3c7341f39452a6", - "transactionPosition": 86 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3be", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x2ffa4fe", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x29a86bf568ea52bc778da4c2f1882f2b2459e8d5e638294f5b3c7341f39452a6", - "transactionPosition": 86 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3be", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x2ed8f8d", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a00480bc5811a045361b10000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x4887a6", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e20392202088a1d5a6621a6fe38348e63ea64d091d8ab3060ee1769ad08c5e608db0ce4b32000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x29a86bf568ea52bc778da4c2f1882f2b2459e8d5e638294f5b3c7341f39452a6", - "transactionPosition": 86 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff0000000000000000000000000000000000040f", - "to": "0xff00000000000000000000000000000000000961", - "gas": "0x2223d42", - "value": "0x8abb87dff2adb5", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000708181870819b878d82a5829000182e20381e802204b991204e7215867548c947fe66e43215213e86fc030522fb3f35f9cd9a6e2311a0037df12811a045b0ded1a0047eca7d82a5828000181e203922020864c24ea0ee60743452cd1444db396bb8e7d84b77867be2b737c1cc7b3fc7e0c00000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x1588de7", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x21e6ee3748a0d4cbe0125a25f22d5f2611d4c3df2f3aa4296736a8470b3ba6da", - "transactionPosition": 87 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000961", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x20f8033", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x21e6ee3748a0d4cbe0125a25f22d5f2611d4c3df2f3aa4296736a8470b3ba6da", - "transactionPosition": 87 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000961", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x1febaeb", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x21e6ee3748a0d4cbe0125a25f22d5f2611d4c3df2f3aa4296736a8470b3ba6da", - "transactionPosition": 87 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000961", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x1eca57a", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a0047eca7811a045b0ded0000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x477494", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e203922020864c24ea0ee60743452cd1444db396bb8e7d84b77867be2b737c1cc7b3fc7e0c000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x21e6ee3748a0d4cbe0125a25f22d5f2611d4c3df2f3aa4296736a8470b3ba6da", - "transactionPosition": 87 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002c43aa", - "to": "0xff000000000000000000000000000000002c43b6", - "gas": "0x413ba3c", - "value": "0x1a86cf1d84124d89", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000787821925b959078097b3ea67b0001eb0ba6353d1eab30d9a7d5f9d64b1fc95a1ba6bbe5f11044aba6e3b40ec1fbaa7166ffc8c64971a27daab26e31ce688f7e1f24885ee59ecc8677d59583424689338d48829a9e4d58d2e906dd592bd26d202a5759808a1acc7a6063ece776d981d3d8444a369728d65d875a3666e804785f65460925fcae2cad1defebb202b02eda7b981f6430479db06ac90441ffaefd375988c308c894388c83db61e80aa53106012c61a3ba70d4eab30435d7186a9f6c536c556a1c994e11cb4fd170312132b5c59b29c3f6b4b1188dc51c92352419f4504d3122eabd074144ecfcaff2dfa6c27781368afb9f0a49f872f659845c00949f638c96e473010286922edbe98e8db6132594fe9f487f7fb66bdd7f9a27b7813c1f29be7cd833d9711d2d04bac97f5ef5598f73110020987ae2da8f2db9befd50a033cd74c2d878d12f9b69b5d7e3192a76362ddad3c8f00b2da963f9c44818ebde797a3343b196751522f4de82fded7fc43d2ceb05b143c103821d56694a5f7f01d3678affbd8c58b110c139d7c49442a2e591d8edd45e9b9d62975ff4fa4c2f75fd4073dc6647ead728e690971b348c1dae698a402a0d697ec1aad2b423077d9cc55b913d016b217d90d21f310e4342b5fe901f492aa7d093a2e4567bdf41a4d9e86cd7242467c1003908559f717a2ab1e4e2baf7311364544229550a8ba213596202efd70cac595c51408723a68bb0aa5984e32dcf3bba7328dbc7ad8dc0b8650cc9995bb99eef736fb4a5f8929e66b787ec028fec84d7487e3125220ec2db0f96b85314be66b88cdf061dc2b5a1442ff6341a038fb1118f81fb2718031634313e65516b8fe58643d215778c7c6afae8d5d5c569d3726b757cc558705e99d76eab551b2fe464c4a79e432ad4eefd478646f6f61fe1613ecf973d42466c1b3dec9d6e8cbc1681808ae1bb85bdacc9f8cb060ed9fba00fba8694b1dbd950ab5e4a597058faa19e77990be62cbba63f476a752bfe48aaac4a5ff8bc85b1b0c1f0fe9b1caa247196c3394c8f0f8f13708b77213155d5614f5bf7af0b9c9f574717b5a5d72f4f7367aa0593563e9be8c5f2d4c3f2096d1f3c9d82bfec6b8ce2edf5aeaab4fba6d9b72e95c9638ae856af76fe598b088f6ddabaf8c1aa1af5137cbf681819f4e4db91ae336431e7c1c5363673ea9b332393003e031ae9bf4b20dd5a4a7859365ccedb10c2e2e226e1684f249afca9268c23352fb36e9ebd18cf254b9e6c70268275c5b83ff8b30c7abea185f4969bb3673330d959675f76b6bd839620c8dec153d4a7d79d012d994211acefeebf8b15f0f07fd7a4420e5328f7ec096d3292574d492698fb159987beb422ec5c620a211bdff153e57f567ac37843ca7dc3844961836f78ef5f590d9d1d8c29c9797060b2386dcb0b3d02b7ffe3b2e25cb45f4ba0de07b71a9864e7e0a2dd895bf642f49954ca2a7f95007a2ffb7dfa570c6e124fe007e06d4b66aa8ce81f550ec3c1cc4ce9186425b348d191232c76cb4018703b42495f28e113c80411566c85d61523f71aa08976c181005d30b306168d2c5c88ebdb951603f190dfd3fe48ede2858e02a77b90fcdbdcc34fa5098c9f3cf6142be856da830f43d8ab50b990c29e4a957241946974fb63f8da961b82b11ad4034e90528548659e5aaf3156dbbfbfa8d83c3e07981758442aa3be462c5983908a882ed8fb298e3c6bce51db1fa61c0faed67f7014d9f7fab463b088ec32aad30c680f463009f785e2f74003a90bcc05c4dedc376a11daca09891c2cee6242d8ace7743525c6b8988b41612fdbeea48098605b597b4cf3b736205688e3c1cfb2c5d8e9a70f805014159b9bc02a6baf2cf5daeec39bd1057343572c776f66be721e139ac74a30f6630e165d498640b4a3074689e7ee7bb04a8d0611b9323b4141195d94b26d4147ae734f3002c644c671f8c971e539323ce50fc44b6e045faafd3dac798d67cbb4268707aa9ea783eedf2300989fce3e2a519775bdfb01a25ba57ec7d608700fed67cc95b4cad48e9301edbb790e79de8acdac2a6cf2d3201251e6931b2ac655dcab2a41a761082a3b3d565b27455a4a278f70b58ae17cba43cbb7865fa9915a5129d9e20f7ffbb8a46e7861c85da4d780a5a0af6b7396c7a64ba30023ad9871671c13a682b600f9c64894524ce34dc8ce1509e8cc5ad34e899833aa8f67933d2f6521e9c425f77e8a978bc052aba8c8d23c30dfb1dc40ada63d5743705b8a6dc053e918d8b5e7f6840b2d9810900ed076c6cdcf7e143d8a6a759a9dde1f10b714fa57d2e87350a9d3c57f973a9db4576f68703b901938504a87267da534c2b6f6bb517c229002df0a23cc7dbebf1b81e8412c13786ce60cff177b940a76e5b499de0d8cf8457ae43ae9908f3ae0cc22b9778ec04404efbf93afce4c8f015894d3e4ae82d11f61d41ad02e0be7cf7c8639b8d280dc152fd75e9d0efcbdb9b90502d757f7a2d386f8bd001a95753bf81308b1dc88da58b1f221dd4e8e5217345b7cb0b6fe940874920af33481091a1e3ed6024afab00eb8cd3e15e142d9b5e1446a4554923ce97cb385867786e95304fd8c62dc0de08ba942d7d7493120b4b4a54b379594dc0558381305d6fba675582f6d7761513470ffeb5de56e9448922b452ec437704f08699c4831684239f07162b6debfe74f2dd61ac707cb98aec2a00000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x62aed2", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf9b127b6341a6e269f846e39956e38fe62f80c4ba8af19158913490d0d87aca3", - "transactionPosition": 88 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002c43b6", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x3e21973", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002c43b61925b9811a045b1f095820834b0ec1478d47321f74ea8410e823e1b5849bad4ad3f9e99398927317394e795820a6370784af85f07361123ee661399b72bef32bebedf8edc99da47959d5e2c81b59078097b3ea67b0001eb0ba6353d1eab30d9a7d5f9d64b1fc95a1ba6bbe5f11044aba6e3b40ec1fbaa7166ffc8c64971a27daab26e31ce688f7e1f24885ee59ecc8677d59583424689338d48829a9e4d58d2e906dd592bd26d202a5759808a1acc7a6063ece776d981d3d8444a369728d65d875a3666e804785f65460925fcae2cad1defebb202b02eda7b981f6430479db06ac90441ffaefd375988c308c894388c83db61e80aa53106012c61a3ba70d4eab30435d7186a9f6c536c556a1c994e11cb4fd170312132b5c59b29c3f6b4b1188dc51c92352419f4504d3122eabd074144ecfcaff2dfa6c27781368afb9f0a49f872f659845c00949f638c96e473010286922edbe98e8db6132594fe9f487f7fb66bdd7f9a27b7813c1f29be7cd833d9711d2d04bac97f5ef5598f73110020987ae2da8f2db9befd50a033cd74c2d878d12f9b69b5d7e3192a76362ddad3c8f00b2da963f9c44818ebde797a3343b196751522f4de82fded7fc43d2ceb05b143c103821d56694a5f7f01d3678affbd8c58b110c139d7c49442a2e591d8edd45e9b9d62975ff4fa4c2f75fd4073dc6647ead728e690971b348c1dae698a402a0d697ec1aad2b423077d9cc55b913d016b217d90d21f310e4342b5fe901f492aa7d093a2e4567bdf41a4d9e86cd7242467c1003908559f717a2ab1e4e2baf7311364544229550a8ba213596202efd70cac595c51408723a68bb0aa5984e32dcf3bba7328dbc7ad8dc0b8650cc9995bb99eef736fb4a5f8929e66b787ec028fec84d7487e3125220ec2db0f96b85314be66b88cdf061dc2b5a1442ff6341a038fb1118f81fb2718031634313e65516b8fe58643d215778c7c6afae8d5d5c569d3726b757cc558705e99d76eab551b2fe464c4a79e432ad4eefd478646f6f61fe1613ecf973d42466c1b3dec9d6e8cbc1681808ae1bb85bdacc9f8cb060ed9fba00fba8694b1dbd950ab5e4a597058faa19e77990be62cbba63f476a752bfe48aaac4a5ff8bc85b1b0c1f0fe9b1caa247196c3394c8f0f8f13708b77213155d5614f5bf7af0b9c9f574717b5a5d72f4f7367aa0593563e9be8c5f2d4c3f2096d1f3c9d82bfec6b8ce2edf5aeaab4fba6d9b72e95c9638ae856af76fe598b088f6ddabaf8c1aa1af5137cbf681819f4e4db91ae336431e7c1c5363673ea9b332393003e031ae9bf4b20dd5a4a7859365ccedb10c2e2e226e1684f249afca9268c23352fb36e9ebd18cf254b9e6c70268275c5b83ff8b30c7abea185f4969bb3673330d959675f76b6bd839620c8dec153d4a7d79d012d994211acefeebf8b15f0f07fd7a4420e5328f7ec096d3292574d492698fb159987beb422ec5c620a211bdff153e57f567ac37843ca7dc3844961836f78ef5f590d9d1d8c29c9797060b2386dcb0b3d02b7ffe3b2e25cb45f4ba0de07b71a9864e7e0a2dd895bf642f49954ca2a7f95007a2ffb7dfa570c6e124fe007e06d4b66aa8ce81f550ec3c1cc4ce9186425b348d191232c76cb4018703b42495f28e113c80411566c85d61523f71aa08976c181005d30b306168d2c5c88ebdb951603f190dfd3fe48ede2858e02a77b90fcdbdcc34fa5098c9f3cf6142be856da830f43d8ab50b990c29e4a957241946974fb63f8da961b82b11ad4034e90528548659e5aaf3156dbbfbfa8d83c3e07981758442aa3be462c5983908a882ed8fb298e3c6bce51db1fa61c0faed67f7014d9f7fab463b088ec32aad30c680f463009f785e2f74003a90bcc05c4dedc376a11daca09891c2cee6242d8ace7743525c6b8988b41612fdbeea48098605b597b4cf3b736205688e3c1cfb2c5d8e9a70f805014159b9bc02a6baf2cf5daeec39bd1057343572c776f66be721e139ac74a30f6630e165d498640b4a3074689e7ee7bb04a8d0611b9323b4141195d94b26d4147ae734f3002c644c671f8c971e539323ce50fc44b6e045faafd3dac798d67cbb4268707aa9ea783eedf2300989fce3e2a519775bdfb01a25ba57ec7d608700fed67cc95b4cad48e9301edbb790e79de8acdac2a6cf2d3201251e6931b2ac655dcab2a41a761082a3b3d565b27455a4a278f70b58ae17cba43cbb7865fa9915a5129d9e20f7ffbb8a46e7861c85da4d780a5a0af6b7396c7a64ba30023ad9871671c13a682b600f9c64894524ce34dc8ce1509e8cc5ad34e899833aa8f67933d2f6521e9c425f77e8a978bc052aba8c8d23c30dfb1dc40ada63d5743705b8a6dc053e918d8b5e7f6840b2d9810900ed076c6cdcf7e143d8a6a759a9dde1f10b714fa57d2e87350a9d3c57f973a9db4576f68703b901938504a87267da534c2b6f6bb517c229002df0a23cc7dbebf1b81e8412c13786ce60cff177b940a76e5b499de0d8cf8457ae43ae9908f3ae0cc22b9778ec04404efbf93afce4c8f015894d3e4ae82d11f61d41ad02e0be7cf7c8639b8d280dc152fd75e9d0efcbdb9b90502d757f7a2d386f8bd001a95753bf81308b1dc88da58b1f221dd4e8e5217345b7cb0b6fe940874920af33481091a1e3ed6024afab00eb8cd3e15e142d9b5e1446a4554923ce97cb385867786e95304fd8c62dc0de08ba942d7d7493120b4b4a54b379594dc0558381305d6fba675582f6d7761513470ffeb5de56e9448922b452ec437704f08699c4831684239f07162b6debfe74f2dd61ac707cb98aec2ad82a5829000182e20381e802207b7490375005b4ab7a45bfe5fbad53683f38a72527393440345e75a6a7ee7d63d82a5828000181e203922020ef12b88fae9a11c8ef6e7e43fdc9bb2059e9a0fc0e291d68a3c49c9c86b3421f000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x30ed5a3", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf9b127b6341a6e269f846e39956e38fe62f80c4ba8af19158913490d0d87aca3", - "transactionPosition": 88 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b4022", - "to": "0xff000000000000000000000000000000002b404f", - "gas": "0x43a0269", - "value": "0x1a3ede932ea26d50", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000007878219e3e0590780ace586801c6af561f2983ca1ad7268d41a4eb125eb08064de6fd3f4d9b98b7ead52c60ac58919e54c354f2dbfc689aa3b9cf5dfad2c937f4ac2eb1bb4c2c370a795f86b6ebe359c6f8f830294dcde42d14ee689d057aa9db003483040ac387d810cf29f32e6a5d52cc11e158b8cf21e0094a4697627d342027f95edcb02865213b28f44787c89c2ac5571e0728de3eadb465cfc2072da9f60e124cfb93b7d246b609bb3ec8ef3388efd231bc257757745e9c2aa3faf541294f3043aedf93bb889127de8e1458fb10461df73403003017c924cf4300df4468afa4fddd67d0d0733b8c8c31db8897163041e77509498f218757d2773e3dae955a7aeeefd8a83443182de39ecbfa995fc03fe7fdc55c719c8057f9a4c8576a584d9fe4f6b1541d060d8350ef3203cc2367660197004902c7f3d2811adea9a7a498e73ce55e48ab54b41c29312c21576e904c45c2d9360331b2faddbed720ea9a1b9791669cc9d7f00bd1d5ba70deef07dfbdd438078a7bd91e137b4ef11f95f73ff0066abd34ccc6995aef7549db39eaaf2903c5b3f12e6f2a22c33e71515ecd3797f9f675660677ed657863235563b44c6b3ee9c27b584b90fc1752ee2418c4c0982fb820956825f874f4f23e958243064022f46523520570cc99803877aa7a987fd19bbb6d4e150a68864b7d49edd4c8ddefde9093e9b3f7b7e6a3ed19e7babddb4128f56de6cf96bd1e8904e2c915f3ac3beabcb7ec4995772c6eaccea909cffe8f7833dd6673427426ebd071743a95311d02867b2e1b82454dcd143978e42f968ccd112606dca3c83fa8365e5bcb170d4b7483fb0006ef832120a065c4fec044678551b78afe8a9a0c75af5d5c2b1c3aeaa02c47c63ca538d1e45ffacf6ea891295b264f2802e0b8031fc297d94f177edb50b207c20ef742fad5b9788cee3dabb6a6840f2bd91714e5cab90b173ff8c576544abdb8108f995c0dad05db88e13fc829b80341e2921d74b61fb063bf7e15df5e06f00d03a9f0c98979243ef53c5a1e78b1c592d901310b8efbe96b72ddb6c2e73e353cb3b769bcf83cc603bfd4a76643927b256c977ac06e3f679e38b455305df070ac3bb320390d3bc1fe6a6a0f5ad0a2c9a1fb8a1229d462b6289d5bfbbe613acf29d588b1888a9a3fa7ba9075d67dec51a97dc0e6a94ec455f15fe9173219a21a60ccae610697f44776dc63c07c8b0bc76fe20e17db74014ab5b3aed158c7f6db1cc0d339d6d72b5c2143574af4931306f800d754790d2703d5a7b15344de0f78a2e4aff46351654e4086b8807a6c93a74d5873a3c3d82a4572a0a9c8ba6f39f13effc3ae5451422e27b9ce09a0a5781f22d5b173352c76c969fa192c7ad6dcb8f8159e710fe8eacad3966df41f61375c5e74f3b5d8d8915faa5725a5301a4aca928cb9235aa184a7a853df2a384664e9dac20eb12d0b08c014c21e9b8611d516b5ec78093d8b9d10b8b27438958e2b89f4701726a3d9e3ec3d9a74418c0bb63dbde0e163fa2dc77560a4deaf90b691bb4fe9b11bfa00206245df864d52c75a73926395b7fbcf65f5ec4b87c8911d4d6ceb45b2a8313ed8e9136ab6ae614032e42cc3b097925cf6d8a6aef13a01a0ee363627ac36c8e0b82be6f304baf2598631bb0f24bd12a27147aa8b4476b9c29ba54f27339b9c3061d7729b78f26de21a701b8190c4d8adefaf926b8aa9212a849f34c50a8706764adb4ee8e3888f69e28b47ba3a320128254d7f7d994e0b9084e01fa3162ce7eed9109edb81dd36c8e96c226adb2c6660f2142c240213f721d134cb27afdcd1675fd19d517eaff5a3fc7693238f50fa55a2666bfdda36f1f8109e85471505f3c73b64bad8bdb801ee8ca53130d253c31573129359854ab14ae3eabe228ebea10bc1664ca4cf47b1888432b5766b9cb5467119ea7ad7ac2b20f0692b5c846dd9658babd9eb787b52d38d7feda3b3500f7850d8f1b6a94de3e79e5f6068ab487fc487790041a030747e086a375b473e33e477efe6d6b9a823f8ed7d541b038b41f6b43e9d6fd91a3ece1537843ea8a1a7266411f4b39c5573575b16a4293194f10a007f0335d984b4b06ff1b68181fcfa1b8f65d97358b248dd2c040de6fb99c3d1501620e7be07c32c30be700820f91312d6519824af63b2b171319b17813ab66487cccf75177cdb023122cf5c21040843a2c917cae89f34d870b4dfa2b867c49c96a06f073f20468226aa3e7b89454f79fca095ec962a28c5c40efd492d68ecdfc4da742368fd6956288b4d5144e7a826a805bbffdf1c46bb9b37069917e2fa997e375d5ccd9a6f646ba803eebf0bf1f1ffcf9d366e1862c5dc816d6db5aadbee419841a8cdde740f73afe869a91a6899c96f2993a1c882b55b37a1032639d1968a9e4046b7af3007d0293af5e4b74389847d6a7faa602f6f97610a14b2316c9045bfb9698c95a6da1bb682f44a269eea77e633cabb12b1d5993a97032b85a929584407182a0349ec834fbc6c87e5198e59ac0eb6e9bc83ad4ceca1bc7bca8cb34349e342e22ba2aea22125d46ebb473c47f6269b73d597fa9f82bbc70ea5198b7f48e016392d189125a15888054763581c87424a486b22e775c70ae9913d57df4428af047814233ef6998f4ea5179318af9872bde490053bd8f778257d599d0f481444f1c922010273acf2026fddc17dd6e5b24184515f6f7f10b5b300000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x80a80b", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc30e8b748b22427010e409aece99f62953c57de4f68383af4fd363beb0801e8d", - "transactionPosition": 89 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002b404f", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x3ea6944", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002b404f19e3e0811a045b29de5820d92f7e1060a1f96fa21101609789a7b51e4dad941c5ed5345bccc93f19e12cc45820e886746cf4ddfc8999519b72e3a9d45b7d53ebdc4865e810d9d7b78a186c479e590780ace586801c6af561f2983ca1ad7268d41a4eb125eb08064de6fd3f4d9b98b7ead52c60ac58919e54c354f2dbfc689aa3b9cf5dfad2c937f4ac2eb1bb4c2c370a795f86b6ebe359c6f8f830294dcde42d14ee689d057aa9db003483040ac387d810cf29f32e6a5d52cc11e158b8cf21e0094a4697627d342027f95edcb02865213b28f44787c89c2ac5571e0728de3eadb465cfc2072da9f60e124cfb93b7d246b609bb3ec8ef3388efd231bc257757745e9c2aa3faf541294f3043aedf93bb889127de8e1458fb10461df73403003017c924cf4300df4468afa4fddd67d0d0733b8c8c31db8897163041e77509498f218757d2773e3dae955a7aeeefd8a83443182de39ecbfa995fc03fe7fdc55c719c8057f9a4c8576a584d9fe4f6b1541d060d8350ef3203cc2367660197004902c7f3d2811adea9a7a498e73ce55e48ab54b41c29312c21576e904c45c2d9360331b2faddbed720ea9a1b9791669cc9d7f00bd1d5ba70deef07dfbdd438078a7bd91e137b4ef11f95f73ff0066abd34ccc6995aef7549db39eaaf2903c5b3f12e6f2a22c33e71515ecd3797f9f675660677ed657863235563b44c6b3ee9c27b584b90fc1752ee2418c4c0982fb820956825f874f4f23e958243064022f46523520570cc99803877aa7a987fd19bbb6d4e150a68864b7d49edd4c8ddefde9093e9b3f7b7e6a3ed19e7babddb4128f56de6cf96bd1e8904e2c915f3ac3beabcb7ec4995772c6eaccea909cffe8f7833dd6673427426ebd071743a95311d02867b2e1b82454dcd143978e42f968ccd112606dca3c83fa8365e5bcb170d4b7483fb0006ef832120a065c4fec044678551b78afe8a9a0c75af5d5c2b1c3aeaa02c47c63ca538d1e45ffacf6ea891295b264f2802e0b8031fc297d94f177edb50b207c20ef742fad5b9788cee3dabb6a6840f2bd91714e5cab90b173ff8c576544abdb8108f995c0dad05db88e13fc829b80341e2921d74b61fb063bf7e15df5e06f00d03a9f0c98979243ef53c5a1e78b1c592d901310b8efbe96b72ddb6c2e73e353cb3b769bcf83cc603bfd4a76643927b256c977ac06e3f679e38b455305df070ac3bb320390d3bc1fe6a6a0f5ad0a2c9a1fb8a1229d462b6289d5bfbbe613acf29d588b1888a9a3fa7ba9075d67dec51a97dc0e6a94ec455f15fe9173219a21a60ccae610697f44776dc63c07c8b0bc76fe20e17db74014ab5b3aed158c7f6db1cc0d339d6d72b5c2143574af4931306f800d754790d2703d5a7b15344de0f78a2e4aff46351654e4086b8807a6c93a74d5873a3c3d82a4572a0a9c8ba6f39f13effc3ae5451422e27b9ce09a0a5781f22d5b173352c76c969fa192c7ad6dcb8f8159e710fe8eacad3966df41f61375c5e74f3b5d8d8915faa5725a5301a4aca928cb9235aa184a7a853df2a384664e9dac20eb12d0b08c014c21e9b8611d516b5ec78093d8b9d10b8b27438958e2b89f4701726a3d9e3ec3d9a74418c0bb63dbde0e163fa2dc77560a4deaf90b691bb4fe9b11bfa00206245df864d52c75a73926395b7fbcf65f5ec4b87c8911d4d6ceb45b2a8313ed8e9136ab6ae614032e42cc3b097925cf6d8a6aef13a01a0ee363627ac36c8e0b82be6f304baf2598631bb0f24bd12a27147aa8b4476b9c29ba54f27339b9c3061d7729b78f26de21a701b8190c4d8adefaf926b8aa9212a849f34c50a8706764adb4ee8e3888f69e28b47ba3a320128254d7f7d994e0b9084e01fa3162ce7eed9109edb81dd36c8e96c226adb2c6660f2142c240213f721d134cb27afdcd1675fd19d517eaff5a3fc7693238f50fa55a2666bfdda36f1f8109e85471505f3c73b64bad8bdb801ee8ca53130d253c31573129359854ab14ae3eabe228ebea10bc1664ca4cf47b1888432b5766b9cb5467119ea7ad7ac2b20f0692b5c846dd9658babd9eb787b52d38d7feda3b3500f7850d8f1b6a94de3e79e5f6068ab487fc487790041a030747e086a375b473e33e477efe6d6b9a823f8ed7d541b038b41f6b43e9d6fd91a3ece1537843ea8a1a7266411f4b39c5573575b16a4293194f10a007f0335d984b4b06ff1b68181fcfa1b8f65d97358b248dd2c040de6fb99c3d1501620e7be07c32c30be700820f91312d6519824af63b2b171319b17813ab66487cccf75177cdb023122cf5c21040843a2c917cae89f34d870b4dfa2b867c49c96a06f073f20468226aa3e7b89454f79fca095ec962a28c5c40efd492d68ecdfc4da742368fd6956288b4d5144e7a826a805bbffdf1c46bb9b37069917e2fa997e375d5ccd9a6f646ba803eebf0bf1f1ffcf9d366e1862c5dc816d6db5aadbee419841a8cdde740f73afe869a91a6899c96f2993a1c882b55b37a1032639d1968a9e4046b7af3007d0293af5e4b74389847d6a7faa602f6f97610a14b2316c9045bfb9698c95a6da1bb682f44a269eea77e633cabb12b1d5993a97032b85a929584407182a0349ec834fbc6c87e5198e59ac0eb6e9bc83ad4ceca1bc7bca8cb34349e342e22ba2aea22125d46ebb473c47f6269b73d597fa9f82bbc70ea5198b7f48e016392d189125a15888054763581c87424a486b22e775c70ae9913d57df4428af047814233ef6998f4ea5179318af9872bde490053bd8f778257d599d0f481444f1c922010273acf2026fddc17dd6e5b24184515f6f7f10b5b3d82a5829000182e20381e80220602e2506c0e5e031448c0607714e86e9c234bfdc00bb8639580eec0e8ea41373d82a5828000181e203922020d5796125fc67380346b8b2734a81ff08e1e9f457606ef2a4528ea411888fef23000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x312ead6", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc30e8b748b22427010e409aece99f62953c57de4f68383af4fd363beb0801e8d", - "transactionPosition": 89 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cc336", - "to": "0xff000000000000000000000000000000002cc33b", - "gas": "0x3528aa9", - "value": "0x8abb7a55e7bee0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007081818708198b12d82a5829000182e20381e80220a4c966fc3621b677c749e58fce4a19d87f495a2bb82e5f5a294900321f38ba3c1a0037e0c2811a045b34061a004f9b81d82a5828000181e2039220200a7617f7ebeee2028f3677e7d760301d0c595d3d786d2daef70aae4cc7fb162100000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x24c0262", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xdd94aee23e9cfa008afff1f60c7eb2a27e2896878f2be8ab834cd71caf5d3ff8", - "transactionPosition": 90 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cc33b", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x33fcd9a", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xdd94aee23e9cfa008afff1f60c7eb2a27e2896878f2be8ab834cd71caf5d3ff8", - "transactionPosition": 90 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cc33b", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x32f0852", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xdd94aee23e9cfa008afff1f60c7eb2a27e2896878f2be8ab834cd71caf5d3ff8", - "transactionPosition": 90 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002cc33b", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x31cf2e1", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a004f9b81811a045b34060000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x476eaa", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e2039220200a7617f7ebeee2028f3677e7d760301d0c595d3d786d2daef70aae4cc7fb1621000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xdd94aee23e9cfa008afff1f60c7eb2a27e2896878f2be8ab834cd71caf5d3ff8", - "transactionPosition": 90 - }, - { - "type": "call", - "subtraces": 2, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000020bb1", - "to": "0xff000000000000000000000000000000000020d3", - "gas": "0x1d9e63c", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000408181870819321dd82a5829000182e20381e80220613a2f762e0bbf0a1fdfeb573b16c9ef105d5ce19c5ec21194cfccdfe2c9c6231a0037e043801a004f8a90f6" - }, - "result": { - "gasUsed": "0x1653202", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc4a33ab23d55c88a9eb2745a97e37dd6b44d96b9f14a00a9a1c06df904b759c3", - "transactionPosition": 91 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000000020d3", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x1cea250", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc4a33ab23d55c88a9eb2745a97e37dd6b44d96b9f14a00a9a1c06df904b759c3", - "transactionPosition": 91 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000000020d3", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x1bddd08", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc4a33ab23d55c88a9eb2745a97e37dd6b44d96b9f14a00a9a1c06df904b759c3", - "transactionPosition": 91 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001bbcc3", - "to": "0xff000000000000000000000000000000001bba4e", - "gas": "0x608c127", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000789821a002138aa5907808c23dc69fd600fac65df2b08b4ee5595049dce3df8a1ca6c1a0b47067300709c7bba1e1b7edf0f1ae8a11dced9fd646c87042bbbfbf8e2e77651c10597cb746fa0ea2caaddb219a8b79a0fdd9bc84242471f8b097d6e6ee1dc547c7f068905f103fff2cb2220608de1f525e034d46bed5968ad6799e44af0ba7805e86c2124dc262c3c6b8d73c10ab458a651ffde6b77a4621bd6af909eb88bb583559717d37c0363d75329d4f444c47a71e44670c1d7c9f6e08fcdbcc8fe70542e445893dd5ea51f205a5d4695eb2ff2a484df3be74a6462cccced5c7c552ec2e3568d02d4eab26e05da573b652f1c5a6bd51092350f83f803a8e496c069e866a9bf26c956bbeb82488d4410d2cab92a151abcc41526e74a57b4131ddcfa7c2d3e0706b6e8310481e5d7e2bf283ab1ae5650716b4ead437aed5bc50aa57138e976e885c28a1fc6e6e9a09619974b09bca55f34348d718ce52829d085704dab7715d49c2541c4a9301c7b45d85526e4829b4c07505c07508879e27a1f61bd261bf5d64d599f7ba195237ddae768b0fd06c377c24beffb203055e3ffed44a4a8584f5433ab586182a403f41d2ed434452ba32a08f9c24d8fe621b8bfc6c7e3d914c706e589e68563c1ee9c18f561d569dfc486f6765e30bdf165806e41b0648e82f1c9ebcdf574145c3c3bc6b92ff866337d46005ef56b786233c9d87f8793081bb7f903cb1e783152b482e86577a5df8cdb63744a47ab8f6d27b6380b9be7a4c53aba1f8b985e06e9623280468ea84aae5c6b4835a20fc43a40f4a0bbbbf2160586f5072fdbf999a08763587c500af0629bcd25bdf32c542e7e7af6835a6949527b804379ea137a17c623aa5917c802756919ba64a76ea8dcd49cd3478dd5d2f6d8d2f00204c2028c1b49136d750360613e277542de1930ee1559c581f1a6b3842abf100034380ecadd6dd1bc13adef306133549d463f45dc29968ad5c15b0adf9c13e051ebae7cf2a5bcb1a858df6daa3e19c2f01120af1b38e1ce0412135c8a1426fc0f009888ecdc65e0d91ad80e1fd42856598cd06e4dfbc6a9c7600fa56a48f755599fac92e816b9dce6982768819a6e24ecd3e0d8ab12fb35283b974796b9e304cdd961b15770331f0186c6db6966ddbbf7bef2a74ea8881ddc1b5cf22c6f764b0e68782b6df21f4dcd69ead4744298eb49c1ad87f086c1236e1e3ca477a7048c480c781254bc5a68886f5d94ff26d00439ac1805266f20547f11753b0d50e724e8930ce5be8af4d3b325d40eaca084d7050143831ec1bc40b5ad1b06aad472a0ff5cefdaa94aa33e5ad0ed5299436799a9a6b42a1222f56995de79fd7979b712eb8212810f8651832c7f4341ef4be3e2801de16898be3fdcd20f45aecc718c7066e160e0867afb2ac813a572645b20857d24388edfb15259db541187c2c5cb2c438b2068c64b2bf275004dabd8259134108df796d4a9603f9e26626521c718a28fc9a10a30613abb792b2a9e9d464295867fdd94968134d3895e7efc6ce4c884d213a81692625c28db3e18d5e03e3e6dd39fe9894eb02c50689c09890cdd1a27ce56623acd2381e7b38250bfa880d59049cdf1adecfe224446513fca3df71db5b5500ca038fb9569adf1f526926b9bfa66e27df4470baa2f1429bb1c2676cf8877f173c8d8c2e0c152e17cb6794cb80083cce0a2e7a57c31a5ba41913ed9806f2d50e3a889fdc888e24841b62b5f1c9cdc74f1152e882fb789846533a5e4caf0d5f1ea050856b5f7f82f8728a859107978cd95d62652a1e7191b1a296096dd8cf250de8b22e948d4102f2955b61a3effa2b667a49cb58c688bf2510a018e360db50394e57c41d39b866f0144e9bf9856108c83572149b6621a5f9491d732ede5b7bcd7b34f810066df96dbcc341b32a0c7756f05345b66ffddffc5eaa5de22928efcc6c80a949374db5a546008c47ef4fbf296b316dd03a57a40a89c730b9f3602df445547ea5ba4262e51919e2cb4c71ed4f5c63740d347c1471d05a9a266fe318ec306fdb9ddc6211caef273867e3e409dc317e4e04d49e40b474e29456f2f3e6252d2a18ed90e5188d13c74cde5e904d8cdb95c55c636c83c085c58ebaccea0ce2b1ec5b0696932007b538bd977ca26f630e9df8a16c2b031692a160ffa45f5d6e9a625412163c2771a54c4432d61f49ce9de85b475dc33eae4b2886cf4343ce2ed591a9f3f48b1b6bfafe3e59515ff9ee1895596e80c9295004d71ec645a4b840a37057c8b07b7ec4b2a2fa07c3624a48b34a49173b6713e85418fd52bf222ab43029c6d1a629633d78153423134ab11ad45b15376c4bf132fb440b81053df7b1b4f25761ea895ea89b5c512f753859fc182cef73d8a33b958d2cc2d4383f0ada70d8d6d466654f62130959ac09a4c380f8f552d8b1afb6ce83abd2fff317cb0d7b2b347a2b0b46e29ebb6a23810ac8e0c4b90dbd81e268f2ce8659fdf1da7c97c41096d9a0a19a69ccf3f7880d49b669384562670c4255e194bd26f3cca9773feaba4c280ba2e357a6c2981f5b28f711f730bb01db7401c184bda05241d71cf8c10694ccd1f852f9c849133bf5cd34dc343735aeabfdea12a318ebf2a94fedd7161d34c30d724f968741c1738ad0fd94e9667b291342901883f0ed894402afa5d3a113a640e230d4ed85b8fc2af361d15402b618eff62b5acc012fe9dc52b245b0000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x7ab5df", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x42d6ff73ca53378a960a6b23df67cf45e01045369c0dec7d21a7a9eabb2cc8f9", - "transactionPosition": 92 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001bba4e", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x5beec87", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000082e8808821a001bba4e1a002138aa805820f39f2cdf9f09e7637320a65dc2e350ad731fba586b3fe5cc1838cfd58ff596fe582081773ad568e0121c5b3b01461b0b703611f4bf3317c88a87897e2e8e123489d85907808c23dc69fd600fac65df2b08b4ee5595049dce3df8a1ca6c1a0b47067300709c7bba1e1b7edf0f1ae8a11dced9fd646c87042bbbfbf8e2e77651c10597cb746fa0ea2caaddb219a8b79a0fdd9bc84242471f8b097d6e6ee1dc547c7f068905f103fff2cb2220608de1f525e034d46bed5968ad6799e44af0ba7805e86c2124dc262c3c6b8d73c10ab458a651ffde6b77a4621bd6af909eb88bb583559717d37c0363d75329d4f444c47a71e44670c1d7c9f6e08fcdbcc8fe70542e445893dd5ea51f205a5d4695eb2ff2a484df3be74a6462cccced5c7c552ec2e3568d02d4eab26e05da573b652f1c5a6bd51092350f83f803a8e496c069e866a9bf26c956bbeb82488d4410d2cab92a151abcc41526e74a57b4131ddcfa7c2d3e0706b6e8310481e5d7e2bf283ab1ae5650716b4ead437aed5bc50aa57138e976e885c28a1fc6e6e9a09619974b09bca55f34348d718ce52829d085704dab7715d49c2541c4a9301c7b45d85526e4829b4c07505c07508879e27a1f61bd261bf5d64d599f7ba195237ddae768b0fd06c377c24beffb203055e3ffed44a4a8584f5433ab586182a403f41d2ed434452ba32a08f9c24d8fe621b8bfc6c7e3d914c706e589e68563c1ee9c18f561d569dfc486f6765e30bdf165806e41b0648e82f1c9ebcdf574145c3c3bc6b92ff866337d46005ef56b786233c9d87f8793081bb7f903cb1e783152b482e86577a5df8cdb63744a47ab8f6d27b6380b9be7a4c53aba1f8b985e06e9623280468ea84aae5c6b4835a20fc43a40f4a0bbbbf2160586f5072fdbf999a08763587c500af0629bcd25bdf32c542e7e7af6835a6949527b804379ea137a17c623aa5917c802756919ba64a76ea8dcd49cd3478dd5d2f6d8d2f00204c2028c1b49136d750360613e277542de1930ee1559c581f1a6b3842abf100034380ecadd6dd1bc13adef306133549d463f45dc29968ad5c15b0adf9c13e051ebae7cf2a5bcb1a858df6daa3e19c2f01120af1b38e1ce0412135c8a1426fc0f009888ecdc65e0d91ad80e1fd42856598cd06e4dfbc6a9c7600fa56a48f755599fac92e816b9dce6982768819a6e24ecd3e0d8ab12fb35283b974796b9e304cdd961b15770331f0186c6db6966ddbbf7bef2a74ea8881ddc1b5cf22c6f764b0e68782b6df21f4dcd69ead4744298eb49c1ad87f086c1236e1e3ca477a7048c480c781254bc5a68886f5d94ff26d00439ac1805266f20547f11753b0d50e724e8930ce5be8af4d3b325d40eaca084d7050143831ec1bc40b5ad1b06aad472a0ff5cefdaa94aa33e5ad0ed5299436799a9a6b42a1222f56995de79fd7979b712eb8212810f8651832c7f4341ef4be3e2801de16898be3fdcd20f45aecc718c7066e160e0867afb2ac813a572645b20857d24388edfb15259db541187c2c5cb2c438b2068c64b2bf275004dabd8259134108df796d4a9603f9e26626521c718a28fc9a10a30613abb792b2a9e9d464295867fdd94968134d3895e7efc6ce4c884d213a81692625c28db3e18d5e03e3e6dd39fe9894eb02c50689c09890cdd1a27ce56623acd2381e7b38250bfa880d59049cdf1adecfe224446513fca3df71db5b5500ca038fb9569adf1f526926b9bfa66e27df4470baa2f1429bb1c2676cf8877f173c8d8c2e0c152e17cb6794cb80083cce0a2e7a57c31a5ba41913ed9806f2d50e3a889fdc888e24841b62b5f1c9cdc74f1152e882fb789846533a5e4caf0d5f1ea050856b5f7f82f8728a859107978cd95d62652a1e7191b1a296096dd8cf250de8b22e948d4102f2955b61a3effa2b667a49cb58c688bf2510a018e360db50394e57c41d39b866f0144e9bf9856108c83572149b6621a5f9491d732ede5b7bcd7b34f810066df96dbcc341b32a0c7756f05345b66ffddffc5eaa5de22928efcc6c80a949374db5a546008c47ef4fbf296b316dd03a57a40a89c730b9f3602df445547ea5ba4262e51919e2cb4c71ed4f5c63740d347c1471d05a9a266fe318ec306fdb9ddc6211caef273867e3e409dc317e4e04d49e40b474e29456f2f3e6252d2a18ed90e5188d13c74cde5e904d8cdb95c55c636c83c085c58ebaccea0ce2b1ec5b0696932007b538bd977ca26f630e9df8a16c2b031692a160ffa45f5d6e9a625412163c2771a54c4432d61f49ce9de85b475dc33eae4b2886cf4343ce2ed591a9f3f48b1b6bfafe3e59515ff9ee1895596e80c9295004d71ec645a4b840a37057c8b07b7ec4b2a2fa07c3624a48b34a49173b6713e85418fd52bf222ab43029c6d1a629633d78153423134ab11ad45b15376c4bf132fb440b81053df7b1b4f25761ea895ea89b5c512f753859fc182cef73d8a33b958d2cc2d4383f0ada70d8d6d466654f62130959ac09a4c380f8f552d8b1afb6ce83abd2fff317cb0d7b2b347a2b0b46e29ebb6a23810ac8e0c4b90dbd81e268f2ce8659fdf1da7c97c41096d9a0a19a69ccf3f7880d49b669384562670c4255e194bd26f3cca9773feaba4c280ba2e357a6c2981f5b28f711f730bb01db7401c184bda05241d71cf8c10694ccd1f852f9c849133bf5cd34dc343735aeabfdea12a318ebf2a94fedd7161d34c30d724f968741c1738ad0fd94e9667b291342901883f0ed894402afa5d3a113a640e230d4ed85b8fc2af361d15402b618eff62b5acc012fe9dc52b245bd82a5829000182e20381e80220c76afb4b41e42e410d2e7e6626ff6b8b33164323e57d3ce64128fbc5010c6860d82a5828000181e203922020077e5fde35c50a9303a55009e3498a4ebedff39c42b710b730d8ec7ac7afa63e000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x316261a", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x42d6ff73ca53378a960a6b23df67cf45e01045369c0dec7d21a7a9eabb2cc8f9", - "transactionPosition": 92 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001bbcc3", - "to": "0xff000000000000000000000000000000001bba4e", - "gas": "0x6adeeba", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000789821a00213857590780b1007b4492daf38cf92b272c7955ed27cd37a1eb17c5e88286ec68b3e21e1461cc1e34069af515a13e1835681f32b368b80a88a4bcb813e6e266251c6f33426c0ee3e8522218409424fdfbb592efc4d5ba8707eb6179df411f1524ee1949b98610e80b9d4b7be48c8bab6b64122a65146aa69f06a7bfe7dcf37596e1d1ddd1b6bca0d1aee19bbbc64f93a79e325cf2e69313642489c202aa3f20834782f9dc0b39e47d670d7ee6a9582d42bd06d77442076d3af3fdefacf2883c2bf52a87e25395748a4c99ff87d6ed45d6343c695bd515adaabeb7a79f4a333ff05c0e73c138a2dc3e540261bc57d7517327c8f81d0a8ac554f0b4407913e9dfffd128c3d0f8a7231f6ecf1023174389f7857f93ab45187fe90ed964e1b63cecc666b202f3f8031eb67cdd680f5ac27bb20aafe403baef23887c9e5983f5222f29bebb67b81ed4e9e4bb32b5c93120b2a13cd83b658d8f5989ff15d3aa81f86fca8850e414c2ea642b71d30bdd6a394394abc13e0d29b32bcbf9cb1843c07124c1c3a960b16c9796b59d275b98430fba4ffcbeb084f96238b1e936727613135ae0dd145fb72a87494f04f54f4855b2e4e699b1eababeabdfb405110110e0e55a3f93c7e89fe6c41f12f79f1f49c45331d2cc3e4653902901092b2b8ef047e2c717aa633d2596184a4b41834bc8473c80348699db9f228964bd4c86f6a8dd41963b3f3cf6673dc47a7fad3b13cda21489782d85aec609a2b03fe1ee546c367eaeba5e010456c7bca14242391787781874d71b7c04e302c7eea0731e869cf55f43a50b4660ef90b59b498b3c00eac63b5f163f921ee30438a214dfb3f1a152578b59d12ffac6f41613edfcacf34d517ab03fee2559f97c97cf212bb8aaf891ca30703ac9d1c06e1ddda63152027199c6c9e6f84e57b801357d3b9db691330d7ad1832cd70a9e821847e10113500d47424c7b7964aee8003dfe2f00f96d31575ac5228e7ae8dd60f1987fa5e2df0b9f733db586b65ad998ad6d88d622756de188a9b03c2311163a6e4771566976fbe573104a637f4888947cf15c0c0655dbce862b386a3ca53081b3b76f81958a5bd20bb5dbcdf009c2a7088c3d8784d51f682669b8f1b45f4764cd073aaa77a8089cee546ecdae57bd94b36d24bfb7e9199407c3cfcdf47fb4b6f681a8dd46017b1da29f0ec3b1d18f14166c5089c7ef26620f7503ddf6d4c1640f4d12e820c8ccc72598d33702f7d2483712306c217b646f938510c81afcda60bfcfbad6a134e18654b85180e4b08285b6b2c79c561af610d6c8ada60e88bc28e7ef39844611db8cb095a2ecfa622b31397cb93711e9207b77cf53867bb9993a97822c38fb055750c9a8022a0f0d90face484c80044eb15c2cafd7cdefb48476ab92cd458e5cfddd975ae85439522466afe858a792d929451c9fc080817e1be3056c3380f912b660d3ec2bf0d42a6fbe609d410e25367b219926c4105110766c1286e389ee46e47d3b1d3434b5365d851bad4e1f77d3e45f8ee3f65365c50f88ad65fa31b0b5701f0d7e3336d462d271a518c1642dbd94f8282faf96f00d85ef614ed27d10bd8479de6ead9109e94091154c162276d5c66fe99b3ffe43e5cb2c8dbc545a0dd131c4a30dc7a0eddb3ae96cace625bf72b9140ed306d90ac5057fbfd4b4b9679e1392f8df971cc1cba61a82779c41a3ba13ca860541c5aded732e46aa71bb64385f83ffa339548afbff8bfa9d289f41125e47fade43eaf9ca210a0d850b1b313023290bf0766f252b83450325221f80262b144db67c40f49b410cad37925cab42e97b1b9f37a485df0ff0b37ef87632ae07c593730bed2538a47fa2717561e0cfaa1c7b3d81e82c0947aac9ea8ec8a40055c1bbebd8cd5e8352bb82c4bfc5c90bbcbb6e6b5388fc7f158b7d1c6d4a4eafb0e496e801ac010aa6989b4d6bb0490642aa76bb4349bee326a8b00c06e9ba01ce15165a4dbb5e7da927fa59cd9175f5ba95e800ff43cdb1e65e19d48e552b2608850ef47e9b40ca7b1c0a3464937f4480211d6f4baae37ca8c956dfcebc6fe4e9389ac61c359ed6a4c9fa2edcfc70ded57ec626f4b7fd758613b104a9caf590cd07c4dd82140b06cbda7aa6d051d7b7d3842b4bd19971ae089e0a6816b3fa483a3d43fe8a4c722ab531a380ba1f50214f63cd0478ba8a1692e724d0c8f66cf4ef24f105e19d6dd2f4a889d5870009daf12259400e055f47799a8af59e5af090f063000bf9249e0873416878ae2e5d96b99559ba442fc66135822748dca90a17b62aba124c9dce11f4bc10de045ef27c250b0bbd300340abb044c0650646aed8570e3001c881254040027e8e8292f539b3cc41cade3427ce1e2fac0925efae5202961ed0eda80f590a19af89bef00fddabc96ee60dd5b9a7354bfd368de599d3f39fe6d00ca22ca43411abdd9519c4ba55e35799b1769f23509637131f493bd9561bb6c284a0711f22b702d7e3d8bc1adcc135bfcf7612277bf88801919448799005e25fa86afabd45820ec3178b0d62de904693b93366a31c9963bafb678f81168abed95b5efdfdb88f181bed43e298bc88ebce505591e9a31f1de187e713f22c1b01299f73e1e69c396d246576157bbec8200ac414ec7ea11c880682b2c2727d4ff5c28b1a049e3b369d992b932605b571f87d8cc9bae645ee150d9624f911e704c64cf9a3c28223d60000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x7aae4a", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf099c22c6ed9edfc55c1546600f7467d56f5e43350d300b9e11e76de226edc2e", - "transactionPosition": 93 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001bba4e", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x66421af", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000082e8808821a001bba4e1a002138578058206b302393a7aff44a8f259b4ca67a22a687cc8e7df35eda9dc30dd32aed8a1afc582091901695a71c68c04728449c97e6f2842d92b0bb83026300a51b3bc8583385ca590780b1007b4492daf38cf92b272c7955ed27cd37a1eb17c5e88286ec68b3e21e1461cc1e34069af515a13e1835681f32b368b80a88a4bcb813e6e266251c6f33426c0ee3e8522218409424fdfbb592efc4d5ba8707eb6179df411f1524ee1949b98610e80b9d4b7be48c8bab6b64122a65146aa69f06a7bfe7dcf37596e1d1ddd1b6bca0d1aee19bbbc64f93a79e325cf2e69313642489c202aa3f20834782f9dc0b39e47d670d7ee6a9582d42bd06d77442076d3af3fdefacf2883c2bf52a87e25395748a4c99ff87d6ed45d6343c695bd515adaabeb7a79f4a333ff05c0e73c138a2dc3e540261bc57d7517327c8f81d0a8ac554f0b4407913e9dfffd128c3d0f8a7231f6ecf1023174389f7857f93ab45187fe90ed964e1b63cecc666b202f3f8031eb67cdd680f5ac27bb20aafe403baef23887c9e5983f5222f29bebb67b81ed4e9e4bb32b5c93120b2a13cd83b658d8f5989ff15d3aa81f86fca8850e414c2ea642b71d30bdd6a394394abc13e0d29b32bcbf9cb1843c07124c1c3a960b16c9796b59d275b98430fba4ffcbeb084f96238b1e936727613135ae0dd145fb72a87494f04f54f4855b2e4e699b1eababeabdfb405110110e0e55a3f93c7e89fe6c41f12f79f1f49c45331d2cc3e4653902901092b2b8ef047e2c717aa633d2596184a4b41834bc8473c80348699db9f228964bd4c86f6a8dd41963b3f3cf6673dc47a7fad3b13cda21489782d85aec609a2b03fe1ee546c367eaeba5e010456c7bca14242391787781874d71b7c04e302c7eea0731e869cf55f43a50b4660ef90b59b498b3c00eac63b5f163f921ee30438a214dfb3f1a152578b59d12ffac6f41613edfcacf34d517ab03fee2559f97c97cf212bb8aaf891ca30703ac9d1c06e1ddda63152027199c6c9e6f84e57b801357d3b9db691330d7ad1832cd70a9e821847e10113500d47424c7b7964aee8003dfe2f00f96d31575ac5228e7ae8dd60f1987fa5e2df0b9f733db586b65ad998ad6d88d622756de188a9b03c2311163a6e4771566976fbe573104a637f4888947cf15c0c0655dbce862b386a3ca53081b3b76f81958a5bd20bb5dbcdf009c2a7088c3d8784d51f682669b8f1b45f4764cd073aaa77a8089cee546ecdae57bd94b36d24bfb7e9199407c3cfcdf47fb4b6f681a8dd46017b1da29f0ec3b1d18f14166c5089c7ef26620f7503ddf6d4c1640f4d12e820c8ccc72598d33702f7d2483712306c217b646f938510c81afcda60bfcfbad6a134e18654b85180e4b08285b6b2c79c561af610d6c8ada60e88bc28e7ef39844611db8cb095a2ecfa622b31397cb93711e9207b77cf53867bb9993a97822c38fb055750c9a8022a0f0d90face484c80044eb15c2cafd7cdefb48476ab92cd458e5cfddd975ae85439522466afe858a792d929451c9fc080817e1be3056c3380f912b660d3ec2bf0d42a6fbe609d410e25367b219926c4105110766c1286e389ee46e47d3b1d3434b5365d851bad4e1f77d3e45f8ee3f65365c50f88ad65fa31b0b5701f0d7e3336d462d271a518c1642dbd94f8282faf96f00d85ef614ed27d10bd8479de6ead9109e94091154c162276d5c66fe99b3ffe43e5cb2c8dbc545a0dd131c4a30dc7a0eddb3ae96cace625bf72b9140ed306d90ac5057fbfd4b4b9679e1392f8df971cc1cba61a82779c41a3ba13ca860541c5aded732e46aa71bb64385f83ffa339548afbff8bfa9d289f41125e47fade43eaf9ca210a0d850b1b313023290bf0766f252b83450325221f80262b144db67c40f49b410cad37925cab42e97b1b9f37a485df0ff0b37ef87632ae07c593730bed2538a47fa2717561e0cfaa1c7b3d81e82c0947aac9ea8ec8a40055c1bbebd8cd5e8352bb82c4bfc5c90bbcbb6e6b5388fc7f158b7d1c6d4a4eafb0e496e801ac010aa6989b4d6bb0490642aa76bb4349bee326a8b00c06e9ba01ce15165a4dbb5e7da927fa59cd9175f5ba95e800ff43cdb1e65e19d48e552b2608850ef47e9b40ca7b1c0a3464937f4480211d6f4baae37ca8c956dfcebc6fe4e9389ac61c359ed6a4c9fa2edcfc70ded57ec626f4b7fd758613b104a9caf590cd07c4dd82140b06cbda7aa6d051d7b7d3842b4bd19971ae089e0a6816b3fa483a3d43fe8a4c722ab531a380ba1f50214f63cd0478ba8a1692e724d0c8f66cf4ef24f105e19d6dd2f4a889d5870009daf12259400e055f47799a8af59e5af090f063000bf9249e0873416878ae2e5d96b99559ba442fc66135822748dca90a17b62aba124c9dce11f4bc10de045ef27c250b0bbd300340abb044c0650646aed8570e3001c881254040027e8e8292f539b3cc41cade3427ce1e2fac0925efae5202961ed0eda80f590a19af89bef00fddabc96ee60dd5b9a7354bfd368de599d3f39fe6d00ca22ca43411abdd9519c4ba55e35799b1769f23509637131f493bd9561bb6c284a0711f22b702d7e3d8bc1adcc135bfcf7612277bf88801919448799005e25fa86afabd45820ec3178b0d62de904693b93366a31c9963bafb678f81168abed95b5efdfdb88f181bed43e298bc88ebce505591e9a31f1de187e713f22c1b01299f73e1e69c396d246576157bbec8200ac414ec7ea11c880682b2c2727d4ff5c28b1a049e3b369d992b932605b571f87d8cc9bae645ee150d9624f911e704c64cf9a3c28223d6d82a5829000182e20381e80220b004dbb253f292969d2a45ab68bd917a5bb6b2f9031f74638403a29361b03e0fd82a5828000181e203922020077e5fde35c50a9303a55009e3498a4ebedff39c42b710b730d8ec7ac7afa63e000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x38bc17e", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf099c22c6ed9edfc55c1546600f7467d56f5e43350d300b9e11e76de226edc2e", - "transactionPosition": 93 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001bbcc3", - "to": "0xff000000000000000000000000000000001bba4e", - "gas": "0x65623bf", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000789821a002138a5590780938809e4804b3536e70cb0531840c36f70f8131906dc55738323fd254b0f8f18832e0f414eecb7ab09e0c9fa971b9524a2df8199e2302002c584d0cfcd96dc2551dd37f06539dbd8721dfda01c3cdb7e8dbb9d4a3fc353056055197b0841ea550a104147ed57d86249ee9838f05fc23ac3656bd18c527862d624b75f4fb29cf1c86cdb9b232b44e35695742b71800b68824d4d88eb47c7ba20babaca7beb5fb74007d26e5f285fce5ecaeac4bad411b8987d887dc99d0030b2861eb7dc4700a9b82b3371e8454fab416288f44520e1308947cc468c68b489340804774cf5c8a4577f7749a84202b8f855170c681dcd0994bbdd6753f174ea6efbadf483278d57cbc93eb3dc9dc841cd3e7f70c93280117a3c3080a498a56b9dc66fcad5732ec70cec935046665103a1d0ed7629409d8cb0f5b8280ec38b07752a7b52bad8142d8c1a1bdf9fa05da66508e62ed1076ea78f4af3062b5546c4fe03dfcf49079c442d4ec450dd90a193ef337323b15c6076feb095dafda9640abcbe8a7c4233226c991ad259bcfaf2db6b55117614b6dec5dcbaadff4fb2ecc0152f74b6f78d7eea32ba4262e26e52246a91c2862f1477a38ea6bc1fa5805a16996af8b23fdb702f4ee31616c827af71b730c19392162b08c449cb72407d720b27ffd9b2b59fc3370db1eb60969f3b4f7c52796d8047963c2872bda2dc3f53ceb0e5bd9861e9ba55fe2ec7efd872b47f4841f4629dc0632197dff41ffdbb9f76176d9255d08a9f757c534b4dd6ecfb6f2700b11208d9f247d2b13661e6c761283d89824688d66049a1fbcc4dc6f623fe513f878a5c5a9de8b89482f9fe61e7dc9ba1f94d53b2162050f03a9056dcbdf255447e2a342e64a281daff99c80fc4460d23571a7bde6f3ec7c74e80adbf0e53b17e88b505d4bd5e54d4daa881819cdd5143d174130ace2e0a4ae39d81d37b2f2130b9fac40b14540bbaae81a0f156e76bb6c40000fae97789f1bd64a43f7ac4869b7d82b1dc569faaf5f4e578001dc2dc4e4b693f78464257d056de197ca2ebf24d02db47ed078a005a3653d3fecc334b6db1c37594781fac82018e230badade7730f75d55c29a594a73539a675fac512fdced85a95b79a874a585ff8a560c0e95bff0cfbd0790a96981373361d59342de7aa41b2592a98e6188626495895fc92b48fef4a34b39b07186fa9c1385e1d4cb12c1046f1483d1736f3881f98162cb507827c265c2f7d20f0a56334e75e056cc245fd34eaba22535ac6f6b81a94d78499a2659908a4af8ef74e19c6c1750589b1168a14cd43f4ba90eeabd5db32ca22056357aa79c914c87430f8e033e9d1ca73a60dd593c8d9b3150771833b871514f590d261ee8172649cbcb1c0af79cddf194261e0b0953f14edf1ded13a435e02dde8a7afa0aa2e858165fd467dacbfd8ad5cb59e51c11dadbab16a24aa34e8bb20a5d52bb4126dd9ef86420ec17a96cdf05aad211c7cec103a46c2b4ce956c73b51cb8421deef8ee19b13d8aa58daf0ae56e202436c193919b844b06fb9b21e9e2a900387a76e68cc654e16def4047035f485fc8b1618e987319d7950ccb42d6a70c8725e49dafff9a476a374a14cd09adef5a5c4b04dfa4887c6b64c4355d3a444c435f22275f7b5d8fc491236e62a0edeb5d2ac2a6d9d68ba7c3e1e6906a03d995934fbd9849b791ae648cdc0f1494ce758bff37b0116f3526cdafab18120f491323065fa345c2066e99d9ba2337bfec891a27e4ab0918d78652c329926d4186bccba63aba373b410e48301bebb03aadc8c36efed11575dd2ef1cbc5b9495dc498733dd4c7eb97c3019ec2f7a13da5c61f4130f4e89fad38b65b3bfaec1b846f70456b78869e25db44e67fbb97c6a680a9812b105c60b77074dca69c0d16a904da5388c5f935835f48e40eaf1e2da59fa2f5598b9653adf96dcaf14c68d5aadfa744fbfcb8688f195d6b8260b9f25015a58779b0ce0c00b1ef06f241effb557985c0680d131f9ba8e8f5d1bf351df45766fe4dacb08602352ac292f27e875c1adce61cc9a71bef08f567bb77e5f7586354711eb1c968bd4aa18966cbfd945b706e1cac579278aa5392da1204b1857c72b2ae68cbedf01c632e4e0aac3f88f691711d80ac557ae31ef84e69007fe74ff3195a3137f363a783418ab3cf1dc2df032e9d0ff227426b003249254b786dfa00b3a950dfd7225b6a372252e0ab276734ca6d61b86b268ca42a5aaf8134f9597ad3f6e6892d3d6aae36305cfc23c52cf519f882d61f8d43303fef819be2a11166fe83170cd28701662da9f08853b2853c09999197098cd894b0ac636192dee9d773a1d4299d93a64fe7f3b4c3897ba23652bc6635e6bbb66120e108e4d1f65ad78aa5ab3f170d7ca0e42626c279e685b3dd432f15a521c0a0e98e5fbcd0ba0d9eefcaa7b01d68b04b31c71ce9799a86f88410e84a3b61201ed738f737fd408cc284dfc7c66fef73490d4b39a0e23430833764084f60c98923296607f8c4fe4ca962118eac687c891fa444c9ccc72147f948be34b412cffcf5a8d635640388bea532cbca5cf32704c3139459d07dc2a82f77d2046008d1bd797a318b07a671879392e67e0bace396957faf2ed64393ea7ae07df1d70381b0bc3e2c70cac7c5f2274c87fac7c56bd4cd3af4297c538ec1fc4f8e862edbf759b59f052bf3aa6e87acbc2ccb78944d0000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x6e6346", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x293e1eb922ef1b490b4d2c4c14b8e51c383ab38933509fdee3de8a7b4b96d0dd", - "transactionPosition": 94 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001bba4e", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x618a1b7", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000082e8808821a001bba4e1a002138a5805820fe51bab9960b3c80d4c6c8357c418a5593f2c35c2a7337a94a6e6ca9486bb990582081773ad568e0121c5b3b01461b0b703611f4bf3317c88a87897e2e8e123489d8590780938809e4804b3536e70cb0531840c36f70f8131906dc55738323fd254b0f8f18832e0f414eecb7ab09e0c9fa971b9524a2df8199e2302002c584d0cfcd96dc2551dd37f06539dbd8721dfda01c3cdb7e8dbb9d4a3fc353056055197b0841ea550a104147ed57d86249ee9838f05fc23ac3656bd18c527862d624b75f4fb29cf1c86cdb9b232b44e35695742b71800b68824d4d88eb47c7ba20babaca7beb5fb74007d26e5f285fce5ecaeac4bad411b8987d887dc99d0030b2861eb7dc4700a9b82b3371e8454fab416288f44520e1308947cc468c68b489340804774cf5c8a4577f7749a84202b8f855170c681dcd0994bbdd6753f174ea6efbadf483278d57cbc93eb3dc9dc841cd3e7f70c93280117a3c3080a498a56b9dc66fcad5732ec70cec935046665103a1d0ed7629409d8cb0f5b8280ec38b07752a7b52bad8142d8c1a1bdf9fa05da66508e62ed1076ea78f4af3062b5546c4fe03dfcf49079c442d4ec450dd90a193ef337323b15c6076feb095dafda9640abcbe8a7c4233226c991ad259bcfaf2db6b55117614b6dec5dcbaadff4fb2ecc0152f74b6f78d7eea32ba4262e26e52246a91c2862f1477a38ea6bc1fa5805a16996af8b23fdb702f4ee31616c827af71b730c19392162b08c449cb72407d720b27ffd9b2b59fc3370db1eb60969f3b4f7c52796d8047963c2872bda2dc3f53ceb0e5bd9861e9ba55fe2ec7efd872b47f4841f4629dc0632197dff41ffdbb9f76176d9255d08a9f757c534b4dd6ecfb6f2700b11208d9f247d2b13661e6c761283d89824688d66049a1fbcc4dc6f623fe513f878a5c5a9de8b89482f9fe61e7dc9ba1f94d53b2162050f03a9056dcbdf255447e2a342e64a281daff99c80fc4460d23571a7bde6f3ec7c74e80adbf0e53b17e88b505d4bd5e54d4daa881819cdd5143d174130ace2e0a4ae39d81d37b2f2130b9fac40b14540bbaae81a0f156e76bb6c40000fae97789f1bd64a43f7ac4869b7d82b1dc569faaf5f4e578001dc2dc4e4b693f78464257d056de197ca2ebf24d02db47ed078a005a3653d3fecc334b6db1c37594781fac82018e230badade7730f75d55c29a594a73539a675fac512fdced85a95b79a874a585ff8a560c0e95bff0cfbd0790a96981373361d59342de7aa41b2592a98e6188626495895fc92b48fef4a34b39b07186fa9c1385e1d4cb12c1046f1483d1736f3881f98162cb507827c265c2f7d20f0a56334e75e056cc245fd34eaba22535ac6f6b81a94d78499a2659908a4af8ef74e19c6c1750589b1168a14cd43f4ba90eeabd5db32ca22056357aa79c914c87430f8e033e9d1ca73a60dd593c8d9b3150771833b871514f590d261ee8172649cbcb1c0af79cddf194261e0b0953f14edf1ded13a435e02dde8a7afa0aa2e858165fd467dacbfd8ad5cb59e51c11dadbab16a24aa34e8bb20a5d52bb4126dd9ef86420ec17a96cdf05aad211c7cec103a46c2b4ce956c73b51cb8421deef8ee19b13d8aa58daf0ae56e202436c193919b844b06fb9b21e9e2a900387a76e68cc654e16def4047035f485fc8b1618e987319d7950ccb42d6a70c8725e49dafff9a476a374a14cd09adef5a5c4b04dfa4887c6b64c4355d3a444c435f22275f7b5d8fc491236e62a0edeb5d2ac2a6d9d68ba7c3e1e6906a03d995934fbd9849b791ae648cdc0f1494ce758bff37b0116f3526cdafab18120f491323065fa345c2066e99d9ba2337bfec891a27e4ab0918d78652c329926d4186bccba63aba373b410e48301bebb03aadc8c36efed11575dd2ef1cbc5b9495dc498733dd4c7eb97c3019ec2f7a13da5c61f4130f4e89fad38b65b3bfaec1b846f70456b78869e25db44e67fbb97c6a680a9812b105c60b77074dca69c0d16a904da5388c5f935835f48e40eaf1e2da59fa2f5598b9653adf96dcaf14c68d5aadfa744fbfcb8688f195d6b8260b9f25015a58779b0ce0c00b1ef06f241effb557985c0680d131f9ba8e8f5d1bf351df45766fe4dacb08602352ac292f27e875c1adce61cc9a71bef08f567bb77e5f7586354711eb1c968bd4aa18966cbfd945b706e1cac579278aa5392da1204b1857c72b2ae68cbedf01c632e4e0aac3f88f691711d80ac557ae31ef84e69007fe74ff3195a3137f363a783418ab3cf1dc2df032e9d0ff227426b003249254b786dfa00b3a950dfd7225b6a372252e0ab276734ca6d61b86b268ca42a5aaf8134f9597ad3f6e6892d3d6aae36305cfc23c52cf519f882d61f8d43303fef819be2a11166fe83170cd28701662da9f08853b2853c09999197098cd894b0ac636192dee9d773a1d4299d93a64fe7f3b4c3897ba23652bc6635e6bbb66120e108e4d1f65ad78aa5ab3f170d7ca0e42626c279e685b3dd432f15a521c0a0e98e5fbcd0ba0d9eefcaa7b01d68b04b31c71ce9799a86f88410e84a3b61201ed738f737fd408cc284dfc7c66fef73490d4b39a0e23430833764084f60c98923296607f8c4fe4ca962118eac687c891fa444c9ccc72147f948be34b412cffcf5a8d635640388bea532cbca5cf32704c3139459d07dc2a82f77d2046008d1bd797a318b07a671879392e67e0bace396957faf2ed64393ea7ae07df1d70381b0bc3e2c70cac7c5f2274c87fac7c56bd4cd3af4297c538ec1fc4f8e862edbf759b59f052bf3aa6e87acbc2ccb78944dd82a5829000182e20381e80220a85c7858b9d76a0adb990410e518b6022325a8bd1743404b270c1ce80c9af42dd82a5828000181e203922020077e5fde35c50a9303a55009e3498a4ebedff39c42b710b730d8ec7ac7afa63e000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x3f9c875", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x293e1eb922ef1b490b4d2c4c14b8e51c383ab38933509fdee3de8a7b4b96d0dd", - "transactionPosition": 94 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001fb931", - "to": "0xff0000000000000000000000000000000012d687", - "gas": "0x18c2204", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000f285058182004081820e58c081685d1d10d34edf3142ea3abfb4d9265e7ea2389b907852444da5f0952c0046f45b50589f6653be572e2590f82f26a4ada3ba4aa1637b7831390365b606352026d216bfc959d08c69ca7965439b86c03356b396062bcb98a458de9f302598960a06fdcdb4ac9ad54cecd47fffda00ea3b8a6fc666669325d25980c3ed20bfaf296f814dbc68978a23a30e03c28fc03fac83f85f9a7c23f15894643a8bef5046b4800a556ef86ff61691c7adfd35382506baf6713194c9f8a3c28be624078ba01a0037e5f8582047ab5fe94c06cdbaec8b74525a8ef23928a1bbb62440ccbcd599b10154404d690000000000000000000000000000" - }, - "result": { - "gasUsed": "0x149e229", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xa66054d53dc628a9f4132fad4beb3b4c8b77a9ecd878c31d58d9773310643125", - "transactionPosition": 95 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000235fdd", - "to": "0xff0000000000000000000000000000000001dd67", - "gas": "0x4e8c5d3", - "value": "0x1a604e782960a295", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000789821a000a0023590780b1f46cf7da5aa8c412fdf20a3d29ffcb51e2090c60eca8ce7a6755cf86aac57d7dd57e66936da82659fbd0c263a7e9f38f3f539c4612078bb4cd042c493dbf3f240d6d2cc8716015b23e328f81bd082f1eacc23be4f00d1a36ae41db43600b4f0e95245fc967a52846d2510376596d14beea198356a3bebb4fc3cfff9b6239b25f5aa7c7ae64601deaed2be87b43a690b207bde83a79cb313f63bce1733fb94edae6bf878fa48803ef520cc8ae554ac1f34af9f55fb2717ce4d8dc9a59f1883d88c2b30a9303dbf7e63d1ba121ccb1d9105105d677a218602469cb651779b2fb8833e3a2fb949662511eb0e48ec57ad3b897e378aade2102729778bf7d9fac14e84a170aaa8a859ae05bd1617bbdee6d18e4f6742f19fcfc4d060f829cc2a19c1901d1c0a05e1ed11268b6218f9b393b29aa9c38fecabfdf11bf3781144e1e8b9aaf33b49b5ac2998cd0bcc19da4393eadf4334ab0053ee3076f4d6df1ed36fce20e00e4a735d3afc854a8752e50b7719a5feedaaa19e3067deea209e79a4bb0aee06ada2bdd419f02248ae079c7d9948c97b7d448e95326fd49a6abdf9506e3ed3a6dec1aecf431b0db2e42092f194fa9a71f648f9037c0a60ff5cab539b4e2ff47cca05d9c68b57acd0659005362fc753908599b75f2e403881bf3a17ea0550d9a1983496c891611b29dcec699c1332ff50847c22ec04a8e102f019aadbec4ef48c92879f449987188031bb4ed8cb68d0512ce2ec9204f85365793b9d9ea58941baab7823223a15805bd194ca9866d74dc3abf6ddec70633fe928518b951f39671b4d21b2c2c12500671848cbc84bb6cf98057fce0cb90f993cbc69c1e106d6195581eb8eb441a7c1502057a9f68f28ef4856c69a3643c3490129176378adfa1a297857a7f2f2ca80383c8ad7fa9969ef8bab649368a92c268b730ee01302e12604ea9644e5b0d1e640d7aca21e09f0cbedcf0eb6327a59d3e98503437ded0fc5894ef0440130f16727512ffbec3bfa4fd02753b12a0c65a040b71984bed42457725c9b2c9f494709f17fe698c157a690cf89339cc91f412109e3765bf76e6951ff2be4cf037b0463266d0bea907d01e76b1ce8e8240fdf9cff6c89baf70b232063f849885461b9d6206663e4d2833819de0187c675cb90d5bac958312fca8e926a113933f99251f8366a39fe273d1f2964c98e007acd7d15afa67ca70f8ff19f6a810ccb02a9e88991b7f54fc1b7248f5e740a6a90e362beb8a3c8bd20971ea11e3a5d5a541015b0690cca58f1e528fbe3b738e24a30e51dd775989f2dba69d89a2abe38865dde95af7a69bda1c610d2944f14de8edc0bdced10dfc0f3fd98e840f0627444e20da4b7947be112db42c1adb81230a603fa69fc0a8f3c5c87da309297a8c5d4a47eb3ee07000f81ad7b96dc27cd2639b359b08f63f79be56d41675c9af34d60f9142fd8c16d8489192ed4587cf2fc8029036d12a134b4c00c8002123b500acda1257dd1c501cf76f84c153cf5c018a3b18eeb1d63d39771938799043cd413e7b8e68b5a42d7054e609b0b5e857b878591dca7c5930fee7e313af3b8c1b48d49b3bafd6710d5ac22f13b4f63f12103bc779855772b229add4c5a21aa769fe3d0f6cad677e2d8a1c6c6da878f2cf49d4b42be7a617536bd924c93645bac6b43fef51efdccbc8679525fcaef83e89a8d41418644b2ab90813ea70a9d999f6b9a7eb233b48dc7928eda4917b07e3e0cba92674dbc8b0168d5d821d0bc093e7ba8c4c020e7a2374a96299b6f85c94a5ea467baab46b24fe798efc5e8debfd737aba2899b9bc759aec49102fa2e50915f3fbbad9ae2cb3f0ceaf287753a97330067c4e45df6da8bb01a34a6e76ccf228a28be3b6de92d158967a65898358bd47b4605ff64e320a62e4a72a394bc35df7e90cec68d8e4df0a771e3958474af79eb4d5376125e3f0b363714b9986d6985263f45f8580e05ec91e51123c868a9470fc490597432364ad04f20653b8335114b15f5c18de5e362ebc32ae2512dddfccc5c4db97e77bfeb4394ba114e2f9447c953ec89a94c39a25f8ec0bd2b0f89e3762043501aed99d6159ab4abeab32f66967fb6cab04fb56ee6ffd652095daa21642ae9083d5afec856642f9ecca82cac18df1f1fef5c868d946759af794f04319696b6fc63a83e75de58192c5f4bf21ef5e6c69a8b6afa88f4daa6c5cba2f03787b3c6cbf33421012bd9788a58dabd91d3b7769c901a46c31ed41d0459afeece50ac8de9d97c91c40c041b1173537a1f49b183ae68b6944756a63fd6000f6f881584d8e7e4863a9f3247956f108ed85a11c3b3d9e60634a62828b38322c845cd5ac80420146154d55fe1bac709803c803ece6de386d0df9d379f9ab10b82f0a29cf2d98e6ba564d2a8368508dc85c8b9876ce150fe7d88e4684723e0190c12de8200207fa88fe3af9590c8f0b19d0f0cc4ea1276779fa6815ce4294147f9189355a4dc71446560f06948889f4b7ef9b2c720260607cd9b01a69de0eec1432cafea0f87c5a1b6d0d11cbdc51c20a724238cdfd949e1221e13e8b29ea7c170eac8d9981eed96e72d59443212096d6299e286db80e661fd68bbd2d49434b90e11db7627860b17531086e59e049d5a510fb61c86c94fbc7fed1474c59280346f879698715675210bedbeab847af0e6c88d478dc20148d564fba21e557dcec0000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x9b3e40", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc209712792b9bb2ca548df72c743c2efa109181e953fac3f07e8e7026d7aa78f", - "transactionPosition": 96 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff0000000000000000000000000000000001dd67", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x47e9165", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008338808821a0001dd671a000a0023811a045b18855820e846cbc689aebc3ec97bcdcb44da2e58c3e092c75ee715dfa353496d8d00286a582040123884cdb875d63ca4b9ceeb2f02840ac99070376e535012b6a5ed5323deee590780b1f46cf7da5aa8c412fdf20a3d29ffcb51e2090c60eca8ce7a6755cf86aac57d7dd57e66936da82659fbd0c263a7e9f38f3f539c4612078bb4cd042c493dbf3f240d6d2cc8716015b23e328f81bd082f1eacc23be4f00d1a36ae41db43600b4f0e95245fc967a52846d2510376596d14beea198356a3bebb4fc3cfff9b6239b25f5aa7c7ae64601deaed2be87b43a690b207bde83a79cb313f63bce1733fb94edae6bf878fa48803ef520cc8ae554ac1f34af9f55fb2717ce4d8dc9a59f1883d88c2b30a9303dbf7e63d1ba121ccb1d9105105d677a218602469cb651779b2fb8833e3a2fb949662511eb0e48ec57ad3b897e378aade2102729778bf7d9fac14e84a170aaa8a859ae05bd1617bbdee6d18e4f6742f19fcfc4d060f829cc2a19c1901d1c0a05e1ed11268b6218f9b393b29aa9c38fecabfdf11bf3781144e1e8b9aaf33b49b5ac2998cd0bcc19da4393eadf4334ab0053ee3076f4d6df1ed36fce20e00e4a735d3afc854a8752e50b7719a5feedaaa19e3067deea209e79a4bb0aee06ada2bdd419f02248ae079c7d9948c97b7d448e95326fd49a6abdf9506e3ed3a6dec1aecf431b0db2e42092f194fa9a71f648f9037c0a60ff5cab539b4e2ff47cca05d9c68b57acd0659005362fc753908599b75f2e403881bf3a17ea0550d9a1983496c891611b29dcec699c1332ff50847c22ec04a8e102f019aadbec4ef48c92879f449987188031bb4ed8cb68d0512ce2ec9204f85365793b9d9ea58941baab7823223a15805bd194ca9866d74dc3abf6ddec70633fe928518b951f39671b4d21b2c2c12500671848cbc84bb6cf98057fce0cb90f993cbc69c1e106d6195581eb8eb441a7c1502057a9f68f28ef4856c69a3643c3490129176378adfa1a297857a7f2f2ca80383c8ad7fa9969ef8bab649368a92c268b730ee01302e12604ea9644e5b0d1e640d7aca21e09f0cbedcf0eb6327a59d3e98503437ded0fc5894ef0440130f16727512ffbec3bfa4fd02753b12a0c65a040b71984bed42457725c9b2c9f494709f17fe698c157a690cf89339cc91f412109e3765bf76e6951ff2be4cf037b0463266d0bea907d01e76b1ce8e8240fdf9cff6c89baf70b232063f849885461b9d6206663e4d2833819de0187c675cb90d5bac958312fca8e926a113933f99251f8366a39fe273d1f2964c98e007acd7d15afa67ca70f8ff19f6a810ccb02a9e88991b7f54fc1b7248f5e740a6a90e362beb8a3c8bd20971ea11e3a5d5a541015b0690cca58f1e528fbe3b738e24a30e51dd775989f2dba69d89a2abe38865dde95af7a69bda1c610d2944f14de8edc0bdced10dfc0f3fd98e840f0627444e20da4b7947be112db42c1adb81230a603fa69fc0a8f3c5c87da309297a8c5d4a47eb3ee07000f81ad7b96dc27cd2639b359b08f63f79be56d41675c9af34d60f9142fd8c16d8489192ed4587cf2fc8029036d12a134b4c00c8002123b500acda1257dd1c501cf76f84c153cf5c018a3b18eeb1d63d39771938799043cd413e7b8e68b5a42d7054e609b0b5e857b878591dca7c5930fee7e313af3b8c1b48d49b3bafd6710d5ac22f13b4f63f12103bc779855772b229add4c5a21aa769fe3d0f6cad677e2d8a1c6c6da878f2cf49d4b42be7a617536bd924c93645bac6b43fef51efdccbc8679525fcaef83e89a8d41418644b2ab90813ea70a9d999f6b9a7eb233b48dc7928eda4917b07e3e0cba92674dbc8b0168d5d821d0bc093e7ba8c4c020e7a2374a96299b6f85c94a5ea467baab46b24fe798efc5e8debfd737aba2899b9bc759aec49102fa2e50915f3fbbad9ae2cb3f0ceaf287753a97330067c4e45df6da8bb01a34a6e76ccf228a28be3b6de92d158967a65898358bd47b4605ff64e320a62e4a72a394bc35df7e90cec68d8e4df0a771e3958474af79eb4d5376125e3f0b363714b9986d6985263f45f8580e05ec91e51123c868a9470fc490597432364ad04f20653b8335114b15f5c18de5e362ebc32ae2512dddfccc5c4db97e77bfeb4394ba114e2f9447c953ec89a94c39a25f8ec0bd2b0f89e3762043501aed99d6159ab4abeab32f66967fb6cab04fb56ee6ffd652095daa21642ae9083d5afec856642f9ecca82cac18df1f1fef5c868d946759af794f04319696b6fc63a83e75de58192c5f4bf21ef5e6c69a8b6afa88f4daa6c5cba2f03787b3c6cbf33421012bd9788a58dabd91d3b7769c901a46c31ed41d0459afeece50ac8de9d97c91c40c041b1173537a1f49b183ae68b6944756a63fd6000f6f881584d8e7e4863a9f3247956f108ed85a11c3b3d9e60634a62828b38322c845cd5ac80420146154d55fe1bac709803c803ece6de386d0df9d379f9ab10b82f0a29cf2d98e6ba564d2a8368508dc85c8b9876ce150fe7d88e4684723e0190c12de8200207fa88fe3af9590c8f0b19d0f0cc4ea1276779fa6815ce4294147f9189355a4dc71446560f06948889f4b7ef9b2c720260607cd9b01a69de0eec1432cafea0f87c5a1b6d0d11cbdc51c20a724238cdfd949e1221e13e8b29ea7c170eac8d9981eed96e72d59443212096d6299e286db80e661fd68bbd2d49434b90e11db7627860b17531086e59e049d5a510fb61c86c94fbc7fed1474c59280346f879698715675210bedbeab847af0e6c88d478dc20148d564fba21e557dcecd82a5829000182e20381e802209e50cb8175ce18db05fdce8f8651f33386cf196129b4be40a075c99187914609d82a5828000181e203922020af0d7d76927d5d2e47081e7d3d428177f43834c9386416c54fca266b3444f21700000000000000000000000000" - }, - "result": { - "gasUsed": "0x31bebc8", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xc209712792b9bb2ca548df72c743c2efa109181e953fac3f07e8e7026d7aa78f", - "transactionPosition": 96 - }, - { - "type": "call", - "subtraces": 7, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000001bac42", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x1273e026", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000eb8181828bd82a5828000181e20392202056bc2504599e6513bd273965038a95a26005013ceb8a54569a0046a513b162371b0000000800000000f555010f29c20971f6e29cead00b57277fb5975fd83f914400a29f74783b6261666b726569686572366e326c6f657735777166783777797768616e63347a74346c6a7776703263356f62727968346a756f356a6371616637651a003814d61a004f81164048001b6a51c29fe8e04058420192040362a715acc12b1e0985cf7fa25f4935d562a4e79f1699945b56c51e1cd242a0363d28cb1f9d735816b771f086829db7b4c88fc94f03a4814cfb597a9d1601000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x8e9f7bd", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000982811a045b576b410c0000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x1fc5fcce6d413a9fd7bd827fd070aeeec4762d054b583f5633da53347117a8b4", - "transactionPosition": 97 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff000000000000000000000000000000001d0fa2", - "gas": "0x12604c76", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000014c1cb970000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000054400c2d86e000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x13301e", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x1fc5fcce6d413a9fd7bd827fd070aeeec4762d054b583f5633da53347117a8b4", - "transactionPosition": 97 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x124c779f", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x1fc5fcce6d413a9fd7bd827fd070aeeec4762d054b583f5633da53347117a8b4", - "transactionPosition": 97 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x123babbf", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x1fc5fcce6d413a9fd7bd827fd070aeeec4762d054b583f5633da53347117a8b4", - "transactionPosition": 97 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 3 - ], - "action": { - "callType": "staticcall", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000021f2a6", - "gas": "0x1228a5ee", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000009d8b06780000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000ea82584192040362a715acc12b1e0985cf7fa25f4935d562a4e79f1699945b56c51e1cd242a0363d28cb1f9d735816b771f086829db7b4c88fc94f03a4814cfb597a9d160158a48bd82a5828000181e20392202056bc2504599e6513bd273965038a95a26005013ceb8a54569a0046a513b162371b0000000800000000f555010f29c20971f6e29cead00b57277fb5975fd83f914400a29f74783b6261666b726569686572366e326c6f657735777166783777797768616e63347a74346c6a7776703263356f62727968346a756f356a6371616637651a003814d61a004f81164048001b6a51c29fe8e04000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2e9853", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x1fc5fcce6d413a9fd7bd827fd070aeeec4762d054b583f5633da53347117a8b4", - "transactionPosition": 97 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 4 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff00000000000000000000000000000000000007", - "gas": "0x111c5e8e", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000c26ddbd50000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000064500a6e587010000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2ce23f", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000104f00120654f8b6172b36ea1e89d8000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x1fc5fcce6d413a9fd7bd827fd070aeeec4762d054b583f5633da53347117a8b4", - "transactionPosition": 97 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [ - 5 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff00000000000000000000000000000000000007", - "gas": "0x10ece94f", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000d7d4deed000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000067844500a6e587014200064d006f05b59d3b20000000000000584d8281861a001d0fa2d82a5828000181e20392202056bc2504599e6513bd273965038a95a26005013ceb8a54569a0046a513b162371b00000008000000001a00176c401a001b60c01a003814d68000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x378d1f2", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000043844f001205e5f30079f016ea1e89d80000500006a8d5434e2fdfc905110000000000520002f050b9c8c93d441ca1915680000000004d83820180820080811a034d18bd0000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x1fc5fcce6d413a9fd7bd827fd070aeeec4762d054b583f5633da53347117a8b4", - "transactionPosition": 97 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 5, - 0 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000007", - "to": "0xff00000000000000000000000000000000000006", - "gas": "0xddc0709", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000de180de300000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000006e821a85223bdf5866861a0021f2a606054d006f05b59d3b20000000000000584d8281861a001d0fa2d82a5828000181e20392202056bc2504599e6513bd273965038a95a26005013ceb8a54569a0046a513b162371b00000008000000001a00176c401a001b60c01a003814d68040000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x2807281", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000d83820180820080811a034d18bd00000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x1fc5fcce6d413a9fd7bd827fd070aeeec4762d054b583f5633da53347117a8b4", - "transactionPosition": 97 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 6 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000000005", - "to": "0xff0000000000000000000000000000000021f2a6", - "gas": "0x311f73c", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000f98c996600000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000009c8258948bd82a5828000181e20392202056bc2504599e6513bd273965038a95a26005013ceb8a54569a0046a513b162371b0000000800000000f54500a6e587014400a29f74783b6261666b726569686572366e326c6f657735777166783777797768616e63347a74346c6a7776703263356f62727968346a756f356a6371616637651a003814d61a004f81164048001b6a51c29fe8e0401a045b576b00000000" - }, - "result": { - "gasUsed": "0x9858a", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x1fc5fcce6d413a9fd7bd827fd070aeeec4762d054b583f5633da53347117a8b4", - "transactionPosition": 97 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce389", - "to": "0xff000000000000000000000000000000002ce3be", - "gas": "0x41499d9", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000078782193e18590780912ab6e26f928bffa3c68c9a467677eec142ecf7a2d7c57711f54aa9bebf313780e8f80cc5646a351ad95444b99ed0dca55ae5fad63d8694ef89818973ae10a1aeb4b104ae7f4803b0465d687d24c4311ff1a29125137f091a1f384e83c6dd6f0f31df5cea648ebaf3bf23e192e5570a56c6146a1a8b60124d047a11b0985d11ad2611bdf7042e2cd719dd55845779cc93293d56ec0c121918b2a2f69de6f516102859fea9b3e7aae7ccbc556e73015f404fbe8f2a6084a013f1b746d69c755b8533c06e199c3a3cd991793311106e59a79f319b007afbc7ec2b8cb93f9a42f14f2e5e7f39e26aa6b677313eca6a1f00ab0ce08d0eb91c0f1c9fad8069012697d54aa1ec9caaedad169001eaa0665504405c765b25f4d1209bb69b2f4b3f52390f1e0235828501c87f43b5bae1f51d54c5bb5e513d33441385086b40c08b2787d88350ef8b2e4e26ae68eb77d6e47c91b19bf6180433e342c57b7128d755a3bb1508a42134043ff697ca4915e666cb1e98ee0176480144a92c4d2a239e16343090965542b9073d87d453707027cd41b3cc4bedf13cd490a355e4da51322757aeb1c42aa193a7c848e55ab81a6e43696084f9c5965d467b8b797db16b4cdd8f15576a7f2efc5ec18c356c7a079edaca1be4b842a820c2776049f49c3e237a6f8d096f7473ca10b1b419adbce98aa92d0d34e7926d463166bf2b7afdaad927c6649d3b719a14db07e358a9b817ba1df393813cea360cf3754a12449716490218ec147c01f394fd5a78e1fd1efd1825f74b6c5ca1640b328ad5b7078f3d00b8988f85acb0bd284a1515d31b7daf53f780c22179fceafcbbef9e4cf3e99c4028671aba7c2adfcf2b455e914f962705f1c96e97042c1eedc665844f2bdf90159975255aaef853a9198cbfcbfdd982ea59ce4ccf92b9463035534c450743eafa9148eb11fafb866c07d79d4cdc3ae79ed0f2b95579adbd9ed25fa3d18a9a3ed4c936a47c9d670cc583a98d71490c569bc4637f93deea73cfa34ed7fd465c93e714b33431255c3300d0d03055df972b067d19bacbfe7fc79b25db29ac9718f188ec0421b2defd12c408a48c5d33bbc655fcce869995f33f9fd53f3dc36a73b7a362430bca272639b40d134e1d7405bdd8983c7bac2d8fb7208c6219b0899ae317015eaebcceee8fc9b93d573cbe70d378e50ba9a969f4a69407524a4023ef97aef3c01d128576f63b5699cb5c691a7039c4ef3ca9af3baf713495f149d7425cb417c52052f3e530e1e438dcf8b627f0263a27caa010917169b91952820d1a46a4b4f54357fda87463a5c866ce6afe7d9b23eefe2e173d737fe492470fa481ab883e0a82b86637364dcdc03c47318423f7f512839510ad64b82dd392b5d0c6acc2b824a9727a7717819dd8aba714e2eb11bf32348c632f06d66ab968e070ee328e357c90db353536eb2a2edca946abafd2f26b6f9ad6096fb44894d5a4d50e573d9c1c2d0fb3bfa5ec202c581b1cc08fd7594ebe6892ce53457fd7268bac26471341a438f811c9130d5397d61a98351e79d64ca387ede4e93eeaced4a56528a04f1299d9086887b2c726eb504e111af2445ed0aa76363a0ee535be1dd027a7adb43426caa0d90cac9116b5c36d120430ca5dae1c98644b72c6f847d74a0e71ae69604a4a4bab5628fdac4f92aaa86c70c4533334b366a751f23473cf65616aea0661fdbee4b9d3f2cb82578f44703e8ec514a72ce9b299e617d37106dd23f4c53dbe27780dcdb0312007fb60c2d43b3257ac99e75b71cbb611ebfc72c1fb724b15a8fe58b2ddcc2a5cd976a15ce41b4dd09cb9d09054e835d6d60b0172bf904074853ca9c6613c0fae8f792586238416b054c8710fd387e4d46c229a6d0d8f9b653bac5386284710668bb3fca857163f6db48e0420f0087dcb0dedde46fe29a90641b6c3c49db5d351a267305eeaea95c6e62e16a91917b7816972524fea118f26bf851baf069b8ecf2081cf91451d697be0b6887bccf0a3b67c96d44d1bff69cef9546d08e8dd66cb7417e7a2723440a6b6442c2a58bece8cba21ab9f197c3d0bc74c781e30efd25cf2542639ac3acfabdea3f7af221ad28c797930d368076cbcd112c0a1bf198d0c081d7f7f216889bcb5b446d720fa7f1333fba0441216568b55b9e3a869c47afaabdeb282b65c774c98db3d00c00406276b12d86c44f8dd8970bd7dc738bbe1ad712a10137ed87968c0f0c8afc53ba3f9a070e700dcd22b47340d17c2c7b2f1cd2039c89334e722e25b7d619c8319a6727f4b68279c64538ce4c03318853f4e1944b8d81d8b8007aca927ffbc02e69a77c45d2632a7c0afcaa90f5e6f2736f58bc304dd1170f685e8b47fccaded0661255d6ab8a290e4a8c44dedcf3c9660a7d35f040f0d63e14bde335b7968d5ed6e984a892b0a6a8139d96eb54a8d1022e8db082e062e0c252c3e01d6403afab8aaf6bc6f24d8157ed6cecad155e259ad5b018db69ccc08c0f15811ea78917fb72ad9e9573c7645fcf59cfa531b078ac8fe5b51efb61d8c1343ec3d890f34d6095d6000f0be39e9d0dfeb532f2708168b75f28ceddb56aa4581da4d5aca0b39a456ea7b8e43e883c9d3b12d51c0826ccb69d1e77956afd2adfc115a1e44ad78e68c2b93b61d71e0ddd4cdaaa7fd3589a1b9d2a42df325724d181b7c96af26643d47603af1289ede8f54c634ddb400000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x604a1e", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x2fcb2ae181befb6e530f111ad25c983e1d2e208d11caa73145c811266bdf34bf", - "transactionPosition": 98 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce3be", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x3e52cf6", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008318808821a002ce3be193e18811a0453643c58200b7238a7ac085ec5b663a2ac9babde2c59068dd6e6e61e57784fee049b0eab2058201015e3ffb83aba7ad6eed57e89a09176c78e97da5c95b02f947c84ec8868be3e590780912ab6e26f928bffa3c68c9a467677eec142ecf7a2d7c57711f54aa9bebf313780e8f80cc5646a351ad95444b99ed0dca55ae5fad63d8694ef89818973ae10a1aeb4b104ae7f4803b0465d687d24c4311ff1a29125137f091a1f384e83c6dd6f0f31df5cea648ebaf3bf23e192e5570a56c6146a1a8b60124d047a11b0985d11ad2611bdf7042e2cd719dd55845779cc93293d56ec0c121918b2a2f69de6f516102859fea9b3e7aae7ccbc556e73015f404fbe8f2a6084a013f1b746d69c755b8533c06e199c3a3cd991793311106e59a79f319b007afbc7ec2b8cb93f9a42f14f2e5e7f39e26aa6b677313eca6a1f00ab0ce08d0eb91c0f1c9fad8069012697d54aa1ec9caaedad169001eaa0665504405c765b25f4d1209bb69b2f4b3f52390f1e0235828501c87f43b5bae1f51d54c5bb5e513d33441385086b40c08b2787d88350ef8b2e4e26ae68eb77d6e47c91b19bf6180433e342c57b7128d755a3bb1508a42134043ff697ca4915e666cb1e98ee0176480144a92c4d2a239e16343090965542b9073d87d453707027cd41b3cc4bedf13cd490a355e4da51322757aeb1c42aa193a7c848e55ab81a6e43696084f9c5965d467b8b797db16b4cdd8f15576a7f2efc5ec18c356c7a079edaca1be4b842a820c2776049f49c3e237a6f8d096f7473ca10b1b419adbce98aa92d0d34e7926d463166bf2b7afdaad927c6649d3b719a14db07e358a9b817ba1df393813cea360cf3754a12449716490218ec147c01f394fd5a78e1fd1efd1825f74b6c5ca1640b328ad5b7078f3d00b8988f85acb0bd284a1515d31b7daf53f780c22179fceafcbbef9e4cf3e99c4028671aba7c2adfcf2b455e914f962705f1c96e97042c1eedc665844f2bdf90159975255aaef853a9198cbfcbfdd982ea59ce4ccf92b9463035534c450743eafa9148eb11fafb866c07d79d4cdc3ae79ed0f2b95579adbd9ed25fa3d18a9a3ed4c936a47c9d670cc583a98d71490c569bc4637f93deea73cfa34ed7fd465c93e714b33431255c3300d0d03055df972b067d19bacbfe7fc79b25db29ac9718f188ec0421b2defd12c408a48c5d33bbc655fcce869995f33f9fd53f3dc36a73b7a362430bca272639b40d134e1d7405bdd8983c7bac2d8fb7208c6219b0899ae317015eaebcceee8fc9b93d573cbe70d378e50ba9a969f4a69407524a4023ef97aef3c01d128576f63b5699cb5c691a7039c4ef3ca9af3baf713495f149d7425cb417c52052f3e530e1e438dcf8b627f0263a27caa010917169b91952820d1a46a4b4f54357fda87463a5c866ce6afe7d9b23eefe2e173d737fe492470fa481ab883e0a82b86637364dcdc03c47318423f7f512839510ad64b82dd392b5d0c6acc2b824a9727a7717819dd8aba714e2eb11bf32348c632f06d66ab968e070ee328e357c90db353536eb2a2edca946abafd2f26b6f9ad6096fb44894d5a4d50e573d9c1c2d0fb3bfa5ec202c581b1cc08fd7594ebe6892ce53457fd7268bac26471341a438f811c9130d5397d61a98351e79d64ca387ede4e93eeaced4a56528a04f1299d9086887b2c726eb504e111af2445ed0aa76363a0ee535be1dd027a7adb43426caa0d90cac9116b5c36d120430ca5dae1c98644b72c6f847d74a0e71ae69604a4a4bab5628fdac4f92aaa86c70c4533334b366a751f23473cf65616aea0661fdbee4b9d3f2cb82578f44703e8ec514a72ce9b299e617d37106dd23f4c53dbe27780dcdb0312007fb60c2d43b3257ac99e75b71cbb611ebfc72c1fb724b15a8fe58b2ddcc2a5cd976a15ce41b4dd09cb9d09054e835d6d60b0172bf904074853ca9c6613c0fae8f792586238416b054c8710fd387e4d46c229a6d0d8f9b653bac5386284710668bb3fca857163f6db48e0420f0087dcb0dedde46fe29a90641b6c3c49db5d351a267305eeaea95c6e62e16a91917b7816972524fea118f26bf851baf069b8ecf2081cf91451d697be0b6887bccf0a3b67c96d44d1bff69cef9546d08e8dd66cb7417e7a2723440a6b6442c2a58bece8cba21ab9f197c3d0bc74c781e30efd25cf2542639ac3acfabdea3f7af221ad28c797930d368076cbcd112c0a1bf198d0c081d7f7f216889bcb5b446d720fa7f1333fba0441216568b55b9e3a869c47afaabdeb282b65c774c98db3d00c00406276b12d86c44f8dd8970bd7dc738bbe1ad712a10137ed87968c0f0c8afc53ba3f9a070e700dcd22b47340d17c2c7b2f1cd2039c89334e722e25b7d619c8319a6727f4b68279c64538ce4c03318853f4e1944b8d81d8b8007aca927ffbc02e69a77c45d2632a7c0afcaa90f5e6f2736f58bc304dd1170f685e8b47fccaded0661255d6ab8a290e4a8c44dedcf3c9660a7d35f040f0d63e14bde335b7968d5ed6e984a892b0a6a8139d96eb54a8d1022e8db082e062e0c252c3e01d6403afab8aaf6bc6f24d8157ed6cecad155e259ad5b018db69ccc08c0f15811ea78917fb72ad9e9573c7645fcf59cfa531b078ac8fe5b51efb61d8c1343ec3d890f34d6095d6000f0be39e9d0dfeb532f2708168b75f28ceddb56aa4581da4d5aca0b39a456ea7b8e43e883c9d3b12d51c0826ccb69d1e77956afd2adfc115a1e44ad78e68c2b93b61d71e0ddd4cdaaa7fd3589a1b9d2a42df325724d181b7c96af26643d47603af1289ede8f54c634ddb4d82a5829000182e20381e802202f151b4c0c83a349d0df400c799410b60872287923ed67236c6fded247435711d82a5828000181e2039220200b139cb28169e7bf96a3e893f06114798319b67738e3f1d3e25c15251fc05809000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x31d48a6", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x2fcb2ae181befb6e530f111ad25c983e1d2e208d11caa73145c811266bdf34bf", - "transactionPosition": 98 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000194d9c", - "to": "0xff0000000000000000000000000000000018b644", - "gas": "0x420cf8b", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000078782197cae590780af74af52a5e7cfc11112a58658f3464000126bb0bdbe9cac881aab7373a5b01444a96ce7cac4da6cab7931ad1302d103ad1a909beafb5e6da3a12dc834107ad467b58db1d330b54187c5e76133ce0dfae5286e16dc77f21200b431311385ccae08f31082cb5fe315e2f03763339b2a8e7f5d4aa22394ab9b1bd2e5a2ee9f457878d7922cc963fb7c9df63dac57bd3bd1b4e474c042691e7691229c7832cd385e5dcf57d63f89f56b417d6a0454f6f7ebe10ddafec855583091e9f0a182fc8efeb873b773a6a2a5ec2f78424c1b274d629653ab7f501ca8e153b5bd133bfbd4875b708ab19a70dba7ade20d6c711e228a96a87a7b889c690cab7afe89b65bf75a1320383fe2575e886a9f2e540c73c683ebcf237a7e9249e1e79897026f5410211837ebfbd01106ada77feb2fddd1594668877f93c92007f819e2958174cfee14c3a9f19e0cb82184ca9a0bcbad69dc7fb54baed0248d1ab6523a03a483b8894ff90a82f720b05cda51ba3473d78070ef62dfcb4db8381b8bf00fce3b1e47c599b29c4e4f3d3537cf32cb0b414a43b05ecac3008bee1e41f7b0e434330b144ca8943d0eb355a21800b489c8747a8df65799ce85c5278b8af057ecaa0094ecc3b7125326e168bb9e3a6840305c138f04e8ac6c9833050e67d2f5b5bae0b82e704f0e478ad5e249af7c71e5f813f4e2762789e22db59bd998a600a96f12ebb4cccbbc0167250e5068db6097b417820193ffb61b14ed1decd0a4c4ce4aa22e0a4b4345b112cc70fbe58e39b55d8658d0293f2e0553fb9296203aab2a8e3921bf12e3abc25b8a68a82b8a32f780d643706c2fdd95aa83e0f80a1148ecdeee6d82f68b7d0970d26535d0f8a45ecbe83ff1736890aee1ca3b2c3c16c04b7a79be725e27a83d2a650c953c7618359ff8248c341387fe194553c7aa2d7c36b90427b78bb9011137de00fa755df244a478ef86182e1154bcb9704693d2a4b36bbef19817ca6fba18738f3ab605231ca40458590ce8a25215e86e4fded86922730312c0e1a9fce06819f308e197e58cc9df7505ef779f011945960debf96779a0d3b4853865953aac108b3d10cbece20950a0612147e65edc8a59aaf7c8ca518f9fdb452b316f4ebf3fd18664b692cf0195b2f66467b9b8c398e118c1e90d45be22969e12a226d9c51d1f5e437de700096f7bca6b5b52b72ea04b89828b0d5d2f2349b11e35150b53c844616524804b13ff6e50bcc2cee62a91bac6c97ef5601a4c6d26b70e3ae78c18b41560995ed358bf8bce8a40aa30218576eab0068c6f4d7a9f16524830fe8418d98fe8475a1ca81174b85c84e70944a47cb840aab45b22036cf0a428b3f42ae6f5b44a9f9cf8a8e160659c887db1eb574690f261328c2d7d61d77e2571823a0e071b541b25779deda2b99aee9712bb5f78d30b6924e67a466cf6e5cee885fd7dd1cbe54e19a0acce5a4ec125b4191402fec6f7cd9e6b74f693b6e1a11552aa1d0c74db96db59cecd8165540ed97de16ae68161ffd0004e46e94db01f6e210ba0befbcc545c75f26f63f6a5d4b4c3390118c4f9d250a816bb83b162ae29dfe7f43161f5383e6a6514a1dfe089af68a6050d8628af0ae8ed721ac2eaf797d0a8291139c8c4f0803ad4d1e1476d641f65ce498b78f28e779aaa3b509105e295b178789c0a88a002a698c32d6500900bc46da2152ba8b48e1adb116603a56ac12af02a647547f6bab816ce8b90909c28b88be764c8534344fea5b4a98d6d13427c0a1a8b3479fd6ab5fcd298c5bbbd53073ac3162a1d71573b03bed5fac16fa8180dbd516bdeb05f8194fba24014950634092426f4a3894967e29fa3911776d59c2c96fc592c73e90542ae654a087a5773045e35dabf4607f78cd01df0c2b14687a7f041dfb54ea9b2edc2e13c91df1d99fab8354d8ac2d7397c332c05573deb2482054e35f750e6b36c337abf0292a2b7dacc7ca94a1b8aae9f2d2355a79fd807e28fa6b7a05ce0bf4246422361a8f92597f47ac6d0536fd34a0e43ce101097d99bb0ca17255046025305e2847ed242c64e86e131b71de29848e8a731dbbf72fc21df1ef705064a4fec6e8dcc64a2e80de3013a9969563667fa312044c74bb6fe0b548f01a00434d80d3f16ee2f0023995c758050f2023e44cdc2d409b98ca630e47e0889e322adff03315a959e517b3ea1bec48e754c7507ffcb6aab665673033a950ea575006f67fe715a380f8c157d40b5a8cb1c9feca96555c1993672a737f13a375700ff5402e77440163eb33d751e7395011d6ec56bad25a7471a154199067a029e6a456b64728fb5551c75d578e6fa7d4c208a5d5121c8068ccbad10d37097f4eaa65ab1fe0aaaf4a246a938d6532465c9ee33568fc97829bc3e8ebe41006890cea32107e374d877f5145541d6c42c9565835f304b072dd680f7b28e0636c37c77c83cf94c7995ed2dd6e3de65145100e31530df58cf3c033e273d8dd958d04c24b0b4d025dddd658c91ac7d9a533d5cfdbd053b222df4fbfbef900da6a930ae232efa070958c210dcc655d513504248ed405924bc76e35ccdba1787732ff9f074899c01a79af782c9ad343869a7b3cde3812d6c51d453469a99f81b1eaca9e1eb1cf672dff039f9790cb72cfc70e23b1810a2ff1fad6f18e0bd7e45b7669f206444383f572c31555dc9aade67df6a23e7f458b47041d2185d4300000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x6c75d9", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x0e60536ae59f9e7056602af5758917ceb7b122029a30d36b81f755578da9b6f9", - "transactionPosition": 99 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff0000000000000000000000000000000018b644", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x3e5247f", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000082c8808821a0018b644197cae805820459182f6c450d64f4ce1b2cdd1f1b72010f8d595fd9303f5cea43b716af1082a58203982169b56bf07db8724ad2c00f8855d497d4eee8ca4ad3562991728ee37cfd7590780af74af52a5e7cfc11112a58658f3464000126bb0bdbe9cac881aab7373a5b01444a96ce7cac4da6cab7931ad1302d103ad1a909beafb5e6da3a12dc834107ad467b58db1d330b54187c5e76133ce0dfae5286e16dc77f21200b431311385ccae08f31082cb5fe315e2f03763339b2a8e7f5d4aa22394ab9b1bd2e5a2ee9f457878d7922cc963fb7c9df63dac57bd3bd1b4e474c042691e7691229c7832cd385e5dcf57d63f89f56b417d6a0454f6f7ebe10ddafec855583091e9f0a182fc8efeb873b773a6a2a5ec2f78424c1b274d629653ab7f501ca8e153b5bd133bfbd4875b708ab19a70dba7ade20d6c711e228a96a87a7b889c690cab7afe89b65bf75a1320383fe2575e886a9f2e540c73c683ebcf237a7e9249e1e79897026f5410211837ebfbd01106ada77feb2fddd1594668877f93c92007f819e2958174cfee14c3a9f19e0cb82184ca9a0bcbad69dc7fb54baed0248d1ab6523a03a483b8894ff90a82f720b05cda51ba3473d78070ef62dfcb4db8381b8bf00fce3b1e47c599b29c4e4f3d3537cf32cb0b414a43b05ecac3008bee1e41f7b0e434330b144ca8943d0eb355a21800b489c8747a8df65799ce85c5278b8af057ecaa0094ecc3b7125326e168bb9e3a6840305c138f04e8ac6c9833050e67d2f5b5bae0b82e704f0e478ad5e249af7c71e5f813f4e2762789e22db59bd998a600a96f12ebb4cccbbc0167250e5068db6097b417820193ffb61b14ed1decd0a4c4ce4aa22e0a4b4345b112cc70fbe58e39b55d8658d0293f2e0553fb9296203aab2a8e3921bf12e3abc25b8a68a82b8a32f780d643706c2fdd95aa83e0f80a1148ecdeee6d82f68b7d0970d26535d0f8a45ecbe83ff1736890aee1ca3b2c3c16c04b7a79be725e27a83d2a650c953c7618359ff8248c341387fe194553c7aa2d7c36b90427b78bb9011137de00fa755df244a478ef86182e1154bcb9704693d2a4b36bbef19817ca6fba18738f3ab605231ca40458590ce8a25215e86e4fded86922730312c0e1a9fce06819f308e197e58cc9df7505ef779f011945960debf96779a0d3b4853865953aac108b3d10cbece20950a0612147e65edc8a59aaf7c8ca518f9fdb452b316f4ebf3fd18664b692cf0195b2f66467b9b8c398e118c1e90d45be22969e12a226d9c51d1f5e437de700096f7bca6b5b52b72ea04b89828b0d5d2f2349b11e35150b53c844616524804b13ff6e50bcc2cee62a91bac6c97ef5601a4c6d26b70e3ae78c18b41560995ed358bf8bce8a40aa30218576eab0068c6f4d7a9f16524830fe8418d98fe8475a1ca81174b85c84e70944a47cb840aab45b22036cf0a428b3f42ae6f5b44a9f9cf8a8e160659c887db1eb574690f261328c2d7d61d77e2571823a0e071b541b25779deda2b99aee9712bb5f78d30b6924e67a466cf6e5cee885fd7dd1cbe54e19a0acce5a4ec125b4191402fec6f7cd9e6b74f693b6e1a11552aa1d0c74db96db59cecd8165540ed97de16ae68161ffd0004e46e94db01f6e210ba0befbcc545c75f26f63f6a5d4b4c3390118c4f9d250a816bb83b162ae29dfe7f43161f5383e6a6514a1dfe089af68a6050d8628af0ae8ed721ac2eaf797d0a8291139c8c4f0803ad4d1e1476d641f65ce498b78f28e779aaa3b509105e295b178789c0a88a002a698c32d6500900bc46da2152ba8b48e1adb116603a56ac12af02a647547f6bab816ce8b90909c28b88be764c8534344fea5b4a98d6d13427c0a1a8b3479fd6ab5fcd298c5bbbd53073ac3162a1d71573b03bed5fac16fa8180dbd516bdeb05f8194fba24014950634092426f4a3894967e29fa3911776d59c2c96fc592c73e90542ae654a087a5773045e35dabf4607f78cd01df0c2b14687a7f041dfb54ea9b2edc2e13c91df1d99fab8354d8ac2d7397c332c05573deb2482054e35f750e6b36c337abf0292a2b7dacc7ca94a1b8aae9f2d2355a79fd807e28fa6b7a05ce0bf4246422361a8f92597f47ac6d0536fd34a0e43ce101097d99bb0ca17255046025305e2847ed242c64e86e131b71de29848e8a731dbbf72fc21df1ef705064a4fec6e8dcc64a2e80de3013a9969563667fa312044c74bb6fe0b548f01a00434d80d3f16ee2f0023995c758050f2023e44cdc2d409b98ca630e47e0889e322adff03315a959e517b3ea1bec48e754c7507ffcb6aab665673033a950ea575006f67fe715a380f8c157d40b5a8cb1c9feca96555c1993672a737f13a375700ff5402e77440163eb33d751e7395011d6ec56bad25a7471a154199067a029e6a456b64728fb5551c75d578e6fa7d4c208a5d5121c8068ccbad10d37097f4eaa65ab1fe0aaaf4a246a938d6532465c9ee33568fc97829bc3e8ebe41006890cea32107e374d877f5145541d6c42c9565835f304b072dd680f7b28e0636c37c77c83cf94c7995ed2dd6e3de65145100e31530df58cf3c033e273d8dd958d04c24b0b4d025dddd658c91ac7d9a533d5cfdbd053b222df4fbfbef900da6a930ae232efa070958c210dcc655d513504248ed405924bc76e35ccdba1787732ff9f074899c01a79af782c9ad343869a7b3cde3812d6c51d453469a99f81b1eaca9e1eb1cf672dff039f9790cb72cfc70e23b1810a2ff1fad6f18e0bd7e45b7669f206444383f572c31555dc9aade67df6a23e7f458b47041d2185d43d82a5829000182e20381e802200b3169218eaabe0a254cadd20d2eac54c4e57c1839c602b8ed7a1fb2bcfa0e5fd82a5828000181e203922020077e5fde35c50a9303a55009e3498a4ebedff39c42b710b730d8ec7ac7afa63e0000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x31f607f", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x0e60536ae59f9e7056602af5758917ceb7b122029a30d36b81f755578da9b6f9", - "transactionPosition": 99 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002d44e1", - "to": "0xff00000000000000000000000000000000116ff5", - "gas": "0x501f15", - "value": "0xd015638ef2350000", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x12c553", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xdf41cbcc8c905708453c4636c5b0ebf709ca0464982158a996000a244a3dcd13", - "transactionPosition": 100 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000241537", - "to": "0xff000000000000000000000000000000002d44d5", - "gas": "0xbbacb", - "value": "0xce8869a46a946e435", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x12c03f", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xd0e4426b5949939e81e1aa223c77a6d1fa37d7687e32f319ad9dbfcf608128e6", - "transactionPosition": 101 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0x53bdfdea92f7a60aef82228926d02878018acb4e", - "to": "0x8460766edc62b525fc1fa4d628fc79229dc73031", - "gas": "0x6b64a5", - "value": "0x0", - "input": "0x5535dbf60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003b6261667962656965677273376934636a6e6a767072666a356d67653367747076767a6261796e3672657a6b6433666a36746a6a70797763707a68690000000000" - }, - "result": { - "gasUsed": "0x6143fc", - "output": "0x000000000000000000000000000000000000000000000000000000000000043e" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xf73074b893cce66d2f798bc0862e12501da242db7004d3bd4aab36d7e0fcbc91", - "transactionPosition": 102 - }, - { - "type": "call", - "subtraces": 1, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000172b9c", - "to": "0xff00000000000000000000000000000000172b21", - "gas": "0x5205362", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000789821a000a651a5907808fd515f991b4f6a33de58875255e78be7d80628951a6f23f375280c3ec01e5aac19d252b2f8ddbe279ddb5758c96dea3b5be4a3f34581d5eba0e152cf8e649c5c272759b0b268aaaf09336e5ef9a9d0eea3b4d6e346f16370709411194f1274f03d6160a838e2d85e954e0070d434f05033a791cd1e0c149df1dab881b11423d43851c40be685faca80d7cc78959beb3975f8c49164ca96c11a2ede913c5da8cf168bd6407080e794bbac843e44f84c57b987103da83c6a9761c4dc00dbfa072b64ba1914e5588853cc3975f3463023dc27f04d6294ccf24b512c371066f3dc102db9c89172683d405c605e81488a03184c0b2996e963bc2297813598f82fa0ebdf4a13e95284e8807ffbb81573b8e73d495d18a53b9a25e39e8ccf36a50078101f9cbb2c68ad953c9e98e99dea30d8f65b821286709adcd4b99754a696e4f782e8715ac7458dda55863e46c87c10365a36bad7db606b42f49dad977252ba8fc2b615a9a68b6e4c5db36af1959788c9c01f6c42e30aec6b9618d475723feee7985d8d8a0119154f3a0cc0b44cfa94acb31e8d34a1eb9db44d389f5e6b840c80997fa17428581ce07cebe0e5a72a57d8b8df76fe7162c5fcd22771f6f39e7f09c420c298016be276d70715ad584b7e27545258c0c082f9df2a642e3f27b85db410aa8cd1e46b323b673d27f93c0aa1508e387d86a474565af8540c5912201af661ad35a7ccf0b7557d3047e6c467afce9b99ef937a026f17c63b6c56cfa2c075555e687959e1ac7d4ef164401b2dab427c4b8925eb4961db305123f932d15517b91d87218e46cad09696eb1b70f82d588c8d52d920ed13bf990f469bbe15e95c04402738842f4690e0d6963eaca9a214e9539f274424752c6855cd172f134fee67c397179ebabc8c67d87ac3862e9cb38b609aadfc476aa45acefeeaf1dde8e1d12cf3be18a24904f5261c36912844abbc6f986be2c730731556cb83ba4fb0a2e1c1f61836b805b0b404ef458aaee2ec5901f50c87c245772d68a062988be6c0178910957cd03a46d9d60cb3c6522a03e84fa2c4e752bffdf0faa82a1fe17374287456fedd11dfd4ea22163d1b3e0d4bd7c84ee73f63f8f6511ecd3297591ceb9bb59a6a52ca4d9316fa852ca949b496e8bc3ababaa8256ce13c413ff4c863fac0abb195ed523fc7ac26750864b33dcbfeb53febbd388ce3cf2cc541d9be71a8f0aa7ea56e806597ca6d08a4b763a68a1d8be498a1bec737872fe8ef18db169071932627a8f9192a498d2cb6a73ac23b3b88a0b1d3bfa271ce50abbe7d1b60cc1b9c0525b7d39daa328b854390c1538a69b6e73168143378d540cf1f11236bf4896da263ea751fe44d06031f0d65d52c735cfe0cbe82809adc1e09691e75a14cfb7c6e8f120544525c0ead12a3471bcfe8bf9aa3555c13c12a07ae2aae1ddf970b9cf714cfad51ff458228e051e00f2b46e1ef86870b6085b8e5820aaac1543cf1348d1fe21bb39dbe6352b52641688b2c2c666c8eba27394edcfe1500633a623ec4e65157ce59cbba97b8dc75694f238b9d980e93e579cc5b3c11a357b5158cbe89dd04a61397cd88524ba05daa6fbdf592ccb92821b05a5a09cd214a6c11162b1b0b250d1556573222dad8504cdf1dfa81b83b4456ec6ed7424597f602d6aab1641fa1e109d1e5ed934780e8f95a5ca829a4877cc3ec49e7bd5abb9ca0ff0af71ef2dd252824457b87f9d5e54dcc5443d8de0490d16517332458d89e25582890016f96e20bbbd6d81b9846d168eed1b6bffa6dabad8df76e144afddb8b5bde341ce94654ee214f7dca3d8ea5a5b925cb3749a3350f700d62380b187aa7e0279fe528d3d752bb741ba9e973d00720302f3ad688c7d538d1d968691578e29ea1bad131aaf34192c96e9753768f80b2ff98bbcb4123ff4bb8023aaf6d8e548c0d834a4d351da4ad560be24ab0a9bc72119a9bf2a8d971a7bf5ebd463410c6eaeb7398b8d563354b54ee99274701b0efcae547875679f7fc9acd9695f569f2d88c51849ad90aaa9c92a5884ccfd8b70c4e95c881d759fb98e0a003b1ad27fa629745a708507d6dddfa415c4e882e960e319960601751a6b4b3dbf9d5c735c72a32d711de64eadaa44becb4230fbb737f5363b3b2cee9b64036872356012cb66ad34b06663639d792681d5aca663e80a103529c6a23e2a229f5ea3bb33651a023c0d8503d3c24feddc4182830fccd14176088ef4c363b154e4d2bb4323469831489e33496294cabc1f764ee81beb08d1995ad6fea8e474bdd2ae988b2e5dfc2508340b9db7a7ad87549061e3cb90a167f894d1a11642920b20afdd87278191e680b07a5c84cab915974e042ec97af0c0ec8eb728f9dc062a0e5b9eaf8bd9aef74e29b0a8ebd6a179cbceadfde17e47a397cc96e46f25f6e2634a1a8d3b9a17b5e36b8560f6c8f508f2ef9eeafb5497300cc7f8a459675f4136f042f92a05a75ba8e9c885d51202888170173b04568d187a18914c246f6359938928a1e799e02f0580fac0c7c0271270c47db574362902cbab47f4ef90f0fe2f25c3aefcbe255abd1915edec356e67835397832bd854d8b191f664e1e721960ee3e4857974a6762f13ae7528366986ad0eaab18e53da543e6eb480782f5bb0932a1a6347dbbdb5d939cf4eb26bbbc1fe6ca38bb130f0aab1fd10dfac48fc5e165a16465840c33ed8e70000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x9b4f05", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x0bd64342ca2f272d82c6a77786e961b5fb1983158a496af3aeffca02bd7fce6a", - "transactionPosition": 103 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff00000000000000000000000000000000172b21", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x4b6bf10", - "value": "0x0", - "input": "0x868e10c400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000008338808821a00172b211a000a651a811a045b2dc45820c84705285a78a4839d7dd9614c60acc16c72e827794f991eccaf716f0cff6ed95820fe634357a254919411f0fa829564b42326cc3ba9d7f50492d56d3a19b48234e85907808fd515f991b4f6a33de58875255e78be7d80628951a6f23f375280c3ec01e5aac19d252b2f8ddbe279ddb5758c96dea3b5be4a3f34581d5eba0e152cf8e649c5c272759b0b268aaaf09336e5ef9a9d0eea3b4d6e346f16370709411194f1274f03d6160a838e2d85e954e0070d434f05033a791cd1e0c149df1dab881b11423d43851c40be685faca80d7cc78959beb3975f8c49164ca96c11a2ede913c5da8cf168bd6407080e794bbac843e44f84c57b987103da83c6a9761c4dc00dbfa072b64ba1914e5588853cc3975f3463023dc27f04d6294ccf24b512c371066f3dc102db9c89172683d405c605e81488a03184c0b2996e963bc2297813598f82fa0ebdf4a13e95284e8807ffbb81573b8e73d495d18a53b9a25e39e8ccf36a50078101f9cbb2c68ad953c9e98e99dea30d8f65b821286709adcd4b99754a696e4f782e8715ac7458dda55863e46c87c10365a36bad7db606b42f49dad977252ba8fc2b615a9a68b6e4c5db36af1959788c9c01f6c42e30aec6b9618d475723feee7985d8d8a0119154f3a0cc0b44cfa94acb31e8d34a1eb9db44d389f5e6b840c80997fa17428581ce07cebe0e5a72a57d8b8df76fe7162c5fcd22771f6f39e7f09c420c298016be276d70715ad584b7e27545258c0c082f9df2a642e3f27b85db410aa8cd1e46b323b673d27f93c0aa1508e387d86a474565af8540c5912201af661ad35a7ccf0b7557d3047e6c467afce9b99ef937a026f17c63b6c56cfa2c075555e687959e1ac7d4ef164401b2dab427c4b8925eb4961db305123f932d15517b91d87218e46cad09696eb1b70f82d588c8d52d920ed13bf990f469bbe15e95c04402738842f4690e0d6963eaca9a214e9539f274424752c6855cd172f134fee67c397179ebabc8c67d87ac3862e9cb38b609aadfc476aa45acefeeaf1dde8e1d12cf3be18a24904f5261c36912844abbc6f986be2c730731556cb83ba4fb0a2e1c1f61836b805b0b404ef458aaee2ec5901f50c87c245772d68a062988be6c0178910957cd03a46d9d60cb3c6522a03e84fa2c4e752bffdf0faa82a1fe17374287456fedd11dfd4ea22163d1b3e0d4bd7c84ee73f63f8f6511ecd3297591ceb9bb59a6a52ca4d9316fa852ca949b496e8bc3ababaa8256ce13c413ff4c863fac0abb195ed523fc7ac26750864b33dcbfeb53febbd388ce3cf2cc541d9be71a8f0aa7ea56e806597ca6d08a4b763a68a1d8be498a1bec737872fe8ef18db169071932627a8f9192a498d2cb6a73ac23b3b88a0b1d3bfa271ce50abbe7d1b60cc1b9c0525b7d39daa328b854390c1538a69b6e73168143378d540cf1f11236bf4896da263ea751fe44d06031f0d65d52c735cfe0cbe82809adc1e09691e75a14cfb7c6e8f120544525c0ead12a3471bcfe8bf9aa3555c13c12a07ae2aae1ddf970b9cf714cfad51ff458228e051e00f2b46e1ef86870b6085b8e5820aaac1543cf1348d1fe21bb39dbe6352b52641688b2c2c666c8eba27394edcfe1500633a623ec4e65157ce59cbba97b8dc75694f238b9d980e93e579cc5b3c11a357b5158cbe89dd04a61397cd88524ba05daa6fbdf592ccb92821b05a5a09cd214a6c11162b1b0b250d1556573222dad8504cdf1dfa81b83b4456ec6ed7424597f602d6aab1641fa1e109d1e5ed934780e8f95a5ca829a4877cc3ec49e7bd5abb9ca0ff0af71ef2dd252824457b87f9d5e54dcc5443d8de0490d16517332458d89e25582890016f96e20bbbd6d81b9846d168eed1b6bffa6dabad8df76e144afddb8b5bde341ce94654ee214f7dca3d8ea5a5b925cb3749a3350f700d62380b187aa7e0279fe528d3d752bb741ba9e973d00720302f3ad688c7d538d1d968691578e29ea1bad131aaf34192c96e9753768f80b2ff98bbcb4123ff4bb8023aaf6d8e548c0d834a4d351da4ad560be24ab0a9bc72119a9bf2a8d971a7bf5ebd463410c6eaeb7398b8d563354b54ee99274701b0efcae547875679f7fc9acd9695f569f2d88c51849ad90aaa9c92a5884ccfd8b70c4e95c881d759fb98e0a003b1ad27fa629745a708507d6dddfa415c4e882e960e319960601751a6b4b3dbf9d5c735c72a32d711de64eadaa44becb4230fbb737f5363b3b2cee9b64036872356012cb66ad34b06663639d792681d5aca663e80a103529c6a23e2a229f5ea3bb33651a023c0d8503d3c24feddc4182830fccd14176088ef4c363b154e4d2bb4323469831489e33496294cabc1f764ee81beb08d1995ad6fea8e474bdd2ae988b2e5dfc2508340b9db7a7ad87549061e3cb90a167f894d1a11642920b20afdd87278191e680b07a5c84cab915974e042ec97af0c0ec8eb728f9dc062a0e5b9eaf8bd9aef74e29b0a8ebd6a179cbceadfde17e47a397cc96e46f25f6e2634a1a8d3b9a17b5e36b8560f6c8f508f2ef9eeafb5497300cc7f8a459675f4136f042f92a05a75ba8e9c885d51202888170173b04568d187a18914c246f6359938928a1e799e02f0580fac0c7c0271270c47db574362902cbab47f4ef90f0fe2f25c3aefcbe255abd1915edec356e67835397832bd854d8b191f664e1e721960ee3e4857974a6762f13ae7528366986ad0eaab18e53da543e6eb480782f5bb0932a1a6347dbbdb5d939cf4eb26bbbc1fe6ca38bb130f0aab1fd10dfac48fc5e165a16465840c33ed8e7d82a5829000182e20381e802209cefc42ae516244f0407001eccfd20d923218826471acf64a8a94d665ae51b2dd82a5828000181e20392202078d7f3d6cd9ba40272efa201f6dfb13dc5fd85d8cb3812bd5af6a23f0204c02e00000000000000000000000000" - }, - "result": { - "gasUsed": "0x32089f0", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0x0bd64342ca2f272d82c6a77786e961b5fb1983158a496af3aeffca02bd7fce6a", - "transactionPosition": 103 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002af885", - "to": "0xff000000000000000000000000000000002afa9a", - "gas": "0x6aa2041", - "value": "0x8abb7a55e7bee0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000072818187081a00019146d82a5829000182e20381e802202690c7f394488d63c61b511be885dd34412491e418f3022c5f2a70594a45da401a0037dcf0811a045adba51a004f7977d82a5828000181e2039220205fd60a468ccf211022611f407c8898cc32d838d4da91777a3eba380889390b3e0000000000000000000000000000" - }, - "result": { - "gasUsed": "0x4f77363", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xda7c7b60dc15d026b250310e1b2db33b40e00542386f18c560612ebae86ff301", - "transactionPosition": 104 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002afa9a", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x6976309", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xda7c7b60dc15d026b250310e1b2db33b40e00542386f18c560612ebae86ff301", - "transactionPosition": 104 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002afa9a", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x6869dc1", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xda7c7b60dc15d026b250310e1b2db33b40e00542386f18c560612ebae86ff301", - "transactionPosition": 104 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002afa9a", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x6748850", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a004f7977811a045adba50000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x487d76", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e2039220205fd60a468ccf211022611f407c8898cc32d838d4da91777a3eba380889390b3e000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xda7c7b60dc15d026b250310e1b2db33b40e00542386f18c560612ebae86ff301", - "transactionPosition": 104 - }, - { - "type": "call", - "subtraces": 3, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ade3c", - "to": "0xff000000000000000000000000000000002ade40", - "gas": "0x375dc86", - "value": "0x8abb7a55e7bee0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007081818708192040d82a5829000182e20381e80220359da1ae33a951238ad1c46d5a5a8af8728e9f3dfb8c420a3c6989e1d451dc351a0037e08c811a045b324e1a004f084ed82a5828000181e203922020d07f551de5c94f8a5e6772d7a22d34450b43bfda443f5a059e35ede55ec0593100000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x268464a", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xbf0919237db95ca6842af1209bd67f3c15587a92fb9f7f1343dd4e66c5828a06", - "transactionPosition": 105 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ade40", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x3631f77", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xbf0919237db95ca6842af1209bd67f3c15587a92fb9f7f1343dd4e66c5828a06", - "transactionPosition": 105 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ade40", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x3525a2f", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xbf0919237db95ca6842af1209bd67f3c15587a92fb9f7f1343dd4e66c5828a06", - "transactionPosition": 105 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 2 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ade40", - "to": "0xff00000000000000000000000000000000000005", - "gas": "0x34044be", - "value": "0x0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000f818183081a004f084e811a045b324e0000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x476423", - "output": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002e8181d82a5828000181e203922020d07f551de5c94f8a5e6772d7a22d34450b43bfda443f5a059e35ede55ec05931000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xbf0919237db95ca6842af1209bd67f3c15587a92fb9f7f1343dd4e66c5828a06", - "transactionPosition": 105 - }, - { - "type": "call", - "subtraces": 2, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce88b", - "to": "0xff000000000000000000000000000000002ce88f", - "gas": "0x1f6eaed", - "value": "0x8abb7a55e7bee0", - "input": "0x868e10c4000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000510000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004081818708190491d82a5829000182e20381e80220e679eb60d986e754a677a0b5219976140fff0f798521f46d166a7e597b7c20211a0037e09f801a004f8a90f6" - }, - "result": { - "gasUsed": "0x17c8c83", - "output": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xb9208ab347ea30b568c4f9ab4fe310eeacfa107e9e5d970b38517334e51b2b2e", - "transactionPosition": 106 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 0 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce88f", - "to": "0xff00000000000000000000000000000000000002", - "gas": "0x1e45019", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0xfabb0", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000418282581a000286387ff107ef952f3612eb2e2a6606418e51c0f2cceeda5f57011731c2a04034dd33df402838a815a0b69ec1184b8c104a0001c0dfc71cd077c4b900000000000000000000000000000000000000000000000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xb9208ab347ea30b568c4f9ab4fe310eeacfa107e9e5d970b38517334e51b2b2e", - "transactionPosition": 106 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [ - 1 - ], - "action": { - "callType": "call", - "from": "0xff000000000000000000000000000000002ce88f", - "to": "0xff00000000000000000000000000000000000004", - "gas": "0x1d38ad1", - "value": "0x0", - "input": "0x868e10c40000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "result": { - "gasUsed": "0x105fb2", - "output": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000578449007d2903b8000000004a000190f76c1adff180004c00907e2dd41a18e7c7a7f2bd82581a0001916cb98a2c3dfb67a389a588fb0e593f762dd6c9195851235601fba7e16707ee65746d4671e80aa2bb15bc7d6ebe3b000000000000000000" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xb9208ab347ea30b568c4f9ab4fe310eeacfa107e9e5d970b38517334e51b2b2e", - "transactionPosition": 106 - }, - { - "type": "call", - "subtraces": 0, - "traceAddress": [], - "action": { - "callType": "call", - "from": "0x4ecdc893beb09121e4f5cbba469d33f5ff618442", - "to": "0x8460766edc62b525fc1fa4d628fc79229dc73031", - "gas": "0x2aac9395", - "value": "0x0", - "input": "0x603119b1000000000000000000000000000000000000000000000000000000000000043d0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000002bc0000000000000000000000008908d75fa9b38aff2997fbb405ecea53ea376235000000000000000000000000066ce586cee61c33b6a7cb88b424f2597b03fdd200000000000000000000000054b19c8921b4b9b7bfc63a7c1267b8b361edcb37000000000000000000000000e5e0ff05a5cbc02fd0b386cfd626d3c537e49103000000000000000000000000ce53e6defbd1f0810bfcd691660ad311d0db24f7000000000000000000000000e6235c47b9aeb4309ad568945dfda530ba3ea4a6000000000000000000000000d93910f8dcbcbf07e86a0b3235d47f04b9b0a8a5000000000000000000000000280f1cee998b784d87f39047f5c757783a131bcc000000000000000000000000ea0fd4a321a9f1fc06e4d46a9e991ebe91302a35000000000000000000000000b0a258e65801e52ad9b709a06af61282194360790000000000000000000000008ad34724ea8c951cb0fefc1cb3b1184bc802f2ad000000000000000000000000d096dbb2a8eec6ae3f1baf9ce6e46ef4e1e0989800000000000000000000000018be4742d88664350e0333af9b493afa77b3b71c00000000000000000000000069eb6aab03510f6d90b206a5cc3e9f7b92349e390000000000000000000000008f3b852c5394ad9618c4fffc0db895e8570033740000000000000000000000004e89c37b3f7d9fdc01075f252c4c9c0e8e1db6e80000000000000000000000002379198a71e2a3cb94e78628a8ebaceee1c0c6480000000000000000000000007f2645e86dbf5fd6fcf613073bd795d0aa8546f5000000000000000000000000137536822037b7fb2e78eeb824af708e1aeeacb2000000000000000000000000aa60b7c819c4fe893e4f24a81ec91b4a22cb526c00000000000000000000000062ef4551567eada4d52c719f810bf27ba90136c800000000000000000000000043457a112c3065f05ac9fba437d184972aa12b03000000000000000000000000dad969443185072d2e1f657aed34c8d970b3bb370000000000000000000000004a64d6b9ef098ed25fcc47a537bb483e2963d0860000000000000000000000005700c1928f7e9cf7af2205de589bf12263db6530000000000000000000000000a31688aeca874dfcb51c26d4adef91bd18f09490000000000000000000000000a6de99edc8a11f761eaadd5b646d2cde410bfb290000000000000000000000000a4979051b9bfe1bc19be8f7793f7a7858292f3e0000000000000000000000001aba27436b6add4de90b81c813764c8a7ebf79210000000000000000000000009e36e3d36b8811be651a9bc7b6557dc76cf43d47000000000000000000000000d472d30a5f14cfe9e489e32b670c11f1b658d28a000000000000000000000000449121e6099b23e3064056106841ebcb5d9f82e30000000000000000000000009872ae4277420bb1cc0c543f63933c044b4aaccf00000000000000000000000098b622ceeac4f1b0118d1ad9dd8ffff809f2075e000000000000000000000000e5d713f13538289634ddfedbfa06fc9180d928960000000000000000000000008bf19b7bd21c6e1b1c562a5dc72b1222de467d9c000000000000000000000000e6badb95e881d2fbef09fbaf07fb5ba12dc6fa670000000000000000000000008dda03750aeaf62032b769ec0441268ebc716d9f000000000000000000000000a547820f60ce4e1133f69eafdd6ef8e2a0d2bb6200000000000000000000000041311a5cb9430a255e91306c0a2134a09c723f76000000000000000000000000d4ed562aefba750c3086b468f19e99848672c9160000000000000000000000005a45f28aeef32da96db5b6c435081a677bdfe938000000000000000000000000ccd5ac13bf889d8e87bc726403cd354fe5e0331b0000000000000000000000007bd4557c830d1b55877aefb30c7bd85dd833d938000000000000000000000000cb0a53f1e7f20a5b4d983ea043989f895daa5e07000000000000000000000000114238b63efd246c18dbabaf0992b5e6c60e5cb2000000000000000000000000be7217c9e2daa6b5401baa8e7e7a79cb4ea873fc0000000000000000000000000f9269499c33a7ba0e23082f058fc1d8c116ee640000000000000000000000006366606f86ae38cd817f3688d49efaf79909409c00000000000000000000000060697cc054ac2e09b82952783b943f98fe75054a0000000000000000000000005d48ebd30cb6f1b3c901764df6472266cebc44eb000000000000000000000000fc253c6c81a5f2b74b804914d53f823b1548f81300000000000000000000000025cc895a5a051cae6874ce6c8dde6637317605fc0000000000000000000000006cf7a2c61da635eca30c5935a400f078a59254a60000000000000000000000003bdb257836e15857a610aac284af3502fc2aca200000000000000000000000009e665e20d2a11a71c9c3a37891b9a0a92e0f01ad0000000000000000000000006e5afb67b5978b1f7e56358577c6bf93789fe572000000000000000000000000f64ec0c50cefa072ff9147dc5a7dc4d3d8a0e6a8000000000000000000000000671f941215ff9db2f38d49cf6836ab0e79afc2c0000000000000000000000000faac9ecb2638acadd9698ba42591a1ec743bddae0000000000000000000000001fe73dd41b686b586fb8f9df8b3660812f608bd3000000000000000000000000d7ce18c2d099c482af073156db2882e8db851ade000000000000000000000000a319ea952a8dba1442c82a4f4f600d3a45dc0e18000000000000000000000000320d66c180785e5c16eb2ad89edca7ca5eb0b0c1000000000000000000000000b160e2fb32b315f0680d0e24a802a4a4ce9598c9000000000000000000000000fc96efc7e05aaf90c3fd23a6bc9d8d4c37b915ef0000000000000000000000007648ab9829b0b50bb73737707c5326952cd7cd23000000000000000000000000aeec7e0100c6f7d0a21666dddfe135cb4fe82306000000000000000000000000162161f0688b0056fdee01153b49f72ed1bffa6c0000000000000000000000007c3847a5876f80296b8504466d28cb92d44957ce00000000000000000000000010f376cd97c8ac7971e256f423fd63be80ca02be0000000000000000000000006899f9339471fbcebf0b78d87404a4bf7f741d38000000000000000000000000d49654bc9eaea08bb21f95e6f1839e92b3fb3352000000000000000000000000dd228dd2a81ec9f32fa17eb82ad0426c644d74960000000000000000000000009b88d5a03487335a05071e1291f416fb9641b8a600000000000000000000000081f6e5e5a83af4a6a153554a76a6948a7ba89664000000000000000000000000d5280e81f1116febd726a8b77501db93c65b783b000000000000000000000000a786214c5d2a4d5ec024fa686134dd73251dcd8f00000000000000000000000078fd38673ea90fa7f68c3c89004b373f7473e9f6000000000000000000000000859bc6c9a987a086aad26c0a0f910ce5cf5daa710000000000000000000000009a1f203bb3280fece69bada8a1ff1c0ec8d27cf4000000000000000000000000f68d6242c590bcb5298ff28f3968db1ae0841a020000000000000000000000007935c0f5c4231ebff41ce526a50e039ea77ab120000000000000000000000000d12b882f7cd12919e9feba6bf62d43aae942197c000000000000000000000000239886f67d8ea66d918f36c56d36115cc497f3d0000000000000000000000000e7bd0aed16097e245afa588dc2bb03c0fbc6786e0000000000000000000000008d0c1b15756be79279c30d13a9590fc1bc8a88b7000000000000000000000000369eab5c688d93d4d5126d848dd9a6d979df51a2000000000000000000000000e919edecc18a570c521ca0964a06464bb4aad2430000000000000000000000002f6a1b9c81f67094d11242a0c33ce10637ee8f30000000000000000000000000b53f321c0fe1e5e60929071e250191425983341e000000000000000000000000813df108a698b94adddcd11783999f649192d7aa000000000000000000000000fcd36d472645cdef280d4b7c3993ec2a90d01cfa0000000000000000000000001885724fd53172e5d862455949f9fde38c80c9d2000000000000000000000000a15bbd0012dc46ef6d222346e6bc4d55010753cc00000000000000000000000012341357e848b29e926ab86f3e5acfd6af451bfd0000000000000000000000004873ca40c9dcbb5fcdb6e5aeef5375dffd8e539300000000000000000000000048ede89338c4ddbbefd4743b3cde9e5672e6b6d1000000000000000000000000d155e64d9bb676006b64998c05508324f4f9f9710000000000000000000000006b0931570e75e89bc9f5ead3b2863eacc0e3172e00000000000000000000000091c291f1b8a414a5049ab689f85195822a377e23000000000000000000000000cc072c2a5bdab10fed049446ce12377f9e033e0d000000000000000000000000198a584e24d3ada3fcbdec1d7b0038833314fd9500000000000000000000000008ffeef28946ef3dba23372d7898be39f16a2ba2000000000000000000000000caea702803dfde8941f87919ce233596800a043b0000000000000000000000006166dc613bc40506042daa7396932adae383bd3e000000000000000000000000a494748369b21af5c869fcf71fe2971c64d21256000000000000000000000000cf9e38d0d01e4eb0f26008c4f67847e702f172ce000000000000000000000000390a9d994552822c16b84aca0117c9ff3dd8a2fe0000000000000000000000008e3fab1bae9d560c14529e0843427f69c2c328b9000000000000000000000000ede338b3c6ba9103a9c903d94a62a9a95edfa93900000000000000000000000088c0d01274f0765ca12f9b594ac1bdb70db09a3a0000000000000000000000000b573e104878da66d49cf33f9bc0e522512438db00000000000000000000000052865c5a84d51760824820cafb5e266554efd5890000000000000000000000006e6b41de78051f7c9ab381bd2e0256ecbc8272a7000000000000000000000000599b04bb8266f01b0fdbf547e253c37210a149920000000000000000000000005c7a4e019866bc832c8eb38a1cb59690683cf2310000000000000000000000004d037ad3b8b55b0ec8b44b2432f9743bf3c3a88c0000000000000000000000003b1732a79f372bd5700ae1eaa3d6daba833f0f76000000000000000000000000118bda236a0532b4a965fc04eb0c8c1c7a0c109300000000000000000000000081011ce12f603d8777abfe4fb4019eb4961f6160000000000000000000000000ec25125cd1bb1d4a8869bc987d305e3f4838a56e0000000000000000000000006294b6595081279238e5d80fd067b874271babc30000000000000000000000002eea10652c5fad91bcc827dd9020cd76e164456d00000000000000000000000011530b782cab259b0a36d62cf6463549b733d4f50000000000000000000000004bdc0e598d89a97072706b31a893e6d73d8c4859000000000000000000000000405b791237c5038f302566f2ed0be69ea8cbeda800000000000000000000000029422f22e47b48594b5c51033035f7213e43f362000000000000000000000000ecdefbf081c278da64dd3fd1937e1a0268d760870000000000000000000000005b0cbe0273d74f22018cf1b37aeb9ec8e3a1d4d8000000000000000000000000ab1be877bf15d074854b89536ced1d9088b55d3c000000000000000000000000988f66a135e579b5cf873d86496215f37401c4620000000000000000000000008699232f37afe708c4388acd0399ddf915c2f110000000000000000000000000a9912b8affd2b4eaf15067218b948a29a54ed6df000000000000000000000000af0aecf4a4d7f87a367b8ddb69e306e5cb7077710000000000000000000000002698ab33b993eb72ab833cb6bbb648a349bda2d70000000000000000000000005cbe0f8ca58fa82692e902ff0eedc79aa25f6d3e00000000000000000000000038169754af0f75f1de60cb768d3af55c588f52960000000000000000000000009e596e717f205b1a6582d86a16b773516df62ad9000000000000000000000000c60927d5794df3b57926a283e5a9bb211d27ed16000000000000000000000000eec6bba315c715931cebaa87d80336b468cbfb0200000000000000000000000053485d58437895522058b3869a6202498d2513230000000000000000000000000e83a2d60778fac08aeb930f3c320801fe2ccca200000000000000000000000042728c70b8abf85a8108ccba50ead645b8105440000000000000000000000000f2abf91e6d6b44e2a213350aa95ae3feb9046f840000000000000000000000007ccb0c9d2fd0bb862265df11b4425f74b77a44120000000000000000000000006a5d831a5171344ad921a4402fad0b961e76e17d00000000000000000000000056e9194e18db791f5b25937c6e570f7ed7b15358000000000000000000000000c841203d9104b3d3f58d5dbabb93f9411c0eff54000000000000000000000000b462cdc9777759a70f1f3377744aac293f40ac58000000000000000000000000279816532ec96b16eda9bb36aed3ca911c7db29e0000000000000000000000001fc416d6ce0af7aa72a0e3d2d622cfca73581bdb000000000000000000000000e393e4d2302001a928572eed995e5c61b4ee6350000000000000000000000000ef2935317885ff5750b58caf90c2addafbda7efb000000000000000000000000254934859cb06d70e72ee28e1852a0f1e321430d0000000000000000000000004f73b09126c5bbf0ee674ae7ca3bbc21b531e0ef0000000000000000000000000a7e2bdf61df32a1907a3d9325c065a3d76ede190000000000000000000000006eefe64ddb2cf3d59e63dcb065154de1512db2c800000000000000000000000055b51c17fc82818b32c0ae1464a3fcb0534e4d5900000000000000000000000037b6c31b4f5f17ce9e67c2144e908185db4d7a040000000000000000000000006213a491ea8a3c34cdea62844b4b2e58270ef6c4000000000000000000000000db2b72ee0c7adf4f6fe85ccde475c67baa29f4d400000000000000000000000086eee8b8d175341ebe65bc7b579fd90be72d59100000000000000000000000000e9bdbd393697e495e3eba1bbdd0b8e3afba0f98000000000000000000000000dd4bed194cf26e3ff836b5c6735a21a1d4fa8aef000000000000000000000000d9434d0aef7f702c558108e8ded78ca4f18ed93e000000000000000000000000ad8826c9b289d4f60e03faa004aaea160353f09900000000000000000000000089b124570529d9f8c89d85b32362a430626dad90000000000000000000000000d53492c49f4fac94027597fa35bc0b7bf42858130000000000000000000000003109643e03cc8d0af5577819854e37f1c3aa944400000000000000000000000012f760c4c3ddab0f7594b76edd807be229dc488500000000000000000000000046a8b144591746de657b136ad79c5f274ef61c86000000000000000000000000c99c12c10832c7762eac23d89cedf48249537ccf0000000000000000000000000573371f08f8e2b70dca981b255f19389f02a0ef0000000000000000000000001254d674dfe65f3f46d8c5d208df0005762dd1f7000000000000000000000000dfb86b184f801327540b19ee64f0cfd17faf96350000000000000000000000000f4fe5a26349044865cb55fcbed6f42522d89be8000000000000000000000000654bd531b78d52ec2bb8b9ae3e4a6592786e1b6f000000000000000000000000a84f80e1d5f1bdf6a01523d4b6f4549b5b0511ae00000000000000000000000011466ded9018d521dd4014605c337a3d3bad09b4000000000000000000000000093d1543dcf93722e1b37d13ce2c2c7b32531b980000000000000000000000007b7be2e99b0bfc47c30519cd2a61bf7cea479f79000000000000000000000000fb559565d4a10c730c0678ef58b739ae7adb75b2000000000000000000000000c0ac795873db5a707e64c2142c663262e2cd8f460000000000000000000000006b99e9cb2c3424b370e42383938f06249222e2c200000000000000000000000039287b94404df817797eb887cb3a387e2a27928f00000000000000000000000060ed857098ed6047bb468a16aaf25dcf273c1c6c0000000000000000000000007dd0923c0cb4d5b331eba921f05af043b796e6b400000000000000000000000075b35cd84d485c768297f8f9afe4b16feadc9104000000000000000000000000015053de878fcefb7ad5db719d6bdde6412e0321000000000000000000000000a3e56c6df3bfadbfcd65d2c7c1046284ce6d6067000000000000000000000000dd0a1890b0f3213102a9f858ccc798b3aec6b94b00000000000000000000000076301ef56a524fe83b891dccefb31178790de3520000000000000000000000007fe88717c62f0ca72ee22e8641bf92c0894638d1000000000000000000000000bcb4ea5da37b9893ab58803306e7150cb83e9752000000000000000000000000fff2abee60d164a8cbfb244d10a2b18dce0713b2000000000000000000000000cea1f873d995ec94b373eb69db2af632311182250000000000000000000000006ad8fd1a1d1916e7aa6f3cabd9c3f478dfef649b00000000000000000000000039d9293694f2b3e9302019b51b4e491429aedcd900000000000000000000000041f4622644bc6cab32938a5bc27f4eafc873efd7000000000000000000000000364dfbbf3861d39aca8fcd6b57123611e98dffca00000000000000000000000092da7004d5cf237bdf4658c9ac19caae13676b22000000000000000000000000e847268ca3b533b00338bf53615fb4c38f70492400000000000000000000000007c58779155a925ed1f5ba2d610d6b02462283cc000000000000000000000000de7c9084a48266320bbd883b5e3e1fbc8ec41e28000000000000000000000000e28ae523d8bddf57e1ecc370cd87d8c16443cbbb000000000000000000000000edd0eb4f0e8bfb28c1ac997c8d1a05c3571eacec0000000000000000000000000779ab62a4587531a1508f1364c0176ecce0b8980000000000000000000000001eabf73ab1c362c2ee70a47b1e0cc8e45f301294000000000000000000000000477cf615451cda2857a049f693e7614f45e548180000000000000000000000009c93175043bf08bd37f8c83b9094602470ca8bfe00000000000000000000000061ce230157c791e12d021001621e11d9f6a4f552000000000000000000000000452283b7c46bba04305654502b0a5d0553680fbe0000000000000000000000006afab857230275fcad0c75114a2d2f7fc154e6c7000000000000000000000000c34e83eabe5e64e903a9aed1b76a8942a286730f00000000000000000000000007221f9fab99c7298e20f182771222dfe118109100000000000000000000000039e41a1c062cd4fbbfa1d7aefe828384daca508c000000000000000000000000622c611ede07222a30f070bf0865437515c21bf9000000000000000000000000db18785ea18f2d4936be51850f368cbf6a6b505700000000000000000000000057177e72844c7633cb14b2ad702b3f1cb7b8924e00000000000000000000000001447dec147917844cea959b914fdc48d16c652100000000000000000000000095eddce628a95cdcf037f84977786b4daa993cd80000000000000000000000002ab19c7edf2e73e58bf987253561e2d1fef34308000000000000000000000000cc313f29d7b55da624cf86728fc1558f353582500000000000000000000000002e061715d5db6b03003a6d5d3745854a8908b383000000000000000000000000705e48b7acc67dd54d90ceb3aba33d4953352e9f0000000000000000000000001bba8da0a4550bb23f0c94f660d8794fa03d27620000000000000000000000004afdc8c6c9a8b3b8661c242c0145c491bc2a283a000000000000000000000000d6850a4a2d9ab65ae27563c543eebf20749341e70000000000000000000000004c68e6e61f7ec16cf79268bfc678e4a97b487f3000000000000000000000000058125994f6f0eabcb9bf80632726ce9610fceac3000000000000000000000000a2e3e1bf252b9eb1437ea2b2f621ae88f5f028080000000000000000000000005fc2c4b5a8dad8794947d134bf0de97ecb220a170000000000000000000000002ce5644f2e470b664e0b87dea744e9ec5e448180000000000000000000000000902aeb78ffaf5e5002ac1646da4c976e3cd20edb0000000000000000000000009f4ed99a67143d9b6f33e7860897d5319b380867000000000000000000000000906c38909af9cd416973553105553b2d1c0b1b670000000000000000000000004a05a2a6083924d195e2d0d656fcd60cf8061d0b0000000000000000000000006035cdb4e3fb3c99b5409da5fe9faaf041bb3c68000000000000000000000000dd3f1c9ea6c073174941de8c10bb6c977e5ccf67000000000000000000000000bffd404ab45201de5e73b8a45c37d91fcf556d340000000000000000000000000d75b9ec54fd3ea0231d7a5f566ece388075306000000000000000000000000069bc8512208d70cde4773ea1796061d222ed4fc7000000000000000000000000fe58475f3d6bece4d8d1493574be8b2a5df72dc5000000000000000000000000b67cee1374515722284e03e432477dda8aa3de130000000000000000000000007e9957075c46708b11bd5cbfbd1b0fa4c9365e17000000000000000000000000864150f2da1e98a6cd6bea45d93630d26a3c9e95000000000000000000000000c60554f08a76a6fbbfb1a0649d461d442e9882df000000000000000000000000a00b4f0670d28ce92b9d317f0c69278646348d9e000000000000000000000000c6a33c5cf611816110e1acc59f6f6cc551119bc6000000000000000000000000cd489fdd0ef4d501c8bc4077b5dd04709077f0b2000000000000000000000000a0e855a4618c4c65985be4850dc8cd6b769c1855000000000000000000000000b7976f651df2ec32e7f061b6db02c3f329a0c8ce0000000000000000000000005f8a6eaed330fc1731762364a11e375e28a7aa820000000000000000000000006a7c6d615feec103835779ae1e61dc2080ab0028000000000000000000000000abb29ea8981e83c989fe7cb608abc4b1a19cb18100000000000000000000000073a2f14385c8d7b7c739a43bf91019893920f953000000000000000000000000b9f5726ae8ae704d1ae39e6635913d1db654446c000000000000000000000000079476d18a486f67cd177462ef9cd0a24fa3d783000000000000000000000000098fe625f917e1d8ae190c1b9cdb9e7d854a94040000000000000000000000006f214b804485cbeb9ac3458019b6312ad734ca0600000000000000000000000047e58e865eaa11214e3560b74a3565c51ee19c3b000000000000000000000000a27d25421f4a0caa73c0482b2888ceafea15896d0000000000000000000000009be0c14dd4d17092e66643b384f101ae41c366d6000000000000000000000000650bd8722b4f6d1e291411788421e21b9682dd43000000000000000000000000b119de3bdf476b726261fddb34f574a19111f1ce000000000000000000000000aff6acef00e48495324920c61dbe226e362aea5d0000000000000000000000000e880fa4f87bd1231349966576ea34999083ab0c00000000000000000000000082da0a99f731bd3cede407a5082dd5ad38a0fa660000000000000000000000008dd09ea0c1486bbce042ab63bd1f00575ec57c10000000000000000000000000ae9fd3be2ad3ab993e8b12f53acb3370d2cf85c30000000000000000000000003de5e8bfa47dc9f7067543196dbd51c8d3a03cda000000000000000000000000f77c1491ea35053886c32542779add9b1bd8ff4a000000000000000000000000d1cc923e28d5cb89e60752f6640f923bc4f8450a000000000000000000000000f606c34a4f67344c1aa9008c77d3d1097005d42c000000000000000000000000d68aea6028172cfd417bbffb1065e2baf832bf5a0000000000000000000000006e763631e535de674135c4f64e78110cd3fc891a000000000000000000000000848823f7dafe1bb36b7e64d188488b70aa4f6c6b0000000000000000000000007dcb17cfc40f9236b08c08adb262b4099a3477e3000000000000000000000000c987478064f7a6339f7a0c63e11053b85b10c88d0000000000000000000000002c69f0b0b32e997ca9fb1adb9b122383c01bc96600000000000000000000000079a87ba6fac6983213b6c1421593a7d64e2fa0be000000000000000000000000497a1f6d9b13905db63f2b9f16fb7cd22aea406a0000000000000000000000007f8139c8269c917085537ad68e3600ddd9eb89c90000000000000000000000007d094037d0f0d5a5ebf67258cfe783ecf57a74040000000000000000000000008ad198abd937666cb3376a20ab6ca257f96f4e2d000000000000000000000000c9549d267d39e494770297a589a5a0bf3171d1690000000000000000000000005d6edea6017e54154e5aed9841e4623bc8877c04000000000000000000000000008880fefb10a5999473943edae788a6d54e4bc900000000000000000000000037668b5e0fc9bf6a45e367d6030b6609bbc32b61000000000000000000000000ae6ca04ed7eebdd5dd256e595072c54c304149d900000000000000000000000008d347e43e361c42a31daeb18ef2cc0d190d6bf10000000000000000000000007f50d5d38ea40b4e331d32aa85dd74bf7ad7e5110000000000000000000000004846c8dde5747a69960ed0dd8538c1b7ba393e940000000000000000000000000df2b635075fa358eed7c126baff1c9b862d3bbb0000000000000000000000001deb1e3a9a33e99886c7b3a91e34f2ec0d9b52c9000000000000000000000000ae4f25c994b518b4a0fd37f666e6f8f77daf16d20000000000000000000000000c2c5314dcd0a7bc928ab028c8ab384f9ac26ab7000000000000000000000000e1d0cfb183ca26466b81bdfcfb1d6775e3f4bb370000000000000000000000005b3c938e6f0303047b57c7d8c13e37783f795ba20000000000000000000000007090e22b5f2d4215a635beb85441b6128d011c76000000000000000000000000a1a93a8431d224f5f99a741d26433d90a4cd069a000000000000000000000000739dee60308f5f7a05be524b0785b36c95153402000000000000000000000000745ca64c4b7519399505ad7ea9ffce90f9cf3d3300000000000000000000000098c9c491b129a73fbc67fcc6afe4b32803bb34ba00000000000000000000000072d721c88690147421092956b0e58bab0bdc3034000000000000000000000000584c6e21412c7a6567c357571204acc2bc57430400000000000000000000000082735888d1c4043bfa804ffff5eda0b0e52bb4b5000000000000000000000000f9b84befdfcd0882aeeaea24f850430ef8666aac000000000000000000000000eb75d14fbdf23f1e79033e99706de359e4316dab00000000000000000000000035c443afd895ffb2b769a3416191a9ea500f946a00000000000000000000000081cd0d8c36d9f43073a6beea7bc2a143ec66885d0000000000000000000000002fe1bb61cc67cf64cce87fbf835b836f6a3c04e0000000000000000000000000c228d0a9137cc5e35bb9770926d138a19d1f73470000000000000000000000007816aae5c3a50999deb2c4467aaa4a1fe34507e4000000000000000000000000c6aac9efe2f7d96bbc1bcfed1612318daf302c69000000000000000000000000226bf460f545730b0cda9cf96c0393a55cf714fc00000000000000000000000017d0827fd96765a7af1018cd93006ccf1fabc5c60000000000000000000000005fd9239392ed3759eb48691b58ca41cea79b831100000000000000000000000011c337ecb620bcbaa3b8b9aaeaf397bebf93eb8e000000000000000000000000f37c8510bf7e21c556e13b625b32e1ab3b790d6700000000000000000000000051761896987eefacf1be63e919d26cbaaff33a300000000000000000000000008d0245ffddb94e6b0012e7a7c44728140aeb1503000000000000000000000000ccf9a84d7eb7eb332377a4ade4bc18448ac47eae000000000000000000000000400acd777f435768d5714d59544dc212cc750b47000000000000000000000000ab67ab90b3e8d8d1d31cde0d69097006819122d20000000000000000000000006261c7f5628c552e245db265b45d878a416b059400000000000000000000000002b7439d09fb3cb7e4c9d4124c06feff55c09a1b0000000000000000000000005cf6052840ecea31369e7bf426145ac2aa59cab6000000000000000000000000e14e36961222c06258eb01e9cd870905d24453e9000000000000000000000000ae1a546aa603395dd8ad9cbddd1c10ad36db904a000000000000000000000000c1b369f561a27830559bbc770ba4fbf96c255eca00000000000000000000000024b7c11f7e06ec108f2b924fc90a49120431fd0b00000000000000000000000071f5a573e4e06e590757326e6a63b02706d319bc000000000000000000000000c4df171fa405df3fdabb136143055cc04ebe97140000000000000000000000002676198d802660bb4121db3afcf1e11540f5d6f9000000000000000000000000244b8ca6996c29bef8ff7d83c6faf4efe9387d6400000000000000000000000094c15b3e5516ec5bb616d17a5833cab29c7d63f20000000000000000000000004b81de6df8bad151f2593c3ffa97c93879e8d9ef000000000000000000000000d32ca710b42fbd87f835b9b595dd1538b605f2f8000000000000000000000000edf1f554a63311572f8683dcc8a09b872d926928000000000000000000000000179f95c078bfb5cc303f54d08da04696833077f2000000000000000000000000d819e937426a84be689a63dcd4adfb2941ff22100000000000000000000000008c54c93ee4b0e630513f195368b615b9d6e236c1000000000000000000000000a758969c692085ef88173c392f2954270b9827bd0000000000000000000000007f1973e5f493ade52b98575bb513c2ea8bc52d93000000000000000000000000522695d7e879c5650759412a73745c7dd76cbaa7000000000000000000000000dde758ca495cbddce69c6fda4acdc855af11d4a30000000000000000000000003d7ff7c5c8bdcc4b0d3e746e20e44024762546ba00000000000000000000000054c58a2db5b2a58dbba43dd1e939daec6445872f00000000000000000000000058331c199e67a9e991e2061cbff70bcb15d65a49000000000000000000000000fc7ce4b74e8b1b5835caee6d34a6072ccfb36d8d0000000000000000000000004bb9a03990ffacea5d23e722091bc5d2718d1cbd000000000000000000000000f46b13d14a9d74ace7557fa069c7d63733de5fc5000000000000000000000000d2bac8ebb2f6f5a92cd13c0b5477f916d0d29bef000000000000000000000000bd1a4ed3b262ac6432602b7a18ca65ca29a364700000000000000000000000009e9e83275d92d3dca640b2f8c14d08e389a75f9d00000000000000000000000014cb0351377e3b206e63dbc435d3f185b958e0dd000000000000000000000000ec820902c78106daf869d2bd1a5ddb7419ef40cf0000000000000000000000000fa9dfd93a1a51c8eb7dcf025730d1ac02f83fa60000000000000000000000001cf0088c14a5a4571a41507736815b00c5e532980000000000000000000000006c1d498debc51fb25b6d06492cfb057e74ee6be00000000000000000000000009d7ec8b52818eeebb6b893b2f09d781c024e0425000000000000000000000000dfb671a4f29e9a7747e5a9bfdede8f9fb81982ce000000000000000000000000d865cab8d312053cb5dc3a8e2b47450a1f9f25ce0000000000000000000000005c6ef10648213c486570c17c76cf0922f9edca4700000000000000000000000088d3a16433ae3833adbc0ac1d3fb30e69cd3bdf7000000000000000000000000615a1d028784a273ab0f465795d793398b3887c600000000000000000000000091f6add8c6992bc3efadd528c20ace3ea9e2218e0000000000000000000000007f3f80ca889de690aece0db872e4c39f52ee0f170000000000000000000000001481f4f9cadbadf25f873631afb6ae22d25503a4000000000000000000000000d5c0356c800e7191a5af4cac2b7ae9c44f07b049000000000000000000000000d778ea38e4ccf3511a0f59c9806f37c7bda92cff000000000000000000000000ced5a895b7970f020819eddde9b0616d2ae053d700000000000000000000000034470cfaf0fbccb5935a6bed3baa686989b04eed000000000000000000000000c936019d1847acc03d01362ff05b19c521be154d00000000000000000000000016157022e231bd391cbe55711d492faa7021b805000000000000000000000000e91f81b0057862d8c5ca642a05db00ddfd6cbd02000000000000000000000000942f6da946cf77fb53170e8e416e595f5e3eb953000000000000000000000000fbc8688736727304d06252134cbba29ba95d1a6d0000000000000000000000006580ddac5674726ac60dba4470024615037df0350000000000000000000000002f7e4cf14adf900672408a7d21070ac34363df0d00000000000000000000000074f6e058315fba3ebf55d33cc6505ab5ab28cc4f000000000000000000000000f21eaeedfb2d605ba091bd08e7c30b23450dea2d000000000000000000000000642df68305263e02627260098c6e023ae29bdc75000000000000000000000000c3037ef8ec35c709f90f2981f322416c8d0fb8830000000000000000000000000d5a5df0f56b7c0c2d77e15a4b066034c30ba6690000000000000000000000008ce354651788679cb1385171412443fd1b51c16f000000000000000000000000d89f429469eaf8e36014149f748ada5404c584e300000000000000000000000019dee827123e28196c6785ab13a8d15cb6721ada00000000000000000000000009cc1b107188bd4b2db85ff1a9731ffdf2735cad0000000000000000000000008d4535ef821c773a905c7bb5aabad9865a4fe9a20000000000000000000000004399555e2fe753f7ae3ca5bf1a6735219733303a00000000000000000000000027af5c6b3b5606ad2d495c90738ed36c2acbb26b0000000000000000000000008e21dc17387e56705289d8fde6aedd71570b0598000000000000000000000000b884787b220a857af3f7d87da295cef35a60ab900000000000000000000000005a33449e1068622d4b9189a10e6b06f282668ed4000000000000000000000000dc69eec0c0c5135d11375bea03b76ca939566a06000000000000000000000000cb3eb3331d3a3021dccc0695f7f5bb356b38ccc80000000000000000000000000a489b5fae86079803fd840dec2cd6a8c9fcbad20000000000000000000000000af8d0491d94389ea61e5ef79f3924581e2e82c1000000000000000000000000db2fa4f76fd1cbb08b52e57387f2c73c8cc067f9000000000000000000000000a152e9b26df4eddd79115ef846bc74a1f2f853c70000000000000000000000003050eb14394f11e8cb74a1af6db83db05fabb12b000000000000000000000000264aa32dad1d8f0388b787eef55e8d5dff7d9d1a000000000000000000000000e599f271f0094c3fd5532648b7436eb68d4025ac00000000000000000000000033b9c695c88df21067855fcbcad5f7ee20d0b9820000000000000000000000008e27a7d508f292d709d7099662f42492bc8e599c0000000000000000000000006b27994021433c75b5115feca4fc31c9e1c3df21000000000000000000000000d299fa49a47faae8f7d1b69f0fb2588687eb4e5200000000000000000000000065efa2d869cffbe5558e4bb789041e32e75f785a00000000000000000000000044bfb7765841a406674f42cadcbf2bbfe69117bc000000000000000000000000c11c272f7c5f127dd911dcc3bfbe62872df77d8d000000000000000000000000f74662dc19c3d6480627e7e7542d28b30a3a1531000000000000000000000000743b0c678f819573d421c8d8f017281d11bf5056000000000000000000000000b8d86887bb379db33a17c11a7ff9cffb16e35131000000000000000000000000c6e678f796591bb22b58571f5db16fcf5a8bff3c000000000000000000000000ac531284c469b20ad3996dc04ceacbff3b4ca2f600000000000000000000000049cc90f95fb3795ada93bf86eb7a55ed93f347270000000000000000000000002cb98f1b05213be609d4f8c9099e5265760c2ab3000000000000000000000000966eb41f9215f74a76f15e101138eff62777231200000000000000000000000077daa2a89281c0936420f7ede2f0e68233501a54000000000000000000000000e05546e3eff21f2fb0fbec055ef6cba23f77304500000000000000000000000052b9d6664da609082e51f0e7a45a7a945de629710000000000000000000000009a6bed0f7f31ea12075593e8c3f7b3d7c16c6f8a0000000000000000000000005fd468903fad6436b61265101e10022292b0f3c0000000000000000000000000b07568a6853d97020a69f48c122c84d2aec76d92000000000000000000000000d0585568d1bfb1f626cf335c6c3e6cdcd84754f700000000000000000000000065cb1900ed9f1f7de66776ec75bd12bf7ab0163b0000000000000000000000008f7c8aa90ebdc51484a16e036b0797667c861912000000000000000000000000f6a6c660c0858007af06dff44050106e49227f57000000000000000000000000cb4e589c58b636291b73f36428a2901f9a901ced00000000000000000000000012f6c398833cbea0b56434d814ac107fba0c9bd8000000000000000000000000eccfc303f108e382b192cc9aab70e30a7aa7810f0000000000000000000000007be2d5150d7d7efdd0ffa8a957dec8895f8d1abb0000000000000000000000007c172e273d6feb01c041211140f39ea8be27b72500000000000000000000000054514197caaec3db94451f432c5092c3783763870000000000000000000000002516ffe75fdc84bf027ee14352839acf63cb041f000000000000000000000000c6508db99f4db213dd71c34784ec0505383789ce000000000000000000000000f8dfa50903d65fcc03da6348bf8fa88483965b160000000000000000000000002499a068f468f8896f2b7021cb04092c2476f56300000000000000000000000061ec0aef14f58a89df9749520a171eee30b4feb6000000000000000000000000b57935e3997173d8d2d471fbf0c5c8f9ae02b0c90000000000000000000000005e64578c4cf7abccb32a264cdaf0c3261975d94e000000000000000000000000c4bb855893df113f1f3616c942e683a440df52cc000000000000000000000000daf5025ec0844c9dadad3b8b3c4cad41b97d6596000000000000000000000000160339dc12dce4971ee95c545931c12bbf6c5287000000000000000000000000b59a8042ceb28de94b49601979d7910172156f03000000000000000000000000e8889b3afa1d2a37996cf3ac56c1e633b4f50dac0000000000000000000000004f001b31c83f6891c9ddd41dcfccee9986d2460c000000000000000000000000449362efaf626063228251f44f61770ed316ad5500000000000000000000000005da93afc48b874ccf93d3646b6a49cd4d0b321100000000000000000000000088b3adba67e1e3b16b11483fc630bb3f81c73b6d0000000000000000000000004d502f02b8da038aed30ab1b8524b6320c84ba1b000000000000000000000000c8d69f95a6e93981bb772f63cf4d61d7a905f9a1000000000000000000000000d4ec18973cf3af5779886907124e55085e91d6420000000000000000000000000de5344d8be36b90e7ca73301102981d3b2fda250000000000000000000000001174c8d3fe1ce6fa955c228760300e91500e4da1000000000000000000000000eead4f379456506a2451852ef1ddf1d8c563cbd7000000000000000000000000707cea598b987ec33371490b4675f3af9eb2b1dd0000000000000000000000000300248c6045adae61585f2e454da9c77b25b46c000000000000000000000000d1319e17a1bf454afdf55335a0dcceb6d72607f9000000000000000000000000f12d4e9585d3f11c9fdecd660a00717bd7e296de000000000000000000000000ef38a797c135876314189704f07725a785d062b8000000000000000000000000a5ded0412981543480a26de10d9575e680dcd7ae0000000000000000000000000be8c925e6d7bbe0ab308e77933bb04e05571082000000000000000000000000e007ee43f5effe8107efc3c5a80bdedcb5959c380000000000000000000000003f108f6b29f941943bd3c187b7dcc97337f0e28300000000000000000000000088efbadfe921329dfc18ae698a56e1917c62764700000000000000000000000032c14c8b0df2ade0d4deaefa67340bbe4b312a910000000000000000000000007058084b8ed1d2934b260b546aecea79ff3ddec5000000000000000000000000aadeaeae853d88b675cd126a02407a55190147980000000000000000000000003b5a09804421c0cc73ade78219e8009f611633c00000000000000000000000002b3241338e81947959246847e199ef164bebfcbf0000000000000000000000007226122e864d80295771965d3df6760ed8dc894c0000000000000000000000004153b278035d95cde210ee624444aaffdba85a4e000000000000000000000000a2ee16c8d256ff2b3390eb20e6481ae43d7a8081000000000000000000000000b8a41cfb33ec38012c542d28a2d2adf50c0335eb0000000000000000000000001e86ae93a09895ffdf65dbc30044a97266fa12c400000000000000000000000075f0c86c268ba51aff3a78cb8696ee350ba8bfde00000000000000000000000015e1c04e9c5ca82e345e9fe549d0e959f15695c70000000000000000000000006201f32c4a95c3b9c64248e76b8fceeb856458640000000000000000000000005b65b896918ebd74f843d0dcfd1e7bf658a9ccd0000000000000000000000000fb95a069bca28020ee26ee9249b8c8c3e39de19e0000000000000000000000002826e0a4cfe5fcec5279aec12c4c928d545f8e3b00000000000000000000000047ff9f6d8b60d7bf7b8b162fd15ff7466c030639000000000000000000000000347691621e017447144fdec87cdcc11a18e0c103000000000000000000000000c941fc5ef8613f0a6fc1928a1310f9f189b358830000000000000000000000004ee081fd9a51c1049164a047b0392b6eacf653bc0000000000000000000000008528520487cc55be4a0c1107245e64cc8c71ca930000000000000000000000000ee876b94c9b78f47a52ba13faf1717a0366d5f500000000000000000000000029bebb64d4ef42be9b84fdd220a3249d3af8af9a000000000000000000000000a3ed2f67fffe1bfd47acf19b2e87b642b6d9372500000000000000000000000057966d342f21a43159652e32ddf6daf55a21fbe20000000000000000000000002fe2d27315d598298bf2450da927ea0f5a8d280300000000000000000000000061364d8034eca6a67382873d6df7a3e2272fa95b0000000000000000000000007739d16c64bcd859b17a43086a037fa56f4c0c1500000000000000000000000032213e3cb433aa8387c8bf5352c8b6fb9a4721ae000000000000000000000000b7f4ed0af44cf6b136cae29d6438832ed51f26b600000000000000000000000068c9a24fc41881daf1a82c1d59048270cc510dac000000000000000000000000c61c871aca1ee6d97221f38192ce7a1e04716eed00000000000000000000000029a7344119c4fd3e582b5e1454a1448a35ef243a0000000000000000000000000b56d716e765c454f3065d7686495eef408371220000000000000000000000002e0f50ea52213cebc28706776a07a97f4796d88d000000000000000000000000bf2c8d2f98a4dbc05f51f475d81c48b3efb7dcba0000000000000000000000004d6954860a5291a60d64ac9f2c224b742dee40730000000000000000000000001ff00121e9f6f4839b08a6efe6348f0bcc9ecd5c0000000000000000000000009029c8a11e310f1abbcfcfdac4dae7fa3cf0f56b000000000000000000000000aa3cfe869ddb8c505ea32e7856aba47c511eef660000000000000000000000002725f5d4ff99d5cc8f20211dabb832c5fbd4f2d8000000000000000000000000a6c7ec7b289fdece521dbaafa274038bb95c5fcf000000000000000000000000e522124f4c57f0976ca00a7ab9125ce8ab6d1d09000000000000000000000000fa39b9dfc6f613217126c34206586d4bc6af3852000000000000000000000000ea2c0ae279e26079926886770b5710a95dc9d86c000000000000000000000000a20d921cec2a6c56d1ecc32e548aa713e2550fe30000000000000000000000006b090ee37ead372281fbedb5d8636796c6c89d35000000000000000000000000a2f266a1cc103b8ac1dcf564c5fae262c9fc8364000000000000000000000000e82fe8fd7beaecbaf02f0879f9cc7fb592e3d0c30000000000000000000000002e13ab492cc3001436ad48be416023ecaf0072c00000000000000000000000006c2e89dd9c3539f3c87f2e31244b664b92c85883000000000000000000000000aab8b753b05c90ec704e9dbe79a9e366f1a4775c000000000000000000000000eaf958a6d7bddfc4b1b5fb0b880b2a998df146850000000000000000000000009ef206fa56f673b57260654183398d1c7e210e2a000000000000000000000000cc9973acb08ee36ce1dd57eb94263449f722763000000000000000000000000019758c4247a22882fcc03f1c764c69774d56a610000000000000000000000000f177b67775e4b8b8b723d94a1e4b300c29c676d0000000000000000000000000be1558074111404da810fd48654941d522ee845d000000000000000000000000ba2d13ae3212781520f11d147fea508d8cf44056000000000000000000000000a5e09eba16c94ec69c24c2785dd7065289146cca00000000000000000000000080e88bb37de082057d587600a7efc1561d86d191000000000000000000000000be378a8d91a14e0cdaa61796509a75ee8255bfce0000000000000000000000005170e50ff672a6431293ce9c693b0ea7d1d2990d000000000000000000000000c98f8e16d9a40477c4053f887b828f1231cdab7a000000000000000000000000f2e67535aa6b4fafbd6e39ab65742542bd3d6780000000000000000000000000a6ef8ad6c76bb33b63f9619953b3584d30654c49000000000000000000000000a66c6cfb6b745a30ce0a18f9cca662a1a78d78d0000000000000000000000000ec287f89381b8fef25858e2854d3c6dfc2ee7ea1000000000000000000000000f50fee3a8196e49bfb6501e86411936ecb03e952000000000000000000000000644f41f35e07a3699df4366808ac08ca1037863b000000000000000000000000c5f5c91c036d2379f2e3ace8a3310246addd7554000000000000000000000000f068395f1adbe0d6f3b3e6c20d550b7a568be369000000000000000000000000018ad443331c1bc8af419e1ab8813e37dbf07b3e000000000000000000000000ca88eb1690dede759e2cbc0261a2601eb49352c9000000000000000000000000dceae123189d87008c87447fdd9466de64674baf000000000000000000000000ae880cbbc6739a153c5fc61aa2dcab963da2656b000000000000000000000000ab717ecbf05da67b55fda9fa23c330fb4afc93a2000000000000000000000000476ee4afa9112842c5f6d7014b6eb1c08f2f339100000000000000000000000003347f84d1a562fd978e05a0f6a37760b271d340000000000000000000000000b802553dc8afc397294c74af9ee04683bc1ae72d00000000000000000000000042d2ae06e68d447eaa5d5978b3ea8e66bf27ab53000000000000000000000000041609c83a2c8e1062f71ce15b4a1f2fff42ffba0000000000000000000000005e9d7face1d1369ac9069cea31336061992b267e000000000000000000000000088d52971c9802823e538a898847e5040c9c76190000000000000000000000008b27f703c1188aeb17d9f7f7d1f99ea7a26f8fb100000000000000000000000014cca55297ebf43fba0ab23613850a0c4d15376b000000000000000000000000c0f9cb7e611f6b9822654909bf6abbef0ccc255b000000000000000000000000d7a2dc21b878757a0cfc915f60a465fa595993f7000000000000000000000000a942d95aa06eb8dfaaf2e7d06f85f026e307810f00000000000000000000000021a415f52a87c12cb3163db935ea929d0843b48e0000000000000000000000000cf5deb1e629b8a0f33685babca4157b62a2336500000000000000000000000076befa597d53035ee0d43eab11e129f198a3b09400000000000000000000000000385aa52a301c030b166d8981c6917aa57a70020000000000000000000000006b668cb92c124dc26a9b4ba9fda27c0a8a60f8f60000000000000000000000009ae1d82fbb009e7bb2afe2547e4b3b6fe45342b3000000000000000000000000435bf4a1c73a382b92816c344ca2ac5e7e9975d200000000000000000000000033a4aa3cc0435ac3d247b62e03797c0a31d3229f0000000000000000000000002638db06e731c4b55afce63e1d933189acb431c70000000000000000000000007a947ac4e196397d91427d245c43e8f99a17c2ea000000000000000000000000b4fa647a23af5dfcc424475de99958063cb0e148000000000000000000000000e766bf1dc39ec15ca80267900d17c1442b544efc000000000000000000000000a860eefe8a03662c7348bb4529781dd21406244100000000000000000000000063cd05475910473ffccd41e729e4068b45dc8d510000000000000000000000002995f0a68fc4f51048f307bf081b8c09a0e4e99700000000000000000000000011222a31be36a6d853fb852a9a44ddb241290b08000000000000000000000000f58db63ae989b13a7ea02743417a1b58defd66740000000000000000000000001ab5971b8d837f4eb082e2c7d262292b9d82e1ff000000000000000000000000479cf15609cc892ecbeab83499a86c14c99c3bab000000000000000000000000ca90af695f8c87dbb080e0a73a6e1b97413a7b59000000000000000000000000329f234c378cd3e236fabb733f193d4008a5cd6d000000000000000000000000f260d61ed3c180539f921048983b3832a69d811a000000000000000000000000149d2fcc8eda76a098966622fc24f36cd74d538700000000000000000000000076cfffa7139903ba7233409eb2cd22c23d4c19fb000000000000000000000000ec65d4e35ca9f9ecaef3a2ffd64df1a0ac9d5be80000000000000000000000000f2556cabed056026f30fd55ee4b4e7c0c2f68b40000000000000000000000002cc1e26bbf1a53695110d78faa7b021aa2c8f20a00000000000000000000000086b6ec7076171530692cb325d94a1425148da97d0000000000000000000000004ac6dc39545947364b416937f6431ba1818f4fbb000000000000000000000000ad3dc8d2d8cf0abc333bcadcd2ffb7cafb912b910000000000000000000000001d6ff73049d71356ee20c5b403c641915faa4de2000000000000000000000000b7267f458b0d4f38744466590a8023a4a7e7ea56000000000000000000000000e56bcc23776410ab3b32ab0497b34154b73ff8b50000000000000000000000001bde8c21b47be99d58c43d283f7501703a64ab0b000000000000000000000000ced24b6a49c5217dd4f9cae19ac5cc2d7df38f61000000000000000000000000785bf7b0235908ba76207081aec8ba4ce361524e000000000000000000000000f0429528ded30121f9c2ab0d4de653d51becedf400000000000000000000000051b21605571c987ca5cb03f03ec7ee7206704af500000000000000000000000070a50c0e174a5e9f4a56e2ae849fcac97199dee9000000000000000000000000d7da71e7ddc95e464607efeac6c6c6d46a26f8610000000000000000000000000a7ffd571a8a2430d10095173dd94c96ed076abd00000000000000000000000031d18eed98e7d819644531252b8caae84b9e1771000000000000000000000000684e2a76cb3281bb3e9dc3e7d5a29151a1f4e160000000000000000000000000d9561a4546e0da11dc8ebe4ab5e02dc61fcd3e3a000000000000000000000000075773cdac7408e3e72919aefb543314c0015943000000000000000000000000ba0267881b1de1aec47ea0418d6c8ad20ef192290000000000000000000000003cff55c18176205ab656481921270b04939426b1000000000000000000000000f5691a5f8afb2c7bb618aa346085832bdd3d6a4a000000000000000000000000a8e5a934c85c4a07a8bf4b0d2320cabd33b59d5d000000000000000000000000f08b880a782d8c4083baa1073b0de22bf67c7d4c0000000000000000000000002776ca69dd33bce8b1cec3e6a51c7268313cb6d9000000000000000000000000847f1db65ce5e9232142c0ca5493c8df09035ef80000000000000000000000000ee44f027548fdcce4174e4471733346222cf446000000000000000000000000af87f44937eda988b6b1cce54d71fd2a6ae25ece000000000000000000000000a9293285ad996d39e187e265bf19ff9f6d58c2b3000000000000000000000000f2537e61d09eba3c0a1e3d2c5ee118deab383343000000000000000000000000974f61efc0dc1bfcfc86312caf88b7881c69fe9d00000000000000000000000076a38674d9bccab9400f06e01b8493cacaed54940000000000000000000000005bfe4e0780057bfb9553818febf3ae87424804fd000000000000000000000000209fdd51c58a081891c615adf5f1b4d59266e4e2000000000000000000000000ba324d55462b0822d9aab6cab88ab12bf7376913000000000000000000000000ad38221bede1e10715c406b89dddd263e85c22c1000000000000000000000000c67ddd52e383df02e3365d7f8569c8a439181d7a0000000000000000000000001261ef5836770e577acee2c1f235339b0639a24a000000000000000000000000d38910fe35774693713c748b98e59ca99acd028d00000000000000000000000072824bb44b35eab26e88e9e1f5e33e2ca66d761100000000000000000000000099ed616b50061b18acfd3aebcc40f6d5053c0afd00000000000000000000000019c1314ddb8089cf54ccfc9743ed4f6b4de16bc3000000000000000000000000af6e61096b8f2ec43f8bb8f74465df5ed0584189000000000000000000000000f55c26bc0da0a50677abb0329344d63e9791cc64000000000000000000000000a7790d6f5a9b708d77d2989ad7cb9403ba215d9d0000000000000000000000002f8c738d0dca8a63d5384ac9271c3b6c2f7b82e8000000000000000000000000735540a4e419242624e333401ed0cff11d8e394c000000000000000000000000fc61dde6fcd54d2d602298097a92810752ea2216000000000000000000000000a474f7df19c449350de2d8ed9530a96bd489e6180000000000000000000000007e1e3abd8dd50a480f1e94ace42024809a9976b00000000000000000000000007a5b3261d1e318b2b1d7cd8395fec2d114cf1e0700000000000000000000000022b8526cb9bf35870fb3b64d2830a342662fae1a000000000000000000000000273c1e818ad4323b261e6989adcd6ec292f39eb6000000000000000000000000b8bc2b71537cdbe0cbbebe3743d456751e48b437000000000000000000000000e5eb897aee540b3b61f9657ae6c7b3a8817e5bbc00000000000000000000000076bc03f1e645601bb46e1b913ad19f5cc68ead9e0000000000000000000000004e907e40d9e14d137ca9a4dc4e837526ab24a75a0000000000000000000000003e872672b1aa1928e2c595c792d3660bdb9bf009000000000000000000000000002c24046f6238c8a23d5e66af11e82b413a747300000000000000000000000016ed4972d859c4863f5aac4827ed35082fefe2dd00000000000000000000000093d7cd3e4b5b02a57ac94536c49e09b3b678298c0000000000000000000000002fe6d5d36fae80f36b49c41cc669495a15d61f1c000000000000000000000000ee336bb5175862703e2d121b2d62fe3230defa8d000000000000000000000000ca40f22df970cfac0fb548f4681544c0fb76d2260000000000000000000000004a0e6f7c6a68b009549f7022fc772f04f0b3d267000000000000000000000000500739e1734d4361a642e3dc1947eab3312cbe020000000000000000000000007f9777b9460f6f2e2fec0737b3ecb764135569cb00000000000000000000000054e8086deb08c761eaa0aa77b6b0846c9d2ffbb2000000000000000000000000c54885669813cabbf2809e1abfc8f64ff963081a000000000000000000000000cebe744ffe69ba5c903a648a9ee1e7951d06d35c000000000000000000000000c4b4c4c30ee327faf976fea34d91ada6dc65aae3000000000000000000000000cd52cd7fe07189bd16d236eb1da587bb9d2fbc690000000000000000000000009ac44065540cb31b69c9f18c22392c6eb6cbb7b30000000000000000000000000a791b8d6c823a58f59782bf901141d2e21d3867000000000000000000000000dc75d344dfcf394da9ef25500b83551809fd7c23000000000000000000000000d76a3139322bf6edcdd8e71cbe225a80c94a43d000000000000000000000000031a3135956b33f9936791e5f11546e7f9a11d91f00000000000000000000000067a045c776f4422d56d6f59c70fb42bba05be7c9000000000000000000000000f3e27a226c9c1c0169119dad4c6f8ce8b07342b2000000000000000000000000eaebad11a448fc6b3fdb40f53bbbcaefc9e12720000000000000000000000000715b03b71847e76ddef8ac19dc0bdf92f2fee7630000000000000000000000005032f427d9b911ebe63ad99f707e1fafa7b9a23b0000000000000000000000001758fe2c3492bed3f6e2a95247b6df04fbdc4cfc00000000000000000000000016aad9880d1504fef8b64ed16ee33e7d42bd9321000000000000000000000000edc03dda760e5793ecda69d9837956f246e2cc7c0000000000000000000000006a89f27f7dee87a4b69167ca795dbe07478de68900000000000000000000000091d762a023ee879222728725d1b4cfeca45aef1e00000000000000000000000046a3d887a93389e59c1081a96c23578a8a97e87100000000000000000000000072da397dfb39e9bdaf2e6e5852886a8e0f422b07000000000000000000000000e2c8c2258ab538d28501e23e938310946df84ac1000000000000000000000000bb022fa11c82a529f67d850029ae9ba111d6ae580000000000000000000000005dc5002fe446e7526aca4c30068f162ff806953b0000000000000000000000002e254caaf6662a65f28d0e6826e23305897686eb0000000000000000000000002da4b85b57ed5445eb9d92190f1cc31f70791cc7000000000000000000000000127f2b9ba900b3698589d66e22b457dcc2e988f00000000000000000000000001c5f2610033871625f91ef349ff5ad29168a5f4f000000000000000000000000381c1fbbf9821e2d3af022d9bfcd38a223d8d733000000000000000000000000af9ca6bf95489773edb5003abf3f96e137b0dce00000000000000000000000002187b84a100effa2c5c5bf6d31a959b9338a799d000000000000000000000000922875f3ac04bc4ee3f249216d83f5a3b30a1f83000000000000000000000000430d3d400469a788bba266a718916c886400c68e000000000000000000000000f2d811c2ac3e8019c18ff3530777c072cc8a580a000000000000000000000000dc5c5b4ef174c2c57fe6c712851729fa86496622000000000000000000000000e59e04766142949d054abfa7a9ae29147a2dbde5000000000000000000000000616e72cb01e23dfb60b412580fa26739185373ca0000000000000000000000005a1e7ef74e0fd75a2ee88cd6cec34ef3362efd0600000000000000000000000056f857ba48c51822a9ee3f0e658c608bc26732b10000000000000000000000008143cbd82f9329f112076510706639c58e7fee2f000000000000000000000000cb00f2d13d7840c4b60b995edc76d250e2bce4b600000000000000000000000016bdc0f3a107af309153772089298277891026d4000000000000000000000000d21fd880f77730e3fe563a3b884e2231426d423e0000000000000000000000000b2eed6a84dd1ba7d8ece83a1665bdb5a4428b94000000000000000000000000ab1ca7e2322b23c5a51c9704684efd8ce32a799e00000000000000000000000091e5e85ee93ca51b26137cb0bafd67ba9de477cc0000000000000000000000006ff0825bc7d48865078fe3413e4be66bd41677e0000000000000000000000000794cdc7d28cda9720186e8f81c57a084eef049b200000000000000000000000000000000000000000000000000000000000002bc0000000000000000000000000000000000000000000000000000005e627181b400000000000000000000000000000000000000000000000000002bc6d43b0bd200000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000e22aaf624700000000000000000000000000000000000000000000000000000843a372ad7e00000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000001726a316270000000000000000000000000000000000000000000000000000017ee175821700000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000ecda0e58cf00000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000001726a3162700000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000002bb6cd2c9a060000000000000000000000000000000000000000000000000000000e3f2948b50000000000000000000000000000000000000000000000000000001396d8c3fa0000000000000000000000000000000000000000000000000000001726a3162700000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000031dd107e7c00000000000000000000000000000000000000000000000000000011cef39ae3000000000000000000000000000000000000000000000000000001f7fa546c210000000000000000000000000000000000000000000000000000006ad9b5a153000000000000000000000000000000000000000000000000000000d05bbbc76300000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000001726a316270000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000903f82003100000000000000000000000000000000000000000000000000000051eb2d62150000000000000000000000000000000000000000000000000000019997e2ea6c0000000000000000000000000000000000000000000000000000001396d8c3fa000000000000000000000000000000000000000000000000000000f5c188264100000000000000000000000000000000000000000000000000000050234838fe000000000000000000000000000000000000000000000000000001597ba9233a00000000000000000000000000000000000000000000000000000010070e71cc0000000000000000000000000000000000000000000000000000004903b394a40000000000000000000000000000000000000000000000000000004e5b630fe8000000000000000000000000000000000000000000000000000000b215840ce10000000000000000000000000000000000000000000000000000015b438e4c5000000000000000000000000000000000000000000000000000000051eb2d621500000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000005742dcdd5900000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000590ac206700000000000000000000000000000000000000000000000000000007ca8a93c370000000000000000000000000000000000000000000000000000005c9a8c589e0000000000000000000000000000000000000000000000000000001396d8c3fa000000000000000000000000000000000000000000000000000000602a56aacb000000000000000000000000000000000000000000000000000000155ebded1000000000000000000000000000000000000000000000000000000073c12f6ec5000000000000000000000000000000000000000000000000000000557af7b443000000000000000000000000000000000000000000000000000000c93c2723080000000000000000000000000000000000000000000000000000006911d0783d000000000000000000000000000000000000000000000000000000f95152786e00000000000000000000000000000000000000000000000000000051eb2d621500000000000000000000000000000000000000000000000000000167bad26bf00000000000000000000000000000000000000000000000000000001ab66d685400000000000000000000000000000000000000000000000000000051eb2d6215000000000000000000000000000000000000000000000000000000356cdad0a9000000000000000000000000000000000000000000000000000000cccbf17536000000000000000000000000000000000000000000000000000000d3eb861991000000000000000000000000000000000000000000000000000000eb12292fb80000000000000000000000000000000000000000000000000000015ed3589e7e0000000000000000000000000000000000000000000000000000005ad2a72f8700000000000000000000000000000000000000000000000000002bca64055e000000000000000000000000000000000000000000000000000000003c8c6f750400000000000000000000000000000000000000000000000000000031dd107e7c0000000000000000000000000000000000000000000000000000002c856103380000000000000000000000000000000000000000000000000000004acb98bdba000000000000000000000000000000000000000000000000000000c77441f9f2000000000000000000000000000000000000000000000000000000155ebded100000000000000000000000000000000000000000000000000000001ab66d685400000000000000000000000000000000000000000000000000000107907bc124000000000000000000000000000000000000000000000000000000eea1f381e6000000000000000000000000000000000000000000000000000000155ebded1000000000000000000000000000000000000000000000000000000010070e71cc00000000000000000000000000000000000000000000000000000041e41ef049000000000000000000000000000000000000000000000000000000401c39c73200000000000000000000000000000000000000000000000000000043ac04195f0000000000000000000000000000000000000000000000000000004acb98bdba000000000000000000000000000000000000000000000000000000239de735c60000000000000000000000000000000000000000000000000000008e779cd71a000000000000000000000000000000000000000000000000000000d223a0f07a0000000000000000000000000000000000000000000000000000018990d4789f000000000000000000000000000000000000000000000000000000e7825edd8b0000000000000000000000000000000000000000000000000000004c937de6d1000000000000000000000000000000000000000000000000000000f069d8aafc000000000000000000000000000000000000000000000000000000e5ba79b47400000000000000000000000000000000000000000000000000000187c8ef4f890000000000000000000000000000000000000000000000000000003c8c6f750400000000000000000000000000000000000000000000000000000033a4f5a793000000000000000000000000000000000000000000000000000000de9ae51019000000000000000000000000000000000000000000000000000001a60f270a0b000000000000000000000000000000000000000000000000000000590ac206700000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000473bce6b8d00000000000000000000000000000000000000000000000000000155ebded10c00000000000000000000000000000000000000000000000000000087580832bf0000000000000000000000000000000000000000000000000000001ab66d6854000000000000000000000000000000000000000000000000000000473bce6b8d000000000000000000000000000000000000000000000000000001726a316278000000000000000000000000000000000000000000000000000000cb040c4c1f0000000000000000000000000000000000000000000000000000018b58b9a1b6000000000000000000000000000000000000000000000000000000f231bdd41300000000000000000000000000000000000000000000000000000165f2ed42d9000000000000000000000000000000000000000000000000000000473bce6b8d0000000000000000000000000000000000000000000000000000016b4a9cbe1d0000000000000000000000000000000000000000000000000000016982b7950600000000000000000000000000000000000000000000000000000053b3128b2c0000000000000000000000000000000000000000000000000000017432168b8f000000000000000000000000000000000000000000000000000000d3eb861991000000000000000000000000000000000000000000000000000000ecda0e58cf000000000000000000000000000000000000000000000000000000e94a4406a200000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000038fca522d7000000000000000000000000000000000000000000000000000000e94a4406a2000000000000000000000000000000000000000000000000000001726a316278000000000000000000000000000000000000000000000000000000eea1f381e6000000000000000000000000000000000000000000000000000000e062ca3930000000000000000000000000000000000000000000000000000000d9433594d50000000000000000000000000000000000000000000000000000010400b16ef7000000000000000000000000000000000000000000000000000000d5b36b42a70000000000000000000000000000000000000000000000000000019b5fc81383000000000000000000000000000000000000000000000000000000658206260f0000000000000000000000000000000000000000000000000000002abd7bda2100000000000000000000000000000000000000000000000000000157b3c3fa230000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000557af7b4430000000000000000000000000000000000000000000000000000018b58b9a1b60000000000000000000000000000000000000000000000000000018ee883f3e40000000000000000000000000000000000000000000000000000010070e71cc900000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000001ab66d685400000000000000000000000000000000000000000000000000000187c8ef4f8900000000000000000000000000000000000000000000000000000030152b556500000000000000000000000000000000000000000000000000000170a24c39610000000000000000000000000000000000000000000000000000009207672948000000000000000000000000000000000000000000000000000000d05bbbc7630000000000000000000000000000000000000000000000000000006749eb4f260000000000000000000000000000000000000000000000000000008cafb7ae030000000000000000000000000000000000000000000000000000017432168b8f000000000000000000000000000000000000000000000000000000038fca522d0000000000000000000000000000000000000000000000000000005742dcdd59000000000000000000000000000000000000000000000000000000db0b1abdec0000000000000000000000000000000000000000000000000000001396d8c3fa00000000000000000000000000000000000000000000000000000186010a2672000000000000000000000000000000000000000000000000000000e062ca3930000000000000000000000000000000000000000000000000000001609b3dc795000000000000000000000000000000000000000000000000000000e3f2948b5d000000000000000000000000000000000000000000000000000000c77441f9f2000000000000000000000000000000000000000000000000000000db0b1abdec0000000000000000000000000000000000000000000000000000005742dcdd59000000000000000000000000000000000000000000000000000000f95152786e0000000000000000000000000000000000000000000000000000002565cc5edd00000000000000000000000000000000000000000000000000000177c1e0ddbc000000000000000000000000000000000000000000000000000000557af7b4430000000000000000000000000000000000000000000000000000018990d4789f000000000000000000000000000000000000000000000000000000fb1937a1850000000000000000000000000000000000000000000000000000007750f9c0f300000000000000000000000000000000000000000000000000000050234838fe00000000000000000000000000000000000000000000000000000087580832bf0000000000000000000000000000000000000000000000000000005ad2a72f870000000000000000000000000000000000000000000000000000010238cc45e0000000000000000000000000000000000000000000000000000000d3eb861991000000000000000000000000000000000000000000000000000000fb1937a18500000000000000000000000000000000000000000000000000000038fca522d7000000000000000000000000000000000000000000000000000000e5ba79b4740000000000000000000000000000000000000000000000000000004e5b630fe8000000000000000000000000000000000000000000000000000000590ac20670000000000000000000000000000000000000000000000000000000be8cc82c80000000000000000000000000000000000000000000000000000000f231bdd4130000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000f069d8aafc000000000000000000000000000000000000000000000000000000557af7b443000000000000000000000000000000000000000000000000000000eea1f381e60000000000000000000000000000000000000000000000000000015b438e4c5000000000000000000000000000000000000000000000000000000182713fd4450000000000000000000000000000000000000000000000000000000c77441f9f0000000000000000000000000000000000000000000000000000017989c606d3000000000000000000000000000000000000000000000000000001324df79b4600000000000000000000000000000000000000000000000000000071f94a45ae00000000000000000000000000000000000000000000000000000075891497dc00000000000000000000000000000000000000000000000000000050234838fe000000000000000000000000000000000000000000000000000001726a3162780000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000cccbf17536000000000000000000000000000000000000000000000000000000d77b506bbe0000000000000000000000000000000000000000000000000000016982b79506000000000000000000000000000000000000000000000000000000f5c1882641000000000000000000000000000000000000000000000000000000f95152786e0000000000000000000000000000000000000000000000000000005ad2a72f87000000000000000000000000000000000000000000000000000000ecda0e58cf00000000000000000000000000000000000000000000000000000075891497dc000000000000000000000000000000000000000000000000000000f7896d4f5700000000000000000000000000000000000000000000000000000053b3128b2c000000000000000000000000000000000000000000000000000000fb1937a1850000000000000000000000000000000000000000000000000000006e697ff3810000000000000000000000000000000000000000000000000000016b4a9cbe1d00000000000000000000000000000000000000000000000000000053b3128b2c00000000000000000000000000000000000000000000000000000063ba20fcf9000000000000000000000000000000000000000000000000000000c93c27230800000000000000000000000000000000000000000000000000000063ba20fcf9000000000000000000000000000000000000000000000000000001843924fd5b000000000000000000000000000000000000000000000000000000557af7b4430000000000000000000000000000000000000000000000000000005ad2a72f8700000000000000000000000000000000000000000000000000000170a24c396100000000000000000000000000000000000000000000000000000175f9fbb4a5000000000000000000000000000000000000000000000000000000f069d8aafc0000000000000000000000000000000000000000000000000000005c9a8c589e000000000000000000000000000000000000000000000000000000ce93d69e4d00000000000000000000000000000000000000000000000000000043ac04195f000000000000000000000000000000000000000000000000000000ce93d69e4d000000000000000000000000000000000000000000000000000000038fca522d0000000000000000000000000000000000000000000000000000004e5b630fe80000000000000000000000000000000000000000000000000000008cafb7ae0300000000000000000000000000000000000000000000000000000011cef39ae3000000000000000000000000000000000000000000000000000000bcc4e303690000000000000000000000000000000000000000000000000000003ac48a4bee000000000000000000000000000000000000000000000000000000356cdad0a9000000000000000000000000000000000000000000000000000000d223a0f07a0000000000000000000000000000000000000000000000000000005742dcdd59000000000000000000000000000000000000000000000000000000d223a0f07a0000000000000000000000000000000000000000000000000000007031651c980000000000000000000000000000000000000000000000000000005c9a8c589e00000000000000000000000000000000000000000000000000000051eb2d62150000000000000000000000000000000000000000000000000000005c9a8c589e00000000000000000000000000000000000000000000000000000001c7e52916000000000000000000000000000000000000000000000000000000ce93d69e4d000000000000000000000000000000000000000000000000000000dcd2ffe7020000000000000000000000000000000000000000000000000000002abd7bda21000000000000000000000000000000000000000000000000000000d05bbbc763000000000000000000000000000000000000000000000000000000557af7b4430000000000000000000000000000000000000000000000000000001396d8c3fa0000000000000000000000000000000000000000000000000000001726a316270000000000000000000000000000000000000000000000000000001396d8c3fa0000000000000000000000000000000000000000000000000000004e5b630fe80000000000000000000000000000000000000000000000000000010238cc45e0000000000000000000000000000000000000000000000000000000d3eb861991000000000000000000000000000000000000000000000000000000fb1937a1850000000000000000000000000000000000000000000000000000008cafb7ae03000000000000000000000000000000000000000000000000000000f95152786e0000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000d9433594d50000000000000000000000000000000000000000000000000000005ad2a72f870000000000000000000000000000000000000000000000000000001726a3162700000000000000000000000000000000000000000000000000000187c8ef4f890000000000000000000000000000000000000000000000000000001726a316270000000000000000000000000000000000000000000000000000017432168b8f00000000000000000000000000000000000000000000000000000053b3128b2c000000000000000000000000000000000000000000000000000000de9ae510190000000000000000000000000000000000000000000000000000004e5b630fe8000000000000000000000000000000000000000000000000000000155ebded100000000000000000000000000000000000000000000000000000006e697ff381000000000000000000000000000000000000000000000000000000b04d9ee3ca000000000000000000000000000000000000000000000000000001642b0819c2000000000000000000000000000000000000000000000000000000ecda0e58cf00000000000000000000000000000000000000000000000000000092076729480000000000000000000000000000000000000000000000000000005742dcdd59000000000000000000000000000000000000000000000000000000c5ac5cd0db00000000000000000000000000000000000000000000000000000149749ab16d0000000000000000000000000000000000000000000000000000006ad9b5a153000000000000000000000000000000000000000000000000000000b5a54e5f0e00000000000000000000000000000000000000000000000000000051eb2d6215000000000000000000000000000000000000000000000000000000155ebded10000000000000000000000000000000000000000000000000000000f231bdd413000000000000000000000000000000000000000000000000000000eea1f381e60000000000000000000000000000000000000000000000000000002e4d462c4f0000000000000000000000000000000000000000000000000000006ca19aca6a00000000000000000000000000000000000000000000000000000051eb2d621500000000000000000000000000000000000000000000000000000197cffdc155000000000000000000000000000000000000000000000000000000e062ca39300000000000000000000000000000000000000000000000000000017ee175821700000000000000000000000000000000000000000000000000000063ba20fcf9000000000000000000000000000000000000000000000000000000cb040c4c1f000000000000000000000000000000000000000000000000000000155ebded100000000000000000000000000000000000000000000000000000001ab66d6854000000000000000000000000000000000000000000000000000000d77b506bbe00000000000000000000000000000000000000000000000000000105c896980d000000000000000000000000000000000000000000000000000000e3f2948b5d0000000000000000000000000000000000000000000000000000008ae7d284ed000000000000000000000000000000000000000000000000000000ecda0e58cf000000000000000000000000000000000000000000000000000000de9ae51019000000000000000000000000000000000000000000000000000000b5a54e5f0e0000000000000000000000000000000000000000000000000000016b4a9cbe1d0000000000000000000000000000000000000000000000000000016b4a9cbe1d00000000000000000000000000000000000000000000000000000033a4f5a7930000000000000000000000000000000000000000000000000000005c9a8c589e0000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000155ebded10000000000000000000000000000000000000000000000000000000590ac20670000000000000000000000000000000000000000000000000000000155ebded100000000000000000000000000000000000000000000000000000017d19905900000000000000000000000000000000000000000000000000000000d77b506bbe0000000000000000000000000000000000000000000000000000004903b394a4000000000000000000000000000000000000000000000000000000b215840ce100000000000000000000000000000000000000000000000000000031dd107e7c000000000000000000000000000000000000000000000000000000e94a4406a2000000000000000000000000000000000000000000000000000000557af7b44300000000000000000000000000000000000000000000000000000053b3128b2c0000000000000000000000000000000000000000000000000000008038738e64000000000000000000000000000000000000000000000000000000e7825edd8b0000000000000000000000000000000000000000000000000000017989c606d3000000000000000000000000000000000000000000000000000000de9ae51019000000000000000000000000000000000000000000000000000000f3f9a2fd2a0000000000000000000000000000000000000000000000000000004e5b630fe8000000000000000000000000000000000000000000000000000000be8cc82c80000000000000000000000000000000000000000000000000000000d9433594d50000000000000000000000000000000000000000000000000000005e627181b40000000000000000000000000000000000000000000000000000008038738e6400000000000000000000000000000000000000000000000000000050234838fe00000000000000000000000000000000000000000000000000000041e41ef0490000000000000000000000000000000000000000000000000000016b4a9cbe1d0000000000000000000000000000000000000000000000000000014b3c7fda840000000000000000000000000000000000000000000000000000005742dcdd59000000000000000000000000000000000000000000000000000000155ebded10000000000000000000000000000000000000000000000000000001609b3dc7950000000000000000000000000000000000000000000000000000001ab66d68540000000000000000000000000000000000000000000000000000008cafb7ae0300000000000000000000000000000000000000000000000000000041e41ef049000000000000000000000000000000000000000000000000000000891fed5bd60000000000000000000000000000000000000000000000000000015423f9a7f6000000000000000000000000000000000000000000000000000000c054ad5597000000000000000000000000000000000000000000000000000000ecda0e58cf000000000000000000000000000000000000000000000000000000590ac2067000000000000000000000000000000000000000000000000000000197cffdc1550000000000000000000000000000000000000000000000000000001726a316270000000000000000000000000000000000000000000000000000007ae0c41320000000000000000000000000000000000000000000000000000000d77b506bbe000000000000000000000000000000000000000000000000000000fb1937a18500000000000000000000000000000000000000000000000000000051eb2d6215000000000000000000000000000000000000000000000000000000590ac20670000000000000000000000000000000000000000000000000000000acbdd4919d0000000000000000000000000000000000000000000000000000017989c606d300000000000000000000000000000000000000000000000000000043ac04195f000000000000000000000000000000000000000000000000000000155ebded100000000000000000000000000000000000000000000000000000004e5b630fe80000000000000000000000000000000000000000000000000000007e708e654e000000000000000000000000000000000000000000000000000000155ebded1000000000000000000000000000000000000000000000000000000170a24c3961000000000000000000000000000000000000000000000000000000602a56aacb0000000000000000000000000000000000000000000000000000019d27ad3c9a000000000000000000000000000000000000000000000000000000e062ca393000000000000000000000000000000000000000000000000000000187c8ef4f89000000000000000000000000000000000000000000000000000000e5ba79b474000000000000000000000000000000000000000000000000000001195f6f5c0700000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000008038738e640000000000000000000000000000000000000000000000000000001396d8c3fa000000000000000000000000000000000000000000000000000000d5b36b42a70000000000000000000000000000000000000000000000000000005ad2a72f8700000000000000000000000000000000000000000000000000000170a24c39610000000000000000000000000000000000000000000000000000015b438e4c500000000000000000000000000000000000000000000000000000007031651c980000000000000000000000000000000000000000000000000000004c937de6d1000000000000000000000000000000000000000000000000000000d5b36b42a700000000000000000000000000000000000000000000000000000031dd107e7c000000000000000000000000000000000000000000000000000000fea901f3b2000000000000000000000000000000000000000000000000000000155ebded100000000000000000000000000000000000000000000000000000005742dcdd590000000000000000000000000000000000000000000000000000006749eb4f2600000000000000000000000000000000000000000000000000000050234838fe000000000000000000000000000000000000000000000000000000e7825edd8b0000000000000000000000000000000000000000000000000000006ad9b5a153000000000000000000000000000000000000000000000000000000e5ba79b47400000000000000000000000000000000000000000000000000000187c8ef4f890000000000000000000000000000000000000000000000000000001726a316270000000000000000000000000000000000000000000000000000004573e94276000000000000000000000000000000000000000000000000000000a0469071fd000000000000000000000000000000000000000000000000000000b76d338825000000000000000000000000000000000000000000000000000000602a56aacb0000000000000000000000000000000000000000000000000000006911d0783d0000000000000000000000000000000000000000000000000000005c9a8c589e000000000000000000000000000000000000000000000000000000a92e0a3f6f0000000000000000000000000000000000000000000000000000002e4d462c4f000000000000000000000000000000000000000000000000000000975f16a48c0000000000000000000000000000000000000000000000000000001ab66d685400000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000007750f9c0f300000000000000000000000000000000000000000000000000000051eb2d62150000000000000000000000000000000000000000000000000000016b4a9cbe1d000000000000000000000000000000000000000000000000000000dcd2ffe7020000000000000000000000000000000000000000000000000000005742dcdd59000000000000000000000000000000000000000000000000000000e94a4406a2000000000000000000000000000000000000000000000000000000e94a4406a2000000000000000000000000000000000000000000000000000000d3eb8619910000000000000000000000000000000000000000000000000000005c9a8c589e0000000000000000000000000000000000000000000000000000006ca19aca6a0000000000000000000000000000000000000000000000000000005e627181b400000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000073c12f6ec50000000000000000000000000000000000000000000000000000010ce82b3c6800000000000000000000000000000000000000000000000000000073c12f6ec500000000000000000000000000000000000000000000000000000105c896980d00000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000001396d8c3fa000000000000000000000000000000000000000000000000000000d223a0f07a00000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000be8cc82c80000000000000000000000000000000000000000000000000000000975f16a48c000000000000000000000000000000000000000000000000000000e5ba79b474000000000000000000000000000000000000000000000000000000d5b36b42a7000000000000000000000000000000000000000000000000000000e3f2948b5d0000000000000000000000000000000000000000000000000000007918deea09000000000000000000000000000000000000000000000000000000f5c18826410000000000000000000000000000000000000000000000000000003ac48a4bee000000000000000000000000000000000000000000000000000000b3dd6935f80000000000000000000000000000000000000000000000000000002565cc5edd000000000000000000000000000000000000000000000000000000eea1f381e600000000000000000000000000000000000000000000000000000050234838fe000000000000000000000000000000000000000000000000000000cccbf17536000000000000000000000000000000000000000000000000000000a59e3fed42000000000000000000000000000000000000000000000000000000b04d9ee3ca000000000000000000000000000000000000000000000000000000de9ae51019000000000000000000000000000000000000000000000000000000e3f2948b5d0000000000000000000000000000000000000000000000000000017d1990590000000000000000000000000000000000000000000000000000000053b3128b2c000000000000000000000000000000000000000000000000000000eea1f381e6000000000000000000000000000000000000000000000000000000e3f2948b5d0000000000000000000000000000000000000000000000000000005742dcdd59000000000000000000000000000000000000000000000000000000d5b36b42a70000000000000000000000000000000000000000000000000000019d27ad3c9a000000000000000000000000000000000000000000000000000000cccbf17536000000000000000000000000000000000000000000000000000001408d20e3fb000000000000000000000000000000000000000000000000000000891fed5bd6000000000000000000000000000000000000000000000000000000155ebded100000000000000000000000000000000000000000000000000000000e3f2948b5000000000000000000000000000000000000000000000000000000239de735c6000000000000000000000000000000000000000000000000000000c93c2723080000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000e22aaf62470000000000000000000000000000000000000000000000000000007918deea09000000000000000000000000000000000000000000000000000000d5b36b42a700000000000000000000000000000000000000000000000000000011cef39ae300000000000000000000000000000000000000000000000000000093cf4c525e0000000000000000000000000000000000000000000000000000006911d0783d000000000000000000000000000000000000000000000000000000356cdad0a900000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000019d27ad3c9a0000000000000000000000000000000000000000000000000000004573e942760000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000de9ae5101900000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000e7825edd8b0000000000000000000000000000000000000000000000000000001726a316270000000000000000000000000000000000000000000000000000001396d8c3fa00000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000087580832bf00000000000000000000000000000000000000000000000000000165f2ed42d9000000000000000000000000000000000000000000000000000000d5b36b42a70000000000000000000000000000000000000000000000000000005ad2a72f87000000000000000000000000000000000000000000000000000000d77b506bbe0000000000000000000000000000000000000000000000000000001726a316270000000000000000000000000000000000000000000000000000001ab66d685400000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000d9433594d5000000000000000000000000000000000000000000000000000000f95152786e000000000000000000000000000000000000000000000000000000de9ae5101900000000000000000000000000000000000000000000000000000050234838fe0000000000000000000000000000000000000000000000000000004e5b630fe8000000000000000000000000000000000000000000000000000000a3d65ac42b000000000000000000000000000000000000000000000000000000c5ac5cd0db00000000000000000000000000000000000000000000000000000063ba20fcf900000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000004c937de6d1000000000000000000000000000000000000000000000000000000602a56aacb00000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000061f23bd3e2000000000000000000000000000000000000000000000000000000356cdad0a900000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000001726a316270000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000557af7b443000000000000000000000000000000000000000000000000000000eb12292fb8000000000000000000000000000000000000000000000000000000f3f9a2fd2a00000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000cb040c4c1f000000000000000000000000000000000000000000000000000000c77441f9f200000000000000000000000000000000000000000000000000000186010a267200000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000006749eb4f26000000000000000000000000000000000000000000000000000000dcd2ffe702000000000000000000000000000000000000000000000000000000cb040c4c1f000000000000000000000000000000000000000000000000000000bcc4e30369000000000000000000000000000000000000000000000000000000658206260f00000000000000000000000000000000000000000000000000000150942f55c800000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000003e54549e1b00000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000ce93d69e4d00000000000000000000000000000000000000000000000000000186010a267200000000000000000000000000000000000000000000000000000149749ab16d000000000000000000000000000000000000000000000000000000e5ba79b474000000000000000000000000000000000000000000000000000000658206260f000000000000000000000000000000000000000000000000000000f3f9a2fd2a00000000000000000000000000000000000000000000000000000011cef39ae30000000000000000000000000000000000000000000000000000017b51ab2fea0000000000000000000000000000000000000000000000000000017989c606d3000000000000000000000000000000000000000000000000000001843924fd5b00000000000000000000000000000000000000000000000000000167bad26bf0000000000000000000000000000000000000000000000000000000c93c272308000000000000000000000000000000000000000000000000000000200e1ce3990000000000000000000000000000000000000000000000000000005ad2a72f870000000000000000000000000000000000000000000000000000004573e942760000000000000000000000000000000000000000000000000000008ae7d284ed0000000000000000000000000000000000000000000000000000009e7eab48e700000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000bcc4e3036900000000000000000000000000000000000000000000000000000031dd107e7c0000000000000000000000000000000000000000000000000000009926fbcda3000000000000000000000000000000000000000000000000000000602a56aacb0000000000000000000000000000000000000000000000000000001726a3162700000000000000000000000000000000000000000000000000000050234838fe0000000000000000000000000000000000000000000000000000005ad2a72f870000000000000000000000000000000000000000000000000000008cafb7ae03000000000000000000000000000000000000000000000000000000c93c27230800000000000000000000000000000000000000000000000000000053b3128b2c000000000000000000000000000000000000000000000000000000557af7b44300000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000be8cc82c80000000000000000000000000000000000000000000000000000000fce11cca9c000000000000000000000000000000000000000000000000000000d05bbbc7630000000000000000000000000000000000000000000000000000007031651c980000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000cccbf17536000000000000000000000000000000000000000000000000000000e94a4406a20000000000000000000000000000000000000000000000000000009926fbcda300000000000000000000000000000000000000000000000000000038fca522d70000000000000000000000000000000000000000000000000000004c937de6d100000000000000000000000000000000000000000000000000000043ac04195f0000000000000000000000000000000000000000000000000000008ae7d284ed0000000000000000000000000000000000000000000000000000004c937de6d10000000000000000000000000000000000000000000000000000001ab66d6854000000000000000000000000000000000000000000000000000000602a56aacb000000000000000000000000000000000000000000000000000000e062ca3930000000000000000000000000000000000000000000000000000000de9ae5101900000000000000000000000000000000000000000000000000000033a4f5a7930000000000000000000000000000000000000000000000000000015b438e4c50000000000000000000000000000000000000000000000000000000e062ca393000000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000fce11cca9c000000000000000000000000000000000000000000000000000000c3e477a7c400000000000000000000000000000000000000000000000000000028f596b10a000000000000000000000000000000000000000000000000000000eea1f381e600000000000000000000000000000000000000000000000000000085902309a800000000000000000000000000000000000000000000000000000050234838fe00000000000000000000000000000000000000000000000000000050234838fe000000000000000000000000000000000000000000000000000000272db187f40000000000000000000000000000000000000000000000000000003e54549e1b0000000000000000000000000000000000000000000000000000001726a3162700000000000000000000000000000000000000000000000000000030152b55650000000000000000000000000000000000000000000000000000004acb98bdba00000000000000000000000000000000000000000000000000000043ac04195f0000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000c054ad55970000000000000000000000000000000000000000000000000000003734bff9c00000000000000000000000000000000000000000000000000000017d1990590000000000000000000000000000000000000000000000000000000093cf4c525e000000000000000000000000000000000000000000000000000000356cdad0a900000000000000000000000000000000000000000000000000000050234838fe00000000000000000000000000000000000000000000000000000073c12f6ec500000000000000000000000000000000000000000000000000000051eb2d6215000000000000000000000000000000000000000000000000000000dcd2ffe70200000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000aaf5ef68860000000000000000000000000000000000000000000000000000008e779cd71a0000000000000000000000000000000000000000000000000000008ae7d284ed0000000000000000000000000000000000000000000000000000010238cc45e000000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000009aeee0f6b90000000000000000000000000000000000000000000000000000007750f9c0f30000000000000000000000000000000000000000000000000000009207672948000000000000000000000000000000000000000000000000000000ecda0e58cf000000000000000000000000000000000000000000000000000000d9433594d5000000000000000000000000000000000000000000000000000000f231bdd4130000000000000000000000000000000000000000000000000000001726a316270000000000000000000000000000000000000000000000000000007918deea09000000000000000000000000000000000000000000000000000000e7825edd8b000000000000000000000000000000000000000000000000000000602a56aacb0000000000000000000000000000000000000000000000000000004903b394a40000000000000000000000000000000000000000000000000000006749eb4f26000000000000000000000000000000000000000000000000000000cb040c4c1f00000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000590ac2067000000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000d223a0f07a00000000000000000000000000000000000000000000000000000021d6020caf0000000000000000000000000000000000000000000000000000001726a316270000000000000000000000000000000000000000000000000000001726a316270000000000000000000000000000000000000000000000000000001396d8c3fa00000000000000000000000000000000000000000000000000000087580832bf0000000000000000000000000000000000000000000000000000010ce82b3c680000000000000000000000000000000000000000000000000000005c9a8c589e000000000000000000000000000000000000000000000000000000590ac206700000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000fea901f3b200000000000000000000000000000000000000000000000000000050234838fe000000000000000000000000000000000000000000000000000000d223a0f07a0000000000000000000000000000000000000000000000000000018ee883f3e4000000000000000000000000000000000000000000000000000000356cdad0a9000000000000000000000000000000000000000000000000000000eea1f381e60000000000000000000000000000000000000000000000000000008ae7d284ed000000000000000000000000000000000000000000000000000000155ebded10000000000000000000000000000000000000000000000000000000b215840ce1000000000000000000000000000000000000000000000000000000b93518b13c0000000000000000000000000000000000000000000000000000017432168b8f00000000000000000000000000000000000000000000000000000085902309a8000000000000000000000000000000000000000000000000000000473bce6b8d0000000000000000000000000000000000000000000000000000002abd7bda210000000000000000000000000000000000000000000000000000007031651c9800000000000000000000000000000000000000000000000000000053b3128b2c000000000000000000000000000000000000000000000000000000eb12292fb800000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000007031651c9800000000000000000000000000000000000000000000000000000041e41ef049000000000000000000000000000000000000000000000000000000e062ca39300000000000000000000000000000000000000000000000000000005e627181b4000000000000000000000000000000000000000000000000000000d77b506bbe000000000000000000000000000000000000000000000000000000658206260f000000000000000000000000000000000000000000000000000000272db187f4000000000000000000000000000000000000000000000000000000e5ba79b47400000000000000000000000000000000000000000000000000000050234838fe000000000000000000000000000000000000000000000000000000e7825edd8b00000000000000000000000000000000000000000000000000000155ebded10c0000000000000000000000000000000000000000000000000000005742dcdd590000000000000000000000000000000000000000000000000000001ab66d6854000000000000000000000000000000000000000000000000000000155ebded10000000000000000000000000000000000000000000000000000000d9433594d5000000000000000000000000000000000000000000000000000000272db187f4000000000000000000000000000000000000000000000000000000c93c2723080000000000000000000000000000000000000000000000000000006ad9b5a15300000000000000000000000000000000000000000000000000000010070e71cc0000000000000000000000000000000000000000000000000000009597317b75000000000000000000000000000000000000000000000000000000f3f9a2fd2a00000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000c77441f9f200000000000000000000000000000000000000000000000000000051eb2d621500000000000000000000000000000000000000000000000000000145e4d05f4000000000000000000000000000000000000000000000000000000010070e71cc00000000000000000000000000000000000000000000000000000083c83de0920000000000000000000000000000000000000000000000000000002565cc5edd00000000000000000000000000000000000000000000000000000083c83de092000000000000000000000000000000000000000000000000000000557af7b44300000000000000000000000000000000000000000000000000000030152b55650000000000000000000000000000000000000000000000000000005c9a8c589e0000000000000000000000000000000000000000000000000000010b20461351000000000000000000000000000000000000000000000000000000cccbf1753600000000000000000000000000000000000000000000000000000165f2ed42d90000000000000000000000000000000000000000000000000000005742dcdd5900000000000000000000000000000000000000000000000000000053b3128b2c000000000000000000000000000000000000000000000000000000155ebded1000000000000000000000000000000000000000000000000000000043ac04195f000000000000000000000000000000000000000000000000000000f95152786e000000000000000000000000000000000000000000000000000000a92e0a3f6f00000000000000000000000000000000000000000000000000000187c8ef4f89000000000000000000000000000000000000000000000000000000155ebded100000000000000000000000000000000000000000000000000000008cafb7ae03000000000000000000000000000000000000000000000000000000bcc4e303690000000000000000000000000000000000000000000000000000002e4d462c4f0000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000dcd2ffe70200000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000018ee883f3e00000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000602a56aacb0000000000000000000000000000000000000000000000000000003734bff9c000000000000000000000000000000000000000000000000000000053b3128b2c0000000000000000000000000000000000000000000000000000001726a316270000000000000000000000000000000000000000000000000000001726a316270000000000000000000000000000000000000000000000000000005742dcdd5900000000000000000000000000000000000000000000000000000075891497dc000000000000000000000000000000000000000000000000000000c5ac5cd0db0000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000155ebded10000000000000000000000000000000000000000000000000000000de9ae5101900000000000000000000000000000000000000000000000000000165f2ed42d90000000000000000000000000000000000000000000000000000001ab66d68540000000000000000000000000000000000000000000000000000001ab66d68540000000000000000000000000000000000000000000000000000001726a31627000000000000000000000000000000000000000000000000000000401c39c73200000000000000000000000000000000000000000000000000000018ee883f3e000000000000000000000000000000000000000000000000000000155ebded1000000000000000000000000000000000000000000000000000000018ee883f3e0000000000000000000000000000000000000000000000000000007ca8a93c3700000000000000000000000000000000000000000000000000000011cef39ae3000000000000000000000000000000000000000000000000000000be8cc82c800000000000000000000000000000000000000000000000000000006749eb4f260000000000000000000000000000000000000000000000000000001726a316270000000000000000000000000000000000000000000000000000002e4d462c4f000000000000000000000000000000000000000000000000000000d05bbbc7630000000000000000000000000000000000000000000000000000004acb98bdba0000000000000000000000000000000000000000000000000000004acb98bdba0000000000000000000000000000000000000000000000000000001726a31627" - }, - "result": { - "gasUsed": "0x24f678b1", - "output": "0x" - }, - "blockHash": "0xbeef70ac3db42f10dd1eb03f5f0640557acd72db61357cf3c4f47945d8beab79", - "blockNumber": 3663377, - "transactionHash": "0xdbbc58127cc21ef53ae41e63d815853a748ca6ecee21264466fb8960e0feefe3", - "transactionPosition": 107 - } + ... ] """ From 11d6a2e9a93dfb0d2d5a975f778be7d702e67ffb Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:16:01 +0400 Subject: [PATCH 136/363] chore: Token balances fetcher slow queue (#10694) --- .../explorer/chain/address/token_balance.ex | 12 +- .../import/runner/address/token_balances.ex | 2 + ...8140638_add_token_balance_retry_fields.exs | 10 ++ .../lib/indexer/fetcher/token_balance.ex | 109 +++++++------- apps/indexer/lib/indexer/token_balances.ex | 27 +--- .../indexer/fetcher/token_balance_test.exs | 138 ++++++++---------- config/runtime.exs | 4 +- docker-compose/envs/common-blockscout.env | 2 + 8 files changed, 138 insertions(+), 166 deletions(-) create mode 100644 apps/explorer/priv/repo/migrations/20240828140638_add_token_balance_retry_fields.exs diff --git a/apps/explorer/lib/explorer/chain/address/token_balance.ex b/apps/explorer/lib/explorer/chain/address/token_balance.ex index e0aef4756354..85a7314eb874 100644 --- a/apps/explorer/lib/explorer/chain/address/token_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/token_balance.ex @@ -25,6 +25,8 @@ defmodule Explorer.Chain.Address.TokenBalance do * `value` - The value that's represents the balance. * `token_id` - The token_id of the transferred token (applicable for ERC-1155, ERC-721 and ERC-404 tokens) * `token_type` - The type of the token + * `refetch_after` - when to refetch the token balance + * `retries_count` - number of times the token balance has been retried """ typed_schema "address_token_balances" do field(:value, :decimal) @@ -32,6 +34,8 @@ defmodule Explorer.Chain.Address.TokenBalance do field(:value_fetched_at, :utc_datetime_usec) field(:token_id, :decimal) field(:token_type, :string, null: false) + field(:refetch_after, :utc_datetime_usec) + field(:retries_count, :integer) belongs_to(:address, Address, foreign_key: :address_hash, references: :hash, type: Hash.Address, null: false) @@ -47,7 +51,7 @@ defmodule Explorer.Chain.Address.TokenBalance do timestamps() end - @optional_fields ~w(value value_fetched_at token_id)a + @optional_fields ~w(value value_fetched_at token_id refetch_after retries_count)a @required_fields ~w(address_hash block_number token_contract_address_hash token_type)a @allowed_fields @optional_fields ++ @required_fields @@ -77,7 +81,8 @@ defmodule Explorer.Chain.Address.TokenBalance do where: ((tb.address_hash != ^@burn_address_hash and tb.token_type == "ERC-721") or tb.token_type == "ERC-20" or tb.token_type == "ERC-1155" or tb.token_type == "ERC-404") and - (is_nil(tb.value_fetched_at) or is_nil(tb.value)) + (is_nil(tb.value_fetched_at) or is_nil(tb.value)) and + (is_nil(tb.refetch_after) or tb.refetch_after < ^Timex.now()) ) else from( @@ -87,7 +92,8 @@ defmodule Explorer.Chain.Address.TokenBalance do where: ((tb.address_hash != ^@burn_address_hash and t.type == "ERC-721") or t.type == "ERC-20" or t.type == "ERC-1155" or t.type == "ERC-404") and - (is_nil(tb.value_fetched_at) or is_nil(tb.value)) + (is_nil(tb.value_fetched_at) or is_nil(tb.value)) and + (is_nil(tb.refetch_after) or tb.refetch_after < ^Timex.now()) ) end end diff --git a/apps/explorer/lib/explorer/chain/import/runner/address/token_balances.ex b/apps/explorer/lib/explorer/chain/import/runner/address/token_balances.ex index af07a27626d1..932b6cc748f1 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/address/token_balances.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/address/token_balances.ex @@ -148,6 +148,8 @@ defmodule Explorer.Chain.Import.Runner.Address.TokenBalances do value: fragment("COALESCE(EXCLUDED.value, ?)", token_balance.value), value_fetched_at: fragment("EXCLUDED.value_fetched_at"), token_type: fragment("EXCLUDED.token_type"), + refetch_after: fragment("EXCLUDED.refetch_after"), + retries_count: fragment("EXCLUDED.retries_count"), inserted_at: fragment("LEAST(EXCLUDED.inserted_at, ?)", token_balance.inserted_at), updated_at: fragment("GREATEST(EXCLUDED.updated_at, ?)", token_balance.updated_at) ] diff --git a/apps/explorer/priv/repo/migrations/20240828140638_add_token_balance_retry_fields.exs b/apps/explorer/priv/repo/migrations/20240828140638_add_token_balance_retry_fields.exs new file mode 100644 index 000000000000..3d1fddf8bb8f --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20240828140638_add_token_balance_retry_fields.exs @@ -0,0 +1,10 @@ +defmodule Explorer.Repo.Migrations.AddTokenBalanceRetryFields do + use Ecto.Migration + + def change do + alter table(:address_token_balances) do + add(:refetch_after, :utc_datetime_usec) + add(:retries_count, :smallint) + end + end +end diff --git a/apps/indexer/lib/indexer/fetcher/token_balance.ex b/apps/indexer/lib/indexer/fetcher/token_balance.ex index 1f808effc6db..5bfd3deaffaa 100644 --- a/apps/indexer/lib/indexer/fetcher/token_balance.ex +++ b/apps/indexer/lib/indexer/fetcher/token_balance.ex @@ -9,7 +9,7 @@ defmodule Indexer.Fetcher.TokenBalance do It behaves as a `BufferedTask`, so we can configure the `max_batch_size` and the `max_concurrency` to control how many token balances will be fetched at the same time. - Also, this module set a `retries_count` for each token balance and increment this number to avoid fetching the ones + Also, this module set a `refetch_after` for each token balance in case of failure to avoid fetching the ones that always raise errors interacting with the Smart Contract. """ @@ -32,8 +32,6 @@ defmodule Indexer.Fetcher.TokenBalance do @timeout :timer.minutes(10) - @max_retries 3 - @spec async_fetch( [ %{ @@ -93,7 +91,7 @@ defmodule Indexer.Fetcher.TokenBalance do @doc """ Fetches the given entries (token_balances) from the Smart Contract and import them in our database. - It also increments the `retries_count` to avoid fetching token balances that always raise errors + It also set the `refetch_after` in case of failure to avoid fetching token balances that always raise errors when reading their balance in the Smart Contract. """ @impl BufferedTask @@ -110,7 +108,6 @@ defmodule Indexer.Fetcher.TokenBalance do result = params |> MissingBalanceOfToken.filter_token_balances_params(true, missing_balance_of_tokens) - |> increase_retries_count() |> fetch_from_blockchain(missing_balance_of_tokens) |> import_token_balances() @@ -122,45 +119,22 @@ defmodule Indexer.Fetcher.TokenBalance do end def fetch_from_blockchain(params_list, missing_balance_of_tokens) do - retryable_params_list = - params_list - |> Enum.filter(&(&1.retries_count <= @max_retries)) - |> Enum.uniq_by(&Map.take(&1, [:token_contract_address_hash, :token_id, :address_hash, :block_number])) - - Logger.metadata(count: Enum.count(retryable_params_list)) - - %{fetched_token_balances: fetched_token_balances, failed_token_balances: _failed_token_balances} = - 1..@max_retries - |> Enum.reduce_while(%{fetched_token_balances: [], failed_token_balances: retryable_params_list}, fn _x, acc -> - {:ok, %{fetched_token_balances: fetched_token_balances, failed_token_balances: failed_token_balances}} = - TokenBalances.fetch_token_balances_from_blockchain(acc.failed_token_balances) - - all_token_balances = %{ - fetched_token_balances: acc.fetched_token_balances ++ fetched_token_balances, - failed_token_balances: failed_token_balances - } - - handle_success_balances(fetched_token_balances, missing_balance_of_tokens) - - if Enum.empty?(failed_token_balances) do - {:halt, all_token_balances} - else - failed_token_balances = - failed_token_balances - |> handle_failed_balances() - |> increase_retries_count() - - token_balances_updated_retries_count = - all_token_balances - |> Map.put(:failed_token_balances, failed_token_balances) - - {:cont, token_balances_updated_retries_count} - end - end) + params_list = + Enum.uniq_by(params_list, &Map.take(&1, [:token_contract_address_hash, :token_id, :address_hash, :block_number])) + + Logger.metadata(count: Enum.count(params_list)) + + {:ok, %{fetched_token_balances: fetched_token_balances, failed_token_balances: failed_token_balances}} = + TokenBalances.fetch_token_balances_from_blockchain(params_list) + + handle_success_balances(fetched_token_balances, missing_balance_of_tokens) + failed_balances_to_keep = handle_failed_balances(failed_token_balances) - fetched_token_balances + fetched_token_balances ++ failed_balances_to_keep end + defp handle_success_balances([], _missing_balance_of_tokens), do: :ok + defp handle_success_balances(fetched_token_balances, missing_balance_of_tokens) do successful_token_hashes = fetched_token_balances @@ -178,7 +152,15 @@ defmodule Indexer.Fetcher.TokenBalance do |> MissingBalanceOfToken.mark_as_implemented() end + defp handle_failed_balances([]), do: [] + defp handle_failed_balances(failed_token_balances) do + failed_token_balances + |> handle_missing_balance_of_tokens() + |> handle_other_errors() + end + + defp handle_missing_balance_of_tokens(failed_token_balances) do {missing_balance_of_balances, other_failed_balances} = Enum.split_with(failed_token_balances, fn %{error: :unable_to_decode} -> true @@ -201,9 +183,27 @@ defmodule Indexer.Fetcher.TokenBalance do other_failed_balances end - defp increase_retries_count(params_list) do - params_list - |> Enum.map(&Map.put(&1, :retries_count, &1.retries_count + 1)) + defp handle_other_errors(failed_token_balances) do + Enum.map(failed_token_balances, fn token_balance_params -> + new_retries_count = token_balance_params.retries_count + 1 + + Map.merge(token_balance_params, %{ + retries_count: new_retries_count, + refetch_after: define_refetch_after(new_retries_count) + }) + end) + end + + defp define_refetch_after(retries_count) do + config = Application.get_env(:indexer, __MODULE__) + + coef = config[:exp_timeout_coeff] + max_refetch_interval = config[:max_refetch_interval] + max_retries_count = :math.log(max_refetch_interval / 1000 / coef) + + value = floor(coef * :math.exp(min(retries_count, max_retries_count))) + + Timex.shift(Timex.now(), seconds: value) end def import_token_balances(token_balances_params) do @@ -259,17 +259,14 @@ defmodule Indexer.Fetcher.TokenBalance do end end - defp entry( - %{ - token_contract_address_hash: token_contract_address_hash, - address_hash: address_hash, - block_number: block_number, - token_type: token_type, - token_id: token_id - } = token_balance - ) do - retries_count = Map.get(token_balance, :retries_count, 0) - + defp entry(%{ + token_contract_address_hash: token_contract_address_hash, + address_hash: address_hash, + block_number: block_number, + token_type: token_type, + token_id: token_id, + retries_count: retries_count + }) do token_id_int = case token_id do %Decimal{} -> Decimal.to_integer(token_id) @@ -277,7 +274,7 @@ defmodule Indexer.Fetcher.TokenBalance do _ -> token_id end - {address_hash.bytes, token_contract_address_hash.bytes, block_number, token_type, token_id_int, retries_count} + {address_hash.bytes, token_contract_address_hash.bytes, block_number, token_type, token_id_int, retries_count || 0} end defp format_params( diff --git a/apps/indexer/lib/indexer/token_balances.ex b/apps/indexer/lib/indexer/token_balances.ex index 5348bd23826b..78180c997864 100644 --- a/apps/indexer/lib/indexer/token_balances.ex +++ b/apps/indexer/lib/indexer/token_balances.ex @@ -8,9 +8,7 @@ defmodule Indexer.TokenBalances do require Indexer.Tracer require Logger - alias Explorer.Chain alias Explorer.Token.BalanceReader - alias Indexer.Fetcher.TokenBalance alias Indexer.Tracer @nft_balance_function_abi [ @@ -85,7 +83,7 @@ defmodule Indexer.TokenBalances do requested_token_balances |> handle_killed_tasks(token_balances) |> unfetched_token_balances(fetched_token_balances) - |> schedule_token_balances + |> log_fetching_errors() failed_token_balances = requested_token_balances @@ -119,27 +117,6 @@ defmodule Indexer.TokenBalances do Map.merge(token_balance, %{value: nil, value_fetched_at: nil, error: error_message}) end - defp schedule_token_balances([]), do: nil - - defp schedule_token_balances(unfetched_token_balances) do - Logger.debug(fn -> "#{Enum.count(unfetched_token_balances)} token balances will be retried" end) - - log_fetching_errors(unfetched_token_balances) - - unfetched_token_balances - |> Enum.map(fn token_balance -> - {:ok, address_hash} = Chain.string_to_address_hash(token_balance.address_hash) - {:ok, token_hash} = Chain.string_to_address_hash(token_balance.token_contract_address_hash) - - Map.merge(token_balance, %{ - address_hash: address_hash, - token_contract_address_hash: token_hash, - block_number: token_balance.block_number - }) - end) - |> TokenBalance.async_fetch(false) - end - defp ignore_request_with_errors(%{value: nil, value_fetched_at: nil, error: _error}), do: false defp ignore_request_with_errors(_token_balance), do: true @@ -161,7 +138,7 @@ defmodule Indexer.TokenBalances do end) if Enum.any?(error_messages) do - Logger.debug( + Logger.error( [ "Errors while fetching TokenBalances through Contract interaction: \n", error_messages diff --git a/apps/indexer/test/indexer/fetcher/token_balance_test.exs b/apps/indexer/test/indexer/fetcher/token_balance_test.exs index e39cc6e98ae6..0c4fb0be44f8 100644 --- a/apps/indexer/test/indexer/fetcher/token_balance_test.exs +++ b/apps/indexer/test/indexer/fetcher/token_balance_test.exs @@ -28,6 +28,22 @@ defmodule Indexer.Fetcher.TokenBalanceTest do {address_hash_bytes, token_contract_address_hash_bytes, 1000, "ERC-20", nil, 0} ] end + + test "omits failed balances with refetch_after in future" do + %Address.TokenBalance{ + address_hash: %Hash{bytes: address_hash_bytes}, + token_contract_address_hash: %Hash{bytes: token_contract_address_hash_bytes}, + block_number: block_number + } = insert(:token_balance, value_fetched_at: nil) + + insert(:token_balance, value_fetched_at: DateTime.utc_now()) + + insert(:token_balance, refetch_after: Timex.shift(Timex.now(), hours: 1)) + + assert TokenBalance.init([], &[&1 | &2], nil) == [ + {address_hash_bytes, token_contract_address_hash_bytes, block_number, "ERC-20", nil, 0} + ] + end end describe "run/3" do @@ -70,86 +86,6 @@ defmodule Indexer.Fetcher.TokenBalanceTest do assert token_balance_updated.value_fetched_at != nil end - test "imports the given token balances from 2nd retry" do - %Address.TokenBalance{ - address_hash: %Hash{bytes: address_hash_bytes} = address_hash, - token_contract_address_hash: %Hash{bytes: token_contract_address_hash_bytes}, - block_number: block_number - } = insert(:token_balance, value_fetched_at: nil, value: nil) - - expect( - EthereumJSONRPC.Mox, - :json_rpc, - fn [%{id: id, method: "eth_call", params: [%{data: _, to: _}, _]}], _options -> - {:ok, - [ - %{ - id: id, - jsonrpc: "2.0", - error: %{code: -32015, message: "VM execution error.", data: ""} - } - ]} - end - ) - - expect( - EthereumJSONRPC.Mox, - :json_rpc, - fn [%{id: id, method: "eth_call", params: [%{data: _, to: _}, _]}], _options -> - {:ok, - [ - %{ - id: id, - jsonrpc: "2.0", - result: "0x00000000000000000000000000000000000000000000d3c21bcecceda1000000" - } - ]} - end - ) - - assert TokenBalance.run( - [{address_hash_bytes, token_contract_address_hash_bytes, block_number, "ERC-20", nil, 0}], - nil - ) == :ok - - token_balance_updated = Repo.get_by(Address.TokenBalance, address_hash: address_hash) - - assert token_balance_updated.value == Decimal.new(1_000_000_000_000_000_000_000_000) - assert token_balance_updated.value_fetched_at != nil - end - - test "does not try to fetch the token balance again if the retry is over" do - max_retries = 3 - - Application.put_env(:indexer, :token_balance_max_retries, max_retries) - - token_balance_a = insert(:token_balance, value_fetched_at: nil, value: nil) - token_balance_b = insert(:token_balance, value_fetched_at: nil, value: nil) - - token_balances = [ - { - token_balance_a.address_hash.bytes, - token_balance_a.token_contract_address_hash.bytes, - "ERC-20", - nil, - token_balance_a.block_number, - # this token balance must be ignored - max_retries - }, - { - token_balance_b.address_hash.bytes, - token_balance_b.token_contract_address_hash.bytes, - "ERC-20", - nil, - token_balance_b.block_number, - # this token balance still have to be retried - max_retries - 2 - } - ] - - assert TokenBalance.run(token_balances, nil) == :ok - end - test "fetches duplicate params only once" do %Address.TokenBalance{ address_hash: %Hash{bytes: address_hash_bytes} = address_hash, @@ -233,7 +169,7 @@ defmodule Indexer.Fetcher.TokenBalanceTest do assert %{currently_implemented: true} = Repo.one(MissingBalanceOfToken) end - test "in case of error deletes token balance placeholders below the given number and inserts new missing balanceOf tokens" do + test "in case of execution reverted error deletes token balance placeholders below the given number and inserts new missing balanceOf tokens" do address = insert(:address) %{contract_address_hash: token_contract_address_hash} = insert(:token) @@ -272,6 +208,46 @@ defmodule Indexer.Fetcher.TokenBalanceTest do assert Repo.all(Address.TokenBalance) == [] end + + test "in case of other error updates the refetch_after and retries_count of token balance" do + address = insert(:address) + %{contract_address_hash: token_contract_address_hash} = insert(:token) + + insert(:token_balance, + token_contract_address_hash: token_contract_address_hash, + address: address, + block_number: 1, + value_fetched_at: nil, + value: nil, + refetch_after: nil, + retries_count: nil + ) + + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn [%{id: id, method: "eth_call", params: [%{data: _, to: _}, _]}], _options -> + {:ok, + [ + %{ + id: id, + jsonrpc: "2.0", + error: %{code: "-32000", message: "other error"} + } + ]} + end + ) + + assert TokenBalance.run( + [ + {address.hash.bytes, token_contract_address_hash.bytes, 1, "ERC-20", nil, 0} + ], + nil + ) == :ok + + assert %{retries_count: 1, refetch_after: refetch_after} = Repo.one(Address.TokenBalance) + refute is_nil(refetch_after) + end end describe "import_token_balances/1" do diff --git a/config/runtime.exs b/config/runtime.exs index f3ffebf53bae..70fdba23d9f5 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -708,7 +708,9 @@ config :indexer, Indexer.Fetcher.Token, concurrency: ConfigHelper.parse_integer_ config :indexer, Indexer.Fetcher.TokenBalance, batch_size: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_BALANCES_BATCH_SIZE", 100), - concurrency: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_BALANCES_CONCURRENCY", 10) + concurrency: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_BALANCES_CONCURRENCY", 10), + max_refetch_interval: ConfigHelper.parse_time_env_var("INDEXER_TOKEN_BALANCES_MAX_REFETCH_INTERVAL", "168h"), + exp_timeout_coeff: ConfigHelper.parse_integer_env_var("INDEXER_TOKEN_BALANCES_EXPONENTIAL_TIMEOUT_COEFF", 100) config :indexer, Indexer.Fetcher.OnDemand.TokenBalance, threshold: ConfigHelper.parse_time_env_var("TOKEN_BALANCE_ON_DEMAND_FETCHER_THRESHOLD", "1h"), diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index bf5a2daac6b0..5ec959df46a3 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -204,6 +204,8 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_TOKEN_CONCURRENCY= # INDEXER_TOKEN_BALANCES_BATCH_SIZE= # INDEXER_TOKEN_BALANCES_CONCURRENCY= +# INDEXER_TOKEN_BALANCES_MAX_REFETCH_INTERVAL= +# INDEXER_TOKEN_BALANCES_EXPONENTIAL_TIMEOUT_COEFF= # INDEXER_TX_ACTIONS_ENABLE= # INDEXER_TX_ACTIONS_MAX_TOKEN_CACHE_SIZE= # INDEXER_TX_ACTIONS_REINDEX_FIRST_BLOCK= From 21b9572b65b3da78b0604752deccbdd6919e13e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 22:20:43 +0300 Subject: [PATCH 137/363] chore(deps): bump redix from 1.5.1 to 1.5.2 (#10738) Bumps [redix](https://github.com/whatyouhide/redix) from 1.5.1 to 1.5.2. - [Changelog](https://github.com/whatyouhide/redix/blob/main/CHANGELOG.md) - [Commits](https://github.com/whatyouhide/redix/compare/v1.5.1...v1.5.2) --- updated-dependencies: - dependency-name: redix dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.lock b/mix.lock index 94d5950f9ebf..745e0972418c 100644 --- a/mix.lock +++ b/mix.lock @@ -92,7 +92,7 @@ "mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"}, "msgpax": {:hex, :msgpax, "2.4.0", "4647575c87cb0c43b93266438242c21f71f196cafa268f45f91498541148c15d", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "ca933891b0e7075701a17507c61642bf6e0407bb244040d5d0a58597a06369d2"}, "nimble_csv": {:hex, :nimble_csv, "1.2.0", "4e26385d260c61eba9d4412c71cea34421f296d5353f914afe3f2e71cce97722", [:mix], [], "hexpm", "d0628117fcc2148178b034044c55359b26966c6eaa8e2ce15777be3bbc91b12a"}, - "nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"}, + "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "number": {:hex, :number, "1.0.5", "d92136f9b9382aeb50145782f116112078b3465b7be58df1f85952b8bb399b0f", [:mix], [{:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "c0733a0a90773a66582b9e92a3f01290987f395c972cb7d685f51dd927cd5169"}, "numbers": {:hex, :numbers, "5.2.4", "f123d5bb7f6acc366f8f445e10a32bd403c8469bdbce8ce049e1f0972b607080", [:mix], [{:coerce, "~> 1.0", [hex: :coerce, repo: "hexpm", optional: false]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "eeccf5c61d5f4922198395bf87a465b6f980b8b862dd22d28198c5e6fab38582"}, @@ -123,7 +123,7 @@ "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "ratio": {:hex, :ratio, "2.4.2", "c8518f3536d49b1b00d88dd20d49f8b11abb7819638093314a6348139f14f9f9", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:numbers, "~> 5.2.0", [hex: :numbers, repo: "hexpm", optional: false]}], "hexpm", "441ef6f73172a3503de65ccf1769030997b0d533b1039422f1e5e0e0b4cbf89e"}, "recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"}, - "redix": {:hex, :redix, "1.5.1", "a2386971e69bf23630fb3a215a831b5478d2ee7dc9ea7ac811ed89186ab5d7b7", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:nimble_options, "~> 0.5.0 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "85224eb2b683c516b80d472eb89b76067d5866913bf0be59d646f550de71f5c4"}, + "redix": {:hex, :redix, "1.5.2", "ab854435a663f01ce7b7847f42f5da067eea7a3a10c0a9d560fa52038fd7ab48", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:nimble_options, "~> 0.5.0 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "78538d184231a5d6912f20567d76a49d1be7d3fca0e1aaaa20f4df8e1142dcb8"}, "remote_ip": {:hex, :remote_ip, "1.2.0", "fb078e12a44414f4cef5a75963c33008fe169b806572ccd17257c208a7bc760f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2ff91de19c48149ce19ed230a81d377186e4412552a597d6a5137373e5877cb7"}, "rustler_precompiled": {:hex, :rustler_precompiled, "0.7.1", "ecadf02cc59a0eccbaed6c1937303a5827fbcf60010c541595e6d3747d3d0f9f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "b9e4657b99a1483ea31502e1d58c464bedebe9028808eda45c3a429af4550c66"}, "sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"}, From 3132cc793a0e06f2db17593230bd35638f35cef3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 22:21:24 +0300 Subject: [PATCH 138/363] chore(deps): bump cldr_utils from 2.28.1 to 2.28.2 (#10737) Bumps [cldr_utils](https://github.com/elixir-cldr/cldr_utils) from 2.28.1 to 2.28.2. - [Release notes](https://github.com/elixir-cldr/cldr_utils/releases) - [Changelog](https://github.com/elixir-cldr/cldr_utils/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-cldr/cldr_utils/commits/v2.28.2) --- updated-dependencies: - dependency-name: cldr_utils dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 745e0972418c..6df0f6f0aaec 100644 --- a/mix.lock +++ b/mix.lock @@ -16,7 +16,7 @@ "castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"}, "cbor": {:hex, :cbor, "1.0.1", "39511158e8ea5a57c1fcb9639aaa7efde67129678fee49ebbda780f6f24959b0", [:mix], [], "hexpm", "5431acbe7a7908f17f6a9cd43311002836a34a8ab01876918d8cfb709cd8b6a2"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, - "cldr_utils": {:hex, :cldr_utils, "2.28.1", "3d85c835e1d0b7bceb9feed1647025ff7df59180246f13b582422f12b1afd52c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "79a5f645481d09b1372962384aa275d67d69273e73e3b38a9fee363eb57c2b79"}, + "cldr_utils": {:hex, :cldr_utils, "2.28.2", "f500667164a9043369071e4f9dcef31f88b8589b2e2c07a1eb9f9fa53cb1dce9", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "c506eb1a170ba7cdca59b304ba02a56795ed119856662f6b1a420af80ec42551"}, "cloak": {:hex, :cloak, "1.1.4", "aba387b22ea4d80d92d38ab1890cc528b06e0e7ef2a4581d71c3fdad59e997e7", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "92b20527b9aba3d939fab0dd32ce592ff86361547cfdc87d74edce6f980eb3d7"}, "cloak_ecto": {:hex, :cloak_ecto, "1.3.0", "0de127c857d7452ba3c3367f53fb814b0410ff9c680a8d20fbe8b9a3c57a1118", [:mix], [{:cloak, "~> 1.1.1", [hex: :cloak, repo: "hexpm", optional: false]}, {:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm", "314beb0c123b8a800418ca1d51065b27ba3b15f085977e65c0f7b2adab2de1cc"}, "coerce": {:hex, :coerce, "1.0.1", "211c27386315dc2894ac11bc1f413a0e38505d808153367bd5c6e75a4003d096", [:mix], [], "hexpm", "b44a691700f7a1a15b4b7e2ff1fa30bebd669929ac8aa43cffe9e2f8bf051cf1"}, From e12b010a0eab8834dfda05d0713014bf6b20c3f7 Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Tue, 10 Sep 2024 14:35:41 +0300 Subject: [PATCH 139/363] feat: support for filecoin native addresses (#10468) * feat: implement `Ecto` type for filecoin address * fix: use proper hashing algorithm for checksum * refactor: avoid hardcoding * feat: add `NativeAddress.ID` type * chore: add `alias Blake2.Blake2b` to fix credo * feat: implement `Ecto` type for filecoin address * chore: rename id address module * feat: fix formatting * feat: add a table for pending address operations * feat: add filecoin fields to addresses relation * feat: create pending operation when new address is imported * feat: implement filecoin native address fetcher * chore: remove merge artifacts * fix: cspell * fix: alias in `native_address_test.exs` * fix: cspell * fix: lock address and corresponding operation for update * feat: trigger async fetch of address info from block fetcher * fix: compilation deadlock * fix: add fetcher supervisor case * feat: add migrator * fix: create pending address operation even if the address exists * feat: render filecoin address info in API v2 views * fix: user controller test * feat: add gauge metric for pending address operations * feat: save http error code for failed fetches * chore: rename fetcher * fix: rebase artifacts * chore: list migrator envs in `common-blockscout.env` * chore: process review comments by @vbaranov * chore: migrate from `blake2_elixir` to `blake2` package * chore: reduce log level to `debug` * chore: set infinity timeout for gauge metric query * refactor: remove redundant `Multi` in filling migration --- .../views/api/v2/filecoin_view.ex | 35 ++ .../block_scout_web/views/api/v2/helper.ex | 14 + .../account/api/v2/user_controller_test.exs | 20 +- apps/explorer/lib/explorer/application.ex | 5 +- apps/explorer/lib/explorer/chain/address.ex | 186 +++++--- .../lib/explorer/chain/filecoin/id.ex | 157 +++++++ .../explorer/chain/filecoin/native_address.ex | 408 ++++++++++++++++++ .../filecoin/pending_address_operation.ex | 73 ++++ apps/explorer/lib/explorer/chain/hash.ex | 2 +- .../explorer/chain/import/runner/addresses.ex | 42 +- .../explorer/chain/import/runner/blocks.ex | 9 +- .../explorer/chain/import/runner/helper.ex | 22 + .../filecoin_pending_address_operations.ex | 71 +++ apps/explorer/mix.exs | 4 +- ...4142_create_pending_address_operations.exs | 23 + ...34138_add_chain_type_fields_to_address.exs | 11 + .../chain/filecoin/native_address_test.exs | 175 ++++++++ apps/indexer/lib/indexer/application.ex | 22 +- .../lib/indexer/block/catchup/fetcher.ex | 2 + apps/indexer/lib/indexer/block/fetcher.ex | 12 +- .../lib/indexer/block/realtime/fetcher.ex | 2 + .../indexer/fetcher/filecoin/address_info.ex | 215 +++++++++ .../lib/indexer/fetcher/filecoin/beryx_api.ex | 50 +++ ...in_pending_address_operations_collector.ex | 29 ++ .../pending_block_operations_collector.ex | 2 +- apps/indexer/lib/indexer/supervisor.ex | 3 + .../indexer/block/catchup/fetcher_test.exs | 1 + .../test/indexer/block/fetcher_test.exs | 4 + .../indexer/block/realtime/fetcher_test.exs | 4 + ...filecoin_native_address_supervisor_case.ex | 17 + config/runtime.exs | 23 + cspell.json | 5 + docker-compose/envs/common-blockscout.env | 7 + mix.lock | 1 + 34 files changed, 1579 insertions(+), 77 deletions(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex create mode 100644 apps/explorer/lib/explorer/chain/filecoin/id.ex create mode 100644 apps/explorer/lib/explorer/chain/filecoin/native_address.ex create mode 100644 apps/explorer/lib/explorer/chain/filecoin/pending_address_operation.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/helper.ex create mode 100644 apps/explorer/lib/explorer/migrator/filecoin_pending_address_operations.ex create mode 100644 apps/explorer/priv/filecoin/migrations/20240801134142_create_pending_address_operations.exs create mode 100644 apps/explorer/priv/filecoin/migrations/20240807134138_add_chain_type_fields_to_address.exs create mode 100644 apps/explorer/test/explorer/chain/filecoin/native_address_test.exs create mode 100644 apps/indexer/lib/indexer/fetcher/filecoin/address_info.ex create mode 100644 apps/indexer/lib/indexer/fetcher/filecoin/beryx_api.ex create mode 100644 apps/indexer/lib/indexer/prometheus/collector/filecoin_pending_address_operations_collector.ex rename apps/indexer/lib/indexer/prometheus/{ => collector}/pending_block_operations_collector.ex (91%) create mode 100644 apps/indexer/test/support/indexer/fetcher/filecoin_native_address_supervisor_case.ex diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex new file mode 100644 index 000000000000..191fbf6f6918 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex @@ -0,0 +1,35 @@ +defmodule BlockScoutWeb.API.V2.FilecoinView do + @moduledoc """ + View functions for rendering Filecoin-related data in JSON format. + """ + + alias Explorer.Chain.Address + + @doc """ + Extends the json output with a sub-map containing information related to + Filecoin native addressing. + """ + @spec extend_address_json_response(map(), Address.t()) :: map() + def extend_address_json_response(result, %Address{} = address) do + filecoin_id = Map.get(address, :filecoin_id) + filecoin_robust = Map.get(address, :filecoin_robust) + filecoin_actor_type = Map.get(address, :filecoin_actor_type) + + is_fetched = + Enum.all?( + [ + filecoin_id, + filecoin_robust, + filecoin_actor_type + ], + &(not is_nil(&1)) + ) + + Map.put(result, :filecoin, %{ + is_fetched: is_fetched, + id: filecoin_id, + robust: filecoin_robust, + actor_type: filecoin_actor_type + }) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex index 5f483b191128..8df0acb81b16 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex @@ -90,6 +90,7 @@ defmodule BlockScoutWeb.API.V2.Helper do "ens_domain_name" => address.ens_domain_name, "metadata" => address.metadata } + |> address_chain_type_fields(address) end def address_with_info(%NotLoaded{}, address_hash) do @@ -120,6 +121,19 @@ defmodule BlockScoutWeb.API.V2.Helper do } end + case Application.compile_env(:explorer, :chain_type) do + :filecoin -> + defp address_chain_type_fields(result, address) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + BlockScoutWeb.API.V2.FilecoinView.extend_address_json_response(result, address) + end + + _ -> + defp address_chain_type_fields(result, _address) do + result + end + end + defp minimal_proxy_pattern?(proxy_implementations) do proxy_implementations.proxy_type == :eip1167 end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs index 30f44f611bcd..cfeeaa31012a 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs @@ -182,7 +182,9 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do |> Map.get("items") assert Enum.all?(created, fn {_, _, map} -> - map in response + Enum.any?(response, fn item -> + addresses_json_match?(map, item) + end) end) end @@ -237,7 +239,11 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do |> json_response(200) |> Map.get("items") - assert Enum.all?(created, fn {_, _, map} -> map in response end) + assert Enum.all?(created, fn {_, _, map} -> + Enum.any?(response, fn item -> + addresses_json_match?(map, item) + end) + end) {_, _, %{"id" => id}} = Enum.at(created, 0) @@ -1264,4 +1270,14 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do assert second_page_resp["next_page_params"] == nil compare_item(Enum.at(list, 0), Enum.at(second_page_resp["items"], 0)) end + + defp addresses_json_match?(expected, actual) do + Enum.all?(expected, fn {key, value} -> + case value do + # Recursively compare nested maps + %{} -> addresses_json_match?(value, actual[key]) + _ -> actual[key] == value + end + end) + end end diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index 8159ef8179ff..ba57a31405ab 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -142,8 +142,9 @@ defmodule Explorer.Application do configure(Explorer.Migrator.TransactionBlockConsensus), configure(Explorer.Migrator.TokenTransferBlockConsensus), configure(Explorer.Migrator.RestoreOmittedWETHTransfers), - configure_chain_type_dependent_process(Explorer.Chain.Cache.StabilityValidatorsCounters, :stability), - configure_mode_dependent_process(Explorer.Migrator.ShrinkInternalTransactions, :indexer) + configure(Explorer.Migrator.FilecoinPendingAddressOperations), + configure_mode_dependent_process(Explorer.Migrator.ShrinkInternalTransactions, :indexer), + configure_chain_type_dependent_process(Explorer.Chain.Cache.StabilityValidatorsCounters, :stability) ] |> List.flatten() diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index e93197069860..52c1d662a951 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -1,15 +1,10 @@ -defmodule Explorer.Chain.Address do +defmodule Explorer.Chain.Address.Schema do @moduledoc """ - A stored representation of a web3 address. - """ - - require Bitwise + A stored representation of a web3 address. - use Explorer.Schema - - alias Ecto.Association.NotLoaded - alias Ecto.Changeset - alias Explorer.{Chain, PagingOptions, Repo} + Changes in the schema should be reflected in the bulk import module: + - Explorer.Chain.Import.Runner.Addresses + """ alias Explorer.Chain.{ Address, @@ -25,13 +20,128 @@ defmodule Explorer.Chain.Address do Withdrawal } + alias Explorer.Chain.Cache.{Accounts, NetVersion} + alias Explorer.Chain.SmartContract.Proxy.Models.Implementation + + @chain_type_fields (case Application.compile_env(:explorer, :chain_type) do + :filecoin -> + alias Explorer.Chain.Filecoin.{IDAddress, NativeAddress} + + quote do + [ + field(:filecoin_id, IDAddress), + field(:filecoin_robust, NativeAddress), + field( + :filecoin_actor_type, + Ecto.Enum, + values: + Enum.with_index([ + :account, + :cron, + :datacap, + :eam, + :ethaccount, + :evm, + :init, + :market, + :miner, + :multisig, + :paych, + :placeholder, + :power, + :reward, + :system, + :verifreg + ]) + ) + ] + end + + _ -> + [] + end) + + defmacro generate do + quote do + @primary_key false + @primary_key false + typed_schema "addresses" do + field(:hash, Hash.Address, primary_key: true) + field(:fetched_coin_balance, Wei) + field(:fetched_coin_balance_block_number, :integer) :: Block.block_number() | nil + field(:contract_code, Data) + field(:nonce, :integer) + field(:decompiled, :boolean, default: false) + field(:verified, :boolean, default: false) + field(:has_decompiled_code?, :boolean, virtual: true) + field(:stale?, :boolean, virtual: true) + field(:transactions_count, :integer) + field(:token_transfers_count, :integer) + field(:gas_used, :integer) + field(:ens_domain_name, :string, virtual: true) + field(:metadata, :any, virtual: true) + + # todo: remove virtual field for a single implementation when frontend is bound to "implementations" object value in API + field(:implementation, :any, virtual: true) + + has_one(:smart_contract, SmartContract, references: :hash) + has_one(:token, Token, foreign_key: :contract_address_hash, references: :hash) + has_one(:proxy_implementations, Implementation, foreign_key: :proxy_address_hash, references: :hash) + + has_one( + :contracts_creation_internal_transaction, + InternalTransaction, + foreign_key: :created_contract_address_hash, + references: :hash + ) + + has_one( + :contracts_creation_transaction, + Transaction, + foreign_key: :created_contract_address_hash, + references: :hash + ) + + has_many(:names, Address.Name, foreign_key: :address_hash, references: :hash) + has_many(:decompiled_smart_contracts, DecompiledSmartContract, foreign_key: :address_hash, references: :hash) + has_many(:withdrawals, Withdrawal, foreign_key: :address_hash, references: :hash) + + timestamps() + + unquote_splicing(@chain_type_fields) + end + end + end +end + +defmodule Explorer.Chain.Address do + @moduledoc """ + A stored representation of a web3 address. + """ + + require Bitwise + require Explorer.Chain.Address.Schema + + use Explorer.Schema + + alias Ecto.Association.NotLoaded + alias Ecto.Changeset alias Explorer.Chain.Cache.{Accounts, NetVersion} alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.SmartContract.Proxy.Models.Implementation + alias Explorer.Chain.{Address, Hash} + alias Explorer.{Chain, PagingOptions, Repo} @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce decompiled verified gas_used transactions_count token_transfers_count)a + @chain_type_optional_attrs (case Application.compile_env(:explorer, :chain_type) do + :filecoin -> + ~w(filecoin_id filecoin_robust filecoin_actor_type)a + + _ -> + [] + end) @required_attrs ~w(hash)a - @allowed_attrs @optional_attrs ++ @required_attrs + @allowed_attrs @optional_attrs ++ @required_attrs ++ @chain_type_optional_attrs @typedoc """ Hash of the public key for this address. @@ -74,54 +184,18 @@ defmodule Explorer.Chain.Address do * `inserted_at` - when this address was inserted * `updated_at` - when this address was last updated * `ens_domain_name` - virtual field for ENS domain name passing - + #{case Application.compile_env(:explorer, :chain_type) do + :filecoin -> """ + * `filecoin_native_address` - robust f0/f1/f2/f3/f4 Filecoin address + * `filecoin_id_address` - short f0 Filecoin address that may change during chain reorgs + * `filecoin_actor_type` - type of actor associated with the Filecoin address + """ + _ -> "" + end} `fetched_coin_balance` and `fetched_coin_balance_block_number` may be updated when a new coin_balance row is fetched. They may also be updated when the balance is fetched via the on demand fetcher. """ - @primary_key false - typed_schema "addresses" do - field(:hash, Hash.Address, primary_key: true) - field(:fetched_coin_balance, Wei) - field(:fetched_coin_balance_block_number, :integer) :: Block.block_number() | nil - field(:contract_code, Data) - field(:nonce, :integer) - field(:decompiled, :boolean, default: false) - field(:verified, :boolean, default: false) - field(:has_decompiled_code?, :boolean, virtual: true) - field(:stale?, :boolean, virtual: true) - field(:transactions_count, :integer) - field(:token_transfers_count, :integer) - field(:gas_used, :integer) - field(:ens_domain_name, :string, virtual: true) - field(:metadata, :any, virtual: true) - - # todo: remove virtual field for a single implementation when frontend is bound to "implementations" object value in API - field(:implementation, :any, virtual: true) - - has_one(:smart_contract, SmartContract, references: :hash) - has_one(:token, Token, foreign_key: :contract_address_hash, references: :hash) - has_one(:proxy_implementations, Implementation, foreign_key: :proxy_address_hash, references: :hash) - - has_one( - :contracts_creation_internal_transaction, - InternalTransaction, - foreign_key: :created_contract_address_hash, - references: :hash - ) - - has_one( - :contracts_creation_transaction, - Transaction, - foreign_key: :created_contract_address_hash, - references: :hash - ) - - has_many(:names, Address.Name, foreign_key: :address_hash, references: :hash) - has_many(:decompiled_smart_contracts, DecompiledSmartContract, foreign_key: :address_hash, references: :hash) - has_many(:withdrawals, Withdrawal, foreign_key: :address_hash, references: :hash) - - timestamps() - end + Explorer.Chain.Address.Schema.generate() @balance_changeset_required_attrs @required_attrs ++ ~w(fetched_coin_balance fetched_coin_balance_block_number)a diff --git a/apps/explorer/lib/explorer/chain/filecoin/id.ex b/apps/explorer/lib/explorer/chain/filecoin/id.ex new file mode 100644 index 000000000000..74ce571e75c9 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/filecoin/id.ex @@ -0,0 +1,157 @@ +defmodule Explorer.Chain.Filecoin.IDAddress do + @moduledoc """ + Handles Filecoin ID addresses, wrapping the `NativeAddress` type. + """ + + alias Explorer.Chain.Filecoin.NativeAddress + alias Poison.Encoder.BitString + + require Integer + + defstruct ~w(value)a + + @protocol_indicator 0 + + use Ecto.Type + + @type t :: %__MODULE__{value: binary()} + + @impl Ecto.Type + @spec type() :: :binary + def type, do: :binary + + defp to_native_address(%__MODULE__{value: value}) do + %NativeAddress{ + protocol_indicator: @protocol_indicator, + payload: value + } + end + + @doc """ + Casts a binary string to a `Explorer.Chain.Filecoin.IDAddress`. + + ## Examples + + iex> Explorer.Chain.Filecoin.IDAddress.cast("f01729") + {:ok, %Explorer.Chain.Filecoin.IDAddress{value: <<193, 13>>}} + + iex> Explorer.Chain.Filecoin.IDAddress.cast(%Explorer.Chain.Filecoin.IDAddress{value: <<193, 13>>}) + {:ok, %Explorer.Chain.Filecoin.IDAddress{value: <<193, 13>>}} + + iex> Explorer.Chain.Filecoin.IDAddress.cast("invalid") + :error + """ + @impl Ecto.Type + def cast(address_string) when is_binary(address_string) do + address_string + |> NativeAddress.cast() + |> case do + {:ok, + %NativeAddress{ + protocol_indicator: @protocol_indicator, + payload: value + }} -> + {:ok, %__MODULE__{value: value}} + + :error -> + :error + end + end + + @impl Ecto.Type + def cast(%__MODULE__{} = address), do: {:ok, address} + + @impl Ecto.Type + def cast(_), do: :error + + @doc """ + Dumps an `Explorer.Chain.Filecoin.IDAddress` to its binary representation. + + ## Examples + + iex> address = %Explorer.Chain.Filecoin.IDAddress{value: <<193, 13>>} + iex> Explorer.Chain.Filecoin.IDAddress.dump(address) + {:ok, <<0, 193, 13>>} + + iex> Explorer.Chain.Filecoin.IDAddress.dump("invalid") + :error + """ + @impl Ecto.Type + def dump(%__MODULE__{} = address) do + address + |> to_native_address() + |> NativeAddress.dump() + end + + def dump(_), do: :error + + @doc """ + Loads a binary representation of an `Explorer.Chain.Filecoin.IDAddress`. + + ## Examples + + iex> Explorer.Chain.Filecoin.IDAddress.load(<<0, 193, 13>>) + {:ok, %Explorer.Chain.Filecoin.IDAddress{value: <<193, 13>>}} + + iex> Explorer.Chain.Filecoin.IDAddress.load("invalid") + :error + """ + @impl Ecto.Type + def load(bytes) when is_binary(bytes) do + bytes + |> NativeAddress.load() + |> case do + {:ok, + %NativeAddress{ + protocol_indicator: @protocol_indicator, + payload: value + }} -> + {:ok, %__MODULE__{value: value}} + + _ -> + :error + end + end + + def load(_), do: :error + + @doc """ + Converts an `Explorer.Chain.Filecoin.IDAddress` to its string representation. + + ## Examples + + iex> address = %Explorer.Chain.Filecoin.IDAddress{value: <<193, 13>>} + iex> Explorer.Chain.Filecoin.IDAddress.to_string(address) + "f01729" + """ + @spec to_string(t()) :: String.t() + def to_string(%__MODULE__{} = address) do + address + |> to_native_address() + |> NativeAddress.to_string() + end + + defimpl String.Chars do + def to_string(address) do + @for.to_string(address) + end + end + + defimpl Poison.Encoder do + def encode(address, options) do + address + |> to_string() + |> BitString.encode(options) + end + end + + defimpl Jason.Encoder do + alias Jason.Encode + + def encode(address, opts) do + address + |> to_string() + |> Encode.string(opts) + end + end +end diff --git a/apps/explorer/lib/explorer/chain/filecoin/native_address.ex b/apps/explorer/lib/explorer/chain/filecoin/native_address.ex new file mode 100644 index 000000000000..aaeac0004d76 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/filecoin/native_address.ex @@ -0,0 +1,408 @@ +defmodule Explorer.Chain.Filecoin.NativeAddress do + @moduledoc """ + Handles Filecoin addresses by parsing, validating, and converting them to and + from their binary representations. + + Addresses are encoded to binary according to the [Filecoin Address + spec](https://spec.filecoin.io/appendix/address/#section-appendix.address.validatechecksum). + Details about f4 addresses are provided in + [FIP-0048](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0048.md). + + Internally, f0/f1/f2/f3 addresses are stored as a binary with the following structure: + + |--------------------|---------| + | protocol indicator | payload | + |--------------------|---------| + | 1 byte | n bytes | + |--------------------|---------| + + 1. The first byte is the protocol indicator. The values are: + - `0` for f0 addresses + - `1` for f1 addresses + - `2` for f2 addresses + - `3` for f3 addresses + + 2. The remaining bytes are the payload. + + f4 addresses are stored as a binary with the following structure: + + |--------------------|----------|---------| + | protocol indicator | actor id | payload | + |--------------------|----------|---------| + | 1 byte | 1 byte | n bytes | + |--------------------|----------|---------| + + 1. The first byte is the protocol indicator. The value is `4`. + 2. The second byte is the actor id. + 3. The remaining bytes are the payload. + """ + + alias Explorer.Chain.Hash + alias Poison.Encoder.BitString + alias Varint.LEB128 + + use Ecto.Type + + defstruct ~w(protocol_indicator actor_id payload checksum)a + + @checksum_bytes_count 4 + + @protocol_indicator_bytes_count 1 + @max_protocol_indicator 2 ** (@protocol_indicator_bytes_count * Hash.bits_per_byte()) - 1 + + @min_address_string_length 3 + + # Payload sizes: + # f1 -- 20 bytes + # f2 -- 20 bytes + # f3 -- 48 bytes + @protocol_indicator_to_payload_byte_count %{ + 1 => 20, + # For some reason, specs tell that payload for f2 is a SHA256 hash, which is + # 32 bytes long. However, in practice, it is 20 bytes long... + # + # https://spec.filecoin.io/appendix/address/#section-appendix.address.protocol-2-actor + 2 => 20, + 3 => 48 + } + @standard_protocol_indicators Map.keys(@protocol_indicator_to_payload_byte_count) + + @type t :: %__MODULE__{ + protocol_indicator: non_neg_integer(), + actor_id: non_neg_integer() | nil, + payload: binary(), + checksum: binary() | nil + } + + @impl Ecto.Type + @spec type() :: :binary + def type, do: :binary + + defp network_prefix do + Atom.to_string(Application.get_env(:explorer, __MODULE__)[:network_prefix]) + end + + @doc """ + Casts `term` to `t:t/0`. + + If the term is already in `t:t/0`, then it is returned + + iex> Explorer.Chain.Filecoin.NativeAddress.cast( + ...> %Explorer.Chain.Filecoin.NativeAddress{ + ...> protocol_indicator: 0, + ...> actor_id: nil, + ...> payload: <<193, 13>>, + ...> checksum: nil + ...> } + ...> ) + { + :ok, + %Explorer.Chain.Filecoin.NativeAddress{ + protocol_indicator: 0, + actor_id: nil, + payload: <<193, 13>>, + checksum: nil + } + } + + If the term is a binary, then it is parsed to `t:t/0` + + iex> Explorer.Chain.Filecoin.NativeAddress.cast("f01729") + { + :ok, + %Explorer.Chain.Filecoin.NativeAddress{ + protocol_indicator: 0, + actor_id: nil, + payload: <<193, 13>>, + checksum: nil + } + } + + iex> Explorer.Chain.Filecoin.NativeAddress.cast("f01729") + { + :ok, + %Explorer.Chain.Filecoin.NativeAddress{ + protocol_indicator: 0, + actor_id: nil, + payload: <<193, 13>>, + checksum: nil + } + } + + iex> NativeAddress.cast("f410fabpafjfjgqkc3douo3yzfug5tq4bwfvuhsewxji") + { + :ok, + %Explorer.Chain.Filecoin.NativeAddress{ + protocol_indicator: 4, + actor_id: 10, + payload: <<0, 94, 2, 164, 169, 52, 20, 45, 141, 212, 118, 241, 146, 208, 221, 156, 56, 27, 22, 180>>, + checksum: <<60, 137, 107, 165>> + } + } + """ + @impl Ecto.Type + @spec cast(t() | String.t()) :: {:ok, t()} | :error + def cast(%__MODULE__{} = address), do: {:ok, address} + + def cast(address_string) when is_binary(address_string) do + network = network_prefix() + + with true <- String.length(address_string) >= @min_address_string_length, + ^network <> protocol_indicator_and_payload <- address_string, + {:ok, address} <- cast_protocol_indicator_and_payload(protocol_indicator_and_payload), + :ok <- verify_checksum(address) do + {:ok, address} + else + _ -> + :error + end + end + + defp cast_protocol_indicator_and_payload("0" <> id_string) do + id_string + |> Integer.parse() + |> case do + {id, ""} when is_integer(id) and id >= 0 -> + payload = LEB128.encode(id) + + {:ok, + %__MODULE__{ + protocol_indicator: 0, + actor_id: nil, + payload: payload, + checksum: nil + }} + + _ -> + :error + end + end + + defp cast_protocol_indicator_and_payload("4" <> rest) do + with [actor_id_string, base32_digits] <- String.split(rest, "f", parts: 2), + {actor_id, ""} when is_integer(actor_id) <- Integer.parse(actor_id_string), + {:ok, {payload, checksum}} <- cast_base32_digits(base32_digits) do + {:ok, + %__MODULE__{ + protocol_indicator: 4, + actor_id: actor_id, + payload: payload, + checksum: checksum + }} + else + _ -> :error + end + end + + defp cast_protocol_indicator_and_payload(protocol_indicator_and_payload) do + with {protocol_indicator_string, base32_digits} <- + String.split_at( + protocol_indicator_and_payload, + 1 + ), + {protocol_indicator, ""} when protocol_indicator in @standard_protocol_indicators <- + Integer.parse(protocol_indicator_string), + {:ok, byte_count} <- + Map.fetch( + @protocol_indicator_to_payload_byte_count, + protocol_indicator + ), + {:ok, {payload, checksum}} <- cast_base32_digits(base32_digits, byte_count) do + {:ok, + %__MODULE__{ + protocol_indicator: protocol_indicator, + actor_id: nil, + payload: payload, + checksum: checksum + }} + else + _ -> :error + end + end + + defp cast_base32_digits(digits) do + with {:ok, bytes} <- Base.decode32(digits, case: :lower, padding: false), + << + payload::binary-size(byte_size(bytes) - @checksum_bytes_count), + checksum::binary-size(@checksum_bytes_count) + >> <- bytes do + {:ok, {payload, checksum}} + else + _ -> :error + end + end + + defp cast_base32_digits(digits, expected_bytes_count) do + with {:ok, {payload, checksum}} <- cast_base32_digits(digits), + true <- byte_size(payload) == expected_bytes_count do + {:ok, {payload, checksum}} + else + _ -> :error + end + end + + @doc """ + Dumps the address to `:binary` (`bytea`) representation format used in + database. + """ + @impl Ecto.Type + @spec dump(t()) :: {:ok, binary()} | :error + def dump(%__MODULE__{protocol_indicator: 4, actor_id: actor_id, payload: payload}) + when is_integer(actor_id) and + is_binary(payload) and + actor_id >= 0 and + actor_id <= @max_protocol_indicator do + {:ok, <<4, actor_id, payload::binary>>} + end + + def dump(%__MODULE__{protocol_indicator: protocol_indicator, payload: payload}) + when is_integer(protocol_indicator) and + is_binary(payload) and + protocol_indicator >= 0 and + protocol_indicator <= @max_protocol_indicator do + {:ok, <>} + end + + def dump(_), do: :error + + @doc """ + Loads the address from `:binary` representation used in database. + """ + @impl Ecto.Type + @spec load(binary()) :: {:ok, t()} | :error + def load(<> = bytes) do + case protocol_indicator do + 0 -> + {:ok, + %__MODULE__{ + protocol_indicator: 0, + actor_id: nil, + payload: rest, + checksum: nil + }} + + 4 -> + checksum = to_checksum(bytes) + <> = rest + + {:ok, + %__MODULE__{ + protocol_indicator: 4, + actor_id: actor_id, + payload: payload, + checksum: checksum + }} + + protocol_indicator when protocol_indicator in @standard_protocol_indicators -> + checksum = to_checksum(bytes) + + {:ok, + %__MODULE__{ + protocol_indicator: protocol_indicator, + actor_id: nil, + payload: rest, + checksum: checksum + }} + + _ -> + :error + end + end + + def load(_), do: :error + + @doc """ + Converts the address to a string representation. + + iex> Explorer.Chain.Filecoin.NativeAddress.to_string( + ...> %Explorer.Chain.Filecoin.NativeAddress{ + ...> protocol_indicator: 0, + ...> actor_id: nil, + ...> payload: <<193, 13>>, + ...> checksum: nil + ...> } + ...> ) + "f01729" + + iex> Explorer.Chain.Filecoin.NativeAddress.to_string( + ...> %Explorer.Chain.Filecoin.NativeAddress{ + ...> protocol_indicator: 4, + ...> actor_id: 10, + ...> payload: <<0, 94, 2, 164, 169, 52, 20, 45, 141, 212, 118, 241, 146, 208, 221, 156, 56, 27, 22, 180>>, + ...> checksum: <<60, 137, 107, 165>> + ...> } + ...> ) + "f410fabpafjfjgqkc3douo3yzfug5tq4bwfvuhsewxji" + """ + @spec to_string(t) :: String.t() + def to_string(%__MODULE__{protocol_indicator: 0, payload: payload}) do + {id, <<>>} = LEB128.decode(payload) + network_prefix() <> "0" <> Integer.to_string(id) + end + + @spec to_string(t) :: String.t() + def to_string(%__MODULE__{ + protocol_indicator: protocol_indicator, + payload: payload, + actor_id: actor_id, + checksum: checksum + }) do + payload_with_checksum = + Base.encode32( + payload <> checksum, + case: :lower, + padding: false + ) + + protocol_indicator_part = + protocol_indicator + |> case do + indicator when indicator in @standard_protocol_indicators -> + Integer.to_string(indicator) + + 4 -> + "4" <> Integer.to_string(actor_id) <> "f" + end + + network_prefix() <> protocol_indicator_part <> payload_with_checksum + end + + defp verify_checksum(%__MODULE__{protocol_indicator: 0, checksum: nil}), do: :ok + + defp verify_checksum(%__MODULE__{checksum: checksum} = address) + when not is_nil(checksum) do + with {:ok, bytes} <- dump(address), + ^checksum <- to_checksum(bytes) do + :ok + else + _ -> :error + end + end + + defp to_checksum(bytes), + do: Blake2.hash2b(bytes, @checksum_bytes_count) + + defimpl String.Chars do + def to_string(hash) do + @for.to_string(hash) + end + end + + defimpl Poison.Encoder do + def encode(hash, options) do + hash + |> to_string() + |> BitString.encode(options) + end + end + + defimpl Jason.Encoder do + alias Jason.Encode + + def encode(hash, opts) do + hash + |> to_string() + |> Encode.string(opts) + end + end +end diff --git a/apps/explorer/lib/explorer/chain/filecoin/pending_address_operation.ex b/apps/explorer/lib/explorer/chain/filecoin/pending_address_operation.ex new file mode 100644 index 000000000000..0fa166e0fb6d --- /dev/null +++ b/apps/explorer/lib/explorer/chain/filecoin/pending_address_operation.ex @@ -0,0 +1,73 @@ +defmodule Explorer.Chain.Filecoin.PendingAddressOperation do + @moduledoc """ + Tracks an address that is pending for fetching of filecoin address info. + """ + + use Explorer.Schema + + import Explorer.Chain, only: [add_fetcher_limit: 2] + alias Explorer.Chain.{Address, Hash} + alias Explorer.Repo + + @http_error_codes 400..526 + + @optional_attrs ~w(http_status_code)a + @required_attrs ~w(address_hash)a + + @attrs @optional_attrs ++ @required_attrs + + @typedoc """ + * `address_hash` - the hash of the address that is pending to be fetched. + * `http_status_code` - the unsuccessful (non-200) http code returned by Beryx + API if the fetcher failed to fetch the address. + """ + @primary_key false + typed_schema "filecoin_pending_address_operations" do + belongs_to(:address, Address, + foreign_key: :address_hash, + references: :hash, + type: Hash.Address, + primary_key: true + ) + + field(:http_status_code, :integer) + + timestamps() + end + + @spec changeset( + Explorer.Chain.Filecoin.PendingAddressOperation.t(), + :invalid | %{optional(:__struct__) => none(), optional(atom() | binary()) => any()} + ) :: Ecto.Changeset.t() + def changeset(%__MODULE__{} = pending_ops, attrs) do + pending_ops + |> cast(attrs, @attrs) + |> validate_required(@required_attrs) + |> foreign_key_constraint(:address_hash, name: :filecoin_pending_address_operations_address_hash_fkey) + |> unique_constraint(:address_hash, name: :filecoin_pending_address_operations_pkey) + |> validate_inclusion(:http_status_code, @http_error_codes) + end + + @doc """ + Returns a stream of pending operations. + """ + @spec stream( + initial :: accumulator, + reducer :: (entry :: term(), accumulator -> accumulator), + limited? :: boolean() + ) :: {:ok, accumulator} + when accumulator: term() + def stream(initial, reducer, limited? \\ false) + when is_function(reducer, 2) do + query = + from( + op in __MODULE__, + select: op, + order_by: [desc: op.address_hash] + ) + + query + |> add_fetcher_limit(limited?) + |> Repo.stream_reduce(initial, reducer) + end +end diff --git a/apps/explorer/lib/explorer/chain/hash.ex b/apps/explorer/lib/explorer/chain/hash.ex index 8bac3ff44260..255861c0d697 100644 --- a/apps/explorer/lib/explorer/chain/hash.ex +++ b/apps/explorer/lib/explorer/chain/hash.ex @@ -121,7 +121,7 @@ defmodule Explorer.Chain.Hash do """ @spec to_integer(t()) :: pos_integer() def to_integer(%__MODULE__{byte_count: byte_count, bytes: bytes}) do - <> = bytes + <> = bytes integer end diff --git a/apps/explorer/lib/explorer/chain/import/runner/addresses.ex b/apps/explorer/lib/explorer/chain/import/runner/addresses.ex index 23a2e7a1d04b..fe6cea4f7d1e 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/addresses.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/addresses.ex @@ -3,14 +3,16 @@ defmodule Explorer.Chain.Import.Runner.Addresses do Bulk imports `t:Explorer.Chain.Address.t/0`. """ - require Ecto.Query + import Ecto.Query, only: [from: 2] + import Explorer.Chain.Import.Runner.Helper, only: [chain_type_dependent_import: 3] alias Ecto.{Multi, Repo} - alias Explorer.Chain.{Address, Hash, Import, Transaction} + alias Explorer.Chain.Filecoin.PendingAddressOperation, as: FilecoinPendingAddressOperation alias Explorer.Chain.Import.Runner + alias Explorer.Chain.{Address, Hash, Import, Transaction} alias Explorer.Prometheus.Instrumenter - import Ecto.Query, only: [from: 2] + require Ecto.Query @behaviour Import.Runner @@ -98,6 +100,21 @@ defmodule Explorer.Chain.Import.Runner.Addresses do :created_address_code_indexed_at_transactions ) end) + |> chain_type_dependent_import( + :filecoin, + &Multi.run( + &1, + :filecoin_pending_address_operations, + fn repo, _ -> + Instrumenter.block_import_stage_runner( + fn -> filecoin_pending_address_operations(repo, ordered_changes_list, insert_options) end, + :addresses, + :addresses, + :filecoin_pending_address_operations + ) + end + ) + ) end @impl Import.Runner @@ -261,4 +278,23 @@ defmodule Explorer.Chain.Import.Runner.Addresses do end end end + + defp filecoin_pending_address_operations(repo, addresses, %{timeout: timeout, timestamps: timestamps}) do + ordered_addresses = + addresses + |> Enum.map(&%{address_hash: &1.hash}) + |> Enum.sort_by(& &1.address_hash) + |> Enum.dedup_by(& &1.address_hash) + + Import.insert_changes_list( + repo, + ordered_addresses, + conflict_target: :address_hash, + on_conflict: :nothing, + for: FilecoinPendingAddressOperation, + returning: true, + timeout: timeout, + timestamps: timestamps + ) + end end diff --git a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex index 0af2896b2c6f..3043ce24eca3 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex @@ -6,6 +6,7 @@ defmodule Explorer.Chain.Import.Runner.Blocks do require Ecto.Query import Ecto.Query, only: [from: 2, where: 3, subquery: 1] + import Explorer.Chain.Import.Runner.Helper, only: [chain_type_dependent_import: 3] alias Ecto.{Changeset, Multi, Repo} @@ -228,14 +229,6 @@ defmodule Explorer.Chain.Import.Runner.Blocks do @impl Runner def timeout, do: @timeout - def chain_type_dependent_import(multi, chain_type, multi_run) do - if Application.get_env(:explorer, :chain_type) == chain_type do - multi_run.(multi) - else - multi - end - end - defp fork_transactions(%{ repo: repo, timeout: timeout, diff --git a/apps/explorer/lib/explorer/chain/import/runner/helper.ex b/apps/explorer/lib/explorer/chain/import/runner/helper.ex new file mode 100644 index 000000000000..6fd5f53e85e5 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/helper.ex @@ -0,0 +1,22 @@ +defmodule Explorer.Chain.Import.Runner.Helper do + @moduledoc """ + Provides utility functions for the chain import runners. + """ + + @doc """ + Executes the import function if the configured chain type matches the + specified `chain_type`. + """ + @spec chain_type_dependent_import( + Ecto.Multi.t(), + chain_type :: atom(), + (Ecto.Multi.t() -> Ecto.Multi.t()) + ) :: Ecto.Multi.t() + def chain_type_dependent_import(multi, chain_type, multi_run) do + if Application.get_env(:explorer, :chain_type) == chain_type do + multi_run.(multi) + else + multi + end + end +end diff --git a/apps/explorer/lib/explorer/migrator/filecoin_pending_address_operations.ex b/apps/explorer/lib/explorer/migrator/filecoin_pending_address_operations.ex new file mode 100644 index 000000000000..47254d880590 --- /dev/null +++ b/apps/explorer/lib/explorer/migrator/filecoin_pending_address_operations.ex @@ -0,0 +1,71 @@ +defmodule Explorer.Migrator.FilecoinPendingAddressOperations do + @moduledoc """ + Creates a pending address operation for each address missing Filecoin address + information, specifically when `filecoin_id`, `filecoin_robust`, and + `filecoin_actor_type` are `nil`. + """ + + use Explorer.Migrator.FillingMigration + + import Ecto.Query + + alias Explorer.Chain.{Address, Filecoin.PendingAddressOperation, Import} + alias Explorer.Migrator.FillingMigration + alias Explorer.Repo + + @migration_name "filecoin_pending_address_operations" + + @impl FillingMigration + def migration_name, do: @migration_name + + @impl FillingMigration + def last_unprocessed_identifiers(state) do + limit = batch_size() * concurrency() + + ids = + unprocessed_data_query() + |> select([address], address.hash) + |> limit(^limit) + |> Repo.all(timeout: :infinity) + + {ids, state} + end + + @impl FillingMigration + def unprocessed_data_query do + from( + address in Address, + left_join: op in PendingAddressOperation, + on: address.hash == op.address_hash, + where: + is_nil(address.filecoin_id) and + is_nil(address.filecoin_robust) and + is_nil(address.filecoin_actor_type) and + is_nil(op.address_hash), + order_by: [asc: address.hash] + ) + end + + @impl FillingMigration + def update_batch(ordered_address_hashes) do + ordered_pending_operations = + Enum.map( + ordered_address_hashes, + &%{address_hash: &1} + ) + + Import.insert_changes_list( + Repo, + ordered_pending_operations, + conflict_target: :address_hash, + on_conflict: :nothing, + for: PendingAddressOperation, + returning: true, + timeout: :infinity, + timestamps: Import.timestamps() + ) + end + + @impl FillingMigration + def update_cache, do: :ok +end diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index 56345b2e1dd9..d772ad4c1989 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -122,7 +122,9 @@ defmodule Explorer.Mixfile do {:logger_json, "~> 5.1"}, {:typed_ecto_schema, "~> 0.4.1", runtime: false}, {:ueberauth, "~> 0.7"}, - {:recon, "~> 2.5"} + {:recon, "~> 2.5"}, + {:varint, "~> 1.4"}, + {:blake2, "~> 1.0"} ] end diff --git a/apps/explorer/priv/filecoin/migrations/20240801134142_create_pending_address_operations.exs b/apps/explorer/priv/filecoin/migrations/20240801134142_create_pending_address_operations.exs new file mode 100644 index 000000000000..7da899939bdb --- /dev/null +++ b/apps/explorer/priv/filecoin/migrations/20240801134142_create_pending_address_operations.exs @@ -0,0 +1,23 @@ +defmodule Explorer.Repo.Filecoin.Migrations.CreatePendingAddressOperations do + use Ecto.Migration + + def change do + create table(:filecoin_pending_address_operations, primary_key: false) do + add( + :address_hash, + references( + :addresses, + column: :hash, + type: :bytea, + on_delete: :delete_all + ), + null: false, + primary_key: true + ) + + add(:http_status_code, :smallint) + + timestamps() + end + end +end diff --git a/apps/explorer/priv/filecoin/migrations/20240807134138_add_chain_type_fields_to_address.exs b/apps/explorer/priv/filecoin/migrations/20240807134138_add_chain_type_fields_to_address.exs new file mode 100644 index 000000000000..378e3b24c0b3 --- /dev/null +++ b/apps/explorer/priv/filecoin/migrations/20240807134138_add_chain_type_fields_to_address.exs @@ -0,0 +1,11 @@ +defmodule Explorer.Repo.Filecoin.Migrations.AddChainTypeFieldsToAddress do + use Ecto.Migration + + def change do + alter table(:addresses) do + add(:filecoin_id, :bytea) + add(:filecoin_robust, :bytea) + add(:filecoin_actor_type, :smallint) + end + end +end diff --git a/apps/explorer/test/explorer/chain/filecoin/native_address_test.exs b/apps/explorer/test/explorer/chain/filecoin/native_address_test.exs new file mode 100644 index 000000000000..28368261e4ca --- /dev/null +++ b/apps/explorer/test/explorer/chain/filecoin/native_address_test.exs @@ -0,0 +1,175 @@ +defmodule Explorer.Chain.Filecoin.NativeAddressTest do + use ExUnit.Case, async: true + + alias Explorer.Chain.Hash + alias Explorer.Chain.Hash.Address + alias Explorer.Chain.Filecoin.{NativeAddress, IDAddress} + + doctest NativeAddress + doctest IDAddress + + @doc """ + The following test cases are taken from the filecoin spec: + https://spec.filecoin.io/appendix/address/#section-appendix.address.test-vectors + + The key is the address and the value is the hex-encoded binary representation + of the address in the database. + """ + # cspell:disable + @test_cases %{ + "f00" => "0000", + "f0150" => "009601", + "f01024" => "008008", + "f01729" => "00c10d", + "f018446744073709551615" => "00ffffffffffffffffff01", + "f17uoq6tp427uzv7fztkbsnn64iwotfrristwpryy" => "01fd1d0f4dfcd7e99afcb99a8326b7dc459d32c628", + "f1xcbgdhkgkwht3hrrnui3jdopeejsoatkzmoltqy" => "01b882619d46558f3d9e316d11b48dcf211327026a", + "f1xtwapqc6nh4si2hcwpr3656iotzmlwumogqbuaa" => "01bcec07c05e69f92468e2b3e3bf77c874f2c5da8c", + "f1wbxhu3ypkuo6eyp6hjx6davuelxaxrvwb2kuwva" => "01b06e7a6f0f551de261fe3a6fe182b422ee0bc6b6", + "f12fiakbhe2gwd5cnmrenekasyn6v5tnaxaqizq6a" => "01d1500504e4d1ac3e89ac891a4502586fabd9b417", + "f24vg6ut43yw2h2jqydgbg2xq7x6f4kub3bg6as6i" => "02e54dea4f9bc5b47d261819826d5e1fbf8bc5503b", + "f25nml2cfbljvn4goqtclhifepvfnicv6g7mfmmvq" => "02eb58bd08a15a6ade19d0989674148fa95a8157c6", + "f2nuqrg7vuysaue2pistjjnt3fadsdzvyuatqtfei" => "026d21137eb4c4814269e894d296cf6500e43cd714", + "f24dd4ox4c2vpf5vk5wkadgyyn6qtuvgcpxxon64a" => "02e0c7c75f82d55e5ed55db28033630df4274a984f", + "f2gfvuyh7v2sx3patm5k23wdzmhyhtmqctasbr23y" => "02316b4c1ff5d4afb7826ceab5bb0f2c3e0f364053", + "f3vvmn62lofvhjd2ugzca6sof2j2ubwok6cj4xxbfzz4yuxfkgobpihhd2thlanmsh3w2ptld2gqkn2jvlss4a" => + "03ad58df696e2d4e91ea86c881e938ba4ea81b395e12797b84b9cf314b9546705e839c7a99d606b247ddb4f9ac7a3414dd", + "f3wmuu6crofhqmm3v4enos73okk2l366ck6yc4owxwbdtkmpk42ohkqxfitcpa57pjdcftql4tojda2poeruwa" => + "03b3294f0a2e29e0c66ebc235d2fedca5697bf784af605c75af608e6a63d5cd38ea85ca8989e0efde9188b382f9372460d", + "f3s2q2hzhkpiknjgmf4zq3ejab2rh62qbndueslmsdzervrhapxr7dftie4kpnpdiv2n6tvkr743ndhrsw6d3a" => + "0396a1a3e4ea7a14d49985e661b22401d44fed402d1d0925b243c923589c0fbc7e32cd04e29ed78d15d37d3aaa3fe6da33", + "f3q22fijmmlckhl56rn5nkyamkph3mcfu5ed6dheq53c244hfmnq2i7efdma3cj5voxenwiummf2ajlsbxc65a" => + "0386b454258c589475f7d16f5aac018a79f6c1169d20fc33921dd8b5ce1cac6c348f90a3603624f6aeb91b64518c2e8095", + "f3u5zgwa4ael3vuocgc5mfgygo4yuqocrntuuhcklf4xzg5tcaqwbyfabxetwtj4tsam3pbhnwghyhijr5mixa" => + "03a7726b038022f75a384617585360cee629070a2d9d28712965e5f26ecc40858382803724ed34f2720336f09db631f074" + } + + # cspell:enable + + describe "cast/1" do + test "parses f0, f1, f2, f3 addresses from spec test vectors" do + for {address, hex_string} <- @test_cases do + {protocol_indicator_hex, payload} = String.split_at(hex_string, 2) + protocol_indicator = String.to_integer(protocol_indicator_hex, 16) + payload = Base.decode16!(payload, case: :lower) + + assert {:ok, + %NativeAddress{ + protocol_indicator: ^protocol_indicator, + actor_id: nil, + payload: ^payload + }} = NativeAddress.cast(address) + end + end + + test "parses f4 addresses" do + address = "f410fabpafjfjgqkc3douo3yzfug5tq4bwfvuhsewxji" + {:ok, evm_address} = Address.cast("0x005E02A4A934142D8DD476F192D0DD9C381B16B4") + evm_address_bytes = evm_address.bytes + + assert {:ok, + %NativeAddress{ + protocol_indicator: 4, + actor_id: 10, + payload: ^evm_address_bytes + }} = NativeAddress.cast(address) + end + end + + describe "dump/1" do + test "encodes f0, f1, f2, f3 addresses to bytes" do + for {address, hex_string} <- @test_cases do + bytes = Base.decode16!(hex_string, case: :lower) + + assert {:ok, ^bytes} = + address + |> NativeAddress.cast() + |> elem(1) + |> NativeAddress.dump() + end + end + + test "converts f4 addresses" do + address = "f410fabpafjfjgqkc3douo3yzfug5tq4bwfvuhsewxji" + {:ok, evm_address} = Address.cast("0x005E02A4A934142D8DD476F192D0DD9C381B16B4") + bytes = <<4, 10, evm_address.bytes::binary>> + + assert {:ok, ^bytes} = + address + |> NativeAddress.cast() + |> elem(1) + |> NativeAddress.dump() + end + end + + describe "load/1" do + test "decodes f0, f1, f2, f3 addresses from bytes" do + for {address, hex_string} <- Map.values(@test_cases) do + {protocol_indicator_hex, payload_hex} = String.split_at(hex_string, 2) + protocol_indicator = String.to_integer(protocol_indicator_hex, 16) + payload = Base.decode16!(payload_hex, case: :lower) + + assert {:ok, + %NativeAddress{ + protocol_indicator: ^protocol_indicator, + actor_id: nil, + payload: ^payload + }} = + address + |> NativeAddress.cast() + |> elem(1) + |> NativeAddress.dump() + |> elem(1) + |> NativeAddress.load() + end + end + + test "decodes f4 addresses" do + address = "f410fabpafjfjgqkc3douo3yzfug5tq4bwfvuhsewxji" + {:ok, %Hash{bytes: payload}} = Address.cast("0x005E02A4A934142D8DD476F192D0DD9C381B16B4") + + assert {:ok, + %NativeAddress{ + protocol_indicator: 4, + actor_id: 10, + payload: ^payload + }} = + address + |> NativeAddress.cast() + |> elem(1) + |> NativeAddress.dump() + |> elem(1) + |> NativeAddress.load() + end + end + + describe "to_string/1" do + test "converts f0, f1, f2, f3 addresses to string" do + for {address, _} <- @test_cases do + assert ^address = + address + |> NativeAddress.cast() + |> elem(1) + |> NativeAddress.dump() + |> elem(1) + |> NativeAddress.load() + |> elem(1) + |> NativeAddress.to_string() + end + end + + test "converts f4 addresses to string" do + address = "f410fabpafjfjgqkc3douo3yzfug5tq4bwfvuhsewxji" + + assert ^address = + address + |> NativeAddress.cast() + |> elem(1) + |> NativeAddress.dump() + |> elem(1) + |> NativeAddress.load() + |> elem(1) + |> NativeAddress.to_string() + end + end +end diff --git a/apps/indexer/lib/indexer/application.ex b/apps/indexer/lib/indexer/application.ex index 2a688a1a6f03..78d05b37b044 100644 --- a/apps/indexer/lib/indexer/application.ex +++ b/apps/indexer/lib/indexer/application.ex @@ -12,12 +12,30 @@ defmodule Indexer.Application do alias Indexer.Fetcher.OnDemand.TokenTotalSupply, as: TokenTotalSupplyOnDemand alias Indexer.Memory - alias Indexer.Prometheus.PendingBlockOperationsCollector alias Prometheus.Registry + @default_prometheus_collectors [ + Indexer.Prometheus.Collector.PendingBlockOperations + ] + + case Application.compile_env(:explorer, :chain_type) do + :filecoin -> + @chain_type_prometheus_collectors [ + Indexer.Prometheus.Collector.FilecoinPendingAddressOperations + ] + + _ -> + @chain_type_prometheus_collectors [] + end + + @prometheus_collectors @default_prometheus_collectors ++ + @chain_type_prometheus_collectors + @impl Application def start(_type, _args) do - Registry.register_collector(PendingBlockOperationsCollector) + for collector <- @prometheus_collectors do + Registry.register_collector(collector) + end memory_monitor_options = case Application.get_env(:indexer, :memory_limit) do diff --git a/apps/indexer/lib/indexer/block/catchup/fetcher.ex b/apps/indexer/lib/indexer/block/catchup/fetcher.ex index 180ddf37ec49..7f3d32ecf58a 100644 --- a/apps/indexer/lib/indexer/block/catchup/fetcher.ex +++ b/apps/indexer/lib/indexer/block/catchup/fetcher.ex @@ -14,6 +14,7 @@ defmodule Indexer.Block.Catchup.Fetcher do async_import_celo_epoch_block_operations: 2, async_import_coin_balances: 2, async_import_created_contract_codes: 2, + async_import_filecoin_addresses_info: 2, async_import_internal_transactions: 2, async_import_replaced_transactions: 2, async_import_token_balances: 2, @@ -141,6 +142,7 @@ defmodule Indexer.Block.Catchup.Fetcher do async_import_token_instances(imported) async_import_blobs(imported, realtime?) async_import_celo_epoch_block_operations(imported, realtime?) + async_import_filecoin_addresses_info(imported, realtime?) end defp stream_fetch_and_import(state, ranges) do diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 03082037a28f..b8a4d716b201 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -11,15 +11,17 @@ defmodule Indexer.Block.Fetcher do alias EthereumJSONRPC.{Blocks, FetchedBeneficiaries} alias Explorer.Chain - alias Explorer.Chain.{Address, Block, Hash, Import, Transaction, Wei} alias Explorer.Chain.Block.Reward alias Explorer.Chain.Cache.Blocks, as: BlocksCache alias Explorer.Chain.Cache.{Accounts, BlockNumber, Transactions, Uncles} + alias Explorer.Chain.Filecoin.PendingAddressOperation, as: FilecoinPendingAddressOperation + alias Explorer.Chain.{Address, Block, Hash, Import, Transaction, Wei} alias Indexer.Block.Fetcher.Receipts alias Indexer.Fetcher.Celo.EpochBlockOperations, as: CeloEpochBlockOperations alias Indexer.Fetcher.Celo.EpochLogs, as: CeloEpochLogs alias Indexer.Fetcher.CoinBalance.Catchup, as: CoinBalanceCatchup alias Indexer.Fetcher.CoinBalance.Realtime, as: CoinBalanceRealtime + alias Indexer.Fetcher.Filecoin.AddressInfo, as: FilecoinAddressInfo alias Indexer.Fetcher.PolygonZkevm.BridgeL1Tokens, as: PolygonZkevmBridgeL1Tokens alias Indexer.Fetcher.TokenInstance.Realtime, as: TokenInstanceRealtime @@ -511,6 +513,14 @@ defmodule Indexer.Block.Fetcher do def async_import_celo_epoch_block_operations(_, _), do: :ok + def async_import_filecoin_addresses_info(%{addresses: addresses}, realtime?) do + addresses + |> Enum.map(&%FilecoinPendingAddressOperation{address_hash: &1.hash}) + |> FilecoinAddressInfo.async_fetch(realtime?) + end + + def async_import_filecoin_addresses_info(_, _), do: :ok + defp block_reward_errors_to_block_numbers(block_reward_errors) when is_list(block_reward_errors) do Enum.map(block_reward_errors, &block_reward_error_to_block_number/1) end diff --git a/apps/indexer/lib/indexer/block/realtime/fetcher.ex b/apps/indexer/lib/indexer/block/realtime/fetcher.ex index 10d6fbafc2b8..a2e7716ac18a 100644 --- a/apps/indexer/lib/indexer/block/realtime/fetcher.ex +++ b/apps/indexer/lib/indexer/block/realtime/fetcher.ex @@ -17,6 +17,7 @@ defmodule Indexer.Block.Realtime.Fetcher do async_import_block_rewards: 2, async_import_celo_epoch_block_operations: 2, async_import_created_contract_codes: 2, + async_import_filecoin_addresses_info: 2, async_import_internal_transactions: 2, async_import_polygon_zkevm_bridge_l1_tokens: 1, async_import_realtime_coin_balances: 1, @@ -467,5 +468,6 @@ defmodule Indexer.Block.Realtime.Fetcher do async_import_blobs(imported, realtime?) async_import_polygon_zkevm_bridge_l1_tokens(imported) async_import_celo_epoch_block_operations(imported, realtime?) + async_import_filecoin_addresses_info(imported, realtime?) end end diff --git a/apps/indexer/lib/indexer/fetcher/filecoin/address_info.ex b/apps/indexer/lib/indexer/fetcher/filecoin/address_info.ex new file mode 100644 index 000000000000..83e5f7cdd193 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/filecoin/address_info.ex @@ -0,0 +1,215 @@ +defmodule Indexer.Fetcher.Filecoin.AddressInfo do + @moduledoc """ + A task for fetching Filecoin addresses info in the Address table using the + Beryx API. + + Due to the lack of batch support in the API, addresses are fetched + individually, making this fetching an expensive operation. + """ + use Indexer.Fetcher, restart: :permanent + use Spandex.Decorators + + alias Ecto.Multi + alias Explorer.Chain.{Address, Filecoin.PendingAddressOperation} + alias Explorer.Repo + alias Indexer.Fetcher.Filecoin.AddressInfo.Supervisor, as: FilecoinAddressInfoSupervisor + alias Indexer.Fetcher.Filecoin.BeryxAPI + alias Indexer.{BufferedTask, Tracer} + + @http_error_codes 400..526 + + @batch_size 1 + + @behaviour BufferedTask + + require Logger + + @doc """ + Asynchronously fetches filecoin addresses info + """ + @spec async_fetch([PendingAddressOperation.t()], boolean(), integer()) :: :ok + def async_fetch(pending_operations, realtime?, timeout \\ 5000) + when is_list(pending_operations) do + if FilecoinAddressInfoSupervisor.disabled?() do + :ok + else + unique_operations = + Enum.uniq_by( + pending_operations, + &to_string(&1.address_hash) + ) + + BufferedTask.buffer(__MODULE__, unique_operations, realtime?, timeout) + end + end + + @doc false + @spec child_spec([...]) :: Supervisor.child_spec() + def child_spec([init_options, gen_server_options]) do + merged_init_opts = + defaults() + |> Keyword.merge(init_options) + |> Keyword.put(:state, nil) + + Supervisor.child_spec( + {BufferedTask, [{__MODULE__, merged_init_opts}, gen_server_options]}, + id: __MODULE__ + ) + end + + @doc false + @impl BufferedTask + def init(initial, reducer, _) do + {:ok, final} = + PendingAddressOperation.stream( + initial, + fn op, acc -> reducer.(op, acc) end + ) + + final + end + + @doc false + @spec defaults() :: Keyword.t() + def defaults do + env = Application.get_env(:indexer, __MODULE__) + + [ + poll: false, + flush_interval: :timer.seconds(30), + max_concurrency: env[:concurrency], + max_batch_size: @batch_size, + task_supervisor: __MODULE__.TaskSupervisor, + metadata: [fetcher: :filecoin_address_info] + ] + end + + @doc """ + Fetches the Filecoin address info for the given pending operation. + """ + @impl BufferedTask + @decorate trace( + name: "fetch", + resource: "Indexer.Fetcher.InternalTransaction.run/2", + service: :indexer, + tracer: Tracer + ) + @spec run([Explorer.Chain.Filecoin.PendingAddressOperation.t(), ...], any()) :: :ok | :retry + def run([pending_operation], _state) do + fetch_and_update(pending_operation) + end + + @spec fetch_and_update(PendingAddressOperation.t()) :: :ok | :retry + defp fetch_and_update(%PendingAddressOperation{address_hash: address_hash} = operation) do + with {:ok, new_params} <- fetch_address_info_using_beryx_api(operation), + {:ok, _} <- update_address_and_remove_pending_operation(operation, new_params) do + Logger.debug("Fetched Filecoin address info for: #{to_string(address_hash)}") + :ok + else + _ -> + Logger.error("Could not fetch Filecoin address info: #{to_string(address_hash)}") + :retry + end + end + + @spec update_address_and_remove_pending_operation( + PendingAddressOperation.t(), + %{ + filecoin_id: String.t(), + filecoin_robust: String.t(), + filecoin_actor_type: String.t() + } + ) :: + {:ok, PendingAddressOperation.t()} + | {:error, Ecto.Changeset.t()} + | Ecto.Multi.failure() + defp update_address_and_remove_pending_operation( + %PendingAddressOperation{} = operation, + new_address_params + ) do + Multi.new() + |> Multi.run( + :acquire_address, + fn repo, _ -> + case repo.get_by( + Address, + [hash: operation.address_hash], + lock: "FOR UPDATE" + ) do + nil -> {:error, :not_found} + address -> {:ok, address} + end + end + ) + |> Multi.run( + :acquire_pending_address_operation, + fn repo, _ -> + case repo.get_by( + PendingAddressOperation, + [address_hash: operation.address_hash], + lock: "FOR UPDATE" + ) do + nil -> {:error, :not_found} + pending_operation -> {:ok, pending_operation} + end + end + ) + |> Multi.run( + :update_address, + fn repo, %{acquire_address: address} -> + address + |> Address.changeset(new_address_params) + |> repo.update() + end + ) + |> Multi.run( + :delete_pending_operation, + fn repo, %{acquire_pending_address_operation: operation} -> + repo.delete(operation) + end + ) + |> Repo.transaction() + end + + @spec fetch_address_info_using_beryx_api(PendingAddressOperation.t()) :: + {:ok, + %{ + filecoin_id: String.t(), + filecoin_robust: String.t(), + filecoin_actor_type: String.t() + }} + | :error + defp fetch_address_info_using_beryx_api(%PendingAddressOperation{} = operation) do + with {:ok, body_json} <- operation.address_hash |> to_string() |> BeryxAPI.fetch_account_info(), + {:ok, id_address_string} <- Map.fetch(body_json, "short"), + {:ok, robust_address_string} <- Map.fetch(body_json, "robust"), + {:ok, actor_type_string} <- Map.fetch(body_json, "actor_type") do + {:ok, + %{ + filecoin_id: id_address_string, + filecoin_robust: robust_address_string, + filecoin_actor_type: actor_type_string + }} + else + {:error, status_code, %{"error" => reason}} when status_code in @http_error_codes -> + Logger.error("Beryx API returned error code #{status_code} with reason: #{reason}") + + operation + |> PendingAddressOperation.changeset(%{http_status_code: status_code}) + |> Repo.update() + |> case do + {:ok, _} -> + Logger.info("Updated pending operation with error status code") + + {:error, changeset} -> + Logger.error("Could not update pending operation with error status code: #{inspect(changeset)}") + end + + :error + + error -> + Logger.error("Error processing Beryx API response: #{inspect(error)}") + :error + end + end +end diff --git a/apps/indexer/lib/indexer/fetcher/filecoin/beryx_api.ex b/apps/indexer/lib/indexer/fetcher/filecoin/beryx_api.ex new file mode 100644 index 000000000000..f4e7f212c9ae --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/filecoin/beryx_api.ex @@ -0,0 +1,50 @@ +defmodule Indexer.Fetcher.Filecoin.BeryxAPI do + @moduledoc """ + Interacts with the Beryx API to fetch account information based on an Ethereum + address hash + """ + + alias Explorer.Helper + alias HTTPoison.Response + + @doc """ + Fetches account information for a given Ethereum address hash from the Beryx API. + + ## Parameters + - `eth_address_hash` - The Ethereum address hash to fetch information for. + + ## Returns + - `{:ok, map()}`: On success, returns the account information as a map. + - `{:error, integer(), map()}`: On failure, returns the HTTP status code and the error message as a map. + - `{:error, HTTPoison.Error.t()}`: On network or other HTTP errors, returns the error structure. + """ + @spec fetch_account_info(EthereumJSONRPC.address()) :: + {:ok, map()} + | {:error, integer(), map()} + | {:error, HTTPoison.Error.t()} + def fetch_account_info(eth_address_hash) do + config = Application.get_env(:indexer, __MODULE__) + base_url = config |> Keyword.get(:base_url) |> String.trim_trailing("/") + api_token = config[:api_token] + + url = "#{base_url}/mainnet/account/info/#{eth_address_hash}" + + headers = [ + {"Authorization", "Bearer #{api_token}"}, + {"Content-Type", "application/json"} + ] + + case HTTPoison.get(url, headers) do + {:ok, %Response{body: body, status_code: 200}} -> + json = Helper.decode_json(body) + {:ok, json} + + {:ok, %Response{body: body, status_code: status_code}} -> + json = Helper.decode_json(body) + {:error, status_code, json} + + {:error, %HTTPoison.Error{}} = error -> + error + end + end +end diff --git a/apps/indexer/lib/indexer/prometheus/collector/filecoin_pending_address_operations_collector.ex b/apps/indexer/lib/indexer/prometheus/collector/filecoin_pending_address_operations_collector.ex new file mode 100644 index 000000000000..207841257542 --- /dev/null +++ b/apps/indexer/lib/indexer/prometheus/collector/filecoin_pending_address_operations_collector.ex @@ -0,0 +1,29 @@ +defmodule Indexer.Prometheus.Collector.FilecoinPendingAddressOperations do + @moduledoc """ + Custom collector to count number of records in filecoin_pending_address_operations table. + """ + + use Prometheus.Collector + + alias Explorer.Chain.Filecoin.PendingAddressOperation + alias Explorer.Repo + alias Prometheus.Model + + def collect_mf(_registry, callback) do + callback.( + create_gauge( + :filecoin_pending_address_operations, + "Number of records in filecoin_pending_address_operations table", + Repo.aggregate(PendingAddressOperation, :count, timeout: :infinity) + ) + ) + end + + def collect_metrics(:filecoin_pending_address_operations, count) do + Model.gauge_metrics([{count}]) + end + + defp create_gauge(name, help, data) do + Model.create_mf(name, help, :gauge, __MODULE__, data) + end +end diff --git a/apps/indexer/lib/indexer/prometheus/pending_block_operations_collector.ex b/apps/indexer/lib/indexer/prometheus/collector/pending_block_operations_collector.ex similarity index 91% rename from apps/indexer/lib/indexer/prometheus/pending_block_operations_collector.ex rename to apps/indexer/lib/indexer/prometheus/collector/pending_block_operations_collector.ex index e1e836a6bcfa..56f7f48408de 100644 --- a/apps/indexer/lib/indexer/prometheus/pending_block_operations_collector.ex +++ b/apps/indexer/lib/indexer/prometheus/collector/pending_block_operations_collector.ex @@ -1,4 +1,4 @@ -defmodule Indexer.Prometheus.PendingBlockOperationsCollector do +defmodule Indexer.Prometheus.Collector.PendingBlockOperations do @moduledoc """ Custom collector to count number of records in pending_block_operations table. """ diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex index 641b4ac65493..e56883914c92 100644 --- a/apps/indexer/lib/indexer/supervisor.ex +++ b/apps/indexer/lib/indexer/supervisor.ex @@ -195,6 +195,9 @@ defmodule Indexer.Supervisor do configure(Indexer.Fetcher.Celo.EpochBlockOperations.Supervisor, [ [json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor] ]), + configure(Indexer.Fetcher.Filecoin.AddressInfo.Supervisor, [ + [memory_monitor: memory_monitor] + ]), {Indexer.Fetcher.Beacon.Blob.Supervisor, [[memory_monitor: memory_monitor]]}, # Out-of-band fetchers diff --git a/apps/indexer/test/indexer/block/catchup/fetcher_test.exs b/apps/indexer/test/indexer/block/catchup/fetcher_test.exs index 292b48183c74..86fac863c388 100644 --- a/apps/indexer/test/indexer/block/catchup/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/catchup/fetcher_test.exs @@ -52,6 +52,7 @@ defmodule Indexer.Block.Catchup.FetcherTest do InternalTransaction.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) Token.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) TokenBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + Indexer.Fetcher.Filecoin.AddressInfo.Supervisor.Case.start_supervised!() MissingRangesCollector.start_link([]) MissingRangesManipulator.start_link([]) diff --git a/apps/indexer/test/indexer/block/fetcher_test.exs b/apps/indexer/test/indexer/block/fetcher_test.exs index ce5ff1686ae7..21416981d000 100644 --- a/apps/indexer/test/indexer/block/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/fetcher_test.exs @@ -275,6 +275,8 @@ defmodule Indexer.Block.FetcherTest do } do block_number = @first_full_block_number + Indexer.Fetcher.Filecoin.AddressInfo.Supervisor.Case.start_supervised!() + if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do case Keyword.fetch!(json_rpc_named_arguments, :variant) do EthereumJSONRPC.Nethermind -> @@ -682,6 +684,8 @@ defmodule Indexer.Block.FetcherTest do } do block_number = 7_374_455 + Indexer.Fetcher.Filecoin.AddressInfo.Supervisor.Case.start_supervised!() + if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do EthereumJSONRPC.Mox |> expect(:json_rpc, 2, fn diff --git a/apps/indexer/test/indexer/block/realtime/fetcher_test.exs b/apps/indexer/test/indexer/block/realtime/fetcher_test.exs index 12e164b634a7..ed249cf12c7a 100644 --- a/apps/indexer/test/indexer/block/realtime/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/realtime/fetcher_test.exs @@ -91,6 +91,8 @@ defmodule Indexer.Block.Realtime.FetcherTest do ReplacedTransaction.Supervisor.Case.start_supervised!() + Indexer.Fetcher.Filecoin.AddressInfo.Supervisor.Case.start_supervised!() + # In CELO network, there is a token duality feature where CELO can be used # as both a native chain currency and as an ERC-20 token (GoldToken). # Transactions that transfer CELO are also counted as token transfers, and @@ -596,6 +598,8 @@ defmodule Indexer.Block.Realtime.FetcherTest do block_fetcher: %Indexer.Block.Fetcher{json_rpc_named_arguments: json_rpc_named_arguments} ) + Indexer.Fetcher.Filecoin.AddressInfo.Supervisor.Case.start_supervised!() + ReplacedTransaction.Supervisor.Case.start_supervised!() # In CELO network, there is a token duality feature where CELO can be used diff --git a/apps/indexer/test/support/indexer/fetcher/filecoin_native_address_supervisor_case.ex b/apps/indexer/test/support/indexer/fetcher/filecoin_native_address_supervisor_case.ex new file mode 100644 index 000000000000..6f46db11c5cd --- /dev/null +++ b/apps/indexer/test/support/indexer/fetcher/filecoin_native_address_supervisor_case.ex @@ -0,0 +1,17 @@ +defmodule Indexer.Fetcher.Filecoin.AddressInfo.Supervisor.Case do + alias Indexer.Fetcher.Filecoin.AddressInfo + + def start_supervised!(fetcher_arguments \\ []) when is_list(fetcher_arguments) do + merged_fetcher_arguments = + Keyword.merge( + fetcher_arguments, + flush_interval: 50, + max_batch_size: 1, + max_concurrency: 1 + ) + + [merged_fetcher_arguments] + |> AddressInfo.Supervisor.child_spec() + |> ExUnit.Callbacks.start_supervised!() + end +end diff --git a/config/runtime.exs b/config/runtime.exs index 70fdba23d9f5..7710b3e3c022 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -638,6 +638,14 @@ config :explorer, Explorer.Chain.Metrics, enabled: ConfigHelper.parse_bool_env_var("PUBLIC_METRICS_ENABLED", "false"), update_period_hours: ConfigHelper.parse_integer_env_var("PUBLIC_METRICS_UPDATE_PERIOD_HOURS", 24) +config :explorer, Explorer.Chain.Filecoin.NativeAddress, + network_prefix: ConfigHelper.parse_catalog_value("FILECOIN_NETWORK_PREFIX", ["f", "t"], true, "f") + +config :explorer, Explorer.Migrator.FilecoinPendingAddressOperations, + enabled: ConfigHelper.chain_type() == :filecoin, + batch_size: ConfigHelper.parse_integer_env_var("FILECOIN_PENDING_ADDRESS_OPERATIONS_MIGRATION_BATCH_SIZE", 100), + concurrency: ConfigHelper.parse_integer_env_var("FILECOIN_PENDING_ADDRESS_OPERATIONS_MIGRATION_CONCURRENCY", 1) + ############### ### Indexer ### ############### @@ -1052,6 +1060,21 @@ config :indexer, Indexer.Fetcher.Celo.EpochBlockOperations.Supervisor, enabled: celo_epoch_fetchers_enabled?, disabled?: not celo_epoch_fetchers_enabled? +config :indexer, Indexer.Fetcher.Filecoin.BeryxAPI, + base_url: ConfigHelper.safe_get_env("BERYX_API_BASE_URL", "https://api.zondax.ch/fil/data/v3"), + api_token: System.get_env("BERYX_API_TOKEN") + +filecoin_native_address_fetcher_enabled? = + ConfigHelper.chain_type() == :filecoin and + not ConfigHelper.parse_bool_env_var("INDEXER_DISABLE_FILECOIN_ADDRESS_INFO_FETCHER") + +config :indexer, Indexer.Fetcher.Filecoin.AddressInfo.Supervisor, + enabled: filecoin_native_address_fetcher_enabled?, + disabled?: not filecoin_native_address_fetcher_enabled? + +config :indexer, Indexer.Fetcher.Filecoin.AddressInfo, + concurrency: ConfigHelper.parse_integer_env_var("INDEXER_FILECOIN_ADDRESS_INFO_CONCURRENCY", 1) + Code.require_file("#{config_env()}.exs", "config/runtime") for config <- "../apps/*/config/runtime/#{config_env()}.exs" |> Path.expand(__DIR__) |> Path.wildcard() do diff --git a/cspell.json b/cspell.json index 6d3d91b6b3e6..3fbe051f3845 100644 --- a/cspell.json +++ b/cspell.json @@ -47,6 +47,7 @@ "bafybeihxuj", "balancemulti", "benchee", + "beryx", "besu", "bignumber", "bigserial", @@ -133,6 +134,7 @@ "Cyclomatic", "cypherpunk", "czilladx", + "datacap", "datapoint", "datepicker", "DATETIME", @@ -178,6 +180,7 @@ "errorb", "erts", "Ethash", + "ethaccount", "etherchain", "ethprice", "ethsupply", @@ -378,6 +381,7 @@ "outcoming", "overengineering", "pawesome", + "paych", "pbcopy", "peeker", "peekers", @@ -587,6 +591,7 @@ "valuemin", "valuenow", "varint", + "verifreg", "verifyproxycontract", "verifysourcecode", "viewerjs", diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 5ec959df46a3..cf21ce115453 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -266,6 +266,13 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_CELO_VALIDATOR_GROUP_VOTES_BATCH_SIZE=200000 # INDEXER_DISABLE_CELO_EPOCH_FETCHER=false # INDEXER_DISABLE_CELO_VALIDATOR_GROUP_VOTES_FETCHER=false +# BERYX_API_TOKEN= +# BERYX_API_BASE_URL= +# FILECOIN_NETWORK_PREFIX=f +# FILECOIN_PENDING_ADDRESS_OPERATIONS_MIGRATION_BATCH_SIZE= +# FILECOIN_PENDING_ADDRESS_OPERATIONS_MIGRATION_CONCURRENCY= +# INDEXER_DISABLE_FILECOIN_ADDRESS_INFO_FETCHER=false +# INDEXER_FILECOIN_ADDRESS_INFO_CONCURRENCY=1 # INDEXER_ARBITRUM_MISSED_MESSAGES_BLOCKS_DEPTH= # INDEXER_REALTIME_FETCHER_MAX_GAP= # INDEXER_FETCHER_INIT_QUERY_LIMIT= diff --git a/mix.lock b/mix.lock index 6df0f6f0aaec..03cf8a5e1f5e 100644 --- a/mix.lock +++ b/mix.lock @@ -8,6 +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.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"}, "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"}, + "blake2": {:hex, :blake2, "1.0.4", "8263c69a191142922bc2510f1ffc0de0ae96e8c3bd5e2ad3fac7e87aed94c8b1", [:mix], [], "hexpm", "e9f4120d163ba14d86304195e50745fa18483e6ad2be94c864ae449bbdd6a189"}, "briefly": {:git, "https://github.com/CargoSense/briefly.git", "4836ba322ffb504a102a15cc6e35d928ef97120e", []}, "brotli": {:hex, :brotli, "0.3.2", "59cf45a399098516f1d34f70d8e010e5c9bf326659d3ef34c7cc56793339002b", [:rebar3], [], "hexpm", "9ec3ef9c753f80d0c657b4905193c55e5198f169fa1d1c044d8601d4d931a2ad"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, From a47893d4fdc8512181db918815faeb0f5c8f2b33 Mon Sep 17 00:00:00 2001 From: varasev <33550681+varasev@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:59:35 +0400 Subject: [PATCH 140/363] feat: Add `INDEXER_OPTIMISM_L1_DEPOSITS_TRANSACTION_TYPE` env variable (#10674) * fix: Use latest block when safe is not determined for OP chains * Add INDEXER_OPTIMISM_L1_DEPOSITS_TX_TYPE env for OP Deposits indexing module * Small refactoring * Fix specs for get_logs function --------- Co-authored-by: POA <33550681+poa@users.noreply.github.com> --- apps/indexer/lib/indexer/fetcher/optimism.ex | 22 +--------- .../lib/indexer/fetcher/optimism/deposit.ex | 43 +++++++++++++------ .../indexer/fetcher/optimism/withdrawal.ex | 2 +- .../indexer/fetcher/polygon_zkevm/bridge.ex | 34 +++------------ .../fetcher/polygon_zkevm/bridge_l1.ex | 4 +- .../fetcher/polygon_zkevm/bridge_l2.ex | 3 +- .../fetcher/rollup_l1_reorg_monitor.ex | 2 +- apps/indexer/lib/indexer/helper.ex | 15 ++++--- config/runtime.exs | 4 +- docker-compose/envs/common-blockscout.env | 1 + 10 files changed, 54 insertions(+), 76 deletions(-) diff --git a/apps/indexer/lib/indexer/fetcher/optimism.ex b/apps/indexer/lib/indexer/fetcher/optimism.ex index d4605d9f08b1..3a63cf01dfa1 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism.ex @@ -56,7 +56,7 @@ defmodule Indexer.Fetcher.Optimism do """ @spec get_block_check_interval(list()) :: {:ok, non_neg_integer(), non_neg_integer()} | {:error, any()} def get_block_check_interval(json_rpc_named_arguments) do - {last_safe_block, _} = get_safe_block(json_rpc_named_arguments) + {last_safe_block, _} = Helper.get_safe_block(json_rpc_named_arguments) first_block = max(last_safe_block - @block_check_interval_range_size, 1) @@ -91,26 +91,6 @@ defmodule Indexer.Fetcher.Optimism do ) end - @doc """ - Tries to get `safe` block number from the RPC node. - If it's not available, gets the `latest` one. - Returns a tuple of `{block_number, is_latest}` - where `is_latest` is true if the `safe` is not available. - """ - @spec get_safe_block(list()) :: {non_neg_integer(), boolean()} - def get_safe_block(json_rpc_named_arguments) do - case get_block_number_by_tag("safe", json_rpc_named_arguments) do - {:ok, safe_block} -> - {safe_block, false} - - {:error, :not_found} -> - {:ok, latest_block} = - get_block_number_by_tag("latest", json_rpc_named_arguments, Helper.infinite_retries_number()) - - {latest_block, true} - end - end - defp get_block_timestamp_by_number_inner(number, json_rpc_named_arguments) do result = %{id: 0, number: number} diff --git a/apps/indexer/lib/indexer/fetcher/optimism/deposit.ex b/apps/indexer/lib/indexer/fetcher/optimism/deposit.ex index cd1116b87b9a..0f1d1e3af22f 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/deposit.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/deposit.ex @@ -28,6 +28,7 @@ defmodule Indexer.Fetcher.Optimism.Deposit do :safe_block, :optimism_portal, :json_rpc_named_arguments, + :transaction_type, mode: :catch_up, filter_id: nil, check_interval: nil @@ -78,7 +79,7 @@ defmodule Indexer.Fetcher.Optimism.Deposit do {last_l1_block_number, last_l1_tx_hash} <- get_last_l1_item(), {:ok, last_l1_tx} <- Optimism.get_transaction_by_hash(last_l1_tx_hash, json_rpc_named_arguments), {:l1_tx_not_found, false} <- {:l1_tx_not_found, !is_nil(last_l1_tx_hash) && is_nil(last_l1_tx)}, - {safe_block, _} = Optimism.get_safe_block(json_rpc_named_arguments), + {safe_block, _} = Helper.get_safe_block(json_rpc_named_arguments), {:start_block_l1_valid, true} <- {:start_block_l1_valid, (start_block_l1 <= last_l1_block_number || last_l1_block_number == 0) && start_block_l1 <= safe_block} do @@ -97,7 +98,8 @@ defmodule Indexer.Fetcher.Optimism.Deposit do safe_block: safe_block, optimism_portal: optimism_portal, json_rpc_named_arguments: json_rpc_named_arguments, - batch_size: parse_integer(env[:batch_size]) || @batch_size + batch_size: parse_integer(env[:batch_size]) || @batch_size, + transaction_type: env[:transaction_type] }} else {:start_block_l1_valid, false} -> @@ -144,7 +146,8 @@ defmodule Indexer.Fetcher.Optimism.Deposit do optimism_portal: optimism_portal, json_rpc_named_arguments: json_rpc_named_arguments, mode: :catch_up, - batch_size: batch_size + batch_size: batch_size, + transaction_type: transaction_type } = state ) do to_block = min(from_block + batch_size, safe_block) @@ -160,7 +163,7 @@ defmodule Indexer.Fetcher.Optimism.Deposit do 3 )}, _ = Helper.log_blocks_chunk_handling(from_block, to_block, start_block, safe_block, nil, :L1), - deposits = events_to_deposits(logs, json_rpc_named_arguments), + deposits = events_to_deposits(logs, transaction_type, json_rpc_named_arguments), {:import, {:ok, _imported}} <- {:import, Chain.import(%{optimism_deposits: %{params: deposits}, timeout: :infinity})} do Publisher.broadcast(%{optimism_deposits: deposits}, :realtime) @@ -212,7 +215,8 @@ defmodule Indexer.Fetcher.Optimism.Deposit do optimism_portal: optimism_portal, json_rpc_named_arguments: json_rpc_named_arguments, batch_size: batch_size, - mode: :catch_up + mode: :catch_up, + transaction_type: transaction_type } = state ) do with {:check_interval, {:ok, check_interval, new_safe}} <- @@ -236,7 +240,7 @@ defmodule Indexer.Fetcher.Optimism.Deposit do @transaction_deposited_event, json_rpc_named_arguments ) do - handle_new_logs(logs, json_rpc_named_arguments) + handle_new_logs(logs, transaction_type, json_rpc_named_arguments) Process.send(self(), :fetch, []) {:noreply, %{state | mode: :realtime, filter_id: filter_id, check_interval: check_interval}} else @@ -268,12 +272,13 @@ defmodule Indexer.Fetcher.Optimism.Deposit do json_rpc_named_arguments: json_rpc_named_arguments, mode: :realtime, filter_id: filter_id, - check_interval: check_interval + check_interval: check_interval, + transaction_type: transaction_type } = state ) do case get_filter_changes(filter_id, json_rpc_named_arguments) do {:ok, logs} -> - handle_new_logs(logs, json_rpc_named_arguments) + handle_new_logs(logs, transaction_type, json_rpc_named_arguments) Process.send_after(self(), :fetch, check_interval) {:noreply, state} @@ -342,7 +347,7 @@ defmodule Indexer.Fetcher.Optimism.Deposit do :ok end - defp handle_new_logs(logs, json_rpc_named_arguments) do + defp handle_new_logs(logs, transaction_type, json_rpc_named_arguments) do {reorgs, logs_to_parse, min_block, max_block, cnt} = logs |> Enum.reduce({MapSet.new(), [], nil, 0, 0}, fn @@ -362,7 +367,7 @@ defmodule Indexer.Fetcher.Optimism.Deposit do handle_reorgs(reorgs) unless Enum.empty?(logs_to_parse) do - deposits = events_to_deposits(logs_to_parse, json_rpc_named_arguments) + deposits = events_to_deposits(logs_to_parse, transaction_type, json_rpc_named_arguments) {:ok, _imported} = Chain.import(%{optimism_deposits: %{params: deposits}, timeout: :infinity}) Publisher.broadcast(%{optimism_deposits: deposits}, :realtime) @@ -378,7 +383,7 @@ defmodule Indexer.Fetcher.Optimism.Deposit do end end - defp events_to_deposits(logs, json_rpc_named_arguments) do + defp events_to_deposits(logs, transaction_type, json_rpc_named_arguments) do timestamps = logs |> Enum.reduce(MapSet.new(), fn %{"blockNumber" => block_number_quantity}, acc -> @@ -399,7 +404,7 @@ defmodule Indexer.Fetcher.Optimism.Deposit do %{} end - Enum.map(logs, &event_to_deposit(&1, timestamps)) + Enum.map(logs, &event_to_deposit(&1, timestamps, transaction_type)) end defp event_to_deposit( @@ -411,7 +416,8 @@ defmodule Indexer.Fetcher.Optimism.Deposit do "topics" => [_, @address_prefix <> from_stripped, @address_prefix <> to_stripped, _], "data" => opaque_data }, - timestamps + timestamps, + transaction_type ) do {_, prefixed_block_hash} = (String.pad_leading("", 64, "0") <> stripped_block_hash) |> String.split_at(-64) {_, prefixed_log_index} = (String.pad_leading("", 64, "0") <> stripped_log_index) |> String.split_at(-64) @@ -452,8 +458,17 @@ defmodule Indexer.Fetcher.Optimism.Deposit do encoding: :hex ) + transaction_type = + transaction_type + |> Integer.to_string(16) + |> String.downcase() + l2_tx_hash = - "0x" <> ("7e#{rlp_encoded}" |> Base.decode16!(case: :mixed) |> ExKeccak.hash_256() |> Base.encode16(case: :lower)) + "0x" <> + ((transaction_type <> "#{rlp_encoded}") + |> Base.decode16!(case: :mixed) + |> ExKeccak.hash_256() + |> Base.encode16(case: :lower)) block_number = quantity_to_integer(block_number_quantity) diff --git a/apps/indexer/lib/indexer/fetcher/optimism/withdrawal.ex b/apps/indexer/lib/indexer/fetcher/optimism/withdrawal.ex index d993111efb9b..5756ad60064c 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/withdrawal.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/withdrawal.ex @@ -57,7 +57,7 @@ defmodule Indexer.Fetcher.Optimism.Withdrawal do false <- is_nil(start_block_l2), true <- start_block_l2 > 0, {last_l2_block_number, last_l2_transaction_hash} <- get_last_l2_item(), - {safe_block, safe_block_is_latest} = Optimism.get_safe_block(json_rpc_named_arguments), + {safe_block, safe_block_is_latest} = Helper.get_safe_block(json_rpc_named_arguments), {:start_block_l2_valid, true} <- {:start_block_l2_valid, (start_block_l2 <= last_l2_block_number || last_l2_block_number == 0) && start_block_l2 <= safe_block}, diff --git a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge.ex b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge.ex index 1f54f1bc7b5b..ec404cf62521 100644 --- a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge.ex +++ b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge.ex @@ -7,10 +7,7 @@ defmodule Indexer.Fetcher.PolygonZkevm.Bridge do import EthereumJSONRPC, only: [ - integer_to_quantity: 1, - json_rpc: 2, quantity_to_integer: 1, - request: 1, timestamp_to_datetime: 1 ] @@ -82,40 +79,19 @@ defmodule Indexer.Fetcher.PolygonZkevm.Bridge do @spec get_logs_all({non_neg_integer(), non_neg_integer()}, binary(), list()) :: list() def get_logs_all({chunk_start, chunk_end}, bridge_contract, json_rpc_named_arguments) do {:ok, result} = - get_logs( + IndexerHelper.get_logs( chunk_start, chunk_end, bridge_contract, [[@bridge_event, @claim_event_v1, @claim_event_v2]], - json_rpc_named_arguments + json_rpc_named_arguments, + 0, + IndexerHelper.infinite_retries_number() ) Logs.elixir_to_params(result) end - defp get_logs(from_block, to_block, address, topics, json_rpc_named_arguments, retries \\ 100_000_000) do - processed_from_block = if is_integer(from_block), do: integer_to_quantity(from_block), else: from_block - processed_to_block = if is_integer(to_block), do: integer_to_quantity(to_block), else: to_block - - req = - request(%{ - id: 0, - method: "eth_getLogs", - params: [ - %{ - :fromBlock => processed_from_block, - :toBlock => processed_to_block, - :address => address, - :topics => topics - } - ] - }) - - error_message = &"Cannot fetch logs for the block range #{from_block}..#{to_block}. Error: #{inspect(&1)}" - - IndexerHelper.repeated_call(&json_rpc/2, [req, json_rpc_named_arguments], error_message, retries) - end - @doc """ Imports the given zkEVM bridge operations into database. Used by Indexer.Fetcher.PolygonZkevm.BridgeL1 and Indexer.Fetcher.PolygonZkevm.BridgeL2 fetchers. @@ -241,7 +217,7 @@ defmodule Indexer.Fetcher.PolygonZkevm.Bridge do defp blocks_to_timestamps(events, json_rpc_named_arguments) do events - |> IndexerHelper.get_blocks_by_events(json_rpc_named_arguments, 100_000_000) + |> IndexerHelper.get_blocks_by_events(json_rpc_named_arguments, IndexerHelper.infinite_retries_number()) |> Enum.reduce(%{}, fn block, acc -> block_number = quantity_to_integer(Map.get(block, "number")) timestamp = timestamp_to_datetime(Map.get(block, "timestamp")) diff --git a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex index 6e01b28d4379..54a6d02b62e2 100644 --- a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex +++ b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex @@ -219,7 +219,9 @@ defmodule Indexer.Fetcher.PolygonZkevm.BridgeL1 do end) new_start_block = last_written_block + 1 - {:ok, new_end_block} = Helper.get_block_number_by_tag("latest", json_rpc_named_arguments, 100_000_000) + + {:ok, new_end_block} = + Helper.get_block_number_by_tag("latest", json_rpc_named_arguments, Helper.infinite_retries_number()) delay = if new_end_block == last_written_block do diff --git a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l2.ex b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l2.ex index 66bdba42d4a2..b5011d6de7a5 100644 --- a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l2.ex +++ b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l2.ex @@ -71,7 +71,8 @@ defmodule Indexer.Fetcher.PolygonZkevm.BridgeL2 do false <- is_nil(start_block), true <- start_block > 0, {last_l2_block_number, last_l2_transaction_hash} = Reader.last_l2_item(), - {:ok, latest_block} = Helper.get_block_number_by_tag("latest", json_rpc_named_arguments, 100_000_000), + {:ok, latest_block} = + Helper.get_block_number_by_tag("latest", json_rpc_named_arguments, Helper.infinite_retries_number()), {:start_block_valid, true} <- {:start_block_valid, (start_block <= last_l2_block_number || last_l2_block_number == 0) && start_block <= latest_block}, diff --git a/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex b/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex index 20b5c278609e..3c810cd7e730 100644 --- a/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex +++ b/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex @@ -114,7 +114,7 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do prev_latest: prev_latest } = state ) do - {:ok, latest} = Helper.get_block_number_by_tag("latest", json_rpc_named_arguments, 100_000_000) + {:ok, latest} = Helper.get_block_number_by_tag("latest", json_rpc_named_arguments, Helper.infinite_retries_number()) if latest < prev_latest do Logger.warning("Reorg detected: previous latest block ##{prev_latest}, current latest block ##{latest}.") diff --git a/apps/indexer/lib/indexer/helper.ex b/apps/indexer/lib/indexer/helper.ex index b5a143e76b1a..512e3716785a 100644 --- a/apps/indexer/lib/indexer/helper.ex +++ b/apps/indexer/lib/indexer/helper.ex @@ -108,9 +108,9 @@ defmodule Indexer.Helper do first_block = max(last_safe_block - @block_check_interval_range_size, 1) with {:ok, first_block_timestamp} <- - get_block_timestamp_by_number(first_block, json_rpc_named_arguments, 100_000_000), + get_block_timestamp_by_number(first_block, json_rpc_named_arguments, @infinite_retries_number), {:ok, last_safe_block_timestamp} <- - get_block_timestamp_by_number(last_safe_block, json_rpc_named_arguments, 100_000_000) do + get_block_timestamp_by_number(last_safe_block, json_rpc_named_arguments, @infinite_retries_number) do block_check_interval = ceil((last_safe_block_timestamp - first_block_timestamp) / (last_safe_block - first_block) * 1000 / 2) @@ -139,8 +139,9 @@ defmodule Indexer.Helper do {:ok, safe_block} -> {safe_block, false} - {:error, :not_found} -> - {:ok, latest_block} = get_block_number_by_tag("latest", json_rpc_named_arguments, 100_000_000) + {:error, _} -> + {:ok, latest_block} = get_block_number_by_tag("latest", json_rpc_named_arguments, @infinite_retries_number) + {latest_block, true} end end @@ -251,14 +252,14 @@ defmodule Indexer.Helper do non_neg_integer() | binary(), non_neg_integer() | binary(), binary(), - [binary()], + [binary()] | [list()], EthereumJSONRPC.json_rpc_named_arguments() ) :: {:error, atom() | binary() | map()} | {:ok, any()} @spec get_logs( non_neg_integer() | binary(), non_neg_integer() | binary(), binary(), - [binary()], + [binary()] | [list()], EthereumJSONRPC.json_rpc_named_arguments(), integer() ) :: {:error, atom() | binary() | map()} | {:ok, any()} @@ -266,7 +267,7 @@ defmodule Indexer.Helper do non_neg_integer() | binary(), non_neg_integer() | binary(), binary(), - [binary()], + [binary()] | [list()], EthereumJSONRPC.json_rpc_named_arguments(), integer(), non_neg_integer() diff --git a/config/runtime.exs b/config/runtime.exs index 7710b3e3c022..756bb1ef2a01 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -858,7 +858,9 @@ config :indexer, Indexer.Fetcher.Optimism, optimism_l1_rpc: System.get_env("INDEXER_OPTIMISM_L1_RPC"), optimism_l1_system_config: System.get_env("INDEXER_OPTIMISM_L1_SYSTEM_CONFIG_CONTRACT") -config :indexer, Indexer.Fetcher.Optimism.Deposit, batch_size: System.get_env("INDEXER_OPTIMISM_L1_DEPOSITS_BATCH_SIZE") +config :indexer, Indexer.Fetcher.Optimism.Deposit, + batch_size: System.get_env("INDEXER_OPTIMISM_L1_DEPOSITS_BATCH_SIZE"), + transaction_type: ConfigHelper.parse_integer_env_var("INDEXER_OPTIMISM_L1_DEPOSITS_TRANSACTION_TYPE", 126) config :indexer, Indexer.Fetcher.Optimism.OutputRoot, output_oracle: System.get_env("INDEXER_OPTIMISM_L1_OUTPUT_ORACLE_CONTRACT") diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index cf21ce115453..fd2f85e1ffc2 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -288,6 +288,7 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_OPTIMISM_L2_WITHDRAWALS_START_BLOCK= # INDEXER_OPTIMISM_L2_MESSAGE_PASSER_CONTRACT= # INDEXER_OPTIMISM_L1_DEPOSITS_BATCH_SIZE= +# INDEXER_OPTIMISM_L1_DEPOSITS_TRANSACTION_TYPE= # ROOTSTOCK_REMASC_ADDRESS= # ROOTSTOCK_BRIDGE_ADDRESS= # ROOTSTOCK_LOCKED_BTC_CACHE_PERIOD= From 924454658e2571acafbae16e8a3885e7015b3bfa Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 10 Sep 2024 17:27:35 +0300 Subject: [PATCH 141/363] Update mix.exs --- apps/block_scout_web/mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs index a81c8c5c6f40..a0312d341220 100644 --- a/apps/block_scout_web/mix.exs +++ b/apps/block_scout_web/mix.exs @@ -23,7 +23,7 @@ defmodule BlockScoutWeb.Mixfile do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "6.7.2", + version: "6.8.0", xref: [ exclude: [ Explorer.Chain.PolygonZkevm.Reader, From 5934c4f6bed9d3b13f92781e0b07da0f827a227e Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 11 Sep 2024 16:05:27 +0300 Subject: [PATCH 142/363] Add rights to logs folder in the Docker image (#10740) --- docker/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index e0bd205c91d0..834c57ff4319 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -99,4 +99,6 @@ COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/config/c COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/config/config_helper.exs /app/releases/${RELEASE_VERSION}/config_helper.exs COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/config/assets/precompiles-arbitrum.json ./config/assets/precompiles-arbitrum.json +RUN chown -R ${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app + USER ${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} From eeee120768ccc63edc6b0d4899701da611e59a1f Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Wed, 11 Sep 2024 18:50:39 +0400 Subject: [PATCH 143/363] chore: Add meta to migrations_status (#10678) --- apps/explorer/config/runtime/test.exs | 1 + .../explorer/migrator/filling_migration.ex | 10 ++-- .../lib/explorer/migrator/migration_status.ex | 55 +++++++++++++++++-- .../migrator/shrink_internal_transactions.ex | 8 +-- ...30142652_add_meta_to_migrations_status.exs | 9 +++ 5 files changed, 71 insertions(+), 12 deletions(-) create mode 100644 apps/explorer/priv/repo/migrations/20240830142652_add_meta_to_migrations_status.exs diff --git a/apps/explorer/config/runtime/test.exs b/apps/explorer/config/runtime/test.exs index 1dae56dfe007..958bff40b841 100644 --- a/apps/explorer/config/runtime/test.exs +++ b/apps/explorer/config/runtime/test.exs @@ -47,6 +47,7 @@ config :explorer, Explorer.Migrator.TokenTransferTokenType, enabled: false config :explorer, Explorer.Migrator.SanitizeIncorrectWETHTokenTransfers, enabled: false config :explorer, Explorer.Migrator.TransactionBlockConsensus, enabled: false config :explorer, Explorer.Migrator.TokenTransferBlockConsensus, enabled: false +config :explorer, Explorer.Migrator.ShrinkInternalTransactions, enabled: false config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, enabled: false config :explorer, diff --git a/apps/explorer/lib/explorer/migrator/filling_migration.ex b/apps/explorer/lib/explorer/migrator/filling_migration.ex index b150dcec3e19..f1afea795de8 100644 --- a/apps/explorer/lib/explorer/migrator/filling_migration.ex +++ b/apps/explorer/lib/explorer/migrator/filling_migration.ex @@ -37,15 +37,15 @@ defmodule Explorer.Migrator.FillingMigration do @impl true def handle_continue(:ok, state) do - case MigrationStatus.get_status(migration_name()) do - "completed" -> + case MigrationStatus.fetch(migration_name()) do + %{status: "completed"} -> update_cache() {:stop, :normal, state} - _ -> + migration_status -> MigrationStatus.set_status(migration_name(), "started") schedule_batch_migration() - {:noreply, %{}} + {:noreply, (migration_status && migration_status.meta) || %{}} end end @@ -63,6 +63,8 @@ defmodule Explorer.Migrator.FillingMigration do |> Enum.map(&run_task/1) |> Task.await_many(:infinity) + MigrationStatus.update_meta(migration_name(), new_state) + schedule_batch_migration() {:noreply, new_state} diff --git a/apps/explorer/lib/explorer/migrator/migration_status.ex b/apps/explorer/lib/explorer/migrator/migration_status.ex index e59011e64a89..ac1fe8f5d141 100644 --- a/apps/explorer/lib/explorer/migrator/migration_status.ex +++ b/apps/explorer/lib/explorer/migrator/migration_status.ex @@ -8,25 +8,72 @@ defmodule Explorer.Migrator.MigrationStatus do @primary_key false typed_schema "migrations_status" do - field(:migration_name, :string) + field(:migration_name, :string, primary_key: true) # ["started", "completed"] field(:status, :string) + field(:meta, :map) timestamps() end @doc false def changeset(migration_status \\ %__MODULE__{}, params) do - cast(migration_status, params, [:migration_name, :status]) + cast(migration_status, params, [:migration_name, :status, :meta]) end + @doc """ + Get the `MigrationStatus` struct by migration name. + """ + @spec fetch(String.t()) :: __MODULE__.t() | nil + def fetch(migration_name) do + migration_name + |> get_by_migration_name_query() + |> Repo.one() + end + + @doc """ + Get the status of migration by its name. + """ + @spec get_status(String.t()) :: String.t() | nil def get_status(migration_name) do - Repo.one(from(ms in __MODULE__, where: ms.migration_name == ^migration_name, select: ms.status)) + migration_name + |> get_by_migration_name_query() + |> select([ms], ms.status) + |> Repo.one() end + @doc """ + Set the status of migration by its name. + """ + @spec set_status(String.t(), String.t()) :: {:ok, __MODULE__.t()} | {:error, Ecto.Changeset.t()} def set_status(migration_name, status) do %{migration_name: migration_name, status: status} |> changeset() - |> Repo.insert(on_conflict: {:replace_all_except, [:inserted_at]}, conflict_target: :migration_name) + |> Repo.insert(on_conflict: {:replace_all_except, [:inserted_at, :meta]}, conflict_target: :migration_name) + end + + @doc """ + Update migration meta by its name. + """ + @spec update_meta(String.t(), map()) :: :ok | {:ok, __MODULE__.t()} | {:error, Ecto.Changeset.t()} + def update_meta(migration_name, new_meta) do + migration_name + |> get_by_migration_name_query() + |> Repo.one() + |> case do + nil -> + :ok + + migration_status -> + updated_meta = Map.merge(migration_status.meta || %{}, new_meta) + + migration_status + |> changeset(%{meta: updated_meta}) + |> Repo.update() + end + end + + defp get_by_migration_name_query(query \\ __MODULE__, migration_name) do + from(ms in query, where: ms.migration_name == ^migration_name) end end diff --git a/apps/explorer/lib/explorer/migrator/shrink_internal_transactions.ex b/apps/explorer/lib/explorer/migrator/shrink_internal_transactions.ex index 414f330b674f..29d8f6565b83 100644 --- a/apps/explorer/lib/explorer/migrator/shrink_internal_transactions.ex +++ b/apps/explorer/lib/explorer/migrator/shrink_internal_transactions.ex @@ -18,13 +18,13 @@ defmodule Explorer.Migrator.ShrinkInternalTransactions do def migration_name, do: @migration_name @impl FillingMigration - def last_unprocessed_identifiers(%{max_block_number: -1} = state), do: {[], state} + def last_unprocessed_identifiers(%{"max_block_number" => -1} = state), do: {[], state} - def last_unprocessed_identifiers(%{max_block_number: from_block_number} = state) do + def last_unprocessed_identifiers(%{"max_block_number" => from_block_number} = state) do limit = batch_size() * concurrency() to_block_number = max(from_block_number - limit + 1, 0) - {Enum.to_list(from_block_number..to_block_number), %{state | max_block_number: to_block_number - 1}} + {Enum.to_list(from_block_number..to_block_number), %{state | "max_block_number" => to_block_number - 1}} end def last_unprocessed_identifiers(state) do @@ -38,7 +38,7 @@ defmodule Explorer.Migrator.ShrinkInternalTransactions do max_block_number = Repo.one(query, timeout: :infinity) state - |> Map.put(:max_block_number, max_block_number || -1) + |> Map.put("max_block_number", max_block_number || -1) |> last_unprocessed_identifiers() end diff --git a/apps/explorer/priv/repo/migrations/20240830142652_add_meta_to_migrations_status.exs b/apps/explorer/priv/repo/migrations/20240830142652_add_meta_to_migrations_status.exs new file mode 100644 index 000000000000..f17486215b9c --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20240830142652_add_meta_to_migrations_status.exs @@ -0,0 +1,9 @@ +defmodule Explorer.Repo.Migrations.AddMetaToMigrationsStatus do + use Ecto.Migration + + def change do + alter table(:migrations_status) do + add(:meta, :map) + end + end +end From b55cc8efb156f0cd006e1478a943191206026396 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Wed, 11 Sep 2024 18:51:00 +0400 Subject: [PATCH 144/363] fix: Fix empty current token balances (#10745) --- apps/explorer/config/config.exs | 1 + apps/explorer/config/runtime/test.exs | 1 + apps/explorer/lib/explorer/application.ex | 3 +- .../runner/address/current_token_balances.ex | 8 +-- .../chain/import/stage/block_following.ex | 1 + .../chain/import/stage/block_referencing.ex | 1 - .../sanitize_missing_token_balances.ex | 61 +++++++++++++++++++ .../sanitize_missing_token_balances_test.exs | 40 ++++++++++++ 8 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 apps/explorer/lib/explorer/migrator/sanitize_missing_token_balances.ex create mode 100644 apps/explorer/test/explorer/migrator/sanitize_missing_token_balances_test.exs diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index 1ffec100ad2d..0827fabb0552 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -123,6 +123,7 @@ config :explorer, Explorer.Migrator.SanitizeIncorrectWETHTokenTransfers, enabled config :explorer, Explorer.Migrator.TransactionBlockConsensus, enabled: true config :explorer, Explorer.Migrator.TokenTransferBlockConsensus, enabled: true config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, enabled: true +config :explorer, Explorer.Migrator.SanitizeMissingTokenBalances, enabled: true config :explorer, Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand, enabled: true diff --git a/apps/explorer/config/runtime/test.exs b/apps/explorer/config/runtime/test.exs index 958bff40b841..eeae8772a77c 100644 --- a/apps/explorer/config/runtime/test.exs +++ b/apps/explorer/config/runtime/test.exs @@ -49,6 +49,7 @@ config :explorer, Explorer.Migrator.TransactionBlockConsensus, enabled: false config :explorer, Explorer.Migrator.TokenTransferBlockConsensus, enabled: false config :explorer, Explorer.Migrator.ShrinkInternalTransactions, enabled: false config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, enabled: false +config :explorer, Explorer.Migrator.SanitizeMissingTokenBalances, 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 ba57a31405ab..1fb5cfabd572 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -144,7 +144,8 @@ defmodule Explorer.Application do configure(Explorer.Migrator.RestoreOmittedWETHTransfers), configure(Explorer.Migrator.FilecoinPendingAddressOperations), configure_mode_dependent_process(Explorer.Migrator.ShrinkInternalTransactions, :indexer), - configure_chain_type_dependent_process(Explorer.Chain.Cache.StabilityValidatorsCounters, :stability) + configure_chain_type_dependent_process(Explorer.Chain.Cache.StabilityValidatorsCounters, :stability), + configure_mode_dependent_process(Explorer.Migrator.SanitizeMissingTokenBalances, :indexer) ] |> List.flatten() diff --git a/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex b/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex index 1ea20e0c3d4c..cc0dd4dfef26 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex @@ -108,17 +108,17 @@ defmodule Explorer.Chain.Import.Runner.Address.CurrentTokenBalances do |> Map.put(:timestamps, timestamps) multi - |> Multi.run(:filter_placeholders, fn _, _ -> + |> Multi.run(:filter_ctb_placeholders, fn _, _ -> Instrumenter.block_import_stage_runner( fn -> TokenBalances.filter_placeholders(changes_list) end, :block_following, :current_token_balances, - :filter_placeholders + :filter_ctb_placeholders ) end) - |> Multi.run(:address_current_token_balances, fn repo, _ -> + |> Multi.run(:address_current_token_balances, fn repo, %{filter_ctb_placeholders: filtered_changes_list} -> Instrumenter.block_import_stage_runner( - fn -> insert(repo, changes_list, insert_options) end, + fn -> insert(repo, filtered_changes_list, insert_options) end, :block_following, :current_token_balances, :address_current_token_balances 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 b59dc20dbd69..984892673ed9 100644 --- a/apps/explorer/lib/explorer/chain/import/stage/block_following.ex +++ b/apps/explorer/lib/explorer/chain/import/stage/block_following.ex @@ -13,6 +13,7 @@ defmodule Explorer.Chain.Import.Stage.BlockFollowing do do: [ Runner.Block.SecondDegreeRelations, Runner.Block.Rewards, + Runner.Address.TokenBalances, Runner.Address.CurrentTokenBalances ] diff --git a/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex b/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex index 0d339a78d331..df3b0c264d79 100644 --- a/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex +++ b/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex @@ -13,7 +13,6 @@ defmodule Explorer.Chain.Import.Stage.BlockReferencing do Runner.Logs, Runner.Tokens, Runner.TokenInstances, - Runner.Address.TokenBalances, Runner.TransactionActions, Runner.Withdrawals ] diff --git a/apps/explorer/lib/explorer/migrator/sanitize_missing_token_balances.ex b/apps/explorer/lib/explorer/migrator/sanitize_missing_token_balances.ex new file mode 100644 index 000000000000..28957fa3bf32 --- /dev/null +++ b/apps/explorer/lib/explorer/migrator/sanitize_missing_token_balances.ex @@ -0,0 +1,61 @@ +defmodule Explorer.Migrator.SanitizeMissingTokenBalances do + @moduledoc """ + Set value and value_fetched_at to nil for those token balances that are already filled but their + current token balances are not so the token balance fetcher could re-fetch them. + """ + + use Explorer.Migrator.FillingMigration + + import Ecto.Query + + alias Explorer.Chain.Address.{CurrentTokenBalance, TokenBalance} + alias Explorer.Migrator.FillingMigration + alias Explorer.Repo + + @migration_name "sanitize_missing_token_balances" + + @impl FillingMigration + def migration_name, do: @migration_name + + @impl FillingMigration + def last_unprocessed_identifiers(state) do + limit = batch_size() * concurrency() + + ids = + unprocessed_data_query() + |> select([_ctb, tb], tb.id) + |> limit(^limit) + |> Repo.all(timeout: :infinity) + + {ids, state} + end + + @impl FillingMigration + def unprocessed_data_query do + from( + ctb in CurrentTokenBalance, + join: tb in TokenBalance, + on: + ctb.address_hash == tb.address_hash and + ctb.token_contract_address_hash == tb.token_contract_address_hash and + ctb.block_number == tb.block_number and + ((is_nil(ctb.token_id) and is_nil(tb.token_id)) or ctb.token_id == tb.token_id), + where: is_nil(ctb.value) or is_nil(ctb.value_fetched_at), + where: not is_nil(tb.value) and not is_nil(tb.value_fetched_at) + ) + end + + @impl FillingMigration + def update_batch(token_balance_ids) do + query = + from(tb in TokenBalance, + where: tb.id in ^token_balance_ids, + update: [set: [value: nil, value_fetched_at: nil]] + ) + + Repo.update_all(query, [], timeout: :infinity) + end + + @impl FillingMigration + def update_cache, do: :ok +end diff --git a/apps/explorer/test/explorer/migrator/sanitize_missing_token_balances_test.exs b/apps/explorer/test/explorer/migrator/sanitize_missing_token_balances_test.exs new file mode 100644 index 000000000000..18a6ffe40b73 --- /dev/null +++ b/apps/explorer/test/explorer/migrator/sanitize_missing_token_balances_test.exs @@ -0,0 +1,40 @@ +defmodule Explorer.Migrator.SanitizeMissingTokenBalancesTest do + use Explorer.DataCase, async: false + + alias Explorer.Chain.Address.TokenBalance + alias Explorer.Migrator.{SanitizeMissingTokenBalances, MigrationStatus} + alias Explorer.Repo + + describe "Migrate token balances" do + test "Unset value and value_fetched_at for token balances related to not processed current token balances" do + Enum.each(0..10, fn _x -> + token_balance = insert(:token_balance) + + insert(:address_current_token_balance, + address: token_balance.address, + token_contract_address_hash: token_balance.token_contract_address_hash, + token_id: token_balance.token_id, + block_number: token_balance.block_number, + value: nil, + value_fetched_at: nil + ) + + refute is_nil(token_balance.value) + refute is_nil(token_balance.value_fetched_at) + end) + + assert MigrationStatus.get_status("sanitize_missing_token_balances") == nil + + SanitizeMissingTokenBalances.start_link([]) + Process.sleep(100) + + TokenBalance + |> Repo.all() + |> Enum.each(fn tb -> + assert %{value: nil, value_fetched_at: nil} = tb + end) + + assert MigrationStatus.get_status("sanitize_missing_token_balances") == "completed" + end + end +end From 85ecef9e305dcc72c3986c67d46dd3502db991e5 Mon Sep 17 00:00:00 2001 From: varasev <33550681+varasev@users.noreply.github.com> Date: Thu, 12 Sep 2024 13:24:09 +0400 Subject: [PATCH 145/363] fix: Avoid key violation error in `Indexer.Fetcher.Optimism.TxnBatch` (#10752) * fix: Remove key violation error from Indexer.Fetcher.Optimism.TxnBatch fetcher * Update cspell.json --------- Co-authored-by: POA <33550681+poa@users.noreply.github.com> --- apps/indexer/lib/indexer/fetcher/optimism/txn_batch.ex | 10 +++++++++- cspell.json | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/indexer/lib/indexer/fetcher/optimism/txn_batch.ex b/apps/indexer/lib/indexer/fetcher/optimism/txn_batch.ex index c122d1713c6b..5f70a79f706e 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/txn_batch.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/txn_batch.ex @@ -1265,7 +1265,15 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do |> Enum.uniq() |> Enum.filter(fn id -> id > 0 end) - Repo.delete_all(from(fs in FrameSequence, where: fs.id in ^ids)) + try do + Repo.delete_all(from(fs in FrameSequence, where: fs.id in ^ids)) + rescue + # we need to ignore `foreign_key_violation` exception when deleting the rows + # because there can be a case when the chain partially replaces the frame sequence + # (e.g. Unichain Private Testnet), and so some rows in `op_transaction_batches` table + # can still reference to the `op_frame_sequences` table + _ -> nil + end end defp set_frame_sequences_view_ready(sequences) do diff --git a/cspell.json b/cspell.json index 3fbe051f3845..be5ffa2ccaa2 100644 --- a/cspell.json +++ b/cspell.json @@ -564,6 +564,7 @@ "unfetched", "unfinalized", "unindexed", + "Unichain", "Unitarion", "Unitorius", "Unitorus", From 014a126849e67cefd4f120ecae2a865265665579 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Thu, 12 Sep 2024 22:23:59 +0400 Subject: [PATCH 146/363] fix: Insert coin balances placeholders in internal transactions fetcher (#10603) --- .../indexer/fetcher/internal_transaction.ex | 6 +++- .../transform/address_coin_balances.ex | 18 ++++++++++-- .../transform/address_coin_balances_test.exs | 28 ++++++++++++------- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/apps/indexer/lib/indexer/fetcher/internal_transaction.ex b/apps/indexer/lib/indexer/fetcher/internal_transaction.ex index 91d23414d985..cb3df8a15550 100644 --- a/apps/indexer/lib/indexer/fetcher/internal_transaction.ex +++ b/apps/indexer/lib/indexer/fetcher/internal_transaction.ex @@ -24,7 +24,7 @@ defmodule Indexer.Fetcher.InternalTransaction do alias Indexer.{BufferedTask, Tracer} alias Indexer.Fetcher.InternalTransaction.Supervisor, as: InternalTransactionSupervisor alias Indexer.Transform.Celo.TransactionTokenTransfers, as: CeloTransactionTokenTransfers - alias Indexer.Transform.{Addresses, AddressTokenBalances} + alias Indexer.Transform.{AddressCoinBalances, Addresses, AddressTokenBalances} @behaviour BufferedTask @@ -278,6 +278,9 @@ defmodule Indexer.Fetcher.InternalTransaction do {String.downcase(hash), block_number} end) + address_coin_balances_params_set = + AddressCoinBalances.params_set(%{internal_transactions_params: internal_transactions_params_marked}) + empty_block_numbers = unique_numbers |> MapSet.new() @@ -310,6 +313,7 @@ defmodule Indexer.Fetcher.InternalTransaction do token_transfers: %{params: celo_token_transfers}, tokens: %{params: celo_tokens}, addresses: %{params: addresses_params}, + address_coin_balances: %{params: address_coin_balances_params_set}, internal_transactions: %{params: internal_transactions_and_empty_block_numbers, with: :blockless_changeset}, timeout: :infinity }) diff --git a/apps/indexer/lib/indexer/transform/address_coin_balances.ex b/apps/indexer/lib/indexer/transform/address_coin_balances.ex index e85cd1ffa1aa..84d692c5caeb 100644 --- a/apps/indexer/lib/indexer/transform/address_coin_balances.ex +++ b/apps/indexer/lib/indexer/transform/address_coin_balances.ex @@ -67,20 +67,32 @@ defmodule Indexer.Transform.AddressCoinBalances do defp internal_transactions_params_reducer(%{block_number: block_number} = internal_transaction_params, acc) when is_integer(block_number) do case internal_transaction_params do - %{type: "call"} -> + %{type: "call"} = params -> acc + |> process_internal_transaction_field(params, :from_address_hash, block_number) + |> process_internal_transaction_field(params, :to_address_hash, block_number) %{type: "create", error: _} -> acc - %{type: "create", created_contract_address_hash: address_hash} when is_binary(address_hash) -> - MapSet.put(acc, %{address_hash: address_hash, block_number: block_number}) + %{type: "create"} = params -> + process_internal_transaction_field(acc, params, :created_contract_address_hash, block_number) %{type: "selfdestruct", from_address_hash: from_address_hash, to_address_hash: to_address_hash} when is_binary(from_address_hash) and is_binary(to_address_hash) -> acc |> MapSet.put(%{address_hash: from_address_hash, block_number: block_number}) |> MapSet.put(%{address_hash: to_address_hash, block_number: block_number}) + + _params -> + acc + end + end + + defp process_internal_transaction_field(acc, params, field, block_number) do + case Map.get(params, field) do + nil -> acc + address_hash -> MapSet.put(acc, %{address_hash: address_hash, block_number: block_number}) end end diff --git a/apps/indexer/test/indexer/transform/address_coin_balances_test.exs b/apps/indexer/test/indexer/transform/address_coin_balances_test.exs index ae2889b07b95..a230b43e5d5d 100644 --- a/apps/indexer/test/indexer/transform/address_coin_balances_test.exs +++ b/apps/indexer/test/indexer/transform/address_coin_balances_test.exs @@ -27,16 +27,24 @@ defmodule Indexer.Transform.AddressCoinBalancesTest do assert MapSet.size(params_set) == 0 end - test "with call internal transaction extracts nothing" do + test "with call internal transaction extracts from_address_hash and to_address_hash" do + block_number = 1 + from_address_hash = to_string(Factory.address_hash()) + to_address_hash = to_string(Factory.address_hash()) + internal_transaction_params = :internal_transaction |> Factory.params_for() - |> Map.update!(:type, &to_string/1) - |> Map.put(:block_number, 1) + |> Map.put(:type, "call") + |> Map.put(:block_number, block_number) + |> Map.put(:from_address_hash, from_address_hash) + |> Map.put(:to_address_hash, to_address_hash) params_set = AddressCoinBalances.params_set(%{internal_transactions_params: [internal_transaction_params]}) - assert MapSet.size(params_set) == 0 + assert MapSet.size(params_set) == 2 + assert MapSet.member?(params_set, %{address_hash: from_address_hash, block_number: block_number}) + assert MapSet.member?(params_set, %{address_hash: to_address_hash, block_number: block_number}) end test "with create internal transaction with error extracts nothing" do @@ -69,7 +77,7 @@ defmodule Indexer.Transform.AddressCoinBalancesTest do params_set = AddressCoinBalances.params_set(%{internal_transactions_params: [internal_transaction_params]}) assert MapSet.size(params_set) == 1 - assert %{address_hash: created_contract_address_hash, block_number: block_number} + assert MapSet.member?(params_set, %{address_hash: created_contract_address_hash, block_number: block_number}) end test "with self-destruct internal transaction extracts from_address_hash and to_address_hash" do @@ -94,8 +102,8 @@ defmodule Indexer.Transform.AddressCoinBalancesTest do params_set = AddressCoinBalances.params_set(%{internal_transactions_params: [internal_transaction_params]}) assert MapSet.size(params_set) == 2 - assert %{address_hash: from_address_hash, block_number: block_number} - assert %{address_hash: to_address_hash, block_number: block_number} + assert MapSet.member?(params_set, %{address_hash: from_address_hash, block_number: block_number}) + assert MapSet.member?(params_set, %{address_hash: to_address_hash, block_number: block_number}) end test "with log extracts address_hash" do @@ -162,7 +170,7 @@ defmodule Indexer.Transform.AddressCoinBalancesTest do params_set = AddressCoinBalances.params_set(%{transactions_params: [transaction_params]}) assert MapSet.size(params_set) == 1 - assert %{address_hash: from_address_hash, block_number: block_number} + assert MapSet.member?(params_set, %{address_hash: from_address_hash, block_number: block_number}) end test "with transaction with to_address_hash extracts from_address_hash and to_address_hash" do @@ -186,8 +194,8 @@ defmodule Indexer.Transform.AddressCoinBalancesTest do params_set = AddressCoinBalances.params_set(%{transactions_params: [transaction_params]}) assert MapSet.size(params_set) == 2 - assert %{address_hash: from_address_hash, block_number: block_number} - assert %{address_hash: to_address_hash, block_number: block_number} + assert MapSet.member?(params_set, %{address_hash: from_address_hash, block_number: block_number}) + assert MapSet.member?(params_set, %{address_hash: to_address_hash, block_number: block_number}) end end end From 889d5117371d7b1c054d79287290095df7e971e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 21:24:42 +0300 Subject: [PATCH 147/363] chore(deps): bump path-to-regexp and express (#10756) Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together. Updates `path-to-regexp` from 0.1.7 to 0.1.10 - [Release notes](https://github.com/pillarjs/path-to-regexp/releases) - [Changelog](https://github.com/pillarjs/path-to-regexp/blob/master/History.md) - [Commits](https://github.com/pillarjs/path-to-regexp/compare/v0.1.7...v0.1.10) Updates `express` from 4.19.2 to 4.21.0 - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/4.21.0/History.md) - [Commits](https://github.com/expressjs/express/compare/4.19.2...4.21.0) --- updated-dependencies: - dependency-name: path-to-regexp dependency-type: indirect - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 368 +++++++++++------- 1 file changed, 220 insertions(+), 148 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 3979c62ff9ce..0764da41abbe 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -5049,9 +5049,9 @@ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -5061,7 +5061,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -5085,11 +5085,11 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/body-parser/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -5456,13 +5456,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6719,16 +6724,19 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { @@ -6983,9 +6991,9 @@ } }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } @@ -7140,6 +7148,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", @@ -8600,36 +8627,36 @@ } }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -8654,11 +8681,11 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/express/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -8861,12 +8888,12 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -9113,15 +9140,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9379,11 +9410,11 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.1.1" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -12676,9 +12707,12 @@ } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", @@ -13569,9 +13603,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "node_modules/pathval": { "version": "1.1.1", @@ -15435,9 +15469,9 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -15470,6 +15504,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -15485,14 +15527,14 @@ } }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" @@ -15519,14 +15561,16 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -15615,13 +15659,17 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -21612,9 +21660,9 @@ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, "body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "requires": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -21624,7 +21672,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -21644,11 +21692,11 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "requires": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" } } } @@ -21922,13 +21970,15 @@ } }, "call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" } }, "callsites": { @@ -22847,13 +22897,13 @@ } }, "define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "requires": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" } }, "define-properties": { @@ -23057,9 +23107,9 @@ "dev": true }, "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" }, "encoding": { "version": "0.1.13", @@ -23183,6 +23233,19 @@ "which-typed-array": "^1.1.13" } }, + "es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "requires": { + "get-intrinsic": "^1.2.4" + } + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, "es-module-lexer": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", @@ -24376,36 +24439,36 @@ } }, "express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -24427,11 +24490,11 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "requires": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" } }, "safe-buffer": { @@ -24590,12 +24653,12 @@ } }, "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "requires": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -24781,10 +24844,11 @@ "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==" }, "get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "requires": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", @@ -24973,11 +25037,11 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "requires": { - "get-intrinsic": "^1.1.1" + "es-define-property": "^1.0.0" } }, "has-proto": { @@ -27468,9 +27532,9 @@ } }, "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" }, "merge-stream": { "version": "2.0.0", @@ -28164,9 +28228,9 @@ } }, "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "pathval": { "version": "1.1.1", @@ -29467,9 +29531,9 @@ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" }, "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "requires": { "debug": "2.6.9", "depd": "2.0.0", @@ -29501,6 +29565,11 @@ } } }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -29518,14 +29587,14 @@ } }, "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "requires": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" } }, "servify": { @@ -29546,14 +29615,16 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "requires": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" } }, "set-function-name": { @@ -29621,13 +29692,14 @@ "dev": true }, "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" } }, "signal-exit": { From 680bb0960e63f744d69c7a3d0fa279c8570ab667 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 13 Sep 2024 15:06:53 +0300 Subject: [PATCH 148/363] perf: Improve performance of transactions list page (#10734) * perf: Improve performance of the transactions list page * Update apps/explorer/lib/explorer/chain/transaction.ex Co-authored-by: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> * Mix format * Update apps/explorer/lib/explorer/chain/smart_contract/proxy.ex Co-authored-by: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> * Refactoring * Process review comment * Second refactoring * Revert latest refactoring * Fix nil address_hash * Update apps/explorer/lib/explorer/chain/transaction.ex Co-authored-by: Kirill Fedoseev * Remove credo comment * Update apps/explorer/lib/explorer/chain/smart_contract/proxy.ex Co-authored-by: Kirill Fedoseev * tuple list to map --------- Co-authored-by: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Co-authored-by: Kirill Fedoseev --- ...saction_token_transfer_controller_test.exs | 2 - apps/explorer/lib/explorer/chain/log.ex | 2 +- .../explorer/chain/smart_contract/proxy.ex | 49 ++++- .../proxy/models/implementation.ex | 22 ++- .../lib/explorer/chain/transaction.ex | 177 +++++++++++++++--- .../chain/smart_contract/proxy_test.exs | 83 ++++---- .../test/explorer/chain/transaction_test.exs | 6 - 7 files changed, 253 insertions(+), 88 deletions(-) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs index de82d6c15571..96fcbe6889c8 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs @@ -159,8 +159,6 @@ defmodule BlockScoutWeb.TransactionTokenTransferControllerTest do end test "preloads to_address smart contract verified", %{conn: conn} do - TestHelper.get_eip1967_implementation_zero_addresses() - transaction = insert(:transaction_to_verified_contract) conn = get(conn, transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, transaction.hash)) diff --git a/apps/explorer/lib/explorer/chain/log.ex b/apps/explorer/lib/explorer/chain/log.ex index 7ef35888444b..8fdb03cb8a84 100644 --- a/apps/explorer/lib/explorer/chain/log.ex +++ b/apps/explorer/lib/explorer/chain/log.ex @@ -224,7 +224,7 @@ defmodule Explorer.Chain.Log do else case Chain.find_contract_address(address_hash, address_options, false) do {:ok, %{smart_contract: smart_contract}} -> - full_abi = Proxy.combine_proxy_implementation_abi(smart_contract, options) + full_abi = Proxy.combine_proxy_implementation_abi(smart_contract, %{}, true, options) {full_abi, Map.put(acc, address_hash, full_abi)} _ -> diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex index 4d9a8bd7cfc3..52784c329aa5 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex @@ -480,17 +480,54 @@ defmodule Explorer.Chain.SmartContract.Proxy do @doc """ Returns combined ABI from proxy and implementation smart-contracts """ - @spec combine_proxy_implementation_abi(any(), any()) :: SmartContract.abi() - def combine_proxy_implementation_abi(smart_contract, options \\ []) + @spec combine_proxy_implementation_abi(any(), map(), boolean(), any()) :: SmartContract.abi() + def combine_proxy_implementation_abi( + smart_contract, + proxy_implementation_addresses_map \\ %{}, + fetch_proxy?, + options \\ [] + ) - def combine_proxy_implementation_abi(%SmartContract{abi: abi} = smart_contract, options) when not is_nil(abi) do - implementation_abi = Proxy.get_implementation_abi_from_proxy(smart_contract, options) + def combine_proxy_implementation_abi( + %SmartContract{abi: abi} = smart_contract, + proxy_implementation_addresses_map, + fetch_proxy?, + options + ) + when not is_nil(abi) do + implementation_abi = + get_implementation_abi(smart_contract, options, proxy_implementation_addresses_map, fetch_proxy?) if Enum.empty?(implementation_abi), do: abi, else: implementation_abi ++ abi end - def combine_proxy_implementation_abi(_, _) do - [] + def combine_proxy_implementation_abi(smart_contract, proxy_implementation_addresses_map, fetch_proxy?, options) do + get_implementation_abi(smart_contract, options, proxy_implementation_addresses_map, fetch_proxy?) + end + + defp get_implementation_abi(smart_contract, options, proxy_implementation_addresses_map, fetch_proxy?) do + if fetch_proxy? do + Proxy.get_implementation_abi_from_proxy(smart_contract, options) + else + implementations = + proxy_implementation_addresses_map + |> Map.get(smart_contract.address_hash) + + parse_abi_from_proxy_implementations(implementations) + end + end + + defp parse_abi_from_proxy_implementations(nil), do: [] + + defp parse_abi_from_proxy_implementations(implementations) do + implementations + |> Enum.reduce([], fn implementation, acc -> + if implementation.smart_contract && implementation.smart_contract.abi do + acc ++ implementation.smart_contract.abi + else + acc + end + end) end defp find_input_by_name(inputs, name) do diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex index 4b78a0d00575..728e0710e518 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex @@ -17,7 +17,6 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do alias Explorer.Chain.{Address, Hash, SmartContract} alias Explorer.Chain.SmartContract.Proxy - alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.Counters.AverageBlockTime alias Explorer.Repo alias Timex.Duration @@ -91,6 +90,16 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do |> select_repo(options).one() end + @doc """ + Returns all implementations for the given smart-contract address hashes + """ + @spec get_proxy_implementations_for_multiple_proxies([Hash.Address.t()], Keyword.t()) :: __MODULE__.t() | nil + def get_proxy_implementations_for_multiple_proxies(proxy_address_hashes, options \\ []) do + proxy_address_hashes + |> get_proxy_implementations_by_multiple_hashes_query() + |> select_repo(options).all() + end + @doc """ Returns the last implementation updated_at for the given smart-contract address hash """ @@ -109,6 +118,13 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do ) end + defp get_proxy_implementations_by_multiple_hashes_query(proxy_address_hashes) do + from( + p in __MODULE__, + where: p.proxy_address_hash in ^proxy_address_hashes + ) + end + @doc """ Returns implementation address, name and proxy type for the given SmartContract """ @@ -210,7 +226,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do {:ok, :error} -> format_proxy_implementations_response(proxy_implementations) - {:ok, %Implementation{} = result} -> + {:ok, %__MODULE__{} = result} -> format_proxy_implementations_response(result) _ -> @@ -289,7 +305,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do Saves proxy's implementation into the DB """ @spec save_implementation_data([String.t()], Hash.Address.t(), atom() | nil, Keyword.t()) :: - Implementation.t() | :empty | :error + __MODULE__.t() | :empty | :error def save_implementation_data(:error, _proxy_address_hash, _proxy_type, _options) do :error end diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index 3ef684d2354f..59f0ec68f40b 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -6,6 +6,8 @@ defmodule Explorer.Chain.Transaction.Schema do - Explorer.Chain.Import.Runner.Transactions """ + alias Explorer.Chain + alias Explorer.Chain.{ Address, Beacon.BlobTransaction, @@ -302,6 +304,8 @@ defmodule Explorer.Chain.Transaction do Wei } + alias Explorer.Chain.SmartContract.Proxy.Models.Implementation + alias Explorer.SmartContract.SigProviderInterface @optional_attrs ~w(max_priority_fee_per_gas max_fee_per_gas block_hash block_number @@ -751,28 +755,59 @@ defmodule Explorer.Chain.Transaction do boolean(), [Chain.api?()], full_abi_acc, - methods_acc + methods_acc, + proxy_implementation_addresses_map ) :: {error_type | success_type, full_abi_acc, methods_acc} when full_abi_acc: map(), methods_acc: map(), + proxy_implementation_addresses_map: map(), error_type: {:error, any()} | {:error, :contract_not_verified | :contract_verified, list()}, success_type: {:ok | binary(), any()} | {:ok, binary(), binary(), list()} - def decoded_input_data(tx, skip_sig_provider? \\ false, options, full_abi_acc \\ %{}, methods_acc \\ %{}) + def decoded_input_data( + tx, + skip_sig_provider? \\ false, + options, + full_abi_acc \\ %{}, + methods_acc \\ %{}, + proxy_implementation_addresses_map \\ %{} + ) - def decoded_input_data(%__MODULE__{to_address: nil}, _, _, full_abi_acc, methods_acc), - do: {{:error, :no_to_address}, full_abi_acc, methods_acc} + def decoded_input_data( + %__MODULE__{to_address: nil}, + _, + _, + full_abi_acc, + methods_acc, + _proxy_implementation_addresses_map + ), + do: {{:error, :no_to_address}, full_abi_acc, methods_acc} - def decoded_input_data(%NotLoaded{}, _, _, full_abi_acc, methods_acc), + def decoded_input_data(%NotLoaded{}, _, _, full_abi_acc, methods_acc, _proxy_implementation_addresses_map), do: {{:error, :not_loaded}, full_abi_acc, methods_acc} - def decoded_input_data(%__MODULE__{input: %{bytes: bytes}}, _, _, full_abi_acc, methods_acc) - when bytes in [nil, <<>>], - do: {{:error, :no_input_data}, full_abi_acc, methods_acc} + def decoded_input_data( + %__MODULE__{input: %{bytes: bytes}}, + _, + _, + full_abi_acc, + methods_acc, + _proxy_implementation_addresses_map + ) + when bytes in [nil, <<>>] do + {{:error, :no_input_data}, full_abi_acc, methods_acc} + end if not Application.compile_env(:explorer, :decode_not_a_contract_calls) do - def decoded_input_data(%__MODULE__{to_address: %{contract_code: nil}}, _, _, full_abi_acc, methods_acc), - do: {{:error, :not_a_contract_call}, full_abi_acc, methods_acc} + def decoded_input_data( + %__MODULE__{to_address: %{contract_code: nil}}, + _, + _, + full_abi_acc, + methods_acc, + _proxy_implementation_addresses_map + ), + do: {{:error, :not_a_contract_call}, full_abi_acc, methods_acc} end def decoded_input_data( @@ -784,7 +819,8 @@ defmodule Explorer.Chain.Transaction do skip_sig_provider?, options, full_abi_acc, - methods_acc + methods_acc, + proxy_implementation_addresses_map ) do decoded_input_data( %__MODULE__{ @@ -795,7 +831,8 @@ defmodule Explorer.Chain.Transaction do skip_sig_provider?, options, full_abi_acc, - methods_acc + methods_acc, + proxy_implementation_addresses_map ) end @@ -808,7 +845,8 @@ defmodule Explorer.Chain.Transaction do skip_sig_provider?, options, full_abi_acc, - methods_acc + methods_acc, + proxy_implementation_addresses_map ) do decoded_input_data( %__MODULE__{ @@ -819,7 +857,8 @@ defmodule Explorer.Chain.Transaction do skip_sig_provider?, options, full_abi_acc, - methods_acc + methods_acc, + proxy_implementation_addresses_map ) end @@ -832,7 +871,8 @@ defmodule Explorer.Chain.Transaction do skip_sig_provider?, options, full_abi_acc, - methods_acc + methods_acc, + proxy_implementation_addresses_map ) do {methods, methods_acc} = method_id @@ -841,7 +881,14 @@ defmodule Explorer.Chain.Transaction do candidates = methods |> Enum.flat_map(fn candidate -> - case do_decoded_input_data(data, %SmartContract{abi: [candidate.abi], address_hash: nil}, hash, options, %{}) do + case do_decoded_input_data( + data, + %SmartContract{abi: [candidate.abi], address_hash: nil}, + hash, + options, + %{}, + proxy_implementation_addresses_map + ) do {{:ok, _, _, _} = decoded, _} -> [decoded] _ -> [] end @@ -852,7 +899,14 @@ defmodule Explorer.Chain.Transaction do full_abi_acc, methods_acc} end - def decoded_input_data(%__MODULE__{to_address: %NotLoaded{}}, _, _, full_abi_acc, methods_acc) do + def decoded_input_data( + %__MODULE__{to_address: %NotLoaded{}}, + _, + _, + full_abi_acc, + methods_acc, + _proxy_implementation_addresses_map + ) do {{:error, :contract_not_verified, []}, full_abi_acc, methods_acc} end @@ -865,9 +919,17 @@ defmodule Explorer.Chain.Transaction do skip_sig_provider?, options, full_abi_acc, - methods_acc + methods_acc, + proxy_implementation_addresses_map ) do - case do_decoded_input_data(data, smart_contract, hash, options, full_abi_acc) do + case do_decoded_input_data( + data, + smart_contract, + hash, + options, + full_abi_acc, + proxy_implementation_addresses_map + ) do # In some cases transactions use methods of some unpredictable contracts, so we can try to look up for method in a whole DB {{:error, :could_not_decode}, full_abi_acc} -> case decoded_input_data( @@ -879,7 +941,8 @@ defmodule Explorer.Chain.Transaction do skip_sig_provider?, options, full_abi_acc, - methods_acc + methods_acc, + proxy_implementation_addresses_map ) do {{:error, :contract_not_verified, []}, full_abi_acc, methods_acc} -> {decode_function_call_via_sig_provider_wrapper(input, hash, skip_sig_provider?), full_abi_acc, methods_acc} @@ -906,8 +969,16 @@ defmodule Explorer.Chain.Transaction do end end - defp do_decoded_input_data(data, smart_contract, hash, options, full_abi_acc) do - {full_abi, full_abi_acc} = check_full_abi_cache(smart_contract, full_abi_acc, options) + defp do_decoded_input_data( + data, + smart_contract, + hash, + options, + full_abi_acc, + proxy_implementation_addresses_map \\ %{} + ) do + {full_abi, full_abi_acc} = + check_full_abi_cache(smart_contract, full_abi_acc, options, proxy_implementation_addresses_map) {with( {:ok, {selector, values}} <- find_and_decode(full_abi, data, hash), @@ -948,11 +1019,22 @@ defmodule Explorer.Chain.Transaction do end end - defp check_full_abi_cache(%{address_hash: address_hash} = smart_contract, full_abi_acc, options) do + defp check_full_abi_cache( + %{address_hash: address_hash} = smart_contract, + full_abi_acc, + options, + proxy_implementation_addresses_map + ) do if !is_nil(address_hash) && Map.has_key?(full_abi_acc, address_hash) do {full_abi_acc[address_hash], full_abi_acc} else - full_abi = Proxy.combine_proxy_implementation_abi(smart_contract, options) + full_abi = + Proxy.combine_proxy_implementation_abi( + smart_contract, + proxy_implementation_addresses_map, + false, + options + ) {full_abi, Map.put(full_abi_acc, address_hash, full_abi)} end @@ -1911,10 +1993,19 @@ defmodule Explorer.Chain.Transaction do """ @spec decode_transactions([Transaction.t()], boolean(), Keyword.t()) :: {[any()], map(), map()} def decode_transactions(transactions, skip_sig_provider?, opts) do + proxy_implementation_addresses_map = combine_proxy_implementation_addresses_map(transactions) + {results, abi_acc, methods_acc} = Enum.reduce(transactions, {[], %{}, %{}}, fn transaction, {results, abi_acc, methods_acc} -> {result, abi_acc, methods_acc} = - decoded_input_data(transaction, skip_sig_provider?, opts, abi_acc, methods_acc) + decoded_input_data( + transaction, + skip_sig_provider?, + opts, + abi_acc, + methods_acc, + proxy_implementation_addresses_map + ) {[format_decoded_input(result) | results], abi_acc, methods_acc} end) @@ -1922,6 +2013,42 @@ defmodule Explorer.Chain.Transaction do {Enum.reverse(results), abi_acc, methods_acc} end + defp combine_proxy_implementation_addresses_map(transactions) do + # parse unique address hashes of smart-contracts from to_address and created_contract_address properties of the transactions list + unique_to_address_hashes = + transactions + |> Enum.flat_map(fn + %Transaction{to_address: %Address{hash: hash}} -> [hash] + %Transaction{created_contract_address: %Address{hash: hash}} -> [hash] + _ -> [] + end) + |> Enum.uniq() + + # query from the DB proxy implementation objects for those address hashes + multiple_proxy_implementations = + Implementation.get_proxy_implementations_for_multiple_proxies(unique_to_address_hashes) + + # query from the DB address objects with smart_contract preload for all found above implementation addresses + implementation_addresses_with_smart_contracts = + multiple_proxy_implementations + |> Enum.flat_map(fn proxy_implementations -> proxy_implementations.address_hashes end) + |> Chain.hashes_to_addresses(necessity_by_association: %{smart_contract: :optional}) + |> Enum.into(%{}, &{&1.hash, &1}) + + # combine map %{proxy_address_hash => the list of implementations as Address.t() object with preloaded SmartContract.t()} + multiple_proxy_implementations + |> Enum.reduce(%{}, fn proxy_implementations, proxy_implementation_addresses_map -> + implementation_addresses_with_smart_contract_preload = + proxy_implementations.address_hashes + |> Enum.map(fn implementation_address_hash -> + Map.get(implementation_addresses_with_smart_contracts, implementation_address_hash) + end) + + proxy_implementation_addresses_map + |> Map.put(proxy_implementations.proxy_address_hash, implementation_addresses_with_smart_contract_preload) + end) + end + @doc """ Receives as input result of decoded_input_data/5, returns either nil or decoded input in format: {:ok, _identifier, _text, _mapping} """ diff --git a/apps/explorer/test/explorer/chain/smart_contract/proxy_test.exs b/apps/explorer/test/explorer/chain/smart_contract/proxy_test.exs index 4bfda97853cb..1e7ea53887c0 100644 --- a/apps/explorer/test/explorer/chain/smart_contract/proxy_test.exs +++ b/apps/explorer/test/explorer/chain/smart_contract/proxy_test.exs @@ -131,70 +131,74 @@ defmodule Explorer.Chain.SmartContract.ProxyTest do } ] - test "combine_proxy_implementation_abi/2 returns empty [] abi if proxy abi is null" do + test "combine_proxy_implementation_abi/4 returns empty [] abi if proxy abi is null" do proxy_contract_address = insert(:contract_address) - assert Proxy.combine_proxy_implementation_abi(%SmartContract{address_hash: proxy_contract_address.hash, abi: nil}) == + assert Proxy.combine_proxy_implementation_abi( + %SmartContract{address_hash: proxy_contract_address.hash, abi: nil}, + %{}, + false + ) == [] end - test "combine_proxy_implementation_abi/2 returns [] abi for unverified proxy" do + test "combine_proxy_implementation_abi/4 returns [] abi for unverified proxy" do proxy_contract_address = insert(:contract_address) smart_contract = insert(:smart_contract, address_hash: proxy_contract_address.hash, abi: [], contract_code_md5: "123") - TestHelper.get_eip1967_implementation_zero_addresses() - - assert Proxy.combine_proxy_implementation_abi(smart_contract) == [] + assert Proxy.combine_proxy_implementation_abi(smart_contract, %{}, false) == [] end - test "combine_proxy_implementation_abi/2 returns proxy abi if implementation is not verified" do + test "combine_proxy_implementation_abi/4 returns proxy abi if implementation is not verified" do proxy_contract_address = insert(:contract_address) smart_contract = insert(:smart_contract, address_hash: proxy_contract_address.hash, abi: @proxy_abi, contract_code_md5: "123") - TestHelper.get_eip1967_implementation_zero_addresses() - - assert Proxy.combine_proxy_implementation_abi(smart_contract) == @proxy_abi + assert Proxy.combine_proxy_implementation_abi(smart_contract, %{}, false) == @proxy_abi end - test "combine_proxy_implementation_abi/2 returns proxy + implementation abi if implementation is verified" do + test "combine_proxy_implementation_abi/4 returns proxy + implementation abi if implementation is verified" do proxy_contract_address = insert(:contract_address) - smart_contract = + proxy_smart_contract = insert(:smart_contract, address_hash: proxy_contract_address.hash, abi: @proxy_abi, contract_code_md5: "123") implementation_contract_address = insert(:contract_address) - insert(:smart_contract, - address_hash: implementation_contract_address.hash, - abi: @implementation_abi, - contract_code_md5: "123" + implementation_smart_contract = + insert(:smart_contract, + address_hash: implementation_contract_address.hash, + abi: @implementation_abi, + contract_code_md5: "123", + name: "impl" + ) + + implementation_contract_address_with_smart_contract_preload = + implementation_contract_address |> Repo.preload(:smart_contract) + + insert(:proxy_implementation, + proxy_address_hash: proxy_contract_address.hash, + proxy_type: "eip1167", + address_hashes: [implementation_contract_address.hash], + names: [implementation_smart_contract.name] ) - implementation_contract_address_hash_string = + _implementation_contract_address_hash_string = Base.encode16(implementation_contract_address.hash.bytes, case: :lower) - TestHelper.get_eip1967_implementation_zero_addresses() + proxy_implementation_addresses_map = + %{} + |> Map.put(proxy_contract_address.hash, [implementation_contract_address_with_smart_contract_preload]) - expect( - EthereumJSONRPC.Mox, - :json_rpc, - fn [%{id: id, method: _, params: [%{data: _, to: _}, _]}], _options -> - {:ok, - [ - %{ - id: id, - jsonrpc: "2.0", - result: "0x000000000000000000000000" <> implementation_contract_address_hash_string - } - ]} - end - ) - - combined_abi = Proxy.combine_proxy_implementation_abi(smart_contract) + combined_abi = + Proxy.combine_proxy_implementation_abi( + proxy_smart_contract, + proxy_implementation_addresses_map, + false + ) assert Enum.any?(@proxy_abi, fn el -> el == Enum.at(@implementation_abi, 0) end) == false assert Enum.any?(@proxy_abi, fn el -> el == Enum.at(@implementation_abi, 1) end) == false @@ -212,17 +216,6 @@ defmodule Explorer.Chain.SmartContract.ProxyTest do [] end - test "get_implementation_abi_from_proxy/2 returns [] abi for unverified proxy" do - proxy_contract_address = insert(:contract_address) - - smart_contract = - insert(:smart_contract, address_hash: proxy_contract_address.hash, abi: [], contract_code_md5: "123") - - TestHelper.get_eip1967_implementation_zero_addresses() - - assert Proxy.combine_proxy_implementation_abi(smart_contract) == [] - end - test "get_implementation_abi_from_proxy/2 returns [] if implementation is not verified" do proxy_contract_address = insert(:contract_address) diff --git a/apps/explorer/test/explorer/chain/transaction_test.exs b/apps/explorer/test/explorer/chain/transaction_test.exs index 85726e79451a..fdb514ce7ba6 100644 --- a/apps/explorer/test/explorer/chain/transaction_test.exs +++ b/apps/explorer/test/explorer/chain/transaction_test.exs @@ -270,8 +270,6 @@ defmodule Explorer.Chain.TransactionTest do |> insert() |> Repo.preload(to_address: :smart_contract) - TestHelper.get_eip1967_implementation_zero_addresses() - assert {{:ok, "60fe47b1", "set(uint256 x)", [{"x", "uint256", 50}]}, _, _} = Transaction.decoded_input_data(transaction, []) end @@ -293,8 +291,6 @@ defmodule Explorer.Chain.TransactionTest do |> insert(to_address: contract.address, input: "0x" <> input_data) |> Repo.preload(to_address: :smart_contract) - TestHelper.get_eip1967_implementation_zero_addresses() - assert {{:ok, "60fe47b1", "set(uint256 x)", [{"x", "uint256", 10}]}, _, _} = Transaction.decoded_input_data(transaction, []) end @@ -327,8 +323,6 @@ defmodule Explorer.Chain.TransactionTest do |> insert(to_address: contract.address, input: "0x" <> input_data) |> Repo.preload(to_address: :smart_contract) - TestHelper.get_eip1967_implementation_zero_addresses() - assert {{:ok, "60fe47b1", "set(uint256 arg0)", [{"arg0", "uint256", 10}]}, _, _} = Transaction.decoded_input_data(transaction, []) end From 5469933699f860dae15b10734c242a54c5dd90a5 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 16 Sep 2024 17:14:18 +0300 Subject: [PATCH 149/363] Fix bug in naming of docker image for Optimism --- .github/workflows/publish-docker-image-for-optimism.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-docker-image-for-optimism.yml b/.github/workflows/publish-docker-image-for-optimism.yml index e640ce6f5916..cb2c75f96a1c 100644 --- a/.github/workflows/publish-docker-image-for-optimism.yml +++ b/.github/workflows/publish-docker-image-for-optimism.yml @@ -144,7 +144,7 @@ jobs: context: . file: ./docker/Dockerfile push: true - tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-indexer-api + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-api labels: ${{ steps.setup.outputs.docker-labels }} platforms: | linux/amd64 From 8ce08ee5fce5ffad10a4fe568462bf2bc7e713d4 Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Mon, 16 Sep 2024 19:08:08 +0400 Subject: [PATCH 150/363] fix: decoding of zero fields in mud (#10764) --- apps/explorer/lib/explorer/chain/mud.ex | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/mud.ex b/apps/explorer/lib/explorer/chain/mud.ex index 386cec09c59f..0663acad3a52 100644 --- a/apps/explorer/lib/explorer/chain/mud.ex +++ b/apps/explorer/lib/explorer/chain/mud.ex @@ -330,7 +330,8 @@ defmodule Explorer.Chain.Mud do |> Enum.zip(static_types) |> Enum.reduce({%{}, (static_data && static_data.bytes) || <<>>}, fn {field, type}, {acc, data} -> type_size = static_type_size(type) - <> = data + {enc, rest} = split_binary(data, type_size) + decoded = decode_type(type, enc) {Map.put(acc, field, decoded), rest} end) @@ -349,7 +350,7 @@ defmodule Explorer.Chain.Mud do [dynamic_fields, dynamic_types, dynamic_type_lengths] |> Enum.zip() |> Enum.reduce({res, (dynamic_data && dynamic_data.bytes) || <<>>}, fn {field, type, length}, {acc, data} -> - <> = data + {enc, rest} = split_binary(data, length) decoded = decode_type(type, enc) {Map.put(acc, field, decoded), rest} @@ -366,6 +367,12 @@ defmodule Explorer.Chain.Mud do end end + defp split_binary(binary, size) do + if byte_size(binary) >= size, + do: :erlang.split_binary(binary, size), + else: {<<0::size(size * 8)>>, <<>>} + end + # credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity defp decode_type(type, raw) do case type do From 5afe65bd0d4baeb79133a05111c2674cb20620cd Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 16 Sep 2024 18:15:09 +0300 Subject: [PATCH 151/363] 6.8.1 --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ apps/block_scout_web/mix.exs | 2 +- apps/ethereum_jsonrpc/mix.exs | 2 +- apps/explorer/mix.exs | 2 +- apps/indexer/mix.exs | 2 +- docker-compose/docker-compose.yml | 2 +- docker/Makefile | 2 +- mix.exs | 2 +- rel/config.exs | 2 +- 9 files changed, 38 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 036e872976ce..5b6440b047a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## 6.8.1 + +### 🚀 Features + +- Add `INDEXER_OPTIMISM_L1_DEPOSITS_TRANSACTION_TYPE` env variable ([#10674](https://github.com/blockscout/blockscout/issues/10674)) +- Support for filecoin native addresses ([#10468](https://github.com/blockscout/blockscout/issues/10468)) + +### 🐛 Bug Fixes + +- Decoding of zero fields in mud ([#10764](https://github.com/blockscout/blockscout/issues/10764)) +- Insert coin balances placeholders in internal transactions fetcher ([#10603](https://github.com/blockscout/blockscout/issues/10603)) +- Avoid key violation error in `Indexer.Fetcher.Optimism.TxnBatch` ([#10752](https://github.com/blockscout/blockscout/issues/10752)) +- Fix empty current token balances ([#10745](https://github.com/blockscout/blockscout/issues/10745)) +- Allow disabling group votes fetcher independently of epoch block fetcher ([#10673](https://github.com/blockscout/blockscout/issues/10673)) +- Fix gettext usage warning ([#10693](https://github.com/blockscout/blockscout/issues/10693)) +- Truncate token symbol in Explorer.Chain.PolygonZkevm.BridgeL1Token ([#10688](https://github.com/blockscout/blockscout/issues/10688)) + +### ⚡ Performance + +- Improve performance of transactions list page ([#10734](https://github.com/blockscout/blockscout/issues/10734)) + +### ⚙️ Miscellaneous Tasks + +- Add meta to migrations_status ([#10678](https://github.com/blockscout/blockscout/issues/10678)) +- Token balances fetcher slow queue ([#10694](https://github.com/blockscout/blockscout/issues/10694)) +- Shrink sample response for the trace in Filecoin chain type +- Extend missing balanceOf function with :unable_to_decode error ([#10713](https://github.com/blockscout/blockscout/issues/10713)) +- Fix flaking explorer tests ([#10676](https://github.com/blockscout/blockscout/issues/10676)) +- Change shrink internal transactions migration default batch_size ([#10689](https://github.com/blockscout/blockscout/issues/10689)) + ## 6.8.0 ### 🚀 Features diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs index a0312d341220..8e2f7b96db4f 100644 --- a/apps/block_scout_web/mix.exs +++ b/apps/block_scout_web/mix.exs @@ -23,7 +23,7 @@ defmodule BlockScoutWeb.Mixfile do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "6.8.0", + version: "6.8.1", xref: [ exclude: [ Explorer.Chain.PolygonZkevm.Reader, diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs index 767b4b8d12b8..d168d4bb4f5c 100644 --- a/apps/ethereum_jsonrpc/mix.exs +++ b/apps/ethereum_jsonrpc/mix.exs @@ -23,7 +23,7 @@ defmodule EthereumJsonrpc.MixProject do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "6.8.0" + version: "6.8.1" ] end diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index d772ad4c1989..363d98e97099 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -24,7 +24,7 @@ defmodule Explorer.Mixfile do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "6.8.0", + version: "6.8.1", xref: [exclude: [BlockScoutWeb.Routers.WebRouter.Helpers, Indexer.Helper]] ] end diff --git a/apps/indexer/mix.exs b/apps/indexer/mix.exs index f8fca0ae95f9..acfebe6c6a45 100644 --- a/apps/indexer/mix.exs +++ b/apps/indexer/mix.exs @@ -14,7 +14,7 @@ defmodule Indexer.MixProject do elixirc_paths: elixirc_paths(Mix.env()), lockfile: "../../mix.lock", start_permanent: Mix.env() == :prod, - version: "6.8.0", + version: "6.8.1", xref: [ exclude: [ Explorer.Chain.Optimism.Deposit, diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index f58826568644..123844330881 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -37,7 +37,7 @@ services: CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED: "" CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL: "" ADMIN_PANEL_ENABLED: "" - RELEASE_VERSION: 6.8.0 + RELEASE_VERSION: 6.8.1 links: - db:database environment: diff --git a/docker/Makefile b/docker/Makefile index 966169956309..246010fc16c9 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -10,7 +10,7 @@ STATS_CONTAINER_NAME := stats STATS_DB_CONTAINER_NAME := stats-db PROXY_CONTAINER_NAME := proxy PG_CONTAINER_NAME := postgres -RELEASE_VERSION ?= '6.8.0' +RELEASE_VERSION ?= '6.8.1' TAG := $(RELEASE_VERSION)-commit-$(shell git log -1 --pretty=format:"%h") STABLE_TAG := $(RELEASE_VERSION) diff --git a/mix.exs b/mix.exs index 37b0f7cb803d..6ad3e626b7d5 100644 --- a/mix.exs +++ b/mix.exs @@ -7,7 +7,7 @@ defmodule BlockScout.Mixfile do [ # app: :block_scout, # aliases: aliases(config_env()), - version: "6.8.0", + version: "6.8.1", apps_path: "apps", deps: deps(), dialyzer: dialyzer(), diff --git a/rel/config.exs b/rel/config.exs index 7cebe1dfff5a..9e050b1477d9 100644 --- a/rel/config.exs +++ b/rel/config.exs @@ -71,7 +71,7 @@ end # will be used by default release :blockscout do - set version: "6.8.0-beta" + set version: "6.8.1-beta" set applications: [ :runtime_tools, block_scout_web: :permanent, From 3a33a70b086158b6f4bceb4a18e237dce71df9d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 11:09:29 +0300 Subject: [PATCH 152/363] chore(deps): bump ex_abi from 0.8.0 to 0.8.1 (#10780) Bumps [ex_abi](https://github.com/poanetwork/ex_abi) from 0.8.0 to 0.8.1. - [Release notes](https://github.com/poanetwork/ex_abi/releases) - [Changelog](https://github.com/poanetwork/ex_abi/blob/master/CHANGELOG.md) - [Commits](https://github.com/poanetwork/ex_abi/compare/0.8.0...0.8.1) --- updated-dependencies: - dependency-name: ex_abi dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.lock b/mix.lock index 03cf8a5e1f5e..431f55d6c759 100644 --- a/mix.lock +++ b/mix.lock @@ -43,7 +43,7 @@ "ecto_sql": {:hex, :ecto_sql, "3.11.3", "4eb7348ff8101fbc4e6bbc5a4404a24fecbe73a3372d16569526b0cf34ebc195", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16 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", "e5f36e3d736b99c7fee3e631333b8394ade4bafe9d96d35669fca2d81c2be928"}, "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, - "ex_abi": {:hex, :ex_abi, "0.8.0", "bb08827bd8d71dbb311c69ac55a008669dfabe2ce5b58d65f97c08c0aba60ec6", [:mix], [{:ex_keccak, "~> 0.7.5", [hex: :ex_keccak, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "bbdae12c186aeeb4c53dd7c7c57f457923602db315aa1f66d7427467c8ad77af"}, + "ex_abi": {:hex, :ex_abi, "0.8.1", "451fa960ddc4dfbb350e13509f3dd64ca586b8484a77aad9f7d778161b5eab79", [:mix], [{:ex_keccak, "~> 0.7.5", [hex: :ex_keccak, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "abcf53d556c2948e5c1241340afd4a72cdf93ab6daef16fc200c16ca1183cdca"}, "ex_cldr": {:hex, :ex_cldr, "2.40.1", "c1fcb0cd9d2a70d28f4540a99f32127e7f1813e0db109d65ab29dea5337ae266", [:mix], [{:cldr_utils, "~> 2.28", [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", "509810702e8e81991851d9426ffe6b34b48b7b9baa12922e7b3fb8f6368606f3"}, "ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.16.2", "670d96cc4fb18cfebd82488ed687742683be2d0725d66ec051578d4b13539aa8", [:mix], [{:ex_cldr, "~> 2.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "2ccfac2838f4df8c8e5424dbc68eb2f3ac9eeb45e10365050901f7ac7a914ce1"}, "ex_cldr_lists": {:hex, :ex_cldr_lists, "2.11.1", "ad18f861d7c5ca82aac6d173469c6a2339645c96790172ab0aa255b64fb7303b", [: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", "00161c04510ccb3f18b19a6b8562e50c21f1e9c15b8ff4c934bea5aad0b4ade2"}, @@ -126,7 +126,7 @@ "recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"}, "redix": {:hex, :redix, "1.5.2", "ab854435a663f01ce7b7847f42f5da067eea7a3a10c0a9d560fa52038fd7ab48", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:nimble_options, "~> 0.5.0 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "78538d184231a5d6912f20567d76a49d1be7d3fca0e1aaaa20f4df8e1142dcb8"}, "remote_ip": {:hex, :remote_ip, "1.2.0", "fb078e12a44414f4cef5a75963c33008fe169b806572ccd17257c208a7bc760f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2ff91de19c48149ce19ed230a81d377186e4412552a597d6a5137373e5877cb7"}, - "rustler_precompiled": {:hex, :rustler_precompiled, "0.7.1", "ecadf02cc59a0eccbaed6c1937303a5827fbcf60010c541595e6d3747d3d0f9f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "b9e4657b99a1483ea31502e1d58c464bedebe9028808eda45c3a429af4550c66"}, + "rustler_precompiled": {:hex, :rustler_precompiled, "0.8.1", "8afe0b6f3a9a677ada046cdd23e3f4c6399618b91a6122289324774961281e1e", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "90b8c2297bf7959cfa1c927b2881faad7bb0707183124955369991b76177a166"}, "sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"}, "spandex": {:hex, :spandex, "3.2.0", "f8cd40146ea988c87f3c14054150c9a47ba17e53cd4515c00e1f93c29c45404d", [:mix], [{:decorator, "~> 1.2", [hex: :decorator, repo: "hexpm", optional: true]}, {:optimal, "~> 0.3.3", [hex: :optimal, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d0a7d5aef4c5af9cf5467f2003e8a5d8d2bdae3823a6cc95d776b9a2251d4d03"}, "spandex_datadog": {:hex, :spandex_datadog, "1.4.0", "0594b9655b0af00ab9137122616bc0208b68ceec01e9916ab13d6fbb33dcce35", [:mix], [{:msgpax, "~> 2.2.1 or ~> 2.3", [hex: :msgpax, repo: "hexpm", optional: false]}, {:spandex, "~> 3.2", [hex: :spandex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "360f8e1b4db238c1749c4872b1697b096429927fa42b8858d0bb782067380123"}, From 930ef016eb5d7cc7e4d74f33f2b37d545fbdf04d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 11:09:44 +0300 Subject: [PATCH 153/363] chore(deps): bump plug_cowboy from 2.7.1 to 2.7.2 (#10782) Bumps [plug_cowboy](https://github.com/elixir-plug/plug_cowboy) from 2.7.1 to 2.7.2. - [Changelog](https://github.com/elixir-plug/plug_cowboy/blob/master/CHANGELOG.md) - [Commits](https://github.com/elixir-plug/plug_cowboy/compare/v2.7.1...v2.7.2) --- updated-dependencies: - dependency-name: plug_cowboy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 431f55d6c759..aac15ab5c2bd 100644 --- a/mix.lock +++ b/mix.lock @@ -107,7 +107,7 @@ "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, "plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"}, - "plug_cowboy": {:hex, :plug_cowboy, "2.7.1", "87677ffe3b765bc96a89be7960f81703223fe2e21efa42c125fcd0127dd9d6b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "02dbd5f9ab571b864ae39418db7811618506256f6d13b4a45037e5fe78dc5de3"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.7.2", "fdadb973799ae691bf9ecad99125b16625b1c6039999da5fe544d99218e662e4", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "245d8a11ee2306094840c000e8816f0cbed69a23fc0ac2bcf8d7835ae019bb2f"}, "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, From 0a3f29eb5289e74d388d2661b5fac2b60b47da14 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Fri, 20 Sep 2024 12:10:18 +0400 Subject: [PATCH 154/363] fix: Filter out tokens with skip_metadata: true from token fetcher (#10736) --- apps/explorer/lib/explorer/chain.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 2b40f4d879d8..c9307fedde47 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -3672,6 +3672,7 @@ defmodule Explorer.Chain do from( token in Token, where: token.cataloged == false or is_nil(token.cataloged), + where: is_nil(token.skip_metadata) or token.skip_metadata == false, select: token.contract_address_hash ) From 74e68ad41876675f0b8e4b92fb017834abe3ca4d Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 20 Sep 2024 11:10:37 +0300 Subject: [PATCH 155/363] fix: Allow string IDs in JSON RPC requests (#10759) * fix: Allow string IDs in JSON RPC requests * Remove duplicated line * Do not try convert hex to string - return it as is * Fix sanitize_error else option for Poison --- .../block_scout_web/views/api/eth_rpc/view.ex | 62 ++++++++++++------- apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex | 18 ++++++ .../lib/ethereum_jsonrpc/http.ex | 4 +- 3 files changed, 59 insertions(+), 25 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/eth_rpc/view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/eth_rpc/view.ex index d2b29c0117c6..0e22ee5ba630 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/eth_rpc/view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/eth_rpc/view.ex @@ -4,6 +4,8 @@ defmodule BlockScoutWeb.API.EthRPC.View do """ use BlockScoutWeb, :view + @jsonrpc_2_0 ~s("jsonrpc":"2.0") + defstruct [:result, :id, :error] def render("show.json", %{result: result, id: id}) do @@ -50,50 +52,64 @@ defmodule BlockScoutWeb.API.EthRPC.View do end) end - defimpl Poison.Encoder, for: BlockScoutWeb.API.EthRPC.View do - def encode(%BlockScoutWeb.API.EthRPC.View{result: result, id: id, error: error}, _options) when is_nil(error) do - result = Poison.encode!(result) + @doc """ + Encodes id into JSON string + """ + @spec sanitize_id(any()) :: non_neg_integer() | String.t() + def sanitize_id(id) do + if is_integer(id), do: id, else: "\"#{id}\"" + end - """ - {"jsonrpc":"2.0","result":#{result},"id":#{id}} - """ + @doc """ + Encodes error into JSON string + """ + @spec sanitize_error(any(), :jason | :poison) :: String.t() + def sanitize_error(error, json_encoder) do + case json_encoder do + :jason -> if is_map(error), do: Jason.encode!(error), else: "\"#{error}\"" + :poison -> if is_map(error), do: Poison.encode!(error), else: "\"#{error}\"" end + end + + @doc """ + Pass "jsonrpc":"2.0" to use in Poison.Encoder and Jason.Encoder below + """ + @spec jsonrpc_2_0() :: String.t() + def jsonrpc_2_0, do: @jsonrpc_2_0 - def encode(%BlockScoutWeb.API.EthRPC.View{id: id, error: error}, _options) when is_map(error) do - error = Poison.encode!(error) + defimpl Poison.Encoder, for: BlockScoutWeb.API.EthRPC.View do + alias BlockScoutWeb.API.EthRPC.View + + def encode(%View{result: result, id: id, error: error}, _options) when is_nil(error) do + result = Poison.encode!(result) """ - {"jsonrpc":"2.0","error": #{error},"id": #{id}} + {#{View.jsonrpc_2_0()},"result": #{result},"id": #{View.sanitize_id(id)}} """ end - def encode(%BlockScoutWeb.API.EthRPC.View{id: id, error: error}, _options) do + def encode(%View{id: id, error: error}, _options) do """ - {"jsonrpc":"2.0","error": "#{error}","id": #{id}} + {#{View.jsonrpc_2_0()},"error": #{View.sanitize_error(error, :poison)},"id": #{View.sanitize_id(id)}} """ end end defimpl Jason.Encoder, for: BlockScoutWeb.API.EthRPC.View do - def encode(%BlockScoutWeb.API.EthRPC.View{result: result, id: id, error: error}, _options) when is_nil(error) do - result = Jason.encode!(result) + # credo:disable-for-next-line + alias BlockScoutWeb.API.EthRPC.View - """ - {"jsonrpc":"2.0","result":#{result},"id":#{id}} - """ - end - - def encode(%BlockScoutWeb.API.EthRPC.View{id: id, error: error}, _options) when is_map(error) do - error = Jason.encode!(error) + def encode(%View{result: result, id: id, error: error}, _options) when is_nil(error) do + result = Jason.encode!(result) """ - {"jsonrpc":"2.0","error": #{error},"id": #{id}} + {#{View.jsonrpc_2_0()},"result": #{result},"id": #{View.sanitize_id(id)}} """ end - def encode(%BlockScoutWeb.API.EthRPC.View{id: id, error: error}, _options) do + def encode(%View{id: id, error: error}, _options) do """ - {"jsonrpc":"2.0","error": "#{error}","id": #{id}} + {#{View.jsonrpc_2_0()},"error": #{View.sanitize_error(error, :jason)},"id": #{View.sanitize_id(id)}} """ end end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex index 954036218db5..c2ac95cb8730 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex @@ -496,6 +496,24 @@ defmodule EthereumJSONRPC do def quantity_to_integer(_), do: nil + @doc """ + Sanitizes ID in JSON RPC request following JSON RPC [spec](https://www.jsonrpc.org/specification#request_object:~:text=An%20identifier%20established%20by%20the%20Client%20that%20MUST%20contain%20a%20String%2C%20Number%2C%20or%20NULL%20value%20if%20included.%20If%20it%20is%20not%20included%20it%20is%20assumed%20to%20be%20a%20notification.%20The%20value%20SHOULD%20normally%20not%20be%20Null%20%5B1%5D%20and%20Numbers%20SHOULD%20NOT%20contain%20fractional%20parts%20%5B2%5D). + """ + @spec sanitize_id(quantity) :: non_neg_integer() | String.t() | nil + + def sanitize_id(integer) when is_integer(integer), do: integer + + def sanitize_id(string) when is_binary(string) do + # match ID string and ID string without non-ASCII characters + if string == for(<>, c < 128, into: "", do: <>) do + string + else + nil + end + end + + def sanitize_id(_), do: nil + @doc """ Converts `t:non_neg_integer/0` to `t:quantity/0` """ diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex index 4335a29e77e9..a411ab921b99 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex @@ -7,7 +7,7 @@ defmodule EthereumJSONRPC.HTTP do require Logger - import EthereumJSONRPC, only: [quantity_to_integer: 1] + import EthereumJSONRPC, only: [sanitize_id: 1] @behaviour Transport @@ -190,7 +190,7 @@ defmodule EthereumJSONRPC.HTTP do # argument matching. # Nethermind return string ids - id = quantity_to_integer(unstandardized["id"]) + id = sanitize_id(unstandardized["id"]) standardized = %{jsonrpc: jsonrpc, id: id} From f3e5520a4764659cd83f211221d50a08f7e96562 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 20 Sep 2024 11:36:15 +0300 Subject: [PATCH 156/363] fix: Fix account db repo definition (#10714) --- apps/explorer/lib/explorer/application.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index 1fb5cfabd572..4839ed13a8de 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -176,7 +176,7 @@ defmodule Explorer.Application do end defp account_repo do - if System.get_env("ACCOUNT_DATABASE_URL") || Mix.env() == :test do + if Application.get_env(:explorer, Explorer.Account)[:enabled] || Mix.env() == :test do [Explorer.Repo.Account] else [] From c6fff8e369374c3363671a476e9b381da2c84d1b Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Fri, 20 Sep 2024 02:45:03 -0600 Subject: [PATCH 157/363] fix: revisited approach to choose L1 blocks to discover missing Arbitrum batches (#10757) * revisited way to choose blocks for missing batches * credo issue addressed --- .../lib/indexer/fetcher/arbitrum/utils/db.ex | 113 +++++++++++++----- 1 file changed, 83 insertions(+), 30 deletions(-) diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex index 8c9c640e1265..27a9b11befb5 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex @@ -126,15 +126,24 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do end @doc """ - Calculates the L1 block number to start the search for committed batches that precede - the earliest batch already discovered. + Calculates the L1 block number to start the search for committed batches. + + Returns the block number of the earliest L1 block containing a transaction + that commits a batch, as found in the database. If no committed batches are + found, it returns a default value. It's safe to use the returned block number + for subsequent searches, even if it corresponds to a block we've previously + processed. This is because multiple transactions committing different batches + can exist within the same block, and revisiting this block ensures no batches + are missed. + + The batch discovery process is expected to handle potential duplicates + correctly without creating redundant database entries. ## Parameters - `value_if_nil`: The default value to return if no committed batch is found. ## Returns - - The L1 block number immediately preceding the earliest committed batch, - or `value_if_nil` if no committed batches are found. + - The L1 block number containing the earliest committed batch or `value_if_nil`. """ @spec l1_block_to_discover_earliest_committed_batch(nil | FullBlock.block_number()) :: nil | FullBlock.block_number() def l1_block_to_discover_earliest_committed_batch(value_if_nil) @@ -145,7 +154,7 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do value_if_nil value -> - value - 1 + value end end @@ -737,7 +746,7 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do Example #1 - Within the range from 1 to 10, the missing batch is 2. The L1 block for the batch #1 is 10, and the L1 block for the batch #3 is 31. - - The output will be `[{11, 30}]`. + - The output will be `[{10, 31}]`. Example #2 - Within the range from 1 to 10, the missing batches are 2 and 6, and @@ -745,14 +754,21 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do - The L1 block for the batch #3 is 31. - The L1 block for the batch #5 is 64. - The L1 block for the batch #7 is 90. - - The output will be `[{11, 30}, {65, 89}]`. + - The output will be `[{10, 31}, {64, 90}]`. Example #3 - Within the range from 1 to 10, the missing batches are 2 and 4, and - The L1 block for the batch #1 is 10. - The L1 block for the batch #3 is 31. - The L1 block for the batch #5 is 64. - - The output will be `[{11, 30}, {32, 63}]`. + - The output will be `[{10, 31}, {32, 64}]`. + + Example #4 + - Within the range from 1 to 10, the missing batches are 2 and 4, and + - The L1 block for the batch #1 is 10. + - The L1 block for the batch #3 is 31. + - The L1 block for the batch #5 is 31. + - The output will be `[{10, 31}]`. """ @spec get_l1_block_ranges_for_missing_batches(non_neg_integer(), non_neg_integer(), FullBlock.block_number()) :: [ {FullBlock.block_number(), FullBlock.block_number()} @@ -765,28 +781,7 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do |> list_to_chunks() |> chunks_to_neighbor_ranges() - if neighbors_of_missing_batches == [] do - [] - else - l1_blocks = - neighbors_of_missing_batches - |> Enum.reduce(MapSet.new(), fn {start_batch, end_batch}, acc -> - acc - |> MapSet.put(start_batch) - |> MapSet.put(end_batch) - end) - # To avoid error in getting L1 block for the batch 0 - |> MapSet.delete(0) - |> MapSet.to_list() - |> Reader.get_l1_blocks_of_batches_by_numbers() - # It is safe to add the block for the batch 0 even if the batch 1 is missing - |> Map.put(0, block_for_batch_0) - - neighbors_of_missing_batches - |> Enum.map(fn {start_batch, end_batch} -> - {l1_blocks[start_batch] + 1, l1_blocks[end_batch] - 1} - end) - end + batches_gaps_to_block_ranges(neighbors_of_missing_batches, block_for_batch_0) end # Splits a list into chunks of consecutive numbers, e.g., [1, 2, 3, 5, 6, 8] becomes [[1, 2, 3], [5, 6], [8]]. @@ -831,6 +826,64 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do end) end + # Converts batch number gaps to L1 block ranges for missing batches discovery. + # + # This function takes a list of neighboring batch number ranges representing gaps + # in the batch sequence and converts them to corresponding L1 block ranges. These + # L1 block ranges can be used to rediscover missing batches. + # + # ## Parameters + # - `neighbors_of_missing_batches`: A list of tuples, each containing the start + # and end batch numbers of a gap in the batch sequence. + # - `block_for_batch_0`: The L1 block number corresponding to batch number 0. + # + # ## Returns + # - A list of tuples, each containing the start and end L1 block numbers for + # ranges where missing batches should be rediscovered. + @spec batches_gaps_to_block_ranges([{non_neg_integer(), non_neg_integer()}], FullBlock.block_number()) :: + [{FullBlock.block_number(), FullBlock.block_number()}] + defp batches_gaps_to_block_ranges(neighbors_of_missing_batches, block_for_batch_0) + + defp batches_gaps_to_block_ranges([], _), do: [] + + defp batches_gaps_to_block_ranges(neighbors_of_missing_batches, block_for_batch_0) do + l1_blocks = + neighbors_of_missing_batches + |> Enum.reduce(MapSet.new(), fn {start_batch, end_batch}, acc -> + acc + |> MapSet.put(start_batch) + |> MapSet.put(end_batch) + end) + # To avoid error in getting L1 block for the batch 0 + |> MapSet.delete(0) + |> MapSet.to_list() + |> Reader.get_l1_blocks_of_batches_by_numbers() + # It is safe to add the block for the batch 0 even if the batch 1 is missing + |> Map.put(0, block_for_batch_0) + + neighbors_of_missing_batches + |> Enum.reduce({[], %{}}, fn {start_batch, end_batch}, {res, blocks_used} -> + range_start = l1_blocks[start_batch] + range_end = l1_blocks[end_batch] + # If the batch's block was already used as a block finishing one of the ranges + # then we should start another range from the next block to avoid discovering + # the same batches batches again. + case {Map.get(blocks_used, range_start, false), range_start == range_end} do + {true, true} -> + # Edge case when the range consists of a single block (several batches in + # the same block) which is going to be inspected up to this moment. + {res, blocks_used} + + {true, false} -> + {[{range_start + 1, range_end} | res], Map.put(blocks_used, range_end, true)} + + {false, _} -> + {[{range_start, range_end} | res], Map.put(blocks_used, range_end, true)} + end + end) + |> elem(0) + end + @doc """ Retrieves the minimum and maximum batch numbers of L1 batches. From f0ef80be8d7accf96f6f5d3eee8668582516d3b8 Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Fri, 20 Sep 2024 02:47:14 -0600 Subject: [PATCH 158/363] fix: get rid of heavy DB query to start Arbitrum missed messages discovery process (#10767) * simplified approach to start discovery * function comment improved --- .../arbitrum/rollup_messages_catchup.ex | 51 +++++------ .../lib/indexer/fetcher/arbitrum/utils/db.ex | 89 +------------------ 2 files changed, 27 insertions(+), 113 deletions(-) diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/rollup_messages_catchup.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/rollup_messages_catchup.ex index a0ec6e0c8ace..56db328eddcd 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/rollup_messages_catchup.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/rollup_messages_catchup.ex @@ -64,7 +64,7 @@ defmodule Indexer.Fetcher.Arbitrum.RollupMessagesCatchup do require Logger @wait_for_new_block_delay 15 - @release_cpu_delay 1 + @release_cpu_delay 2 def child_spec(start_link_arguments) do spec = %{ @@ -167,43 +167,44 @@ defmodule Indexer.Fetcher.Arbitrum.RollupMessagesCatchup do {:noreply, %{state | data: new_data}} end - # Sets the initial parameters for discovering historical messages. This function - # inspects the database for missed messages and, if any are found, identifies the - # end blocks of the ranges for both L1-to-L2 and L2-to-L1 messages. If no missed - # messages are found, the block number before the latest indexed block will be used. - # These end blocks are used to initiate the discovery process in subsequent iterations. + # Sets the end blocks of the ranges for discovering historical L1-to-L2 and L2-to-L1 messages. # - # After identifying the initial values, the function immediately transitions to - # the L2-to-L1 message discovery process by sending the `:historical_msg_from_l2` - # message. + # There is likely a way to query the DB and discover the exact block of the + # first missed message (both L1-to-L2 and L2-to-L1) and start the discovery + # process from there. However, such a query is very expensive and can take a + # long time for chains with a high number of transactions. Instead, it's + # possible to start looking for missed messages from the block before the + # latest indexed block. + # + # Although this approach is not optimal for Blockscout instances where there + # are no missed messages (assumed to be the majority), it is still preferable + # to the first approach. The reason is that a finite number of relatively + # cheap queries (which can be tuned with `missed_messages_blocks_depth`) are + # preferable to one expensive query that becomes even more expensive as the + # number of indexed transactions grows. + # + # After identifying the initial values, the function immediately transitions + # to the L2-to-L1 message discovery process by sending the + # `:historical_msg_from_l2` message. # # ## Parameters # - `:init_worker`: The message that triggers the handler. - # - `state`: The current state of the fetcher containing the first rollup block - # number and the number of the most recent block indexed. + # - `state`: The current state of the fetcher containing the number of the + # most recent block indexed. # # ## Returns - # - `{:noreply, new_state}` where the end blocks for both L1-to-L2 and L2-to-L1 - # message discovery are established. + # - `{:noreply, new_state}` where `new_state` contains the updated state with + # end blocks for both L1-to-L2 and L2-to-L1 message discovery established. @impl GenServer - def handle_info( - :init_worker, - %{config: %{rollup_rpc: %{first_block: rollup_first_block}}, data: %{new_block: just_received_block}} = state - ) do - historical_msg_from_l2_end_block = - Db.rollup_block_to_discover_missed_messages_from_l2(just_received_block, rollup_first_block) - - historical_msg_to_l2_end_block = - Db.rollup_block_to_discover_missed_messages_to_l2(just_received_block, rollup_first_block) - + def handle_info(:init_worker, %{data: %{new_block: just_received_block}} = state) do Process.send(self(), :historical_msg_from_l2, []) new_data = Map.merge(state.data, %{ duration: 0, progressed: false, - historical_msg_from_l2_end_block: historical_msg_from_l2_end_block, - historical_msg_to_l2_end_block: historical_msg_to_l2_end_block + historical_msg_from_l2_end_block: just_received_block, + historical_msg_to_l2_end_block: just_received_block }) {:noreply, %{state | data: new_data}} diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex index 27a9b11befb5..9f7b6c8778c4 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex @@ -3,7 +3,7 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do Common functions to simplify DB routines for Indexer.Fetcher.Arbitrum fetchers """ - import Indexer.Fetcher.Arbitrum.Utils.Logging, only: [log_warning: 1, log_info: 1] + import Indexer.Fetcher.Arbitrum.Utils.Logging, only: [log_warning: 1] alias Explorer.Chain alias Explorer.Chain.Arbitrum @@ -224,83 +224,6 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do end end - @doc """ - Determines the rollup block number to discover missed L2-to-L1 messages within - a specified range. - - The function checks for the first missed L2-to-L1 message and whether historical - block fetching is still in progress. If no missed messages are found and - historical fetching is complete, it returns the block number just before the - first rollup block. Otherwise, it returns the appropriate block number based on - the findings. - - ## Parameters - - `initial_value`: The initial block number to start the further search of the - missed messages from if no missed messages are found and historical blocks - are not fetched yet. - - `rollup_first_block`: The block number of the first rollup block. - - ## Returns - - The block number of the first missed L2-to-L1 message. - """ - @spec rollup_block_to_discover_missed_messages_from_l2(FullBlock.block_number(), FullBlock.block_number()) :: - nil | FullBlock.block_number() - def rollup_block_to_discover_missed_messages_from_l2(initial_value, rollup_first_block) do - arbsys_contract = Application.get_env(:indexer, Indexer.Fetcher.Arbitrum.Messaging)[:arbsys_contract] - - with {:block, nil} <- - {:block, Reader.rollup_block_of_first_missed_message_from_l2(arbsys_contract, @l2_to_l1_event)}, - {:synced, true} <- {:synced, rollup_synced?()} do - log_info("No missed messages from L2 found") - rollup_first_block - 1 - else - {:block, value} -> - log_info("First missed message from L2 found in block #{value}") - value - - {:synced, false} -> - log_info("No missed messages from L2 found but historical blocks fetching still in progress") - initial_value - end - end - - @doc """ - Determines the rollup block number to discover missed L1-to-L2 messages within - a specified range. - - The function checks for the first missed L1-to-L2 message and whether historical - block fetching is still in progress. If no missed messages are found and - historical fetching is complete, it returns the block number just before the - first rollup block. Otherwise, it returns the appropriate block number based on - the findings. - - ## Parameters - - `initial_value`: The initial block number to start the further search of the - missed messages from if no missed messages are found and historical blocks - are not fetched yet. - - `rollup_first_block`: The block number of the first rollup block. - - ## Returns - - The block number of the first missed L1-to-L2 message. - """ - @spec rollup_block_to_discover_missed_messages_to_l2(FullBlock.block_number(), FullBlock.block_number()) :: - nil | FullBlock.block_number() - def rollup_block_to_discover_missed_messages_to_l2(initial_value, rollup_first_block) do - with {:block, nil} <- {:block, Reader.rollup_block_of_first_missed_message_to_l2()}, - {:synced, true} <- {:synced, rollup_synced?()} do - log_info("No missed messages to L2 found") - rollup_first_block - 1 - else - {:block, value} -> - log_info("First missed message to L2 found in block #{value}") - value - - {:synced, false} -> - log_info("No missed messages to L2 found but historical blocks fetching still in progress") - initial_value - end - end - @doc """ Retrieves the L1 block number immediately following the block where the confirmation transaction for the highest confirmed rollup block was included. @@ -969,16 +892,6 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do Reader.get_da_info_by_batch_number(batch_number) end - # Checks if the rollup is synced by verifying if the block after the first block exists in the database. - @spec rollup_synced?() :: boolean() - defp rollup_synced? do - # Since zero block does not have any useful data, it make sense to consider - # the block just after it - rollup_tail = Application.get_all_env(:indexer)[:first_block] + 1 - - Reader.rollup_block_exists?(rollup_tail) - end - @spec lifecycle_transaction_to_map(Arbitrum.LifecycleTransaction.t()) :: Arbitrum.LifecycleTransaction.to_import() defp lifecycle_transaction_to_map(tx) do [:id, :hash, :block_number, :timestamp, :status] From e68090523c494e47a8a1f8054238cf7547b58351 Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Fri, 20 Sep 2024 02:49:29 -0600 Subject: [PATCH 159/363] fix: proper handling of old batches on Arbitrum Nova (#10786) * AnyTrust old certificates support * proper calculation the block range size * don't exclude the block 0 from the boundaries search --- .../indexer/fetcher/arbitrum/da/anytrust.ex | 19 +++++++++++++++ .../lib/indexer/fetcher/arbitrum/da/common.ex | 3 +-- .../lib/indexer/fetcher/arbitrum/utils/rpc.ex | 4 +++- .../fetcher/arbitrum/workers/new_batches.ex | 23 +++++++++++++++++-- 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/da/anytrust.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/da/anytrust.ex index 59c401c54ed4..40459db704f5 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/da/anytrust.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/da/anytrust.ex @@ -124,6 +124,25 @@ defmodule Indexer.Fetcher.Arbitrum.DA.Anytrust do }} end + def parse_batch_accompanying_data(batch_number, << + keyset_hash::binary-size(32), + data_hash::binary-size(32), + timeout::big-unsigned-integer-size(64), + signers_mask::big-unsigned-integer-size(64), + bls_signature::binary-size(96) + >>) do + # https://github.com/OffchainLabs/nitro/blob/ad9ab00723e13cf98307b9b65774ad455594ef7b/arbstate/das_reader.go#L95-L151 + {:ok, :in_anytrust, + %__MODULE__{ + batch_number: batch_number, + keyset_hash: keyset_hash, + data_hash: data_hash, + timeout: IndexerHelper.timestamp_to_datetime(timeout), + signers_mask: signers_mask, + bls_signature: bls_signature + }} + end + def parse_batch_accompanying_data(_, _) do log_error("Can not parse Anytrust DA message.") {:error, nil, nil} diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/da/common.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/da/common.ex index 493ea49c0900..80acc1a7c2ad 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/da/common.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/da/common.ex @@ -124,8 +124,7 @@ defmodule Indexer.Fetcher.Arbitrum.DA.Common do {:error, nil, nil} 128 -> - log_error("DAS messages are not supported.") - {:error, nil, nil} + Anytrust.parse_batch_accompanying_data(batch_number, rest) 136 -> Anytrust.parse_batch_accompanying_data(batch_number, rest) diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex index 6f4ef02ba316..5f4c688dcd4b 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex @@ -646,7 +646,9 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Rpc do # inspected block is in the boundary of the required batch: the current batch is the same # as one found in the previous iteration or the step is not the smallest possible. - next_block_to_inspect = max(1, inspected_block - new_step) + # it is OK to use the earliest block 0 as since the corresponding batch (0) + # will be returned by get_batch_number_for_rollup_block. + next_block_to_inspect = max(0, inspected_block - new_step) do_binary_search_of_opposite_block( next_block_to_inspect, diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex index 6a5e8c389e5e..fc58788f5e2b 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex @@ -1275,7 +1275,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do {nil, nil} %Arbitrum.L1Batch{start_block: start_block, end_block: end_block} -> - {start_block - 1, div(end_block - start_block, 2)} + {start_block - 1, half_of_block_range(start_block, end_block, :descending)} end end @@ -1289,7 +1289,26 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do {nil, nil} %Arbitrum.L1Batch{start_block: start_block, end_block: end_block} -> - {end_block + 1, div(start_block - end_block, 2)} + {end_block + 1, half_of_block_range(start_block, end_block, :ascending)} + end + end + + # Calculates half the range between two block numbers, with direction adjustment. + # + # ## Parameters + # - `start_block`: The starting block number. + # - `end_block`: The ending block number. + # - `direction`: The direction of calculation, either `:ascending` or `:descending`. + # + # ## Returns + # - An integer representing half the block range, adjusted for direction: + # - For `:descending`, a positive integer >= 1. + # - For `:ascending`, a negative integer <= -1. + @spec half_of_block_range(non_neg_integer(), non_neg_integer(), :ascending | :descending) :: integer() + defp half_of_block_range(start_block, end_block, direction) do + case direction do + :descending -> max(div(end_block - start_block + 1, 2), 1) + :ascending -> min(div(start_block - end_block - 1, 2), -1) end end From 78ebdf88e9651113e98e11536c140fb9d4b1bbe4 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 20 Sep 2024 12:05:07 +0300 Subject: [PATCH 160/363] fix: Set min query length in the search API endpoints (#10698) --- .../controllers/api/v2/search_controller.ex | 22 +++++++++++++++++++ .../controllers/search_controller.ex | 12 ++++++++++ 2 files changed, 34 insertions(+) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/search_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/search_controller.ex index 0a3c0f17aed4..af3324ad1052 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/search_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/search_controller.ex @@ -8,6 +8,16 @@ defmodule BlockScoutWeb.API.V2.SearchController do alias Explorer.PagingOptions @api_true [api?: true] + @min_query_length 3 + + def search(conn, %{"q" => query}) when byte_size(query) < @min_query_length do + conn + |> put_status(200) + |> render(:search_results, %{ + search_results: [], + next_page_params: nil + }) + end def search(conn, %{"q" => query} = params) do [paging_options: paging_options] = paging_options(params) @@ -29,6 +39,12 @@ defmodule BlockScoutWeb.API.V2.SearchController do }) end + def check_redirect(conn, %{"q" => query}) when byte_size(query) < @min_query_length do + conn + |> put_status(200) + |> render(:search_results, %{result: {:error, :not_found}}) + end + def check_redirect(conn, %{"q" => query}) do result = query @@ -40,6 +56,12 @@ defmodule BlockScoutWeb.API.V2.SearchController do |> render(:search_results, %{result: result}) end + def quick_search(conn, %{"q" => query}) when byte_size(query) < @min_query_length do + conn + |> put_status(200) + |> render(:search_results, %{search_results: []}) + end + def quick_search(conn, %{"q" => query}) do search_results = Search.balanced_unpaginated_search(%PagingOptions{page_size: 50}, query, @api_true) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/search_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/search_controller.ex index 937c4b2603a6..a797076826b6 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/search_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/search_controller.ex @@ -7,6 +7,18 @@ defmodule BlockScoutWeb.SearchController do alias Explorer.Chain.Search alias Phoenix.View + @min_query_length 3 + + def search_results(conn, %{"q" => query, "type" => "JSON"}) when byte_size(query) < @min_query_length do + json( + conn, + %{ + items: [], + next_page_path: nil + } + ) + end + def search_results(conn, %{"q" => query, "type" => "JSON"} = params) do [paging_options: paging_options] = paging_options(params) offset = (max(paging_options.page_number, 1) - 1) * paging_options.page_size From 26a8899548bdccfc98e46ac07d1b188440d9e4c1 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 20 Sep 2024 12:06:35 +0300 Subject: [PATCH 161/363] fix: Preload additional sources for bytecode twin smart-contract (#10692) * fix: Preload additional sources for bytecode twin smart-contract * change order of cases in get_additional_sources function --- .../block_scout_web/views/api/v2/smart_contract_view.ex | 9 +++++---- apps/explorer/lib/explorer/chain/smart_contract.ex | 5 +++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex index c653d40c7865..7dea4bc6ab8a 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex @@ -257,18 +257,19 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do end @doc """ - Returns additional sources of the smart-contract from bytecode twin + Returns additional sources of the smart-contract or from its bytecode twin """ @spec get_additional_sources(SmartContract.t(), boolean, SmartContract.t() | nil) :: [SmartContractAdditionalSource.t()] | nil def get_additional_sources(smart_contract, smart_contract_verified, bytecode_twin_contract) do cond do + smart_contract_verified && is_list(smart_contract.smart_contract_additional_sources) && + !Enum.empty?(smart_contract.smart_contract_additional_sources) -> + smart_contract.smart_contract_additional_sources + !is_nil(bytecode_twin_contract) -> bytecode_twin_contract.smart_contract_additional_sources - smart_contract_verified -> - smart_contract.smart_contract_additional_sources - true -> [] end diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index d6a881176c6f..3e80fbfdf2b4 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -699,6 +699,10 @@ defmodule Explorer.Chain.SmartContract do """ @spec get_verified_bytecode_twin_contract(Address.t(), any()) :: SmartContract.t() | nil def get_verified_bytecode_twin_contract(%Address{} = target_address, options \\ []) do + necessity_by_association = %{ + :smart_contract_additional_sources => :optional + } + case target_address do %{contract_code: %Chain.Data{bytes: contract_code_bytes}} -> target_address_hash = target_address.hash @@ -715,6 +719,7 @@ defmodule Explorer.Chain.SmartContract do ) verified_bytecode_twin_contract_query + |> Chain.join_associations(necessity_by_association) |> Chain.select_repo(options).one(timeout: 10_000) _ -> From 45c8368fad0fa9d268ddd82145f744abf9e99931 Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Fri, 20 Sep 2024 13:11:52 +0400 Subject: [PATCH 162/363] fix: decode addresses as checksummed (#10777) * fix: decode addresses as checksummed * chore: fix failing tests --- .../lib/block_scout_web/views/abi_encoded_value_view.ex | 6 +++++- .../controllers/api/v2/smart_contract_controller_test.exs | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex b/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex index 5fee550b6bbc..12bde160651b 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex @@ -8,6 +8,7 @@ defmodule BlockScoutWeb.ABIEncodedValueView do use BlockScoutWeb, :view alias ABI.FunctionSelector + alias Explorer.Chain.{Address, Hash} alias Phoenix.HTML require Logger @@ -196,7 +197,10 @@ defmodule BlockScoutWeb.ABIEncodedValueView do end defp base_value_json(:address, value) do - hex_for_json(value) + case Hash.Address.cast(value) do + {:ok, address} -> Address.checksum(address) + :error -> "0x" + end end defp base_value_json(:bytes, value) do diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs index 082f8db55ddd..58b7a38ca140 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs @@ -263,7 +263,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "constructor_args" => target_contract.constructor_arguments, "decoded_constructor_args" => [ ["0x0000000000000000000000000000000000000000", %{"name" => "_proxyStorage", "type" => "address"}], - ["0x2cf6e7c9ec35d0b08a1062e13854f74b1aaae54e", %{"name" => "_implementationAddress", "type" => "address"}] + ["0x2Cf6E7c9eC35D0B08A1062e13854f74b1aaae54e", %{"name" => "_implementationAddress", "type" => "address"}] ], "is_self_destructed" => false, "deployed_bytecode" => @@ -849,7 +849,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response["decoded_constructor_args"] == [ [ - "0xc35dadb65012ec5796536bd9864ed8773abc74c4", + "0xc35DADB65012eC5796536bD9864eD8773aBc74C4", %{ "internalType" => "address", "name" => "_factory", @@ -857,7 +857,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do } ], [ - "0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6", + "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6", %{ "internalType" => "address", "name" => "_WETH", From dd6b1c9d4bbdd83bdcf088f90921e13b7003a481 Mon Sep 17 00:00:00 2001 From: Hanlu Date: Fri, 20 Sep 2024 18:39:27 +0800 Subject: [PATCH 163/363] chore: Add missing symbols (#10749) --- .../lib/block_scout_web/views/api/v2/smart_contract_view.ex | 2 +- apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex index 7dea4bc6ab8a..5f559decee79 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex @@ -416,7 +416,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do case type do "tuple[" <> rest -> # we need to convert tuple[...][] or tuple[...][n] into (...)[] or (...)[n] - # before sending it to the `FunctionSelector.decode_type/1. See https://github.com/poanetwork/ex_abi/issues/168. + # before sending it to the `FunctionSelector.decode_type/1`. See https://github.com/poanetwork/ex_abi/issues/168. tuple_item_types = rest |> String.split("]") diff --git a/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations.ex b/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations.ex index e023ac2bb9c4..6d110f493dad 100644 --- a/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations.ex +++ b/apps/indexer/lib/indexer/fetcher/celo/epoch_block_operations.ex @@ -35,7 +35,7 @@ defmodule Indexer.Fetcher.Celo.EpochBlockOperations do unless state do raise ArgumentError, - ":json_rpc_named_arguments must be provided to `#{__MODULE__}.child_spec " <> + ":json_rpc_named_arguments must be provided to `#{__MODULE__}.child_spec` " <> "to allow for json_rpc calls when running." end From 547032e2047bc4dab86f95396a3773be6465ea3d Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:38:41 +0400 Subject: [PATCH 164/363] fix: Clear null round blocks from missing block ranges (#10805) --- apps/indexer/lib/indexer/block/catchup/fetcher.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/indexer/lib/indexer/block/catchup/fetcher.ex b/apps/indexer/lib/indexer/block/catchup/fetcher.ex index 7f3d32ecf58a..ea5454b5a214 100644 --- a/apps/indexer/lib/indexer/block/catchup/fetcher.ex +++ b/apps/indexer/lib/indexer/block/catchup/fetcher.ex @@ -176,8 +176,8 @@ defmodule Indexer.Block.Catchup.Fetcher do case result do {:ok, %{inserted: inserted, errors: errors}} -> - handle_null_rounds(errors) - clear_missing_ranges(range, errors) + valid_errors = handle_null_rounds(errors) + clear_missing_ranges(range, valid_errors) {:ok, inserted: inserted} From bb1c194d62d86474d00f93852360b2d57f29ffd8 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Mon, 23 Sep 2024 18:43:19 +0400 Subject: [PATCH 165/363] feat: Retry ERC-1155 token instance metadata fetch from baseURI + tokenID (#10766) --- .../lib/indexer/fetcher/token_instance/helper.ex | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/indexer/lib/indexer/fetcher/token_instance/helper.ex b/apps/indexer/lib/indexer/fetcher/token_instance/helper.ex index 5067e601ab7d..0987055f3328 100644 --- a/apps/indexer/lib/indexer/fetcher/token_instance/helper.ex +++ b/apps/indexer/lib/indexer/fetcher/token_instance/helper.ex @@ -239,13 +239,17 @@ defmodule Indexer.Fetcher.TokenInstance.Helper do end end - def prepare_request(_token_type, contract_address_hash_string, token_id, _retry) do - %{ + def prepare_request(_token_type, contract_address_hash_string, token_id, from_base_uri?) do + request = %{ contract_address: contract_address_hash_string, - method_id: @uri, - args: [token_id], block_number: nil } + + if from_base_uri? do + request |> Map.put(:method_id, @base_uri) |> Map.put(:args, []) + else + request |> Map.put(:method_id, @uri) |> Map.put(:args, [token_id]) + end end def normalize_token_id("ERC-721", _token_id), do: nil From b80a27ca28153a52320e8081a5de0e96391f77d9 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Mon, 23 Sep 2024 18:49:55 +0400 Subject: [PATCH 166/363] chore: Support non-unique log index for rsk chain type (#10807) --- apps/explorer/lib/explorer/chain/import/runner/blocks.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex index 3043ce24eca3..3b88982e2861 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex @@ -738,7 +738,7 @@ defmodule Explorer.Chain.Import.Runner.Blocks do end defp refs_to_token_transfers_query(historical_token_transfers_query, filtered_query) do - if Application.get_env(:explorer, :chain_type) == :polygon_zkevm do + if Application.get_env(:explorer, :chain_type) in [:polygon_zkevm, :rsk] do from(historical_tt in subquery(historical_token_transfers_query), inner_join: tt in subquery(filtered_query), on: @@ -779,7 +779,7 @@ defmodule Explorer.Chain.Import.Runner.Blocks do end defp derived_token_transfers_query(refs_to_token_transfers, filtered_query) do - if Application.get_env(:explorer, :chain_type) == :polygon_zkevm do + if Application.get_env(:explorer, :chain_type) in [:polygon_zkevm, :rsk] do from(tt in filtered_query, inner_join: tt_1 in subquery(refs_to_token_transfers), on: From fa00acfb74920cdb12cb16762a98836c8a2a6b35 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Mon, 23 Sep 2024 18:52:35 +0400 Subject: [PATCH 167/363] chore: Refactor OrderedCache preloads (#10803) --- .../lib/explorer/chain/ordered_cache.ex | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/ordered_cache.ex b/apps/explorer/lib/explorer/chain/ordered_cache.ex index b27a79490bd8..46b1f8f9391c 100644 --- a/apps/explorer/lib/explorer/chain/ordered_cache.ex +++ b/apps/explorer/lib/explorer/chain/ordered_cache.ex @@ -247,6 +247,7 @@ defmodule Explorer.Chain.OrderedCache do ConCache.update(cache_name(), ids_list_key(), fn ids -> updated_list = elements + |> do_preloads() |> Enum.map(&{element_to_id(&1), &1}) |> Enum.sort(&prevails?(&1, &2)) |> merge_and_update(ids || [], max_size()) @@ -258,6 +259,14 @@ defmodule Explorer.Chain.OrderedCache do def update(element), do: update([element]) + defp do_preloads(elements) do + if Enum.empty?(preloads()) do + elements + else + Explorer.Repo.preload(elements, preloads()) + end + end + defp merge_and_update(_candidates, existing, 0) do # if there is no more space in the list remove the remaining existing # elements and return an empty list @@ -323,17 +332,10 @@ defmodule Explorer.Chain.OrderedCache do end defp put_element(element_id, element) do - full_element = - if Enum.empty?(preloads()) do - element - else - Explorer.Repo.preload(element, preloads()) - end - # dirty puts are a little faster than puts with locks. # this is not a problem because this is the only function modifying rows # and it only gets called inside `update`, which works isolated - ConCache.dirty_put(cache_name(), element_id, full_element) + ConCache.dirty_put(cache_name(), element_id, element) end ### Supervisor's child specification From 29a04293d3f2fc627e13d8ff952fd3558a346a4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 22:03:06 +0300 Subject: [PATCH 168/363] chore(deps-dev): bump exvcr from 0.15.1 to 0.15.2 (#10814) Bumps [exvcr](https://github.com/parroty/exvcr) from 0.15.1 to 0.15.2. - [Release notes](https://github.com/parroty/exvcr/releases) - [Changelog](https://github.com/parroty/exvcr/blob/master/CHANGELOG.md) - [Commits](https://github.com/parroty/exvcr/compare/v0.15.1...v0.15.2) --- updated-dependencies: - dependency-name: exvcr dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index aac15ab5c2bd..76a143e5982b 100644 --- a/mix.lock +++ b/mix.lock @@ -59,7 +59,7 @@ "exactor": {:hex, :exactor, "2.2.4", "5efb4ddeb2c48d9a1d7c9b465a6fffdd82300eb9618ece5d34c3334d5d7245b1", [:mix], [], "hexpm", "1222419f706e01bfa1095aec9acf6421367dcfab798a6f67c54cf784733cd6b5"}, "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm", "32e95820a97cffea67830e91514a2ad53b888850442d6d395f53a1ac60c82e07"}, "expo": {:hex, :expo, "1.0.1", "f9e2f984f5b8d195815d52d0ba264798c12c8d2f2606f76fa4c60e8ebe39474d", [:mix], [], "hexpm", "f250b33274e3e56513644858c116f255d35c767c2b8e96a512fe7839ef9306a1"}, - "exvcr": {:hex, :exvcr, "0.15.1", "772db4d065f5136c6a984c302799a79e4ade3e52701c95425fa2229dd6426886", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:finch, "~> 0.16", [hex: :finch, repo: "hexpm", optional: true]}, {:httpoison, "~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "de4fc18b1d672d9b72bc7468735e19779aa50ea963a1f859ef82cd9e294b13e3"}, + "exvcr": {:hex, :exvcr, "0.15.2", "2216c8605b5c3e300160c2a5bd896b4928fa51fc3fb3420d3e792ad833ac89ba", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:finch, "~> 0.16", [hex: :finch, repo: "hexpm", optional: true]}, {:httpoison, "~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "2bd4125889bd3953d7fbb7b388c34190c31e292f12896da56ecf0743d40439ed"}, "file_info": {:hex, :file_info, "0.0.4", "2e0e77f211e833f38ead22cb29ce53761d457d80b3ffe0ffe0eb93880b0963b2", [:mix], [{:mimetype_parser, "~> 0.1.2", [hex: :mimetype_parser, repo: "hexpm", optional: false]}], "hexpm", "50e7ad01c2c8b9339010675fe4dc4a113b8d6ca7eddce24d1d74fd0e762781a5"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"}, From f7a434df1732edc6d5b5a252bf8f26090ee6d54b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 22:03:27 +0300 Subject: [PATCH 169/363] chore(deps): bump con_cache from 1.1.0 to 1.1.1 (#10815) Bumps [con_cache](https://github.com/sasa1977/con_cache) from 1.1.0 to 1.1.1. - [Changelog](https://github.com/sasa1977/con_cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/sasa1977/con_cache/compare/1.1.0...1.1.1) --- updated-dependencies: - dependency-name: con_cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 76a143e5982b..41ca20603865 100644 --- a/mix.lock +++ b/mix.lock @@ -23,7 +23,7 @@ "coerce": {:hex, :coerce, "1.0.1", "211c27386315dc2894ac11bc1f413a0e38505d808153367bd5c6e75a4003d096", [:mix], [], "hexpm", "b44a691700f7a1a15b4b7e2ff1fa30bebd669929ac8aa43cffe9e2f8bf051cf1"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, "comeonin": {:hex, :comeonin, "5.4.0", "246a56ca3f41d404380fc6465650ddaa532c7f98be4bda1b4656b3a37cc13abe", [:mix], [], "hexpm", "796393a9e50d01999d56b7b8420ab0481a7538d0caf80919da493b4a6e51faf1"}, - "con_cache": {:hex, :con_cache, "1.1.0", "45c7c6cd6dc216e47636232e8c683734b7fe293221fccd9454fa1757bc685044", [:mix], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8655f2ae13a1e56c8aef304d250814c7ed929c12810f126fc423ecc8e871593b"}, + "con_cache": {:hex, :con_cache, "1.1.1", "9f47a68dfef5ac3bbff8ce2c499869dbc5ba889dadde6ac4aff8eb78ddaf6d82", [:mix], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1def4d1bec296564c75b5bbc60a19f2b5649d81bfa345a2febcc6ae380e8ae15"}, "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, "cors_plug": {:hex, :cors_plug, "3.0.3", "7c3ac52b39624bc616db2e937c282f3f623f25f8d550068b6710e58d04a0e330", [:mix], [{:plug, "~> 1.13", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3f2d759e8c272ed3835fab2ef11b46bddab8c1ab9528167bd463b6452edf830d"}, "cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"}, From 629620bc52a8a88e216ff1f58066fbed49268c36 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Tue, 24 Sep 2024 11:37:35 +0300 Subject: [PATCH 170/363] feat: Add Blackfort validators (#10744) * feat: Add Blackfort validators * Finishing touches * Add missing specs, docs, pre-release workflow for Blackfort * Remove blocks_validated --------- Co-authored-by: Victor Baranov --- .github/workflows/config.yml | 2 +- .github/workflows/pre-release-blackfort.yml | 97 +++++++++ .../publish-docker-image-for-blackfort.yml | 51 +++++ .github/workflows/release-blackfort.yml | 94 ++++++++ .../lib/block_scout_web/chain.ex | 15 ++ .../api/v2/validator_controller.ex | 50 ++++- .../lib/block_scout_web/paging_helper.ex | 14 ++ .../lib/block_scout_web/routers/api_router.ex | 20 +- .../views/api/v2/validator_view.ex | 21 +- apps/explorer/config/config.exs | 5 + apps/explorer/config/dev.exs | 2 + apps/explorer/config/prod.exs | 5 + apps/explorer/config/test.exs | 3 +- apps/explorer/lib/explorer/application.ex | 4 +- .../lib/explorer/chain/blackfort/validator.ex | 201 ++++++++++++++++++ .../cache/blackfort_validators_counters.ex | 97 +++++++++ apps/explorer/lib/explorer/repo.ex | 10 + ...0240910112251_add_blackfort_validators.exs | 20 ++ .../lib/indexer/block/realtime/fetcher.ex | 23 +- .../indexer/fetcher/blackfort/validator.ex | 49 +++++ apps/indexer/lib/indexer/supervisor.ex | 4 + config/config_helper.exs | 4 +- config/runtime.exs | 2 + config/runtime/dev.exs | 7 + config/runtime/prod.exs | 6 + cspell.json | 5 +- 26 files changed, 789 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/pre-release-blackfort.yml create mode 100644 .github/workflows/publish-docker-image-for-blackfort.yml create mode 100644 .github/workflows/release-blackfort.yml create mode 100644 apps/explorer/lib/explorer/chain/blackfort/validator.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/blackfort_validators_counters.ex create mode 100644 apps/explorer/priv/blackfort/migrations/20240910112251_add_blackfort_validators.exs create mode 100644 apps/indexer/lib/indexer/fetcher/blackfort/validator.ex diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml index 33237b7202aa..17cb27192c87 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/config.yml @@ -49,7 +49,7 @@ jobs: // Add/remove CI matrix chain types here const defaultChainTypes = ["default"]; - const chainTypes = ["ethereum", "polygon_zkevm", "rsk", "stability", "filecoin", "optimism", "arbitrum", "celo", "zetachain", "zksync", "shibarium"]; + const chainTypes = ["ethereum", "polygon_zkevm", "rsk", "stability", "filecoin", "optimism", "arbitrum", "celo", "zetachain", "zksync", "shibarium", "blackfort"]; const extraChainTypes = ["suave", "polygon_edge"]; // Chain type matrix we use in master branch diff --git a/.github/workflows/pre-release-blackfort.yml b/.github/workflows/pre-release-blackfort.yml new file mode 100644 index 000000000000..66e730b6427e --- /dev/null +++ b/.github/workflows/pre-release-blackfort.yml @@ -0,0 +1,97 @@ +name: Pre-release for Blackfort + +on: + workflow_dispatch: + inputs: + number: + type: number + required: true + +env: + OTP_VERSION: ${{ vars.OTP_VERSION }} + ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + steps: + - uses: actions/checkout@v4 + - name: Setup repo + uses: ./.github/actions/setup-repo + id: setup + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + docker-remote-multi-platform: true + docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} + docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} + + - name: Build and push Docker image for Blackfort (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-blackfort:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=blackfort + + - name: Build and push Docker image for Blackfort (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-blackfort:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=blackfort + + - name: Build and push Docker image for Blackfort (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-blackfort:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=blackfort \ No newline at end of file diff --git a/.github/workflows/publish-docker-image-for-blackfort.yml b/.github/workflows/publish-docker-image-for-blackfort.yml new file mode 100644 index 000000000000..b0fbf41b3be3 --- /dev/null +++ b/.github/workflows/publish-docker-image-for-blackfort.yml @@ -0,0 +1,51 @@ +name: Blackfort Publish Docker image + +on: + workflow_dispatch: + push: + branches: + - production-blackfort +env: + OTP_VERSION: ${{ vars.OTP_VERSION }} + ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + DOCKER_CHAIN_NAME: blackfort + steps: + - uses: actions/checkout@v4 + - name: Setup repo + uses: ./.github/actions/setup-repo + id: setup + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + docker-remote-multi-platform: true + docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} + docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }} + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=blackfort \ No newline at end of file diff --git a/.github/workflows/release-blackfort.yml b/.github/workflows/release-blackfort.yml new file mode 100644 index 000000000000..76e22b0fde1f --- /dev/null +++ b/.github/workflows/release-blackfort.yml @@ -0,0 +1,94 @@ +name: Release for Blackfort + +on: + release: + types: [published] + +env: + OTP_VERSION: ${{ vars.OTP_VERSION }} + ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + steps: + - uses: actions/checkout@v4 + - name: Setup repo + uses: ./.github/actions/setup-repo + id: setup + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + docker-remote-multi-platform: true + docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} + docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} + + - name: Build and push Docker image for Blackfort (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-blackfort:latest, blockscout/blockscout-blackfort:${{ env.RELEASE_VERSION }} + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=blackfort + + - name: Build and push Docker image for Blackfort (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-blackfort:${{ env.RELEASE_VERSION }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=blackfort + + - name: Build and push Docker image for Blackfort (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-blackfort:${{ env.RELEASE_VERSION }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=blackfort \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/chain.ex b/apps/block_scout_web/lib/block_scout_web/chain.ex index b6b4ddd1c36d..87bd279ae872 100644 --- a/apps/block_scout_web/lib/block_scout_web/chain.ex +++ b/apps/block_scout_web/lib/block_scout_web/chain.ex @@ -508,6 +508,21 @@ defmodule BlockScoutWeb.Chain do [paging_options: %{@default_paging_options | key: %{block_index: index}}] end + # Clause for `Explorer.Chain.Blackfort.Validator`, + # returned by `BlockScoutWeb.API.V2.ValidatorController.blackfort_validators_list/2` (`/api/v2/validators/blackfort`) + def paging_options(%{ + "address_hash" => address_hash_string + }) do + [ + paging_options: %{ + @default_paging_options + | key: %{ + address_hash: parse_address_hash(address_hash_string) + } + } + ] + end + def paging_options(_params), do: [paging_options: @default_paging_options] def put_key_value_to_paging_options([paging_options: paging_options], key, value) do diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/validator_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/validator_controller.ex index 78736763dbc2..78a59a0ddf10 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/validator_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/validator_controller.ex @@ -1,13 +1,15 @@ defmodule BlockScoutWeb.API.V2.ValidatorController do use BlockScoutWeb, :controller - alias Explorer.Chain.Cache.StabilityValidatorsCounters + alias Explorer.Chain.Blackfort.Validator, as: ValidatorBlackfort + alias Explorer.Chain.Cache.{BlackfortValidatorsCounters, StabilityValidatorsCounters} alias Explorer.Chain.Stability.Validator, as: ValidatorStability import BlockScoutWeb.PagingHelper, only: [ delete_parameters_from_next_page_params: 1, stability_validators_state_options: 1, + validators_blackfort_sorting: 1, validators_stability_sorting: 1 ] @@ -71,6 +73,52 @@ defmodule BlockScoutWeb.API.V2.ValidatorController do }) end + @doc """ + Function to handle GET requests to `/api/v2/validators/blackfort` endpoint. + """ + @spec blackfort_validators_list(Plug.Conn.t(), map()) :: Plug.Conn.t() + def blackfort_validators_list(conn, params) do + options = + [ + necessity_by_association: %{ + [address: [:names, :smart_contract, :proxy_implementations]] => :optional + } + ] + |> Keyword.merge(@api_true) + |> Keyword.merge(paging_options(params)) + |> Keyword.merge(validators_blackfort_sorting(params)) + + {validators, next_page} = options |> ValidatorBlackfort.get_paginated_validators() |> split_list_by_page() + + next_page_params = + next_page + |> next_page_params( + validators, + delete_parameters_from_next_page_params(params), + &ValidatorBlackfort.next_page_params/1 + ) + + conn + |> render(:blackfort_validators, %{validators: validators, next_page_params: next_page_params}) + end + + @doc """ + Function to handle GET requests to `/api/v2/validators/blackfort/counters` endpoint. + """ + @spec blackfort_validators_counters(Plug.Conn.t(), map()) :: Plug.Conn.t() + def blackfort_validators_counters(conn, _params) do + %{ + validators_counter: validators_counter, + new_validators_counter: new_validators_counter + } = BlackfortValidatorsCounters.get_counters(@api_true) + + conn + |> json(%{ + validators_counter: validators_counter, + new_validators_counter_24h: new_validators_counter + }) + end + defp calculate_active_validators_percentage(active_validators_counter, validators_counter) do if Decimal.compare(validators_counter, Decimal.new(0)) == :gt do active_validators_counter diff --git a/apps/block_scout_web/lib/block_scout_web/paging_helper.ex b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex index 25dec818db16..15bdf142ef8d 100644 --- a/apps/block_scout_web/lib/block_scout_web/paging_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex @@ -334,4 +334,18 @@ defmodule BlockScoutWeb.PagingHelper do defp do_mud_records_sorting("key1", "asc"), do: [asc_nulls_first: :key1] defp do_mud_records_sorting("key1", "desc"), do: [desc_nulls_last: :key1] defp do_mud_records_sorting(_, _), do: [] + + @spec validators_blackfort_sorting(%{required(String.t()) => String.t()}) :: [ + {:sorting, SortingHelper.sorting_params()} + ] + def validators_blackfort_sorting(%{"sort" => sort_field, "order" => order}) do + [sorting: do_validators_blackfort_sorting(sort_field, order)] + end + + def validators_blackfort_sorting(_), do: [] + + defp do_validators_blackfort_sorting("address_hash", "asc"), do: [asc_nulls_first: :address_hash] + defp do_validators_blackfort_sorting("address_hash", "desc"), do: [desc_nulls_last: :address_hash] + + defp do_validators_blackfort_sorting(_, _), do: [] end diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index d0e05070afa5..2a987813fcaf 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -327,11 +327,21 @@ defmodule BlockScoutWeb.Routers.ApiRouter do end scope "/validators" do - if Application.compile_env(:explorer, :chain_type) == :stability do - scope "/stability" do - get("/", V2.ValidatorController, :stability_validators_list) - get("/counters", V2.ValidatorController, :stability_validators_counters) - end + case Application.compile_env(:explorer, :chain_type) do + :stability -> + scope "/stability" do + get("/", V2.ValidatorController, :stability_validators_list) + get("/counters", V2.ValidatorController, :stability_validators_counters) + end + + :blackfort -> + scope "/blackfort" do + get("/", V2.ValidatorController, :blackfort_validators_list) + get("/counters", V2.ValidatorController, :blackfort_validators_counters) + end + + _ -> + nil end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/validator_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/validator_view.ex index e5b719557b95..e54e4f726a31 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/validator_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/validator_view.ex @@ -4,14 +4,31 @@ defmodule BlockScoutWeb.API.V2.ValidatorView do alias BlockScoutWeb.API.V2.Helper def render("stability_validators.json", %{validators: validators, next_page_params: next_page_params}) do - %{"items" => Enum.map(validators, &prepare_validator(&1)), "next_page_params" => next_page_params} + %{"items" => Enum.map(validators, &prepare_stability_validator(&1)), "next_page_params" => next_page_params} end - defp prepare_validator(validator) do + def render("blackfort_validators.json", %{validators: validators, next_page_params: next_page_params}) do + %{"items" => Enum.map(validators, &prepare_blackfort_validator(&1)), "next_page_params" => next_page_params} + end + + defp prepare_stability_validator(validator) do %{ "address" => Helper.address_with_info(nil, validator.address, validator.address_hash, true), "state" => validator.state, "blocks_validated_count" => validator.blocks_validated } end + + defp prepare_blackfort_validator(validator) do + %{ + "address" => Helper.address_with_info(nil, validator.address, validator.address_hash, true), + "name" => validator.name, + "commission" => validator.commission, + "self_bonded_amount" => validator.self_bonded_amount, + "delegated_amount" => validator.delegated_amount, + "slashing_status_is_slashed" => validator.slashing_status_is_slashed, + "slashing_status_by_block" => validator.slashing_status_by_block, + "slashing_status_multiplier" => validator.slashing_status_multiplier + } + end end diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index 0827fabb0552..233186aa4745 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -81,6 +81,11 @@ config :explorer, Explorer.Chain.Cache.StabilityValidatorsCounters, enable_consolidation: true, update_interval_in_milliseconds: update_interval_in_milliseconds_default +config :explorer, Explorer.Chain.Cache.BlackfortValidatorsCounters, + enabled: true, + enable_consolidation: true, + update_interval_in_milliseconds: update_interval_in_milliseconds_default + config :explorer, Explorer.Chain.Cache.TransactionActionTokensData, enabled: true config :explorer, Explorer.Chain.Cache.TransactionActionUniswapPools, enabled: true diff --git a/apps/explorer/config/dev.exs b/apps/explorer/config/dev.exs index 71bfcbe32928..08c665e9c4dc 100644 --- a/apps/explorer/config/dev.exs +++ b/apps/explorer/config/dev.exs @@ -46,6 +46,8 @@ config :explorer, Explorer.Repo.Mud, timeout: :timer.seconds(80) config :explorer, Explorer.Repo.ShrunkInternalTransactions, timeout: :timer.seconds(80) +config :explorer, Explorer.Repo.Blackfort, timeout: :timer.seconds(80) + config :explorer, Explorer.Tracer, env: "dev", disabled?: true config :logger, :explorer, diff --git a/apps/explorer/config/prod.exs b/apps/explorer/config/prod.exs index e27374333564..585b7f38df53 100644 --- a/apps/explorer/config/prod.exs +++ b/apps/explorer/config/prod.exs @@ -94,6 +94,11 @@ config :explorer, Explorer.Repo.ShrunkInternalTransactions, timeout: :timer.seconds(60), ssl_opts: [verify: :verify_none] +config :explorer, Explorer.Repo.Blackfort, + prepare: :unnamed, + timeout: :timer.seconds(60), + ssl_opts: [verify: :verify_none] + config :explorer, Explorer.Tracer, env: "production", disabled?: true config :logger, :explorer, diff --git a/apps/explorer/config/test.exs b/apps/explorer/config/test.exs index f47582559d25..0352d8bc4fad 100644 --- a/apps/explorer/config/test.exs +++ b/apps/explorer/config/test.exs @@ -68,7 +68,8 @@ for repo <- [ Explorer.Repo.Filecoin, Explorer.Repo.Stability, Explorer.Repo.Mud, - Explorer.Repo.ShrunkInternalTransactions + Explorer.Repo.ShrunkInternalTransactions, + Explorer.Repo.Blackfort ] do config :explorer, repo, database: database, diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index 4839ed13a8de..96685ea646cf 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -144,6 +144,7 @@ defmodule Explorer.Application do configure(Explorer.Migrator.RestoreOmittedWETHTransfers), configure(Explorer.Migrator.FilecoinPendingAddressOperations), configure_mode_dependent_process(Explorer.Migrator.ShrinkInternalTransactions, :indexer), + configure_chain_type_dependent_process(Explorer.Chain.Cache.BlackfortValidatorsCounters, :blackfort), configure_chain_type_dependent_process(Explorer.Chain.Cache.StabilityValidatorsCounters, :stability), configure_mode_dependent_process(Explorer.Migrator.SanitizeMissingTokenBalances, :indexer) ] @@ -168,7 +169,8 @@ defmodule Explorer.Application do Explorer.Repo.BridgedTokens, Explorer.Repo.Filecoin, Explorer.Repo.Stability, - Explorer.Repo.ShrunkInternalTransactions + Explorer.Repo.ShrunkInternalTransactions, + Explorer.Repo.Blackfort ] else [] diff --git a/apps/explorer/lib/explorer/chain/blackfort/validator.ex b/apps/explorer/lib/explorer/chain/blackfort/validator.ex new file mode 100644 index 000000000000..e36a87df638e --- /dev/null +++ b/apps/explorer/lib/explorer/chain/blackfort/validator.ex @@ -0,0 +1,201 @@ +defmodule Explorer.Chain.Blackfort.Validator do + @moduledoc """ + Blackfort validators + """ + + use Explorer.Schema + + alias Explorer.Chain.{Address, Import} + alias Explorer.Chain.Hash.Address, as: HashAddress + alias Explorer.{Chain, Repo, SortingHelper} + + require Logger + + @default_sorting [ + asc: :address_hash + ] + + @primary_key false + typed_schema "validators_blackfort" do + field(:address_hash, HashAddress, primary_key: true) + field(:name, :binary) + field(:commission, :integer) + field(:self_bonded_amount, :decimal) + field(:delegated_amount, :decimal) + field(:slashing_status_is_slashed, :boolean, default: false) + field(:slashing_status_by_block, :integer) + field(:slashing_status_multiplier, :integer) + + has_one(:address, Address, foreign_key: :hash, references: :address_hash) + timestamps() + end + + @required_attrs ~w(address_hash)a + @optional_attrs ~w(name commission self_bonded_amount delegated_amount)a + def changeset(%__MODULE__{} = validator, attrs) do + validator + |> cast(attrs, @required_attrs ++ @optional_attrs) + |> validate_required(@required_attrs) + |> unique_constraint(:address_hash) + end + + @doc """ + Get validators list. + Keyword could contain: + - paging_options + - necessity_by_association + - sorting (supported by `Explorer.SortingHelper` module) + - state (one of `@state_enum`) + """ + @spec get_paginated_validators(keyword()) :: [t()] + def get_paginated_validators(options \\ []) do + paging_options = Keyword.get(options, :paging_options, Chain.default_paging_options()) + necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) + sorting = Keyword.get(options, :sorting, []) + + __MODULE__ + |> Chain.join_associations(necessity_by_association) + |> SortingHelper.apply_sorting(sorting, @default_sorting) + |> SortingHelper.page_with_sorting(paging_options, sorting, @default_sorting) + |> Chain.select_repo(options).all() + end + + @doc """ + Get all validators + """ + @spec get_all_validators(keyword()) :: [t()] + def get_all_validators(options \\ []) do + __MODULE__ + |> Chain.select_repo(options).all() + end + + @doc """ + Delete validators by address hashes + """ + @spec delete_validators_by_address_hashes([binary() | HashAddress.t()]) :: {non_neg_integer(), nil | []} | :ignore + def delete_validators_by_address_hashes(list) when is_list(list) and length(list) > 0 do + __MODULE__ + |> where([vs], vs.address_hash in ^list) + |> Repo.delete_all() + end + + def delete_validators_by_address_hashes(_), do: :ignore + + @doc """ + Insert validators + """ + @spec insert_validators([map()]) :: {non_neg_integer(), nil | []} + def insert_validators(validators) do + Repo.insert_all(__MODULE__, validators, + on_conflict: {:replace_all_except, [:inserted_at]}, + conflict_target: [:address_hash] + ) + end + + @doc """ + Append timestamps (:inserted_at, :updated_at) + """ + @spec append_timestamps(map()) :: map() + def append_timestamps(validator) do + Map.merge(validator, Import.timestamps()) + end + + @doc """ + Derive next page params from %Explorer.Chain.Blackfort.Validator{} + """ + @spec next_page_params(t()) :: map() + def next_page_params(%__MODULE__{address_hash: address_hash}) do + %{"address_hash" => address_hash} + end + + @doc """ + Returns dynamic query for validated blocks count. Needed for SortingHelper + """ + @spec dynamic_validated_blocks() :: Ecto.Query.dynamic_expr() + def dynamic_validated_blocks do + dynamic( + [vs], + fragment( + "SELECT count(*) FROM blocks WHERE miner_hash = ?", + vs.address_hash + ) + ) + end + + @doc """ + Returns total count of validators. + """ + @spec count_validators() :: integer() + def count_validators do + Repo.aggregate(__MODULE__, :count, :address_hash) + end + + @doc """ + Returns count of new validators (inserted withing last 24h). + """ + @spec count_new_validators() :: integer() + def count_new_validators do + __MODULE__ + |> where([vs], vs.inserted_at >= ago(1, "day")) + |> Repo.aggregate(:count, :address_hash) + end + + @doc """ + Fetch list of Blackfort validators + """ + @spec fetch_validators_list() :: {:ok, list()} | :error + def fetch_validators_list do + case HTTPoison.get(validator_url(), [], follow_redirect: true) do + {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> + body |> Jason.decode() |> parse_validators_info() + + error -> + Logger.error("Failed to fetch blackfort validator info: #{inspect(error)}") + :error + end + end + + defp parse_validators_info({:ok, validators}) do + {:ok, + validators + |> Enum.map(fn %{ + "address" => address_hash_string, + "name" => name, + "commission" => commission, + "self_bonded_amount" => self_bonded_amount, + "delegated_amount" => delegated_amount, + "slashing_status" => %{ + "is_slashed" => slashing_status_is_slashed, + "by_block" => slashing_status_by_block, + "multiplier" => slashing_status_multiplier + } + } -> + {:ok, address_hash} = HashAddress.cast(address_hash_string) + + %{ + address_hash: address_hash, + name: name, + commission: parse_number(commission), + self_bonded_amount: parse_number(self_bonded_amount), + delegated_amount: parse_number(delegated_amount), + slashing_status_is_slashed: slashing_status_is_slashed, + slashing_status_by_block: slashing_status_by_block, + slashing_status_multiplier: slashing_status_multiplier + } + end)} + end + + defp parse_validators_info({:error, error}) do + Logger.error("Failed to parse blackfort validator info: #{inspect(error)}") + :error + end + + defp validator_url do + Application.get_env(:explorer, __MODULE__)[:api_url] + end + + defp parse_number(string) do + {number, _} = Integer.parse(string) + number + end +end diff --git a/apps/explorer/lib/explorer/chain/cache/blackfort_validators_counters.ex b/apps/explorer/lib/explorer/chain/cache/blackfort_validators_counters.ex new file mode 100644 index 000000000000..cf2a77411183 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/cache/blackfort_validators_counters.ex @@ -0,0 +1,97 @@ +defmodule Explorer.Chain.Cache.BlackfortValidatorsCounters do + @moduledoc """ + Counts and store counters of validators blackfort. + + It loads the count asynchronously and in a time interval of 30 minutes. + """ + + use GenServer + + alias Explorer.Chain + alias Explorer.Chain.Blackfort.Validator, as: ValidatorBlackfort + + @validators_counter_key "blackfort_validators_counter" + @new_validators_counter_key "new_blackfort_validators_counter" + + # It is undesirable to automatically start the consolidation in all environments. + # Consider the test environment: if the consolidation initiates but does not + # finish before a test ends, that test will fail. This way, hundreds of + # tests were failing before disabling the consolidation and the scheduler in + # the test env. + config = Application.compile_env(:explorer, __MODULE__) + @enable_consolidation Keyword.get(config, :enable_consolidation) + + @update_interval_in_milliseconds Keyword.get(config, :update_interval_in_milliseconds) + + @doc """ + Starts a process to periodically update validators blackfort counters + """ + @spec start_link(term()) :: GenServer.on_start() + def start_link(_) do + GenServer.start_link(__MODULE__, [], name: __MODULE__) + end + + @impl true + def init(_args) do + {:ok, %{consolidate?: @enable_consolidation}, {:continue, :ok}} + end + + defp schedule_next_consolidation do + Process.send_after(self(), :consolidate, @update_interval_in_milliseconds) + end + + @impl true + def handle_continue(:ok, %{consolidate?: true} = state) do + consolidate() + schedule_next_consolidation() + + {:noreply, state} + end + + @impl true + def handle_continue(:ok, state) do + {:noreply, state} + end + + @impl true + def handle_info(:consolidate, state) do + consolidate() + schedule_next_consolidation() + + {:noreply, state} + end + + @doc """ + Fetches values for a blackfort validators counters from the `last_fetched_counters` table. + """ + @spec get_counters(Keyword.t()) :: map() + def get_counters(options) do + %{ + validators_counter: Chain.get_last_fetched_counter(@validators_counter_key, options), + new_validators_counter: Chain.get_last_fetched_counter(@new_validators_counter_key, options) + } + end + + @doc """ + Consolidates the info by populating the `last_fetched_counters` table with the current database information. + """ + @spec consolidate() :: any() + def consolidate do + tasks = [ + Task.async(fn -> ValidatorBlackfort.count_validators() end), + Task.async(fn -> ValidatorBlackfort.count_new_validators() end) + ] + + [validators_counter, new_validators_counter] = Task.await_many(tasks, :infinity) + + Chain.upsert_last_fetched_counter(%{ + counter_type: @validators_counter_key, + value: validators_counter + }) + + Chain.upsert_last_fetched_counter(%{ + counter_type: @new_validators_counter_key, + value: new_validators_counter + }) + end +end diff --git a/apps/explorer/lib/explorer/repo.ex b/apps/explorer/lib/explorer/repo.ex index 08a2a924bee0..548165168e03 100644 --- a/apps/explorer/lib/explorer/repo.ex +++ b/apps/explorer/lib/explorer/repo.ex @@ -286,4 +286,14 @@ defmodule Explorer.Repo do ConfigHelper.init_repo_module(__MODULE__, opts) end end + + defmodule Blackfort do + use Ecto.Repo, + otp_app: :explorer, + adapter: Ecto.Adapters.Postgres + + def init(_, opts) do + ConfigHelper.init_repo_module(__MODULE__, opts) + end + end end diff --git a/apps/explorer/priv/blackfort/migrations/20240910112251_add_blackfort_validators.exs b/apps/explorer/priv/blackfort/migrations/20240910112251_add_blackfort_validators.exs new file mode 100644 index 000000000000..d1d2b55e8d0d --- /dev/null +++ b/apps/explorer/priv/blackfort/migrations/20240910112251_add_blackfort_validators.exs @@ -0,0 +1,20 @@ +defmodule Explorer.Repo.Blackfort.Migrations.AddBlackfortValidators do + use Ecto.Migration + + def change do + create table(:validators_blackfort, primary_key: false) do + add(:address_hash, :bytea, null: false, primary_key: true) + add(:name, :string) + add(:commission, :smallint) + add(:self_bonded_amount, :numeric, precision: 100) + add(:delegated_amount, :numeric, precision: 100) + add(:slashing_status_is_slashed, :boolean, default: false) + add(:slashing_status_by_block, :bigint) + add(:slashing_status_multiplier, :integer) + + timestamps() + end + + create_if_not_exists(index(:validators_blackfort, ["address_hash ASC"])) + end +end diff --git a/apps/indexer/lib/indexer/block/realtime/fetcher.ex b/apps/indexer/lib/indexer/block/realtime/fetcher.ex index a2e7716ac18a..598a24865535 100644 --- a/apps/indexer/lib/indexer/block/realtime/fetcher.ex +++ b/apps/indexer/lib/indexer/block/realtime/fetcher.ex @@ -170,14 +170,21 @@ defmodule Indexer.Block.Realtime.Fetcher do Process.cancel_timer(timer) end - if Application.compile_env(:explorer, :chain_type) == :stability do - defp fetch_validators_async do - GenServer.cast(Indexer.Fetcher.Stability.Validator, :update_validators_list) - end - else - defp fetch_validators_async do - :ignore - end + case Application.compile_env(:explorer, :chain_type) do + :stability -> + defp fetch_validators_async do + GenServer.cast(Indexer.Fetcher.Stability.Validator, :update_validators_list) + end + + :blackfort -> + defp fetch_validators_async do + GenServer.cast(Indexer.Fetcher.Blackfort.Validator, :update_validators_list) + end + + _ -> + defp fetch_validators_async do + :ignore + end end defp subscribe_to_new_heads(%__MODULE__{subscription: nil} = state, subscribe_named_arguments) diff --git a/apps/indexer/lib/indexer/fetcher/blackfort/validator.ex b/apps/indexer/lib/indexer/fetcher/blackfort/validator.ex new file mode 100644 index 000000000000..75cbc791d124 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/blackfort/validator.ex @@ -0,0 +1,49 @@ +defmodule Indexer.Fetcher.Blackfort.Validator do + @moduledoc """ + GenServer responsible for updating the list of blackfort validators in the database. + """ + use GenServer + + alias Explorer.Chain.Blackfort.Validator + + def start_link(_) do + GenServer.start_link(__MODULE__, [], name: __MODULE__) + end + + @impl true + def init(state) do + GenServer.cast(__MODULE__, :update_validators_list) + + {:ok, state} + end + + @impl true + def handle_cast(:update_validators_list, state) do + case Validator.fetch_validators_list() do + {:ok, validators} -> + validators_from_db = Validator.get_all_validators() + + validators_map = + Enum.reduce(validators, %{}, fn %{address_hash: address_hash}, map -> + Map.put(map, address_hash.bytes, true) + end) + + address_hashes_to_drop_from_db = + Enum.flat_map(validators_from_db, fn validator -> + (is_nil(validators_map[validator.address_hash.bytes]) && + [validator.address_hash]) || [] + end) + + Validator.delete_validators_by_address_hashes(address_hashes_to_drop_from_db) + + validators + |> Enum.map(&Validator.append_timestamps/1) + |> Validator.insert_validators() + + _ -> + nil + end + + {:noreply, state} + end +end diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex index e56883914c92..70ea093532fe 100644 --- a/apps/indexer/lib/indexer/supervisor.ex +++ b/apps/indexer/lib/indexer/supervisor.ex @@ -18,6 +18,7 @@ defmodule Indexer.Supervisor do alias Indexer.Block.Catchup, as: BlockCatchup alias Indexer.Block.Realtime, as: BlockRealtime + alias Indexer.Fetcher.Blackfort.Validator, as: ValidatorBlackfort alias Indexer.Fetcher.CoinBalance.Catchup, as: CoinBalanceCatchup alias Indexer.Fetcher.CoinBalance.Realtime, as: CoinBalanceRealtime alias Indexer.Fetcher.Stability.Validator, as: ValidatorStability @@ -285,6 +286,9 @@ defmodule Indexer.Supervisor do :stability -> [{ValidatorStability, []} | fetchers] + :blackfort -> + [{ValidatorBlackfort, []} | fetchers] + _ -> fetchers end diff --git a/config/config_helper.exs b/config/config_helper.exs index 540f887a0a99..6def45a00aaa 100644 --- a/config/config_helper.exs +++ b/config/config_helper.exs @@ -23,6 +23,7 @@ defmodule ConfigHelper do :zksync -> base_repos ++ [Explorer.Repo.ZkSync] :celo -> base_repos ++ [Explorer.Repo.Celo] :arbitrum -> base_repos ++ [Explorer.Repo.Arbitrum] + :blackfort -> base_repos ++ [Explorer.Repo.Blackfort] _ -> base_repos end @@ -316,7 +317,8 @@ defmodule ConfigHelper do "suave", "zetachain", "zksync", - "celo" + "celo", + "blackfort" ] @spec chain_type() :: atom() | nil diff --git a/config/runtime.exs b/config/runtime.exs index 756bb1ef2a01..7875f4c47658 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -646,6 +646,8 @@ config :explorer, Explorer.Migrator.FilecoinPendingAddressOperations, batch_size: ConfigHelper.parse_integer_env_var("FILECOIN_PENDING_ADDRESS_OPERATIONS_MIGRATION_BATCH_SIZE", 100), concurrency: ConfigHelper.parse_integer_env_var("FILECOIN_PENDING_ADDRESS_OPERATIONS_MIGRATION_CONCURRENCY", 1) +config :explorer, Explorer.Chain.Blackfort.Validator, api_url: System.get_env("BLACKFORT_VALIDATOR_API_URL") + ############### ### Indexer ### ############### diff --git a/config/runtime/dev.exs b/config/runtime/dev.exs index cdd35cc636c4..0e2d20cc0d35 100644 --- a/config/runtime/dev.exs +++ b/config/runtime/dev.exs @@ -199,6 +199,13 @@ config :explorer, Explorer.Repo.ShrunkInternalTransactions, url: System.get_env("DATABASE_URL"), pool_size: 1 +# Configures Blackfort database +config :explorer, Explorer.Repo.Blackfort, + database: database, + hostname: hostname, + url: System.get_env("DATABASE_URL"), + pool_size: 1 + variant = Variant.get() Code.require_file("#{variant}.exs", "apps/explorer/config/dev") diff --git a/config/runtime/prod.exs b/config/runtime/prod.exs index 2b10f79f287c..f52ef52b5912 100644 --- a/config/runtime/prod.exs +++ b/config/runtime/prod.exs @@ -159,6 +159,12 @@ config :explorer, Explorer.Repo.ShrunkInternalTransactions, pool_size: 1, ssl: ExplorerConfigHelper.ssl_enabled?() +# Configures Blackfort database +config :explorer, Explorer.Repo.Blackfort, + url: System.get_env("DATABASE_URL"), + pool_size: 1, + ssl: ExplorerConfigHelper.ssl_enabled?() + variant = Variant.get() Code.require_file("#{variant}.exs", "apps/explorer/config/prod") diff --git a/cspell.json b/cspell.json index be5ffa2ccaa2..22872a9c5637 100644 --- a/cspell.json +++ b/cspell.json @@ -18,8 +18,8 @@ "AION", "AIRTABLE", "Aiubo", - "alloc", "alfajores", + "alloc", "amzootyukbugmx", "anytrust", "apikey", @@ -54,6 +54,7 @@ "binwrite", "bitmask", "bizbuz", + "Blackfort", "Blockchair", "blockheight", "blockless", @@ -200,9 +201,9 @@ "falala", "feelin", "FEVM", + "filecoin", "Filecoin", "Filesize", - "filecoin", "fixidity", "fkey", "Floki", From b6aef2149b294d98ed08c5a51b064f5ae6bd64e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 13:01:04 +0300 Subject: [PATCH 171/363] chore(deps): bump ecto from 3.11.2 to 3.12.3 (#10739) * chore(deps): bump ecto from 3.11.2 to 3.12.3 Bumps [ecto](https://github.com/elixir-ecto/ecto) from 3.11.2 to 3.12.3. - [Release notes](https://github.com/elixir-ecto/ecto/releases) - [Changelog](https://github.com/elixir-ecto/ecto/blob/master/CHANGELOG.md) - [Commits](https://github.com/elixir-ecto/ecto/compare/v3.11.2...v3.12.3) --- updated-dependencies: - dependency-name: ecto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Fix dialyzer --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Viktor Baranov --- apps/explorer/lib/explorer/etherscan/contracts.ex | 8 ++++---- mix.lock | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/explorer/lib/explorer/etherscan/contracts.ex b/apps/explorer/lib/explorer/etherscan/contracts.ex index 4ecd397f56d5..56d4f2e24a5e 100644 --- a/apps/explorer/lib/explorer/etherscan/contracts.ex +++ b/apps/explorer/lib/explorer/etherscan/contracts.ex @@ -153,7 +153,7 @@ defmodule Explorer.Etherscan.Contracts do query = from( address in Address, - where: address.contract_code != <<>>, + where: address.contract_code != ^%Explorer.Chain.Data{bytes: <<>>}, where: not is_nil(address.contract_code), where: address.decompiled == true, limit: ^limit, @@ -171,7 +171,7 @@ defmodule Explorer.Etherscan.Contracts do query = from( address in Address, - where: address.contract_code != <<>>, + where: address.contract_code != ^%Explorer.Chain.Data{bytes: <<>>}, where: not is_nil(address.contract_code), where: fragment("? IS NOT TRUE", address.verified), limit: ^limit, @@ -191,7 +191,7 @@ defmodule Explorer.Etherscan.Contracts do address in Address, where: fragment("? IS NOT TRUE", address.verified), where: fragment("? IS NOT TRUE", address.decompiled), - where: address.contract_code != <<>>, + where: address.contract_code != ^%Explorer.Chain.Data{bytes: <<>>}, where: not is_nil(address.contract_code), limit: ^limit, offset: ^offset @@ -207,7 +207,7 @@ defmodule Explorer.Etherscan.Contracts do def list_empty_contracts(limit, offset) do query = from(address in Address, - where: address.contract_code == <<>>, + where: address.contract_code == ^%Explorer.Chain.Data{bytes: <<>>}, preload: [:smart_contract, :decompiled_smart_contracts], order_by: [asc: address.inserted_at], limit: ^limit, diff --git a/mix.lock b/mix.lock index 41ca20603865..7e93f8121c76 100644 --- a/mix.lock +++ b/mix.lock @@ -39,8 +39,8 @@ "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, "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.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, - "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 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", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, - "ecto_sql": {:hex, :ecto_sql, "3.11.3", "4eb7348ff8101fbc4e6bbc5a4404a24fecbe73a3372d16569526b0cf34ebc195", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16 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", "e5f36e3d736b99c7fee3e631333b8394ade4bafe9d96d35669fca2d81c2be928"}, + "ecto": {:hex, :ecto, "3.12.3", "1a9111560731f6c3606924c81c870a68a34c819f6d4f03822f370ea31a582208", [:mix], [{:decimal, "~> 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", "9efd91506ae722f95e48dc49e70d0cb632ede3b7a23896252a60a14ac6d59165"}, + "ecto_sql": {:hex, :ecto_sql, "3.12.0", "73cea17edfa54bde76ee8561b30d29ea08f630959685006d9c6e7d1e59113b7d", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 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", "dc9e4d206f274f3947e96142a8fdc5f69a2a6a9abb4649ef5c882323b6d512f0"}, "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_abi": {:hex, :ex_abi, "0.8.1", "451fa960ddc4dfbb350e13509f3dd64ca586b8484a77aad9f7d778161b5eab79", [:mix], [{:ex_keccak, "~> 0.7.5", [hex: :ex_keccak, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "abcf53d556c2948e5c1241340afd4a72cdf93ab6daef16fc200c16ca1183cdca"}, From 96bca662050c6bb87363ec18e2bfcb849695bcd1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 13:08:13 +0300 Subject: [PATCH 172/363] chore(deps): bump elliptic and web3 in /apps/block_scout_web/assets (#10690) Bumps [elliptic](https://github.com/indutny/elliptic) to 6.5.7 and updates ancestor dependency [web3](https://github.com/ChainSafe/web3.js). These dependencies need to be updated together. Updates `elliptic` from 6.5.4 to 6.5.7 - [Commits](https://github.com/indutny/elliptic/compare/v6.5.4...v6.5.7) Updates `web3` from 1.10.4 to 4.12.1 - [Release notes](https://github.com/ChainSafe/web3.js/releases) - [Changelog](https://github.com/web3/web3.js/blob/4.x/CHANGELOG.md) - [Commits](https://github.com/ChainSafe/web3.js/compare/v1.10.4...v4.12.1) --- updated-dependencies: - dependency-name: elliptic dependency-type: indirect - dependency-name: web3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 4836 +++-------------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 671 insertions(+), 4167 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 0764da41abbe..bff2686cf1be 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -66,7 +66,7 @@ "url": "^0.11.3", "util": "^0.12.5", "viewerjs": "^1.11.6", - "web3": "^1.10.4", + "web3": "^4.12.1", "web3modal": "^1.9.12", "xss": "^1.0.15" }, @@ -115,6 +115,11 @@ "node": ">=0.10.0" } }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==" + }, "node_modules/@amplitude/analytics-browser": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.9.3.tgz", @@ -2120,15 +2125,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@ethereumjs/common": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.5.tgz", - "integrity": "sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA==", - "dependencies": { - "crc-32": "^1.2.0", - "ethereumjs-util": "^7.1.5" - } - }, "node_modules/@ethereumjs/rlp": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", @@ -2140,413 +2136,6 @@ "node": ">=14" } }, - "node_modules/@ethereumjs/tx": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.5.2.tgz", - "integrity": "sha512-gQDNJWKrSDGu2w7w0PzVXVBNMzb7wwdDOmOqczmhNjqFxFuIbhVJDwiGEnxFNC2/b8ifcZzY7MLcluizohRzNw==", - "dependencies": { - "@ethereumjs/common": "^2.6.4", - "ethereumjs-util": "^7.1.5" - } - }, - "node_modules/@ethereumjs/util": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", - "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", - "dependencies": { - "@ethereumjs/rlp": "^4.0.1", - "ethereum-cryptography": "^2.0.0", - "micro-ftch": "^0.3.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz", - "integrity": "sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==", - "dependencies": { - "@noble/curves": "1.3.0", - "@noble/hashes": "1.3.3", - "@scure/bip32": "1.3.3", - "@scure/bip39": "1.2.2" - } - }, - "node_modules/@ethersproject/abi": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", - "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "node_modules/@ethersproject/abstract-provider": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", - "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/networks": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/web": "^5.7.0" - } - }, - "node_modules/@ethersproject/abstract-signer": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", - "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0" - } - }, - "node_modules/@ethersproject/address": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", - "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/rlp": "^5.7.0" - } - }, - "node_modules/@ethersproject/base64": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", - "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0" - } - }, - "node_modules/@ethersproject/bignumber": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", - "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "bn.js": "^5.2.1" - } - }, - "node_modules/@ethersproject/bytes": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", - "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/logger": "^5.7.0" - } - }, - "node_modules/@ethersproject/constants": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", - "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bignumber": "^5.7.0" - } - }, - "node_modules/@ethersproject/hash": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", - "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/base64": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "node_modules/@ethersproject/keccak256": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", - "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "js-sha3": "0.8.0" - } - }, - "node_modules/@ethersproject/logger": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", - "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ] - }, - "node_modules/@ethersproject/networks": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", - "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/logger": "^5.7.0" - } - }, - "node_modules/@ethersproject/properties": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", - "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/logger": "^5.7.0" - } - }, - "node_modules/@ethersproject/rlp": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", - "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "node_modules/@ethersproject/signing-key": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", - "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "bn.js": "^5.2.1", - "elliptic": "6.5.4", - "hash.js": "1.1.7" - } - }, - "node_modules/@ethersproject/strings": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", - "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "node_modules/@ethersproject/transactions": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", - "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/rlp": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0" - } - }, - "node_modules/@ethersproject/web": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", - "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "dependencies": { - "@ethersproject/base64": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, "node_modules/@fortawesome/fontawesome-free": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.6.0.tgz", @@ -3350,20 +2939,20 @@ "integrity": "sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==" }, "node_modules/@noble/curves": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", - "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", "dependencies": { - "@noble/hashes": "1.3.3" + "@noble/hashes": "1.4.0" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@noble/hashes": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", - "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", "engines": { "node": ">= 16" }, @@ -3415,33 +3004,33 @@ } }, "node_modules/@scure/base": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", - "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.8.tgz", + "integrity": "sha512-6CyAclxj3Nb0XT7GHK6K4zK6k2xJm6E4Ft0Ohjt4WgegiFUHEtFb2CGzmPmGBwoIhrLsqNLYfLr04Y1GePrzZg==", "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/bip32": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.3.tgz", - "integrity": "sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", "dependencies": { - "@noble/curves": "~1.3.0", - "@noble/hashes": "~1.3.2", - "@scure/base": "~1.1.4" + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/bip39": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.2.tgz", - "integrity": "sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", "dependencies": { - "@noble/hashes": "~1.3.2", - "@scure/base": "~1.1.4" + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -3453,17 +3042,6 @@ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, "node_modules/@sindresorhus/merge-streams": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz", @@ -3494,17 +3072,6 @@ "@sinonjs/commons": "^2.0.0" } }, - "node_modules/@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "dependencies": { - "defer-to-connect": "^2.0.1" - }, - "engines": { - "node": ">=14.16" - } - }, "node_modules/@tarekraafat/autocomplete.js": { "version": "10.2.7", "resolved": "https://registry.npmjs.org/@tarekraafat/autocomplete.js/-/autocomplete.js-10.2.7.tgz", @@ -3582,17 +3149,6 @@ "@types/node": "*" } }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, "node_modules/@types/css-font-loading-module": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz", @@ -3613,11 +3169,6 @@ "@types/node": "*" } }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" - }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -3665,14 +3216,6 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/node": { "version": "16.11.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", @@ -3686,14 +3229,6 @@ "@types/node": "*" } }, - "node_modules/@types/responselike": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/secp256k1": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", @@ -3714,6 +3249,14 @@ "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", "dev": true }, + "node_modules/@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.11", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.11.tgz", @@ -4193,11 +3736,20 @@ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, - "node_modules/abortcontroller-polyfill": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz", - "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==" - }, + "node_modules/abitype": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-0.7.1.tgz", + "integrity": "sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ==", + "peerDependencies": { + "typescript": ">=4.9.4", + "zod": "^3 >=3.19.1" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, "node_modules/abstract-leveldown": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz", @@ -4206,18 +3758,6 @@ "xtend": "~4.0.0" } }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/acorn": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", @@ -4429,11 +3969,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, "node_modules/array-includes": { "version": "3.1.7", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", @@ -5038,66 +4573,11 @@ "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.1.tgz", "integrity": "sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg==" }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, "node_modules/bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -5347,11 +4827,6 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, - "node_modules/buffer-to-arraybuffer": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", - "integrity": "sha512-3dthu5CYiVB1DEJp61FtApNnNndTckcqe4pFcLdvHtrpG+kcyekCJKg4MRiDcFW7A6AODnXB9U4dwQiCW5kzJQ==" - }, "node_modules/buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -5362,6 +4837,8 @@ "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", "hasInstallScript": true, + "optional": true, + "peer": true, "dependencies": { "node-gyp-build": "^4.3.0" }, @@ -5400,61 +4877,6 @@ "node": ">=10" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacheable-lookup": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz", - "integrity": "sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==", - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "engines": { - "node": ">=8" - } - }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -5644,11 +5066,6 @@ "node": ">= 6" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -5664,33 +5081,6 @@ "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", "dev": true }, - "node_modules/cids": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", - "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", - "deprecated": "This module has been superseded by the multiformats module", - "dependencies": { - "buffer": "^5.5.0", - "class-is": "^1.1.0", - "multibase": "~0.6.0", - "multicodec": "^1.0.0", - "multihashes": "~0.4.15" - }, - "engines": { - "node": ">=4.0.0", - "npm": ">=3.0.0" - } - }, - "node_modules/cids/node_modules/multicodec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", - "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", - "deprecated": "This module has been superseded by the multiformats module", - "dependencies": { - "buffer": "^5.6.0", - "varint": "^5.0.0" - } - }, "node_modules/cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -5706,11 +5096,6 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, - "node_modules/class-is": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", - "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" - }, "node_modules/clipboard": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz", @@ -5757,17 +5142,6 @@ "node": ">=6" } }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -5841,54 +5215,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/content-hash": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", - "integrity": "sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==", - "dependencies": { - "cids": "^0.7.1", - "multicodec": "^0.5.5", - "multihashes": "^0.4.15" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/convert-source-map": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", @@ -5898,19 +5224,6 @@ "safe-buffer": "~5.1.1" } }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, "node_modules/cookiejar": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", @@ -5975,18 +5288,6 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/cosmiconfig": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", @@ -6570,15 +5871,6 @@ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", "dev": true }, - "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -6642,31 +5934,6 @@ "node": ">=0.10" } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/dedent": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", @@ -6707,14 +5974,6 @@ "node": ">=0.10.0" } }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "engines": { - "node": ">=10" - } - }, "node_modules/deferred-leveldown": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz", @@ -6767,14 +6026,6 @@ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/des.js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", @@ -6784,15 +6035,6 @@ "minimalistic-assert": "^1.0.0" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, "node_modules/detect-browser": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.2.1.tgz", @@ -6934,20 +6176,15 @@ "safer-buffer": "^2.1.0" } }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, "node_modules/electron-to-chromium": { "version": "1.5.13", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==" }, "node_modules/elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", + "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -6990,14 +6227,6 @@ "node": ">= 4" } }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/encoding": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", @@ -7021,14 +6250,6 @@ "node": ">=0.10.0" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/enhanced-resolve": { "version": "5.17.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", @@ -7213,45 +6434,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "hasInstallScript": true, - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -7260,11 +6442,6 @@ "node": ">=6" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -7938,25 +7115,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esniff/node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -8047,14 +7205,6 @@ "node": ">=0.10.0" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/eth-block-tracker": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/eth-block-tracker/-/eth-block-tracker-4.4.3.tgz", @@ -8068,20 +7218,6 @@ "safe-event-emitter": "^1.0.1" } }, - "node_modules/eth-ens-namehash": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", - "integrity": "sha512-VWEI1+KJfz4Km//dadyvBBoBeSQ0MHTXPvr8UIXiLW6IanxvAV+DmlZAijZwAyggqGUfwQBeHf7tc9wzc1piSw==", - "dependencies": { - "idna-uts46-hx": "^2.3.1", - "js-sha3": "^0.5.7" - } - }, - "node_modules/eth-ens-namehash/node_modules/js-sha3": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", - "integrity": "sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==" - }, "node_modules/eth-json-rpc-filters": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/eth-json-rpc-filters/-/eth-json-rpc-filters-4.2.2.tgz", @@ -8172,34 +7308,6 @@ "safe-event-emitter": "^1.0.1" } }, - "node_modules/eth-lib": { - "version": "0.1.29", - "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.29.tgz", - "integrity": "sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ==", - "dependencies": { - "bn.js": "^4.11.6", - "elliptic": "^6.4.0", - "nano-json-stream-parser": "^0.1.2", - "servify": "^0.1.12", - "ws": "^3.0.0", - "xhr-request-promise": "^0.1.2" - } - }, - "node_modules/eth-lib/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "node_modules/eth-lib/node_modules/ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "dependencies": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } - }, "node_modules/eth-net-props": { "version": "1.0.41", "resolved": "https://registry.npmjs.org/eth-net-props/-/eth-net-props-1.0.41.tgz", @@ -8254,14 +7362,6 @@ "safe-buffer": "^5.1.1" } }, - "node_modules/ethereum-bloom-filters": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", - "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", - "dependencies": { - "js-sha3": "^0.8.0" - } - }, "node_modules/ethereum-common": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", @@ -8418,29 +7518,6 @@ "safe-buffer": "^5.1.1" } }, - "node_modules/ethereumjs-util": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", - "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", - "dependencies": { - "@types/bn.js": "^5.1.0", - "bn.js": "^5.1.2", - "create-hash": "^1.1.2", - "ethereum-cryptography": "^0.1.3", - "rlp": "^2.2.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/ethereumjs-util/node_modules/@types/bn.js": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", - "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/ethereumjs-vm": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz", @@ -8516,24 +7593,6 @@ "rlp": "^2.2.3" } }, - "node_modules/ethjs-unit": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", - "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", - "dependencies": { - "bn.js": "4.11.6", - "number-to-bn": "1.7.0" - }, - "engines": { - "node": ">=6.5.0", - "npm": ">=3" - } - }, - "node_modules/ethjs-unit/node_modules/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==" - }, "node_modules/ethjs-util": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", @@ -8547,19 +7606,10 @@ "npm": ">=3" } }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "node_modules/eventemitter3": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" }, "node_modules/events": { "version": "3.3.0", @@ -8626,106 +7676,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/express/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "dependencies": { - "type": "^2.7.2" - } - }, - "node_modules/ext/node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -8887,36 +7837,6 @@ "node": ">=8" } }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "node_modules/find-cache-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", @@ -9003,19 +7923,6 @@ "node": ">= 0.12" } }, - "node_modules/form-data-encoder": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", - "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==" - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -9029,32 +7936,6 @@ "url": "https://github.com/sponsors/rawify" } }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "node_modules/fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "dependencies": { - "minipass": "^2.6.0" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -9170,6 +8051,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, "engines": { "node": ">=10" }, @@ -9334,36 +8216,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/got": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/got/-/got-12.1.0.tgz", - "integrity": "sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==", - "dependencies": { - "@sindresorhus/is": "^4.6.0", - "@szmarczak/http-timer": "^5.0.1", - "@types/cacheable-request": "^6.0.2", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^6.0.4", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "form-data-encoder": "1.7.1", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, "node_modules/graphemer": { "version": "1.4.0", @@ -9552,31 +8409,6 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-https": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", - "integrity": "sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg==" - }, "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -9591,18 +8423,6 @@ "npm": ">=1.3.7" } }, - "node_modules/http2-wrapper": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", - "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, "node_modules/https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -9635,17 +8455,6 @@ "resolved": "https://registry.npmjs.org/humps/-/humps-2.0.1.tgz", "integrity": "sha1-3QLqYIG9BWjcXQcxhEY5V7qe+ao=" }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/icss-utils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", @@ -9658,25 +8467,6 @@ "postcss": "^8.1.0" } }, - "node_modules/idna-uts46-hx": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", - "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", - "dependencies": { - "punycode": "2.1.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/idna-uts46-hx/node_modules/punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA==", - "engines": { - "node": ">=6" - } - }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -9807,14 +8597,6 @@ "node": ">=10.13.0" } }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -10201,6 +8983,14 @@ "node": ">=0.10.0" } }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "peerDependencies": { + "ws": "*" + } + }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -12121,11 +10911,6 @@ "node": ">=4" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -12197,14 +10982,6 @@ "node": ">=6" } }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/jsonify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", @@ -12241,14 +11018,6 @@ "node": ">=10.0.0" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dependencies": { - "json-buffer": "3.0.1" - } - }, "node_modules/keyvaluestorage-interface": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz", @@ -12578,17 +11347,6 @@ "loose-envify": "cli.js" } }, - "node_modules/lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -12677,14 +11435,6 @@ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/memdown": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", @@ -12706,14 +11456,6 @@ "xtend": "~4.0.0" } }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -12790,19 +11532,6 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micro-ftch": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", - "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==" - }, "node_modules/micromatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", @@ -12833,17 +11562,6 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -12872,14 +11590,6 @@ "node": ">=6" } }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "engines": { - "node": ">=4" - } - }, "node_modules/min-document": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", @@ -12933,29 +11643,8 @@ "node_modules/minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "node_modules/minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dependencies": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "node_modules/minipass/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "node_modules/minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "dependencies": { - "minipass": "^2.9.0" - } + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true }, "node_modules/mitt": { "version": "3.0.1", @@ -12970,37 +11659,6 @@ "rrweb": "2.0.0-alpha.13" } }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mkdirp-promise": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", - "integrity": "sha512-Hepn5kb1lJPtVW84RFT40YG1OddBNTOVUZR2bzQUHc+Z03en8/3uX0+060JDhcEzyO08HmipsN9DcnFMxhIL9w==", - "deprecated": "This package is broken and no longer maintained. 'mkdirp' itself supports promises now, please switch to that.", - "dependencies": { - "mkdirp": "*" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mock-fs": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz", - "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==" - }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -13014,50 +11672,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/multibase": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", - "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", - "deprecated": "This module has been superseded by the multiformats module", - "dependencies": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - } - }, - "node_modules/multicodec": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz", - "integrity": "sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==", - "deprecated": "This module has been superseded by the multiformats module", - "dependencies": { - "varint": "^5.0.0" - } - }, - "node_modules/multihashes": { - "version": "0.4.21", - "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", - "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", - "dependencies": { - "buffer": "^5.5.0", - "multibase": "^0.7.0", - "varint": "^5.0.0" - } - }, - "node_modules/multihashes/node_modules/multibase": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", - "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", - "deprecated": "This module has been superseded by the multiformats module", - "dependencies": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - } - }, - "node_modules/nano-json-stream-parser": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", - "integrity": "sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew==" - }, "node_modules/nanoassert": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-1.1.0.tgz", @@ -13095,25 +11709,12 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, "node_modules/node-addon-api": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", @@ -13196,17 +11797,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -13231,24 +11821,6 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/number-to-bn": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", - "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", - "dependencies": { - "bn.js": "4.11.6", - "strip-hex-prefix": "1.0.0" - }, - "engines": { - "node": ">=6.5.0", - "npm": ">=3" - } - }, - "node_modules/number-to-bn/node_modules/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==" - }, "node_modules/numeral": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", @@ -13373,31 +11945,13 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/oboe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz", - "integrity": "sha512-zRFWiF+FoicxEs3jNI/WYUrVEgA7DeET/InK0XQuudGHRg8iIob3cNPrJTKaz4004uaA9Pbe+Dwa8iluhjLZWA==", + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "dependencies": { - "http-https": "^1.0.0" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" + "wrappy": "1" } }, "node_modules/onetime": { @@ -13437,14 +11991,6 @@ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" }, - "node_modules/p-cancelable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", - "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", - "engines": { - "node": ">=12.20" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -13554,14 +12100,6 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -13602,11 +12140,6 @@ "tslib": "^1.10.0" } }, - "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" - }, "node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -14469,18 +13002,6 @@ "react-is": "^16.8.1" } }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -14509,15 +13030,6 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -14717,19 +13229,6 @@ "node": ">=0.6" } }, - "node_modules/query-string": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", - "dependencies": { - "decode-uri-component": "^0.2.0", - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -14756,17 +13255,6 @@ } ] }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -14784,28 +13272,6 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/react": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", @@ -15071,11 +13537,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" - }, "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", @@ -15115,25 +13576,6 @@ "node": ">=10" } }, - "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "dependencies": { - "lowercase-keys": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/responselike/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "engines": { - "node": ">=8" - } - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -15468,55 +13910,6 @@ "semver": "bin/semver.js" } }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", @@ -15526,35 +13919,6 @@ "randombytes": "^2.1.0" } }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/servify": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", - "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", - "dependencies": { - "body-parser": "^1.16.0", - "cors": "^2.8.1", - "express": "^4.14.0", - "request": "^2.79.0", - "xhr": "^2.3.3" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -15603,11 +13967,6 @@ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, "node_modules/sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", @@ -15681,46 +14040,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/simple-get": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz", - "integrity": "sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw==", - "dependencies": { - "decompress-response": "^3.3.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/simple-get/node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -15823,14 +14142,6 @@ "node": ">=8" } }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/stream-browserify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", @@ -15851,14 +14162,6 @@ "xtend": "^4.0.2" } }, - "node_modules/strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -16120,95 +14423,6 @@ "url": "https://opencollective.com/svgo" } }, - "node_modules/swarm-js": { - "version": "0.1.42", - "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.42.tgz", - "integrity": "sha512-BV7c/dVlA3R6ya1lMlSSNPLYrntt0LUq4YMgy3iwpCIc6rZnS5W2wUoctarZ5pXlpKtxDDf9hNziEkcfrxdhqQ==", - "dependencies": { - "bluebird": "^3.5.0", - "buffer": "^5.0.5", - "eth-lib": "^0.1.26", - "fs-extra": "^4.0.2", - "got": "^11.8.5", - "mime-types": "^2.1.16", - "mkdirp-promise": "^5.0.1", - "mock-fs": "^4.1.0", - "setimmediate": "^1.0.5", - "tar": "^4.0.2", - "xhr-request": "^1.0.1" - } - }, - "node_modules/swarm-js/node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/swarm-js/node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "engines": { - "node": ">=10.6.0" - } - }, - "node_modules/swarm-js/node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/swarm-js/node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/swarm-js/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/swarm-js/node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "engines": { - "node": ">=8" - } - }, "node_modules/sweetalert2": { "version": "11.12.4", "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.12.4.tgz", @@ -16233,58 +14447,6 @@ "node": ">=6" } }, - "node_modules/tar": { - "version": "4.4.19", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", - "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", - "dependencies": { - "chownr": "^1.1.4", - "fs-minipass": "^1.2.7", - "minipass": "^2.9.0", - "minizlib": "^1.3.3", - "mkdirp": "^0.5.5", - "safe-buffer": "^5.2.1", - "yallist": "^3.1.1" - }, - "engines": { - "node": ">=4.5" - } - }, - "node_modules/tar/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/tar/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/tar/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, "node_modules/terser": { "version": "5.27.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", @@ -16381,14 +14543,6 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "node_modules/timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/tiny-emitter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", @@ -16425,14 +14579,6 @@ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, "node_modules/tough-cookie": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", @@ -16523,11 +14669,6 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, - "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -16560,18 +14701,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/typed-array-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", @@ -16645,10 +14774,18 @@ "is-typedarray": "^1.0.0" } }, - "node_modules/ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + "node_modules/typescript": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } }, "node_modules/unbox-primitive": { "version": "1.0.2", @@ -16717,22 +14854,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/update-browserslist-db": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", @@ -16794,11 +14915,6 @@ "requires-port": "^1.0.0" } }, - "node_modules/url-set-query": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", - "integrity": "sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg==" - }, "node_modules/url/node_modules/punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -16823,6 +14939,8 @@ "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", "integrity": "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==", "hasInstallScript": true, + "optional": true, + "peer": true, "dependencies": { "node-gyp-build": "^4.3.0" }, @@ -16830,11 +14948,6 @@ "node": ">=6.14.2" } }, - "node_modules/utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", - "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" - }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -16852,14 +14965,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", @@ -16883,19 +14988,6 @@ "node": ">=10.12.0" } }, - "node_modules/varint": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", - "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -16958,306 +15050,229 @@ } }, "node_modules/web3": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3/-/web3-1.10.4.tgz", - "integrity": "sha512-kgJvQZjkmjOEKimx/tJQsqWfRDPTTcBfYPa9XletxuHLpHcXdx67w8EFn5AW3eVxCutE9dTVHgGa9VYe8vgsEA==", - "hasInstallScript": true, - "dependencies": { - "web3-bzz": "1.10.4", - "web3-core": "1.10.4", - "web3-eth": "1.10.4", - "web3-eth-personal": "1.10.4", - "web3-net": "1.10.4", - "web3-shh": "1.10.4", - "web3-utils": "1.10.4" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-bzz": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.10.4.tgz", - "integrity": "sha512-ZZ/X4sJ0Uh2teU9lAGNS8EjveEppoHNQiKlOXAjedsrdWuaMErBPdLQjXfcrYvN6WM6Su9PMsAxf3FXXZ+HwQw==", - "hasInstallScript": true, - "dependencies": { - "@types/node": "^12.12.6", - "got": "12.1.0", - "swarm-js": "^0.1.40" - }, - "engines": { - "node": ">=8.0.0" + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/web3/-/web3-4.12.1.tgz", + "integrity": "sha512-zIFUPdgo2uG5Vbl7C4KrTv8dmWKN3sGnY/GundbiJzcaJZDxaCyu3a5HXAcgUM1VvvsVb1zaUQAFPceq05/q/Q==", + "dependencies": { + "web3-core": "^4.5.1", + "web3-errors": "^1.3.0", + "web3-eth": "^4.8.2", + "web3-eth-abi": "^4.2.3", + "web3-eth-accounts": "^4.2.1", + "web3-eth-contract": "^4.7.0", + "web3-eth-ens": "^4.4.0", + "web3-eth-iban": "^4.0.7", + "web3-eth-personal": "^4.0.8", + "web3-net": "^4.1.0", + "web3-providers-http": "^4.2.0", + "web3-providers-ws": "^4.0.8", + "web3-rpc-methods": "^1.3.0", + "web3-rpc-providers": "^1.0.0-rc.2", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "web3-validator": "^2.0.6" + }, + "engines": { + "node": ">=14.0.0", + "npm": ">=6.12.0" } }, - "node_modules/web3-bzz/node_modules/@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" - }, "node_modules/web3-core": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.10.4.tgz", - "integrity": "sha512-B6elffYm81MYZDTrat7aEhnhdtVE3lDBUZft16Z8awYMZYJDbnykEbJVS+l3mnA7AQTnSDr/1MjWofGDLBJPww==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-4.5.1.tgz", + "integrity": "sha512-mFMOO/IWdKsLL1o2whh3oJ0LCG9P3l5c4lpiMoVsVln3QXh/B0Gf8gW3aY8S+Ixm0OHyzFDXJVc2CodxqmI4Gw==", "dependencies": { - "@types/bn.js": "^5.1.1", - "@types/node": "^12.12.6", - "bignumber.js": "^9.0.0", - "web3-core-helpers": "1.10.4", - "web3-core-method": "1.10.4", - "web3-core-requestmanager": "1.10.4", - "web3-utils": "1.10.4" + "web3-errors": "^1.3.0", + "web3-eth-accounts": "^4.2.0", + "web3-eth-iban": "^4.0.7", + "web3-providers-http": "^4.2.0", + "web3-providers-ws": "^4.0.8", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "web3-validator": "^2.0.6" }, "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-core-helpers": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.10.4.tgz", - "integrity": "sha512-r+L5ylA17JlD1vwS8rjhWr0qg7zVoVMDvWhajWA5r5+USdh91jRUYosp19Kd1m2vE034v7Dfqe1xYRoH2zvG0g==", - "dependencies": { - "web3-eth-iban": "1.10.4", - "web3-utils": "1.10.4" + "node": ">=14", + "npm": ">=6.12.0" }, - "engines": { - "node": ">=8.0.0" + "optionalDependencies": { + "web3-providers-ipc": "^4.0.7" } }, - "node_modules/web3-core-method": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.10.4.tgz", - "integrity": "sha512-uZTb7flr+Xl6LaDsyTeE2L1TylokCJwTDrIVfIfnrGmnwLc6bmTWCCrm71sSrQ0hqs6vp/MKbQYIYqUN0J8WyA==", + "node_modules/web3-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/web3-errors/-/web3-errors-1.3.0.tgz", + "integrity": "sha512-j5JkAKCtuVMbY3F5PYXBqg1vWrtF4jcyyMY1rlw8a4PV67AkqlepjGgpzWJZd56Mt+TvHy6DA1F/3Id8LatDSQ==", "dependencies": { - "@ethersproject/transactions": "^5.6.2", - "web3-core-helpers": "1.10.4", - "web3-core-promievent": "1.10.4", - "web3-core-subscriptions": "1.10.4", - "web3-utils": "1.10.4" + "web3-types": "^1.7.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=14", + "npm": ">=6.12.0" } }, - "node_modules/web3-core-promievent": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.10.4.tgz", - "integrity": "sha512-2de5WnJQ72YcIhYwV/jHLc4/cWJnznuoGTJGD29ncFQHAfwW/MItHFSVKPPA5v8AhJe+r6y4Y12EKvZKjQVBvQ==", + "node_modules/web3-eth": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-4.8.2.tgz", + "integrity": "sha512-DLV/fIMG6gBp/B0gv0+G4FzxZ4YCDQsY3lzqqv7avwh3uU7/O27aifCUcFd7Ye+3ixTqCjAvLEl9wYSeyG3zQw==", "dependencies": { - "eventemitter3": "4.0.4" + "setimmediate": "^1.0.5", + "web3-core": "^4.5.0", + "web3-errors": "^1.2.1", + "web3-eth-abi": "^4.2.3", + "web3-eth-accounts": "^4.1.3", + "web3-net": "^4.1.0", + "web3-providers-ws": "^4.0.8", + "web3-rpc-methods": "^1.3.0", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "web3-validator": "^2.0.6" }, "engines": { - "node": ">=8.0.0" + "node": ">=14", + "npm": ">=6.12.0" } }, - "node_modules/web3-core-requestmanager": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.10.4.tgz", - "integrity": "sha512-vqP6pKH8RrhT/2MoaU+DY/OsYK9h7HmEBNCdoMj+4ZwujQtw/Mq2JifjwsJ7gits7Q+HWJwx8q6WmQoVZAWugg==", + "node_modules/web3-eth-abi": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-4.2.3.tgz", + "integrity": "sha512-rPVwTn0O1CzbtfXwEfIjUP0W5Y7u1OFjugwKpSqJzPQE6+REBg6OELjomTGZBu+GThxHnv0rp15SOxvqp+tyXA==", "dependencies": { - "util": "^0.12.5", - "web3-core-helpers": "1.10.4", - "web3-providers-http": "1.10.4", - "web3-providers-ipc": "1.10.4", - "web3-providers-ws": "1.10.4" + "abitype": "0.7.1", + "web3-errors": "^1.2.0", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "web3-validator": "^2.0.6" }, "engines": { - "node": ">=8.0.0" + "node": ">=14", + "npm": ">=6.12.0" } }, - "node_modules/web3-core-subscriptions": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.10.4.tgz", - "integrity": "sha512-o0lSQo/N/f7/L76C0HV63+S54loXiE9fUPfHFcTtpJRQNDBVsSDdWRdePbWwR206XlsBqD5VHApck1//jEafTw==", + "node_modules/web3-eth-accounts": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-4.2.1.tgz", + "integrity": "sha512-aOlEZFzqAgKprKs7+DGArU4r9b+ILBjThpeq42aY7LAQcP+mSpsWcQgbIRK3r/n3OwTYZ3aLPk0Ih70O/LwnYA==", "dependencies": { - "eventemitter3": "4.0.4", - "web3-core-helpers": "1.10.4" + "@ethereumjs/rlp": "^4.0.1", + "crc-32": "^1.2.2", + "ethereum-cryptography": "^2.0.0", + "web3-errors": "^1.3.0", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "web3-validator": "^2.0.6" }, "engines": { - "node": ">=8.0.0" + "node": ">=14", + "npm": ">=6.12.0" } }, - "node_modules/web3-core/node_modules/@types/bn.js": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", - "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", + "node_modules/web3-eth-accounts/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", "dependencies": { - "@types/node": "*" - } - }, - "node_modules/web3-core/node_modules/@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" - }, - "node_modules/web3-eth": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.10.4.tgz", - "integrity": "sha512-Sql2kYKmgt+T/cgvg7b9ce24uLS7xbFrxE4kuuor1zSCGrjhTJ5rRNG8gTJUkAJGKJc7KgnWmgW+cOfMBPUDSA==", - "dependencies": { - "web3-core": "1.10.4", - "web3-core-helpers": "1.10.4", - "web3-core-method": "1.10.4", - "web3-core-subscriptions": "1.10.4", - "web3-eth-abi": "1.10.4", - "web3-eth-accounts": "1.10.4", - "web3-eth-contract": "1.10.4", - "web3-eth-ens": "1.10.4", - "web3-eth-iban": "1.10.4", - "web3-eth-personal": "1.10.4", - "web3-net": "1.10.4", - "web3-utils": "1.10.4" - }, - "engines": { - "node": ">=8.0.0" + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" } }, - "node_modules/web3-eth-abi": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.10.4.tgz", - "integrity": "sha512-cZ0q65eJIkd/jyOlQPDjr8X4fU6CRL1eWgdLwbWEpo++MPU/2P4PFk5ZLAdye9T5Sdp+MomePPJ/gHjLMj2VfQ==", + "node_modules/web3-eth-contract": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-4.7.0.tgz", + "integrity": "sha512-fdStoBOjFyMHwlyJmSUt/BTDL1ATwKGmG3zDXQ/zTKlkkW/F/074ut0Vry4GuwSBg9acMHc0ycOiZx9ZKjNHsw==", "dependencies": { - "@ethersproject/abi": "^5.6.3", - "web3-utils": "1.10.4" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-eth-accounts": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.10.4.tgz", - "integrity": "sha512-ysy5sVTg9snYS7tJjxVoQAH6DTOTkRGR8emEVCWNGLGiB9txj+qDvSeT0izjurS/g7D5xlMAgrEHLK1Vi6I3yg==", - "dependencies": { - "@ethereumjs/common": "2.6.5", - "@ethereumjs/tx": "3.5.2", - "@ethereumjs/util": "^8.1.0", - "eth-lib": "0.2.8", - "scrypt-js": "^3.0.1", - "uuid": "^9.0.0", - "web3-core": "1.10.4", - "web3-core-helpers": "1.10.4", - "web3-core-method": "1.10.4", - "web3-utils": "1.10.4" + "@ethereumjs/rlp": "^5.0.2", + "web3-core": "^4.5.1", + "web3-errors": "^1.3.0", + "web3-eth": "^4.8.2", + "web3-eth-abi": "^4.2.3", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "web3-validator": "^2.0.6" }, "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-eth-accounts/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "node_modules/web3-eth-accounts/node_modules/eth-lib": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", - "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", - "dependencies": { - "bn.js": "^4.11.6", - "elliptic": "^6.4.0", - "xhr-request-promise": "^0.1.2" + "node": ">=14", + "npm": ">=6.12.0" } }, - "node_modules/web3-eth-accounts/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], + "node_modules/web3-eth-contract/node_modules/@ethereumjs/rlp": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.2.tgz", + "integrity": "sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA==", "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/web3-eth-contract": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.10.4.tgz", - "integrity": "sha512-Q8PfolOJ4eV9TvnTj1TGdZ4RarpSLmHnUnzVxZ/6/NiTfe4maJz99R0ISgwZkntLhLRtw0C7LRJuklzGYCNN3A==", - "dependencies": { - "@types/bn.js": "^5.1.1", - "web3-core": "1.10.4", - "web3-core-helpers": "1.10.4", - "web3-core-method": "1.10.4", - "web3-core-promievent": "1.10.4", - "web3-core-subscriptions": "1.10.4", - "web3-eth-abi": "1.10.4", - "web3-utils": "1.10.4" + "rlp": "bin/rlp.cjs" }, "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/web3-eth-contract/node_modules/@types/bn.js": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", - "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", - "dependencies": { - "@types/node": "*" + "node": ">=18" } }, "node_modules/web3-eth-ens": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.10.4.tgz", - "integrity": "sha512-LLrvxuFeVooRVZ9e5T6OWKVflHPFgrVjJ/jtisRWcmI7KN/b64+D/wJzXqgmp6CNsMQcE7rpmf4CQmJCrTdsgg==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-4.4.0.tgz", + "integrity": "sha512-DeyVIS060hNV9g8dnTx92syqvgbvPricE3MerCxe/DquNZT3tD8aVgFfq65GATtpCgDDJffO2bVeHp3XBemnSQ==", "dependencies": { - "content-hash": "^2.5.2", - "eth-ens-namehash": "2.0.8", - "web3-core": "1.10.4", - "web3-core-helpers": "1.10.4", - "web3-core-promievent": "1.10.4", - "web3-eth-abi": "1.10.4", - "web3-eth-contract": "1.10.4", - "web3-utils": "1.10.4" + "@adraffy/ens-normalize": "^1.8.8", + "web3-core": "^4.5.0", + "web3-errors": "^1.2.0", + "web3-eth": "^4.8.0", + "web3-eth-contract": "^4.5.0", + "web3-net": "^4.1.0", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.0", + "web3-validator": "^2.0.6" }, "engines": { - "node": ">=8.0.0" + "node": ">=14", + "npm": ">=6.12.0" } }, "node_modules/web3-eth-iban": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.10.4.tgz", - "integrity": "sha512-0gE5iNmOkmtBmbKH2aTodeompnNE8jEyvwFJ6s/AF6jkw9ky9Op9cqfzS56AYAbrqEFuClsqB/AoRves7LDELw==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-4.0.7.tgz", + "integrity": "sha512-8weKLa9KuKRzibC87vNLdkinpUE30gn0IGY027F8doeJdcPUfsa4IlBgNC4k4HLBembBB2CTU0Kr/HAOqMeYVQ==", "dependencies": { - "bn.js": "^5.2.1", - "web3-utils": "1.10.4" + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" }, "engines": { - "node": ">=8.0.0" + "node": ">=14", + "npm": ">=6.12.0" } }, "node_modules/web3-eth-personal": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.10.4.tgz", - "integrity": "sha512-BRa/hs6jU1hKHz+AC/YkM71RP3f0Yci1dPk4paOic53R4ZZG4MgwKRkJhgt3/GPuPliwS46f/i5A7fEGBT4F9w==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-4.0.8.tgz", + "integrity": "sha512-sXeyLKJ7ddQdMxz1BZkAwImjqh7OmKxhXoBNF3isDmD4QDpMIwv/t237S3q4Z0sZQamPa/pHebJRWVuvP8jZdw==", "dependencies": { - "@types/node": "^12.12.6", - "web3-core": "1.10.4", - "web3-core-helpers": "1.10.4", - "web3-core-method": "1.10.4", - "web3-net": "1.10.4", - "web3-utils": "1.10.4" + "web3-core": "^4.3.0", + "web3-eth": "^4.3.1", + "web3-rpc-methods": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" }, "engines": { - "node": ">=8.0.0" + "node": ">=14", + "npm": ">=6.12.0" } }, - "node_modules/web3-eth-personal/node_modules/@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" - }, "node_modules/web3-net": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.10.4.tgz", - "integrity": "sha512-mKINnhOOnZ4koA+yV2OT5s5ztVjIx7IY9a03w6s+yao/BUn+Luuty0/keNemZxTr1E8Ehvtn28vbOtW7Ids+Ow==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-4.1.0.tgz", + "integrity": "sha512-WWmfvHVIXWEoBDWdgKNYKN8rAy6SgluZ0abyRyXOL3ESr7ym7pKWbfP4fjApIHlYTh8tNqkrdPfM4Dyi6CA0SA==", "dependencies": { - "web3-core": "1.10.4", - "web3-core-method": "1.10.4", - "web3-utils": "1.10.4" + "web3-core": "^4.4.0", + "web3-rpc-methods": "^1.3.0", + "web3-types": "^1.6.0", + "web3-utils": "^4.3.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=14", + "npm": ">=6.12.0" } }, "node_modules/web3-provider-engine": { @@ -17339,17 +15354,18 @@ } }, "node_modules/web3-providers-http": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.10.4.tgz", - "integrity": "sha512-m2P5Idc8hdiO0l60O6DSCPw0kw64Zgi0pMjbEFRmxKIck2Py57RQMu4bxvkxJwkF06SlGaEQF8rFZBmuX7aagQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-4.2.0.tgz", + "integrity": "sha512-IPMnDtHB7dVwaB7/mMxAZzyq7d5ezfO1+Vw0bNfAeIi7gaDlJiggp85SdyAfOgov8AMUA/dyiY72kQ0KmjXKvQ==", "dependencies": { - "abortcontroller-polyfill": "^1.7.5", "cross-fetch": "^4.0.0", - "es6-promise": "^4.2.8", - "web3-core-helpers": "1.10.4" + "web3-errors": "^1.3.0", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1" }, "engines": { - "node": ">=8.0.0" + "node": ">=14", + "npm": ">=6.12.0" } }, "node_modules/web3-providers-http/node_modules/cross-fetch": { @@ -17361,72 +15377,129 @@ } }, "node_modules/web3-providers-ipc": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.10.4.tgz", - "integrity": "sha512-YRF/bpQk9z3WwjT+A6FI/GmWRCASgd+gC0si7f9zbBWLXjwzYAKG73bQBaFRAHex1hl4CVcM5WUMaQXf3Opeuw==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-4.0.7.tgz", + "integrity": "sha512-YbNqY4zUvIaK2MHr1lQFE53/8t/ejHtJchrWn9zVbFMGXlTsOAbNoIoZWROrg1v+hCBvT2c9z8xt7e/+uz5p1g==", + "optional": true, "dependencies": { - "oboe": "2.1.5", - "web3-core-helpers": "1.10.4" + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7" }, "engines": { - "node": ">=8.0.0" + "node": ">=14", + "npm": ">=6.12.0" } }, "node_modules/web3-providers-ws": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.10.4.tgz", - "integrity": "sha512-j3FBMifyuFFmUIPVQR4pj+t5ILhAexAui0opgcpu9R5LxQrLRUZxHSnU+YO25UycSOa/NAX8A+qkqZNpcFAlxA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-4.0.8.tgz", + "integrity": "sha512-goJdgata7v4pyzHRsg9fSegUG4gVnHZSHODhNnn6J93ykHkBI1nz4fjlGpcQLUMi4jAMz6SHl9Ibzs2jj9xqPw==", "dependencies": { - "eventemitter3": "4.0.4", - "web3-core-helpers": "1.10.4", - "websocket": "^1.0.32" + "@types/ws": "8.5.3", + "isomorphic-ws": "^5.0.0", + "web3-errors": "^1.2.0", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "ws": "^8.17.1" }, "engines": { - "node": ">=8.0.0" + "node": ">=14", + "npm": ">=6.12.0" } }, - "node_modules/web3-shh": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.10.4.tgz", - "integrity": "sha512-cOH6iFFM71lCNwSQrC3niqDXagMqrdfFW85hC9PFUrAr3PUrIem8TNstTc3xna2bwZeWG6OBy99xSIhBvyIACw==", - "hasInstallScript": true, + "node_modules/web3-rpc-methods": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/web3-rpc-methods/-/web3-rpc-methods-1.3.0.tgz", + "integrity": "sha512-/CHmzGN+IYgdBOme7PdqzF+FNeMleefzqs0LVOduncSaqsppeOEoskLXb2anSpzmQAP3xZJPaTrkQPWSJMORig==", "dependencies": { - "web3-core": "1.10.4", - "web3-core-method": "1.10.4", - "web3-core-subscriptions": "1.10.4", - "web3-net": "1.10.4" + "web3-core": "^4.4.0", + "web3-types": "^1.6.0", + "web3-validator": "^2.0.6" }, "engines": { - "node": ">=8.0.0" + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-rpc-providers": { + "version": "1.0.0-rc.2", + "resolved": "https://registry.npmjs.org/web3-rpc-providers/-/web3-rpc-providers-1.0.0-rc.2.tgz", + "integrity": "sha512-ocFIEXcBx/DYQ90HhVepTBUVnL9pGsZw8wyPb1ZINSenwYus9SvcFkjU1Hfvd/fXjuhAv2bUVch9vxvMx1mXAQ==", + "dependencies": { + "web3-errors": "^1.3.0", + "web3-providers-http": "^4.2.0", + "web3-providers-ws": "^4.0.8", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "web3-validator": "^2.0.6" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-types": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-types/-/web3-types-1.7.0.tgz", + "integrity": "sha512-nhXxDJ7a5FesRw9UG5SZdP/C/3Q2EzHGnB39hkAV+YGXDMgwxBXFWebQLfEzZzuArfHnvC0sQqkIHNwSKcVjdA==", + "engines": { + "node": ">=14", + "npm": ">=6.12.0" } }, "node_modules/web3-utils": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.4.tgz", - "integrity": "sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-4.3.1.tgz", + "integrity": "sha512-kGwOk8FxOLJ9DQC68yqNQc7AzN+k9YDLaW+ZjlAXs3qORhf8zXk5SxWAAGLbLykMs3vTeB0FTb1Exut4JEYfFA==", "dependencies": { - "@ethereumjs/util": "^8.1.0", - "bn.js": "^5.2.1", - "ethereum-bloom-filters": "^1.0.6", - "ethereum-cryptography": "^2.1.2", - "ethjs-unit": "0.1.6", - "number-to-bn": "1.7.0", - "randombytes": "^2.1.0", - "utf8": "3.0.0" + "ethereum-cryptography": "^2.0.0", + "eventemitter3": "^5.0.1", + "web3-errors": "^1.2.0", + "web3-types": "^1.7.0", + "web3-validator": "^2.0.6" }, "engines": { - "node": ">=8.0.0" + "node": ">=14", + "npm": ">=6.12.0" } }, "node_modules/web3-utils/node_modules/ethereum-cryptography": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz", - "integrity": "sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/web3-validator": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/web3-validator/-/web3-validator-2.0.6.tgz", + "integrity": "sha512-qn9id0/l1bWmvH4XfnG/JtGKKwut2Vokl6YXP5Kfg424npysmtRLe9DgiNBM9Op7QL/aSiaA0TVXibuIuWcizg==", + "dependencies": { + "ethereum-cryptography": "^2.0.0", + "util": "^0.12.5", + "web3-errors": "^1.2.0", + "web3-types": "^1.6.0", + "zod": "^3.21.4" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-validator/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", "dependencies": { - "@noble/curves": "1.3.0", - "@noble/hashes": "1.3.3", - "@scure/bip32": "1.3.3", - "@scure/bip39": "1.2.2" + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" } }, "node_modules/web3modal": { @@ -17591,35 +15664,6 @@ "node": ">=10.13.0" } }, - "node_modules/websocket": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", - "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", - "dependencies": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/websocket/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/websocket/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "node_modules/whatwg-encoding": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", @@ -17793,7 +15837,8 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, "node_modules/write-file-atomic": { "version": "4.0.2", @@ -17809,16 +15854,15 @@ } }, "node_modules/ws": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", - "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", - "dev": true, + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -17840,28 +15884,6 @@ "xtend": "^4.0.0" } }, - "node_modules/xhr-request": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", - "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", - "dependencies": { - "buffer-to-arraybuffer": "^0.0.5", - "object-assign": "^4.1.1", - "query-string": "^5.0.1", - "simple-get": "^2.7.0", - "timed-out": "^4.0.1", - "url-set-query": "^1.0.0", - "xhr": "^2.0.4" - } - }, - "node_modules/xhr-request-promise": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz", - "integrity": "sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==", - "dependencies": { - "xhr-request": "^1.1.0" - } - }, "node_modules/xhr2-cookies": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz", @@ -17918,14 +15940,6 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" }, - "node_modules/yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", - "engines": { - "node": ">=0.10.32" - } - }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -17979,6 +15993,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } }, "dependencies": { @@ -17988,6 +16010,11 @@ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", "dev": true }, + "@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==" + }, "@amplitude/analytics-browser": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.9.3.tgz", @@ -19416,246 +17443,11 @@ "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true }, - "@ethereumjs/common": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.5.tgz", - "integrity": "sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA==", - "requires": { - "crc-32": "^1.2.0", - "ethereumjs-util": "^7.1.5" - } - }, "@ethereumjs/rlp": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==" }, - "@ethereumjs/tx": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.5.2.tgz", - "integrity": "sha512-gQDNJWKrSDGu2w7w0PzVXVBNMzb7wwdDOmOqczmhNjqFxFuIbhVJDwiGEnxFNC2/b8ifcZzY7MLcluizohRzNw==", - "requires": { - "@ethereumjs/common": "^2.6.4", - "ethereumjs-util": "^7.1.5" - } - }, - "@ethereumjs/util": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", - "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", - "requires": { - "@ethereumjs/rlp": "^4.0.1", - "ethereum-cryptography": "^2.0.0", - "micro-ftch": "^0.3.1" - }, - "dependencies": { - "ethereum-cryptography": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz", - "integrity": "sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==", - "requires": { - "@noble/curves": "1.3.0", - "@noble/hashes": "1.3.3", - "@scure/bip32": "1.3.3", - "@scure/bip39": "1.2.2" - } - } - } - }, - "@ethersproject/abi": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", - "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", - "requires": { - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/abstract-provider": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", - "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/networks": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/web": "^5.7.0" - } - }, - "@ethersproject/abstract-signer": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", - "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", - "requires": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0" - } - }, - "@ethersproject/address": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", - "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/rlp": "^5.7.0" - } - }, - "@ethersproject/base64": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", - "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", - "requires": { - "@ethersproject/bytes": "^5.7.0" - } - }, - "@ethersproject/bignumber": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", - "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "bn.js": "^5.2.1" - } - }, - "@ethersproject/bytes": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", - "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", - "requires": { - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/constants": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", - "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", - "requires": { - "@ethersproject/bignumber": "^5.7.0" - } - }, - "@ethersproject/hash": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", - "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", - "requires": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/base64": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/keccak256": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", - "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "js-sha3": "0.8.0" - } - }, - "@ethersproject/logger": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", - "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==" - }, - "@ethersproject/networks": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", - "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", - "requires": { - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/properties": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", - "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", - "requires": { - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/rlp": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", - "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/signing-key": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", - "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "bn.js": "^5.2.1", - "elliptic": "6.5.4", - "hash.js": "1.1.7" - } - }, - "@ethersproject/strings": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", - "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/transactions": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", - "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", - "requires": { - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/rlp": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0" - } - }, - "@ethersproject/web": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", - "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", - "requires": { - "@ethersproject/base64": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, "@fortawesome/fontawesome-free": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.6.0.tgz", @@ -20270,17 +18062,17 @@ "integrity": "sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==" }, "@noble/curves": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", - "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", "requires": { - "@noble/hashes": "1.3.3" + "@noble/hashes": "1.4.0" } }, "@noble/hashes": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", - "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==" }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -20317,27 +18109,27 @@ } }, "@scure/base": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", - "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==" + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.8.tgz", + "integrity": "sha512-6CyAclxj3Nb0XT7GHK6K4zK6k2xJm6E4Ft0Ohjt4WgegiFUHEtFb2CGzmPmGBwoIhrLsqNLYfLr04Y1GePrzZg==" }, "@scure/bip32": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.3.tgz", - "integrity": "sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", "requires": { - "@noble/curves": "~1.3.0", - "@noble/hashes": "~1.3.2", - "@scure/base": "~1.1.4" + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" } }, "@scure/bip39": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.2.tgz", - "integrity": "sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", "requires": { - "@noble/hashes": "~1.3.2", - "@scure/base": "~1.1.4" + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" } }, "@sinclair/typebox": { @@ -20346,11 +18138,6 @@ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, - "@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==" - }, "@sindresorhus/merge-streams": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz", @@ -20375,14 +18162,6 @@ "@sinonjs/commons": "^2.0.0" } }, - "@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "requires": { - "defer-to-connect": "^2.0.1" - } - }, "@tarekraafat/autocomplete.js": { "version": "10.2.7", "resolved": "https://registry.npmjs.org/@tarekraafat/autocomplete.js/-/autocomplete.js-10.2.7.tgz", @@ -20443,17 +18222,6 @@ "@types/node": "*" } }, - "@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "requires": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, "@types/css-font-loading-module": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz", @@ -20474,11 +18242,6 @@ "@types/node": "*" } }, - "@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" - }, "@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -20526,14 +18289,6 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, - "@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "requires": { - "@types/node": "*" - } - }, "@types/node": { "version": "16.11.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", @@ -20547,14 +18302,6 @@ "@types/node": "*" } }, - "@types/responselike": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", - "requires": { - "@types/node": "*" - } - }, "@types/secp256k1": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", @@ -20575,6 +18322,14 @@ "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", "dev": true }, + "@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "requires": { + "@types/node": "*" + } + }, "@types/yargs": { "version": "17.0.11", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.11.tgz", @@ -21015,10 +18770,11 @@ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, - "abortcontroller-polyfill": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz", - "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==" + "abitype": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-0.7.1.tgz", + "integrity": "sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ==", + "requires": {} }, "abstract-leveldown": { "version": "2.6.3", @@ -21028,15 +18784,6 @@ "xtend": "~4.0.0" } }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, "acorn": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", @@ -21194,11 +18941,6 @@ "is-array-buffer": "^3.0.1" } }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, "array-includes": { "version": "3.1.7", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", @@ -21649,58 +19391,11 @@ "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.1.tgz", "integrity": "sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg==" }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, "bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, - "body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "requires": { - "side-channel": "^1.0.6" - } - } - } - }, "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -21885,11 +19580,6 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, - "buffer-to-arraybuffer": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", - "integrity": "sha512-3dthu5CYiVB1DEJp61FtApNnNndTckcqe4pFcLdvHtrpG+kcyekCJKg4MRiDcFW7A6AODnXB9U4dwQiCW5kzJQ==" - }, "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -21899,6 +19589,8 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", + "optional": true, + "peer": true, "requires": { "node-gyp-build": "^4.3.0" } @@ -21930,45 +19622,6 @@ } } }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "cacheable-lookup": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz", - "integrity": "sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==" - }, - "cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" - } - } - }, "call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -22102,11 +19755,6 @@ } } }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, "chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -22119,29 +19767,6 @@ "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", "dev": true }, - "cids": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", - "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", - "requires": { - "buffer": "^5.5.0", - "class-is": "^1.1.0", - "multibase": "~0.6.0", - "multicodec": "^1.0.0", - "multihashes": "~0.4.15" - }, - "dependencies": { - "multicodec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", - "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", - "requires": { - "buffer": "^5.6.0", - "varint": "^5.0.0" - } - } - } - }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -22157,11 +19782,6 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, - "class-is": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", - "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" - }, "clipboard": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz", @@ -22199,14 +19819,6 @@ "shallow-clone": "^3.0.0" } }, - "clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "requires": { - "mimic-response": "^1.0.0" - } - }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -22270,36 +19882,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { - "safe-buffer": "5.2.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } - } - }, - "content-hash": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", - "integrity": "sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==", - "requires": { - "cids": "^0.7.1", - "multicodec": "^0.5.5", - "multihashes": "^0.4.15" - } - }, - "content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" - }, "convert-source-map": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", @@ -22309,16 +19891,6 @@ "safe-buffer": "~5.1.1" } }, - "cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, "cookiejar": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", @@ -22364,15 +19936,6 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, "cosmiconfig": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", @@ -22789,15 +20352,6 @@ } } }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -22841,21 +20395,6 @@ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" }, - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "requires": { - "mimic-response": "^3.1.0" - }, - "dependencies": { - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" - } - } - }, "dedent": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", @@ -22883,11 +20422,6 @@ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true }, - "defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" - }, "deferred-leveldown": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz", @@ -22925,11 +20459,6 @@ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, "des.js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", @@ -22939,11 +20468,6 @@ "minimalistic-assert": "^1.0.0" } }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, "detect-browser": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.2.1.tgz", @@ -23057,20 +20581,15 @@ "safer-buffer": "^2.1.0" } }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, "electron-to-chromium": { "version": "1.5.13", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==" }, "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", + "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", "requires": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -23106,11 +20625,6 @@ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true }, - "encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" - }, "encoding": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", @@ -23133,14 +20647,6 @@ } } }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, "enhanced-resolve": { "version": "5.17.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", @@ -23283,51 +20789,11 @@ "is-symbol": "^1.0.2" } }, - "es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "requires": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, "escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -23793,24 +21259,6 @@ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, - "esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "dependencies": { - "type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - } - } - }, "espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -23874,11 +21322,6 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" - }, "eth-block-tracker": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/eth-block-tracker/-/eth-block-tracker-4.4.3.tgz", @@ -23892,22 +21335,6 @@ "safe-event-emitter": "^1.0.1" } }, - "eth-ens-namehash": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", - "integrity": "sha512-VWEI1+KJfz4Km//dadyvBBoBeSQ0MHTXPvr8UIXiLW6IanxvAV+DmlZAijZwAyggqGUfwQBeHf7tc9wzc1piSw==", - "requires": { - "idna-uts46-hx": "^2.3.1", - "js-sha3": "^0.5.7" - }, - "dependencies": { - "js-sha3": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", - "integrity": "sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==" - } - } - }, "eth-json-rpc-filters": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/eth-json-rpc-filters/-/eth-json-rpc-filters-4.2.2.tgz", @@ -23998,36 +21425,6 @@ } } }, - "eth-lib": { - "version": "0.1.29", - "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.29.tgz", - "integrity": "sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ==", - "requires": { - "bn.js": "^4.11.6", - "elliptic": "^6.4.0", - "nano-json-stream-parser": "^0.1.2", - "servify": "^0.1.12", - "ws": "^3.0.0", - "xhr-request-promise": "^0.1.2" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } - } - } - }, "eth-net-props": { "version": "1.0.41", "resolved": "https://registry.npmjs.org/eth-net-props/-/eth-net-props-1.0.41.tgz", @@ -24083,14 +21480,6 @@ } } }, - "ethereum-bloom-filters": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", - "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", - "requires": { - "js-sha3": "^0.8.0" - } - }, "ethereum-common": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", @@ -24251,28 +21640,6 @@ } } }, - "ethereumjs-util": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", - "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", - "requires": { - "@types/bn.js": "^5.1.0", - "bn.js": "^5.1.2", - "create-hash": "^1.1.2", - "ethereum-cryptography": "^0.1.3", - "rlp": "^2.2.4" - }, - "dependencies": { - "@types/bn.js": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", - "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", - "requires": { - "@types/node": "*" - } - } - } - }, "ethereumjs-vm": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz", @@ -24349,22 +21716,6 @@ } } }, - "ethjs-unit": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", - "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", - "requires": { - "bn.js": "4.11.6", - "number-to-bn": "1.7.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==" - } - } - }, "ethjs-util": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", @@ -24374,19 +21725,10 @@ "strip-hex-prefix": "1.0.0" } }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "eventemitter3": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" }, "events": { "version": "3.3.0", @@ -24438,87 +21780,6 @@ "jest-util": "^29.7.0" } }, - "express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "requires": { - "side-channel": "^1.0.6" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } - } - }, - "ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "requires": { - "type": "^2.7.2" - }, - "dependencies": { - "type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - } - } - }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -24652,35 +21913,6 @@ "to-regex-range": "^5.0.1" } }, - "finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, "find-cache-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", @@ -24748,45 +21980,12 @@ "mime-types": "^2.1.12" } }, - "form-data-encoder": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", - "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==" - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, "fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "dev": true }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" - }, - "fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "requires": { - "minipass": "^2.6.0" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -24864,7 +22063,8 @@ "get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true }, "get-symbol-description": { "version": "1.0.0", @@ -24980,30 +22180,11 @@ "get-intrinsic": "^1.1.3" } }, - "got": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/got/-/got-12.1.0.tgz", - "integrity": "sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==", - "requires": { - "@sindresorhus/is": "^4.6.0", - "@szmarczak/http-timer": "^5.0.1", - "@types/cacheable-request": "^6.0.2", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^6.0.4", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "form-data-encoder": "1.7.1", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^2.0.0" - } - }, "graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, "graphemer": { "version": "1.4.0", @@ -25134,28 +22315,6 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "http-https": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", - "integrity": "sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg==" - }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -25166,15 +22325,6 @@ "sshpk": "^1.7.0" } }, - "http2-wrapper": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", - "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", - "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.0" - } - }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -25201,14 +22351,6 @@ "resolved": "https://registry.npmjs.org/humps/-/humps-2.0.1.tgz", "integrity": "sha1-3QLqYIG9BWjcXQcxhEY5V7qe+ao=" }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, "icss-utils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", @@ -25216,21 +22358,6 @@ "dev": true, "requires": {} }, - "idna-uts46-hx": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", - "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", - "requires": { - "punycode": "2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA==" - } - } - }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -25322,11 +22449,6 @@ "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, "is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -25586,6 +22708,12 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, + "isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "requires": {} + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -27016,11 +24144,6 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, - "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -27085,14 +24208,6 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "requires": { - "graceful-fs": "^4.1.6" - } - }, "jsonify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", @@ -27119,14 +24234,6 @@ "readable-stream": "^3.6.0" } }, - "keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "requires": { - "json-buffer": "3.0.1" - } - }, "keyvaluestorage-interface": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz", @@ -27426,11 +24533,6 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, - "lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==" - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -27503,11 +24605,6 @@ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" - }, "memdown": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", @@ -27531,11 +24628,6 @@ } } }, - "merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" - }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -27611,16 +24703,6 @@ } } }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" - }, - "micro-ftch": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", - "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==" - }, "micromatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", @@ -27647,11 +24729,6 @@ } } }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -27671,11 +24748,6 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" - }, "min-document": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", @@ -27716,31 +24788,8 @@ "minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - }, - "dependencies": { - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - } + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true }, "mitt": { "version": "3.0.1", @@ -27755,24 +24804,6 @@ "rrweb": "2.0.0-alpha.13" } }, - "mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==" - }, - "mkdirp-promise": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", - "integrity": "sha512-Hepn5kb1lJPtVW84RFT40YG1OddBNTOVUZR2bzQUHc+Z03en8/3uX0+060JDhcEzyO08HmipsN9DcnFMxhIL9w==", - "requires": { - "mkdirp": "*" - } - }, - "mock-fs": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz", - "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==" - }, "moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -27783,49 +24814,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "multibase": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", - "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", - "requires": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - } - }, - "multicodec": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz", - "integrity": "sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==", - "requires": { - "varint": "^5.0.0" - } - }, - "multihashes": { - "version": "0.4.21", - "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", - "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", - "requires": { - "buffer": "^5.5.0", - "multibase": "^0.7.0", - "varint": "^5.0.0" - }, - "dependencies": { - "multibase": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", - "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", - "requires": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - } - } - } - }, - "nano-json-stream-parser": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", - "integrity": "sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew==" - }, "nanoassert": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-1.1.0.tgz", @@ -27851,22 +24839,12 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" - }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, "node-addon-api": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", @@ -27929,11 +24907,6 @@ "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", "dev": true }, - "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" - }, "npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -27952,22 +24925,6 @@ "boolbase": "^1.0.0" } }, - "number-to-bn": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", - "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", - "requires": { - "bn.js": "4.11.6", - "strip-hex-prefix": "1.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==" - } - } - }, "numeral": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", @@ -28053,26 +25010,11 @@ "es-abstract": "^1.22.1" } }, - "oboe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz", - "integrity": "sha512-zRFWiF+FoicxEs3jNI/WYUrVEgA7DeET/InK0XQuudGHRg8iIob3cNPrJTKaz4004uaA9Pbe+Dwa8iluhjLZWA==", - "requires": { - "http-https": "^1.0.0" - } - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { - "ee-first": "1.1.1" - } - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "requires": { "wrappy": "1" } @@ -28105,11 +25047,6 @@ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" }, - "p-cancelable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", - "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==" - }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -28191,11 +25128,6 @@ "entities": "^4.3.0" } }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -28227,11 +25159,6 @@ "tslib": "^1.10.0" } }, - "path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" - }, "pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -28776,15 +25703,6 @@ "react-is": "^16.8.1" } }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -28815,15 +25733,6 @@ } } }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -28973,16 +25882,6 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" }, - "query-string": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", - "requires": { - "decode-uri-component": "^0.2.0", - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - }, "querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -28995,11 +25894,6 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -29017,22 +25911,6 @@ "safe-buffer": "^5.1.0" } }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, "react": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", @@ -29244,11 +26122,6 @@ "supports-preserve-symlinks-flag": "^1.0.0" } }, - "resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" - }, "resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", @@ -29278,21 +26151,6 @@ "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", "dev": true }, - "responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "requires": { - "lowercase-keys": "^2.0.0" - }, - "dependencies": { - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" - } - } - }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -29530,53 +26388,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" }, - "send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, "serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", @@ -29586,29 +26397,6 @@ "randombytes": "^2.1.0" } }, - "serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "requires": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - } - }, - "servify": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", - "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", - "requires": { - "body-parser": "^1.16.0", - "cors": "^2.8.1", - "express": "^4.14.0", - "request": "^2.79.0", - "xhr": "^2.3.3" - } - }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -29648,11 +26436,6 @@ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, "sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", @@ -29708,31 +26491,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" - }, - "simple-get": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz", - "integrity": "sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw==", - "requires": { - "decompress-response": "^3.3.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - }, - "dependencies": { - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", - "requires": { - "mimic-response": "^1.0.0" - } - } - } - }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -29811,11 +26569,6 @@ } } }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, "stream-browserify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", @@ -29836,11 +26589,6 @@ "xtend": "^4.0.2" } }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==" - }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -30007,76 +26755,6 @@ "picocolors": "^1.0.0" } }, - "swarm-js": { - "version": "0.1.42", - "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.42.tgz", - "integrity": "sha512-BV7c/dVlA3R6ya1lMlSSNPLYrntt0LUq4YMgy3iwpCIc6rZnS5W2wUoctarZ5pXlpKtxDDf9hNziEkcfrxdhqQ==", - "requires": { - "bluebird": "^3.5.0", - "buffer": "^5.0.5", - "eth-lib": "^0.1.26", - "fs-extra": "^4.0.2", - "got": "^11.8.5", - "mime-types": "^2.1.16", - "mkdirp-promise": "^5.0.1", - "mock-fs": "^4.1.0", - "setimmediate": "^1.0.5", - "tar": "^4.0.2", - "xhr-request": "^1.0.1" - }, - "dependencies": { - "@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "requires": { - "defer-to-connect": "^2.0.0" - } - }, - "cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" - }, - "got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "requires": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - } - }, - "http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" - }, - "p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" - } - } - }, "sweetalert2": { "version": "11.12.4", "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.12.4.tgz", @@ -30094,40 +26772,6 @@ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true }, - "tar": { - "version": "4.4.19", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", - "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", - "requires": { - "chownr": "^1.1.4", - "fs-minipass": "^1.2.7", - "minipass": "^2.9.0", - "minizlib": "^1.3.3", - "mkdirp": "^0.5.5", - "safe-buffer": "^5.2.1", - "yallist": "^3.1.1" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "requires": { - "minimist": "^1.2.6" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - }, "terser": { "version": "5.27.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", @@ -30191,11 +26835,6 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==" - }, "tiny-emitter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", @@ -30226,11 +26865,6 @@ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - }, "tough-cookie": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", @@ -30307,11 +26941,6 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -30332,15 +26961,6 @@ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, "typed-array-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", @@ -30396,10 +27016,11 @@ "is-typedarray": "^1.0.0" } }, - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + "typescript": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "peer": true }, "unbox-primitive": { "version": "1.0.2", @@ -30447,16 +27068,6 @@ "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", "dev": true }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" - }, "update-browserslist-db": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", @@ -30513,24 +27124,16 @@ "requires-port": "^1.0.0" } }, - "url-set-query": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", - "integrity": "sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg==" - }, "utf-8-validate": { "version": "5.0.7", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", "integrity": "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==", + "optional": true, + "peer": true, "requires": { "node-gyp-build": "^4.3.0" } }, - "utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", - "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" - }, "util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -30548,11 +27151,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" - }, "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", @@ -30569,16 +27167,6 @@ "convert-source-map": "^1.6.0" } }, - "varint": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", - "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" - }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -30632,259 +27220,181 @@ } }, "web3": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3/-/web3-1.10.4.tgz", - "integrity": "sha512-kgJvQZjkmjOEKimx/tJQsqWfRDPTTcBfYPa9XletxuHLpHcXdx67w8EFn5AW3eVxCutE9dTVHgGa9VYe8vgsEA==", - "requires": { - "web3-bzz": "1.10.4", - "web3-core": "1.10.4", - "web3-eth": "1.10.4", - "web3-eth-personal": "1.10.4", - "web3-net": "1.10.4", - "web3-shh": "1.10.4", - "web3-utils": "1.10.4" - } - }, - "web3-bzz": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.10.4.tgz", - "integrity": "sha512-ZZ/X4sJ0Uh2teU9lAGNS8EjveEppoHNQiKlOXAjedsrdWuaMErBPdLQjXfcrYvN6WM6Su9PMsAxf3FXXZ+HwQw==", - "requires": { - "@types/node": "^12.12.6", - "got": "12.1.0", - "swarm-js": "^0.1.40" - }, - "dependencies": { - "@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" - } + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/web3/-/web3-4.12.1.tgz", + "integrity": "sha512-zIFUPdgo2uG5Vbl7C4KrTv8dmWKN3sGnY/GundbiJzcaJZDxaCyu3a5HXAcgUM1VvvsVb1zaUQAFPceq05/q/Q==", + "requires": { + "web3-core": "^4.5.1", + "web3-errors": "^1.3.0", + "web3-eth": "^4.8.2", + "web3-eth-abi": "^4.2.3", + "web3-eth-accounts": "^4.2.1", + "web3-eth-contract": "^4.7.0", + "web3-eth-ens": "^4.4.0", + "web3-eth-iban": "^4.0.7", + "web3-eth-personal": "^4.0.8", + "web3-net": "^4.1.0", + "web3-providers-http": "^4.2.0", + "web3-providers-ws": "^4.0.8", + "web3-rpc-methods": "^1.3.0", + "web3-rpc-providers": "^1.0.0-rc.2", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "web3-validator": "^2.0.6" } }, "web3-core": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.10.4.tgz", - "integrity": "sha512-B6elffYm81MYZDTrat7aEhnhdtVE3lDBUZft16Z8awYMZYJDbnykEbJVS+l3mnA7AQTnSDr/1MjWofGDLBJPww==", - "requires": { - "@types/bn.js": "^5.1.1", - "@types/node": "^12.12.6", - "bignumber.js": "^9.0.0", - "web3-core-helpers": "1.10.4", - "web3-core-method": "1.10.4", - "web3-core-requestmanager": "1.10.4", - "web3-utils": "1.10.4" - }, - "dependencies": { - "@types/bn.js": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", - "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", - "requires": { - "@types/node": "*" - } - }, - "@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" - } - } - }, - "web3-core-helpers": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.10.4.tgz", - "integrity": "sha512-r+L5ylA17JlD1vwS8rjhWr0qg7zVoVMDvWhajWA5r5+USdh91jRUYosp19Kd1m2vE034v7Dfqe1xYRoH2zvG0g==", - "requires": { - "web3-eth-iban": "1.10.4", - "web3-utils": "1.10.4" - } - }, - "web3-core-method": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.10.4.tgz", - "integrity": "sha512-uZTb7flr+Xl6LaDsyTeE2L1TylokCJwTDrIVfIfnrGmnwLc6bmTWCCrm71sSrQ0hqs6vp/MKbQYIYqUN0J8WyA==", - "requires": { - "@ethersproject/transactions": "^5.6.2", - "web3-core-helpers": "1.10.4", - "web3-core-promievent": "1.10.4", - "web3-core-subscriptions": "1.10.4", - "web3-utils": "1.10.4" - } - }, - "web3-core-promievent": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.10.4.tgz", - "integrity": "sha512-2de5WnJQ72YcIhYwV/jHLc4/cWJnznuoGTJGD29ncFQHAfwW/MItHFSVKPPA5v8AhJe+r6y4Y12EKvZKjQVBvQ==", - "requires": { - "eventemitter3": "4.0.4" - } - }, - "web3-core-requestmanager": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.10.4.tgz", - "integrity": "sha512-vqP6pKH8RrhT/2MoaU+DY/OsYK9h7HmEBNCdoMj+4ZwujQtw/Mq2JifjwsJ7gits7Q+HWJwx8q6WmQoVZAWugg==", - "requires": { - "util": "^0.12.5", - "web3-core-helpers": "1.10.4", - "web3-providers-http": "1.10.4", - "web3-providers-ipc": "1.10.4", - "web3-providers-ws": "1.10.4" - } - }, - "web3-core-subscriptions": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.10.4.tgz", - "integrity": "sha512-o0lSQo/N/f7/L76C0HV63+S54loXiE9fUPfHFcTtpJRQNDBVsSDdWRdePbWwR206XlsBqD5VHApck1//jEafTw==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-4.5.1.tgz", + "integrity": "sha512-mFMOO/IWdKsLL1o2whh3oJ0LCG9P3l5c4lpiMoVsVln3QXh/B0Gf8gW3aY8S+Ixm0OHyzFDXJVc2CodxqmI4Gw==", + "requires": { + "web3-errors": "^1.3.0", + "web3-eth-accounts": "^4.2.0", + "web3-eth-iban": "^4.0.7", + "web3-providers-http": "^4.2.0", + "web3-providers-ipc": "^4.0.7", + "web3-providers-ws": "^4.0.8", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "web3-validator": "^2.0.6" + } + }, + "web3-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/web3-errors/-/web3-errors-1.3.0.tgz", + "integrity": "sha512-j5JkAKCtuVMbY3F5PYXBqg1vWrtF4jcyyMY1rlw8a4PV67AkqlepjGgpzWJZd56Mt+TvHy6DA1F/3Id8LatDSQ==", "requires": { - "eventemitter3": "4.0.4", - "web3-core-helpers": "1.10.4" + "web3-types": "^1.7.0" } }, "web3-eth": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.10.4.tgz", - "integrity": "sha512-Sql2kYKmgt+T/cgvg7b9ce24uLS7xbFrxE4kuuor1zSCGrjhTJ5rRNG8gTJUkAJGKJc7KgnWmgW+cOfMBPUDSA==", - "requires": { - "web3-core": "1.10.4", - "web3-core-helpers": "1.10.4", - "web3-core-method": "1.10.4", - "web3-core-subscriptions": "1.10.4", - "web3-eth-abi": "1.10.4", - "web3-eth-accounts": "1.10.4", - "web3-eth-contract": "1.10.4", - "web3-eth-ens": "1.10.4", - "web3-eth-iban": "1.10.4", - "web3-eth-personal": "1.10.4", - "web3-net": "1.10.4", - "web3-utils": "1.10.4" + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-4.8.2.tgz", + "integrity": "sha512-DLV/fIMG6gBp/B0gv0+G4FzxZ4YCDQsY3lzqqv7avwh3uU7/O27aifCUcFd7Ye+3ixTqCjAvLEl9wYSeyG3zQw==", + "requires": { + "setimmediate": "^1.0.5", + "web3-core": "^4.5.0", + "web3-errors": "^1.2.1", + "web3-eth-abi": "^4.2.3", + "web3-eth-accounts": "^4.1.3", + "web3-net": "^4.1.0", + "web3-providers-ws": "^4.0.8", + "web3-rpc-methods": "^1.3.0", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "web3-validator": "^2.0.6" } }, "web3-eth-abi": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.10.4.tgz", - "integrity": "sha512-cZ0q65eJIkd/jyOlQPDjr8X4fU6CRL1eWgdLwbWEpo++MPU/2P4PFk5ZLAdye9T5Sdp+MomePPJ/gHjLMj2VfQ==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-4.2.3.tgz", + "integrity": "sha512-rPVwTn0O1CzbtfXwEfIjUP0W5Y7u1OFjugwKpSqJzPQE6+REBg6OELjomTGZBu+GThxHnv0rp15SOxvqp+tyXA==", "requires": { - "@ethersproject/abi": "^5.6.3", - "web3-utils": "1.10.4" + "abitype": "0.7.1", + "web3-errors": "^1.2.0", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "web3-validator": "^2.0.6" } }, "web3-eth-accounts": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.10.4.tgz", - "integrity": "sha512-ysy5sVTg9snYS7tJjxVoQAH6DTOTkRGR8emEVCWNGLGiB9txj+qDvSeT0izjurS/g7D5xlMAgrEHLK1Vi6I3yg==", - "requires": { - "@ethereumjs/common": "2.6.5", - "@ethereumjs/tx": "3.5.2", - "@ethereumjs/util": "^8.1.0", - "eth-lib": "0.2.8", - "scrypt-js": "^3.0.1", - "uuid": "^9.0.0", - "web3-core": "1.10.4", - "web3-core-helpers": "1.10.4", - "web3-core-method": "1.10.4", - "web3-utils": "1.10.4" + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-4.2.1.tgz", + "integrity": "sha512-aOlEZFzqAgKprKs7+DGArU4r9b+ILBjThpeq42aY7LAQcP+mSpsWcQgbIRK3r/n3OwTYZ3aLPk0Ih70O/LwnYA==", + "requires": { + "@ethereumjs/rlp": "^4.0.1", + "crc-32": "^1.2.2", + "ethereum-cryptography": "^2.0.0", + "web3-errors": "^1.3.0", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "web3-validator": "^2.0.6" }, "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "eth-lib": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", - "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", "requires": { - "bn.js": "^4.11.6", - "elliptic": "^6.4.0", - "xhr-request-promise": "^0.1.2" + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" } - }, - "uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" } } }, "web3-eth-contract": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.10.4.tgz", - "integrity": "sha512-Q8PfolOJ4eV9TvnTj1TGdZ4RarpSLmHnUnzVxZ/6/NiTfe4maJz99R0ISgwZkntLhLRtw0C7LRJuklzGYCNN3A==", - "requires": { - "@types/bn.js": "^5.1.1", - "web3-core": "1.10.4", - "web3-core-helpers": "1.10.4", - "web3-core-method": "1.10.4", - "web3-core-promievent": "1.10.4", - "web3-core-subscriptions": "1.10.4", - "web3-eth-abi": "1.10.4", - "web3-utils": "1.10.4" - }, - "dependencies": { - "@types/bn.js": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", - "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", - "requires": { - "@types/node": "*" - } + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-4.7.0.tgz", + "integrity": "sha512-fdStoBOjFyMHwlyJmSUt/BTDL1ATwKGmG3zDXQ/zTKlkkW/F/074ut0Vry4GuwSBg9acMHc0ycOiZx9ZKjNHsw==", + "requires": { + "@ethereumjs/rlp": "^5.0.2", + "web3-core": "^4.5.1", + "web3-errors": "^1.3.0", + "web3-eth": "^4.8.2", + "web3-eth-abi": "^4.2.3", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "web3-validator": "^2.0.6" + }, + "dependencies": { + "@ethereumjs/rlp": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.2.tgz", + "integrity": "sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA==" } } }, "web3-eth-ens": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.10.4.tgz", - "integrity": "sha512-LLrvxuFeVooRVZ9e5T6OWKVflHPFgrVjJ/jtisRWcmI7KN/b64+D/wJzXqgmp6CNsMQcE7rpmf4CQmJCrTdsgg==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-4.4.0.tgz", + "integrity": "sha512-DeyVIS060hNV9g8dnTx92syqvgbvPricE3MerCxe/DquNZT3tD8aVgFfq65GATtpCgDDJffO2bVeHp3XBemnSQ==", "requires": { - "content-hash": "^2.5.2", - "eth-ens-namehash": "2.0.8", - "web3-core": "1.10.4", - "web3-core-helpers": "1.10.4", - "web3-core-promievent": "1.10.4", - "web3-eth-abi": "1.10.4", - "web3-eth-contract": "1.10.4", - "web3-utils": "1.10.4" + "@adraffy/ens-normalize": "^1.8.8", + "web3-core": "^4.5.0", + "web3-errors": "^1.2.0", + "web3-eth": "^4.8.0", + "web3-eth-contract": "^4.5.0", + "web3-net": "^4.1.0", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.0", + "web3-validator": "^2.0.6" } }, "web3-eth-iban": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.10.4.tgz", - "integrity": "sha512-0gE5iNmOkmtBmbKH2aTodeompnNE8jEyvwFJ6s/AF6jkw9ky9Op9cqfzS56AYAbrqEFuClsqB/AoRves7LDELw==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-4.0.7.tgz", + "integrity": "sha512-8weKLa9KuKRzibC87vNLdkinpUE30gn0IGY027F8doeJdcPUfsa4IlBgNC4k4HLBembBB2CTU0Kr/HAOqMeYVQ==", "requires": { - "bn.js": "^5.2.1", - "web3-utils": "1.10.4" + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" } }, "web3-eth-personal": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.10.4.tgz", - "integrity": "sha512-BRa/hs6jU1hKHz+AC/YkM71RP3f0Yci1dPk4paOic53R4ZZG4MgwKRkJhgt3/GPuPliwS46f/i5A7fEGBT4F9w==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-4.0.8.tgz", + "integrity": "sha512-sXeyLKJ7ddQdMxz1BZkAwImjqh7OmKxhXoBNF3isDmD4QDpMIwv/t237S3q4Z0sZQamPa/pHebJRWVuvP8jZdw==", "requires": { - "@types/node": "^12.12.6", - "web3-core": "1.10.4", - "web3-core-helpers": "1.10.4", - "web3-core-method": "1.10.4", - "web3-net": "1.10.4", - "web3-utils": "1.10.4" - }, - "dependencies": { - "@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" - } + "web3-core": "^4.3.0", + "web3-eth": "^4.3.1", + "web3-rpc-methods": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" } }, "web3-net": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.10.4.tgz", - "integrity": "sha512-mKINnhOOnZ4koA+yV2OT5s5ztVjIx7IY9a03w6s+yao/BUn+Luuty0/keNemZxTr1E8Ehvtn28vbOtW7Ids+Ow==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-4.1.0.tgz", + "integrity": "sha512-WWmfvHVIXWEoBDWdgKNYKN8rAy6SgluZ0abyRyXOL3ESr7ym7pKWbfP4fjApIHlYTh8tNqkrdPfM4Dyi6CA0SA==", "requires": { - "web3-core": "1.10.4", - "web3-core-method": "1.10.4", - "web3-utils": "1.10.4" + "web3-core": "^4.4.0", + "web3-rpc-methods": "^1.3.0", + "web3-types": "^1.6.0", + "web3-utils": "^4.3.0" } }, "web3-provider-engine": { @@ -30968,14 +27478,14 @@ } }, "web3-providers-http": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.10.4.tgz", - "integrity": "sha512-m2P5Idc8hdiO0l60O6DSCPw0kw64Zgi0pMjbEFRmxKIck2Py57RQMu4bxvkxJwkF06SlGaEQF8rFZBmuX7aagQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-4.2.0.tgz", + "integrity": "sha512-IPMnDtHB7dVwaB7/mMxAZzyq7d5ezfO1+Vw0bNfAeIi7gaDlJiggp85SdyAfOgov8AMUA/dyiY72kQ0KmjXKvQ==", "requires": { - "abortcontroller-polyfill": "^1.7.5", "cross-fetch": "^4.0.0", - "es6-promise": "^4.2.8", - "web3-core-helpers": "1.10.4" + "web3-errors": "^1.3.0", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1" }, "dependencies": { "cross-fetch": { @@ -30989,59 +27499,103 @@ } }, "web3-providers-ipc": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.10.4.tgz", - "integrity": "sha512-YRF/bpQk9z3WwjT+A6FI/GmWRCASgd+gC0si7f9zbBWLXjwzYAKG73bQBaFRAHex1hl4CVcM5WUMaQXf3Opeuw==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-4.0.7.tgz", + "integrity": "sha512-YbNqY4zUvIaK2MHr1lQFE53/8t/ejHtJchrWn9zVbFMGXlTsOAbNoIoZWROrg1v+hCBvT2c9z8xt7e/+uz5p1g==", + "optional": true, "requires": { - "oboe": "2.1.5", - "web3-core-helpers": "1.10.4" + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7" } }, "web3-providers-ws": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.10.4.tgz", - "integrity": "sha512-j3FBMifyuFFmUIPVQR4pj+t5ILhAexAui0opgcpu9R5LxQrLRUZxHSnU+YO25UycSOa/NAX8A+qkqZNpcFAlxA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-4.0.8.tgz", + "integrity": "sha512-goJdgata7v4pyzHRsg9fSegUG4gVnHZSHODhNnn6J93ykHkBI1nz4fjlGpcQLUMi4jAMz6SHl9Ibzs2jj9xqPw==", + "requires": { + "@types/ws": "8.5.3", + "isomorphic-ws": "^5.0.0", + "web3-errors": "^1.2.0", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "ws": "^8.17.1" + } + }, + "web3-rpc-methods": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/web3-rpc-methods/-/web3-rpc-methods-1.3.0.tgz", + "integrity": "sha512-/CHmzGN+IYgdBOme7PdqzF+FNeMleefzqs0LVOduncSaqsppeOEoskLXb2anSpzmQAP3xZJPaTrkQPWSJMORig==", "requires": { - "eventemitter3": "4.0.4", - "web3-core-helpers": "1.10.4", - "websocket": "^1.0.32" + "web3-core": "^4.4.0", + "web3-types": "^1.6.0", + "web3-validator": "^2.0.6" } }, - "web3-shh": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.10.4.tgz", - "integrity": "sha512-cOH6iFFM71lCNwSQrC3niqDXagMqrdfFW85hC9PFUrAr3PUrIem8TNstTc3xna2bwZeWG6OBy99xSIhBvyIACw==", + "web3-rpc-providers": { + "version": "1.0.0-rc.2", + "resolved": "https://registry.npmjs.org/web3-rpc-providers/-/web3-rpc-providers-1.0.0-rc.2.tgz", + "integrity": "sha512-ocFIEXcBx/DYQ90HhVepTBUVnL9pGsZw8wyPb1ZINSenwYus9SvcFkjU1Hfvd/fXjuhAv2bUVch9vxvMx1mXAQ==", "requires": { - "web3-core": "1.10.4", - "web3-core-method": "1.10.4", - "web3-core-subscriptions": "1.10.4", - "web3-net": "1.10.4" + "web3-errors": "^1.3.0", + "web3-providers-http": "^4.2.0", + "web3-providers-ws": "^4.0.8", + "web3-types": "^1.7.0", + "web3-utils": "^4.3.1", + "web3-validator": "^2.0.6" } }, + "web3-types": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-types/-/web3-types-1.7.0.tgz", + "integrity": "sha512-nhXxDJ7a5FesRw9UG5SZdP/C/3Q2EzHGnB39hkAV+YGXDMgwxBXFWebQLfEzZzuArfHnvC0sQqkIHNwSKcVjdA==" + }, "web3-utils": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.4.tgz", - "integrity": "sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-4.3.1.tgz", + "integrity": "sha512-kGwOk8FxOLJ9DQC68yqNQc7AzN+k9YDLaW+ZjlAXs3qORhf8zXk5SxWAAGLbLykMs3vTeB0FTb1Exut4JEYfFA==", "requires": { - "@ethereumjs/util": "^8.1.0", - "bn.js": "^5.2.1", - "ethereum-bloom-filters": "^1.0.6", - "ethereum-cryptography": "^2.1.2", - "ethjs-unit": "0.1.6", - "number-to-bn": "1.7.0", - "randombytes": "^2.1.0", - "utf8": "3.0.0" + "ethereum-cryptography": "^2.0.0", + "eventemitter3": "^5.0.1", + "web3-errors": "^1.2.0", + "web3-types": "^1.7.0", + "web3-validator": "^2.0.6" + }, + "dependencies": { + "ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "requires": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + } + } + }, + "web3-validator": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/web3-validator/-/web3-validator-2.0.6.tgz", + "integrity": "sha512-qn9id0/l1bWmvH4XfnG/JtGKKwut2Vokl6YXP5Kfg424npysmtRLe9DgiNBM9Op7QL/aSiaA0TVXibuIuWcizg==", + "requires": { + "ethereum-cryptography": "^2.0.0", + "util": "^0.12.5", + "web3-errors": "^1.2.0", + "web3-types": "^1.6.0", + "zod": "^3.21.4" }, "dependencies": { "ethereum-cryptography": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz", - "integrity": "sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", "requires": { - "@noble/curves": "1.3.0", - "@noble/hashes": "1.3.3", - "@scure/bip32": "1.3.3", - "@scure/bip39": "1.2.2" + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" } } } @@ -31154,34 +27708,6 @@ "wildcard": "^2.0.0" } }, - "websocket": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", - "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", - "requires": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, "whatwg-encoding": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", @@ -31314,7 +27840,8 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, "write-file-atomic": { "version": "4.0.2", @@ -31327,10 +27854,9 @@ } }, "ws": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", - "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", - "dev": true, + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "requires": {} }, "xhr": { @@ -31344,28 +27870,6 @@ "xtend": "^4.0.0" } }, - "xhr-request": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", - "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", - "requires": { - "buffer-to-arraybuffer": "^0.0.5", - "object-assign": "^4.1.1", - "query-string": "^5.0.1", - "simple-get": "^2.7.0", - "timed-out": "^4.0.1", - "url-set-query": "^1.0.0", - "xhr": "^2.0.4" - } - }, - "xhr-request-promise": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz", - "integrity": "sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==", - "requires": { - "xhr-request": "^1.1.0" - } - }, "xhr2-cookies": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz", @@ -31412,11 +27916,6 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==" - }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -31457,6 +27956,11 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==" } } } diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 133619fce372..64ab6c6a49c2 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -78,7 +78,7 @@ "url": "^0.11.3", "util": "^0.12.5", "viewerjs": "^1.11.6", - "web3": "^1.10.4", + "web3": "^4.12.1", "web3modal": "^1.9.12", "xss": "^1.0.15" }, From d95332fc6b2c595c71aa7c6269ed621e66dfdb65 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 25 Sep 2024 13:26:06 +0300 Subject: [PATCH 173/363] chore: Add primary key to address_tags table (#10818) * chore: Add primary key to address_tags table * Remove create_chainlink_oracle_tag/0 function as it is unused --- .../explorer/lib/explorer/tags/address_tag.ex | 79 ++++++++++--------- .../explorer/tags/address_tag_cataloger.ex | 57 ++----------- .../lib/explorer/tags/address_to_tag.ex | 13 +-- ...923173516_address_tags_add_primary_key.exs | 9 +++ 4 files changed, 66 insertions(+), 92 deletions(-) create mode 100644 apps/explorer/priv/repo/migrations/20240923173516_address_tags_add_primary_key.exs diff --git a/apps/explorer/lib/explorer/tags/address_tag.ex b/apps/explorer/lib/explorer/tags/address_tag.ex index 65d7cc008baf..d6c20c2d2694 100644 --- a/apps/explorer/lib/explorer/tags/address_tag.ex +++ b/apps/explorer/lib/explorer/tags/address_tag.ex @@ -1,6 +1,6 @@ defmodule Explorer.Tags.AddressTag do @moduledoc """ - Represents a Tag object. + Represents an address Tag object. """ use Explorer.Schema @@ -9,11 +9,12 @@ defmodule Explorer.Tags.AddressTag do import Ecto.Query, only: [ - from: 2 + from: 2, + select: 3 ] alias Explorer.Repo - alias Explorer.Tags.{AddressTag, AddressToTag} + alias Explorer.Tags.AddressToTag @typedoc """ * `:id` - id of Tag @@ -21,7 +22,7 @@ defmodule Explorer.Tags.AddressTag do * `:display_name` - Label's display name """ typed_schema "address_tags" do - field(:label, :string, null: false) + field(:label, :string, primary_key: true, null: false) field(:display_name, :string, null: false) has_many(:tag_id, AddressToTag, foreign_key: :id) @@ -38,55 +39,61 @@ defmodule Explorer.Tags.AddressTag do |> unique_constraint(:label, name: :address_tags_label_index) end - def set_tag(name, display_name) do - tag = get_tag(name) + @spec set(String.t() | nil, String.t() | nil) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()} | :invalid + def set(name, display_name) + + def set(nil, _), do: :invalid + + def set(_, nil), do: :invalid + + def set(name, display_name) do + tag = get_by_label(name) if tag do tag - |> AddressTag.changeset(%{display_name: display_name}) + |> __MODULE__.changeset(%{display_name: display_name}) |> Repo.update() else - %AddressTag{} - |> AddressTag.changeset(%{label: name, display_name: display_name}) + %__MODULE__{} + |> __MODULE__.changeset(%{label: name, display_name: display_name}) |> Repo.insert() end end - def get_tag_id(nil), do: nil - - def get_tag_id(label) do - query = - from( - tag in AddressTag, - where: tag.label == ^label, - select: tag.id - ) + @doc """ + Fetches AddressTag.t() by label name from the DB + """ + @spec get_id_by_label(String.t()) :: non_neg_integer() + def get_id_by_label(nil), do: nil - query + def get_id_by_label(label) do + label + |> get_by_label_query() + |> select([tag], tag.id) |> Repo.one() end - def get_tag(nil), do: nil + @doc """ + Fetches all AddressTag.t() from the DB + """ + @spec get_all() :: __MODULE__.t() + def get_all do + __MODULE__ + |> Repo.all() + end - def get_tag(label) do - query = - from( - tag in AddressTag, - where: tag.label == ^label - ) + defp get_by_label(nil), do: nil - query + defp get_by_label(label) do + label + |> get_by_label_query() |> Repo.one() end - def get_all_tags do - query = - from( - tag in AddressTag, - select: tag - ) - - query - |> Repo.all() + defp get_by_label_query(label) do + from( + tag in __MODULE__, + where: tag.label == ^label + ) end end diff --git a/apps/explorer/lib/explorer/tags/address_tag_cataloger.ex b/apps/explorer/lib/explorer/tags/address_tag_cataloger.ex index da54d81e2d88..5295a29d30f6 100644 --- a/apps/explorer/lib/explorer/tags/address_tag_cataloger.ex +++ b/apps/explorer/lib/explorer/tags/address_tag_cataloger.ex @@ -9,7 +9,6 @@ defmodule Explorer.Tags.AddressTag.Cataloger do alias Explorer.EnvVarTranslator alias Explorer.Tags.{AddressTag, AddressToTag} alias Explorer.Validator.MetadataRetriever - alias Poison.Parser def start_link(_) do GenServer.start_link(__MODULE__, :ok, name: __MODULE__) @@ -24,9 +23,6 @@ defmodule Explorer.Tags.AddressTag.Cataloger do @impl GenServer def handle_info(:fetch_tags, state) do - # set tag for every chainlink oracle - create_chainlink_oracle_tag() - create_new_tags() send(self(), :bind_addresses) @@ -47,7 +43,7 @@ defmodule Explorer.Tags.AddressTag.Cataloger do # set L2 tag set_l2_tag() - all_tags = AddressTag.get_all_tags() + all_tags = AddressTag.get_all() all_tags |> Enum.each(fn %{label: tag_name} -> @@ -68,21 +64,6 @@ defmodule Explorer.Tags.AddressTag.Cataloger do |> String.replace(".", "_") end - def create_chainlink_oracle_tag do - chainlink_oracles_config = Application.get_env(:block_scout_web, :chainlink_oracles) - - if chainlink_oracles_config do - chainlink_oracles_config - |> Parser.parse!(%{keys: :atoms!}) - |> Enum.each(fn %{:name => name, :address => address} -> - chainlink_tag_name = "chainlink oracle #{String.downcase(name)}" - AddressTag.set_tag(chainlink_tag_name, chainlink_tag_name) - tag_id = AddressTag.get_tag_id(chainlink_tag_name) - AddressToTag.set_tag_to_addresses(tag_id, [address]) - end) - end - end - defp set_tag_for_multiple_env_var_addresses(env_vars, tag) do addresses = env_vars @@ -92,7 +73,7 @@ defmodule Explorer.Tags.AddressTag.Cataloger do |> String.downcase() end) - tag_id = AddressTag.get_tag_id(tag) + tag_id = AddressTag.get_id_by_label(tag) AddressToTag.set_tag_to_addresses(tag_id, addresses) end @@ -112,23 +93,23 @@ defmodule Explorer.Tags.AddressTag.Cataloger do end) end) - tag_id = AddressTag.get_tag_id(tag) + tag_id = AddressTag.get_id_by_label(tag) AddressToTag.set_tag_to_addresses(tag_id, addresses) end - def create_new_tags do + defp create_new_tags do tags = EnvVarTranslator.map_array_env_var_to_list(:new_tags) tags |> Enum.each(fn %{tag: tag_name, title: tag_display_name} -> - AddressTag.set_tag(tag_name, tag_display_name) + AddressTag.set(tag_name, tag_display_name) end) end defp set_tag_for_env_var_multiple_addresses(env_var, tag) do addresses = env_var_string_array_to_list(env_var) - tag_id = AddressTag.get_tag_id(tag) + tag_id = AddressTag.get_id_by_label(tag) AddressToTag.set_tag_to_addresses(tag_id, addresses) end @@ -152,7 +133,7 @@ defmodule Explorer.Tags.AddressTag.Cataloger do defp set_validator_tag do validators = MetadataRetriever.fetch_validators_list() FetchValidatorInfoOnDemand.trigger_fetch(validators) - tag_id = AddressTag.get_tag_id("validator") + tag_id = AddressTag.get_id_by_label("validator") AddressToTag.set_tag_to_addresses(tag_id, validators) end @@ -177,28 +158,4 @@ defmodule Explorer.Tags.AddressTag.Cataloger do defp set_l2_tag do set_tag_for_multiple_env_var_addresses(["CUSTOM_CONTRACT_ADDRESSES_AOX"], "l2") end - - def set_chainlink_oracle_tag do - chainlink_oracles = chainlink_oracles_list() - - tag_id = AddressTag.get_tag_id("chainlink oracle") - AddressToTag.set_tag_to_addresses(tag_id, chainlink_oracles) - end - - defp chainlink_oracles_list do - chainlink_oracles_config = Application.get_env(:block_scout_web, :chainlink_oracles) - - if chainlink_oracles_config do - try do - chainlink_oracles_config - |> Parser.parse!(%{keys: :atoms!}) - |> Enum.map(fn %{:name => _name, :address => address} -> address end) - rescue - _ -> - [] - end - else - [] - end - end end diff --git a/apps/explorer/lib/explorer/tags/address_to_tag.ex b/apps/explorer/lib/explorer/tags/address_to_tag.ex index 729b5eb50594..6f83a0cdceb5 100644 --- a/apps/explorer/lib/explorer/tags/address_to_tag.ex +++ b/apps/explorer/lib/explorer/tags/address_to_tag.ex @@ -1,6 +1,6 @@ defmodule Explorer.Tags.AddressToTag do @moduledoc """ - Represents ann Address to Tag relation. + Represents Address to Tag relation. """ use Explorer.Schema @@ -9,7 +9,7 @@ defmodule Explorer.Tags.AddressToTag do alias Explorer.{Chain, Repo} alias Explorer.Chain.{Address, Hash} - alias Explorer.Tags.{AddressTag, AddressToTag} + alias Explorer.Tags.AddressTag # Notation.import_types(BlockScoutWeb.GraphQL.Schema.Types) @@ -53,7 +53,7 @@ defmodule Explorer.Tags.AddressToTag do defp get_address_hashes_mapped_to_tag(tag_id) do query = from( - att in AddressToTag, + att in __MODULE__, where: att.tag_id == ^tag_id, select: att.address_hash ) @@ -62,6 +62,7 @@ defmodule Explorer.Tags.AddressToTag do |> Repo.all() end + @spec set_tag_to_addresses(non_neg_integer(), list()) :: any() def set_tag_to_addresses(tag_id, address_hash_string_list) do current_address_hashes = get_address_hashes_mapped_to_tag(tag_id) @@ -105,10 +106,10 @@ defmodule Explorer.Tags.AddressToTag do end) |> Enum.filter(&(!is_nil(&1))) - if not Enum.empty?(addresses_to_delete) do + unless Enum.empty?(addresses_to_delete) do delete_query_base = from( - att in AddressToTag, + att in __MODULE__, where: att.tag_id == ^tag_id ) @@ -119,7 +120,7 @@ defmodule Explorer.Tags.AddressToTag do Repo.delete_all(delete_query) end - Repo.insert_all(AddressToTag, changeset_to_add_list, + Repo.insert_all(__MODULE__, changeset_to_add_list, on_conflict: :nothing, conflict_target: [:address_hash, :tag_id] ) diff --git a/apps/explorer/priv/repo/migrations/20240923173516_address_tags_add_primary_key.exs b/apps/explorer/priv/repo/migrations/20240923173516_address_tags_add_primary_key.exs new file mode 100644 index 000000000000..97a93144c57a --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20240923173516_address_tags_add_primary_key.exs @@ -0,0 +1,9 @@ +defmodule Explorer.Repo.Migrations.AddressTagsAddPrimaryKey do + use Ecto.Migration + + def change do + alter table(:address_tags) do + modify(:label, :string, null: false, primary_key: true) + end + end +end From 3fffd46197b251d032c114de24ca9e358c3c979a Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:29:34 +0400 Subject: [PATCH 174/363] fix: Revert the deletion of deriving current token balances (#10811) --- .../explorer/chain/import/runner/blocks.ex | 86 ++++++-- .../sanitize_missing_token_balances.ex | 38 ++-- ...issing_token_balances_migration_status.exs | 7 + .../chain/import/runner/blocks_test.exs | 96 ++++++++- .../test/explorer/chain/import_test.exs | 184 +++++++++++++++++- .../sanitize_missing_token_balances_test.exs | 20 +- 6 files changed, 394 insertions(+), 37 deletions(-) create mode 100644 apps/explorer/priv/repo/migrations/20240923135258_reset_sanitize_missing_token_balances_migration_status.exs diff --git a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex index 3b88982e2861..0eab751f7436 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex @@ -530,10 +530,8 @@ defmodule Explorer.Chain.Import.Runner.Blocks do select: map(ctb, [ :address_hash, - :block_number, :token_contract_address_hash, :token_id, - :token_type, # Used to determine if `address_hash` was a holder of `token_contract_address_hash` before # `address_current_token_balance` is deleted in `update_tokens_holder_count`. @@ -566,28 +564,43 @@ defmodule Explorer.Chain.Import.Runner.Blocks do %{timeout: timeout} = options ) when is_list(deleted_address_current_token_balances) do - new_current_token_balances_placeholders = - Enum.map(deleted_address_current_token_balances, fn deleted_balance -> - now = DateTime.utc_now() + final_query = derive_address_current_token_balances_grouped_query(deleted_address_current_token_balances) - %{ - address_hash: deleted_balance.address_hash, - token_contract_address_hash: deleted_balance.token_contract_address_hash, - token_id: deleted_balance.token_id, - token_type: deleted_balance.token_type, - block_number: deleted_balance.block_number, - value: nil, - value_fetched_at: nil, - inserted_at: now, - updated_at: now - } - end) + new_current_token_balance_query = + from(new_current_token_balance in subquery(final_query), + inner_join: tb in Address.TokenBalance, + on: + tb.address_hash == new_current_token_balance.address_hash and + tb.token_contract_address_hash == new_current_token_balance.token_contract_address_hash and + ((is_nil(tb.token_id) and is_nil(new_current_token_balance.token_id)) or + (tb.token_id == new_current_token_balance.token_id and + not is_nil(tb.token_id) and not is_nil(new_current_token_balance.token_id))) and + tb.block_number == new_current_token_balance.block_number, + select: %{ + address_hash: new_current_token_balance.address_hash, + token_contract_address_hash: new_current_token_balance.token_contract_address_hash, + token_id: new_current_token_balance.token_id, + token_type: tb.token_type, + block_number: new_current_token_balance.block_number, + value: tb.value, + value_fetched_at: tb.value_fetched_at, + inserted_at: over(min(tb.inserted_at), :w), + updated_at: over(max(tb.updated_at), :w) + }, + windows: [ + w: [partition_by: [tb.address_hash, tb.token_contract_address_hash, tb.token_id]] + ] + ) + + current_token_balance = + new_current_token_balance_query + |> repo.all() timestamps = Import.timestamps() result = CurrentTokenBalances.insert_changes_list_with_and_without_token_id( - new_current_token_balances_placeholders, + current_token_balance, repo, timestamps, timeout, @@ -812,6 +825,43 @@ defmodule Explorer.Chain.Import.Runner.Blocks do ) end + defp derive_address_current_token_balances_grouped_query(deleted_address_current_token_balances) do + initial_query = + from(tb in Address.TokenBalance, + select: %{ + address_hash: tb.address_hash, + token_contract_address_hash: tb.token_contract_address_hash, + token_id: tb.token_id, + block_number: max(tb.block_number) + }, + group_by: [tb.address_hash, tb.token_contract_address_hash, tb.token_id] + ) + + Enum.reduce(deleted_address_current_token_balances, initial_query, fn %{ + address_hash: address_hash, + token_contract_address_hash: + token_contract_address_hash, + token_id: token_id + }, + acc_query -> + if token_id do + from(tb in acc_query, + or_where: + tb.address_hash == ^address_hash and + tb.token_contract_address_hash == ^token_contract_address_hash and + tb.token_id == ^token_id + ) + else + from(tb in acc_query, + or_where: + tb.address_hash == ^address_hash and + tb.token_contract_address_hash == ^token_contract_address_hash and + is_nil(tb.token_id) + ) + end + end) + end + # `block_rewards` are linked to `blocks.hash`, but fetched by `blocks.number`, so when a block with the same number is # inserted, the old block rewards need to be deleted, so that the old and new rewards aren't combined. defp delete_rewards(repo, blocks_changes, %{timeout: timeout}) do diff --git a/apps/explorer/lib/explorer/migrator/sanitize_missing_token_balances.ex b/apps/explorer/lib/explorer/migrator/sanitize_missing_token_balances.ex index 28957fa3bf32..5ec34fa3bfc4 100644 --- a/apps/explorer/lib/explorer/migrator/sanitize_missing_token_balances.ex +++ b/apps/explorer/lib/explorer/migrator/sanitize_missing_token_balances.ex @@ -1,7 +1,7 @@ defmodule Explorer.Migrator.SanitizeMissingTokenBalances do @moduledoc """ - Set value and value_fetched_at to nil for those token balances that are already filled but their - current token balances are not so the token balance fetcher could re-fetch them. + Deletes empty current token balances if the related highest block_number historical token balance is filled. + Set value and value_fetched_at to nil for those token balances so the token balance fetcher could re-fetch them. """ use Explorer.Migrator.FillingMigration @@ -23,7 +23,7 @@ defmodule Explorer.Migrator.SanitizeMissingTokenBalances do ids = unprocessed_data_query() - |> select([_ctb, tb], tb.id) + |> select([ctb, tb], {ctb.id, tb.id}) |> limit(^limit) |> Repo.all(timeout: :infinity) @@ -38,22 +38,34 @@ defmodule Explorer.Migrator.SanitizeMissingTokenBalances do on: ctb.address_hash == tb.address_hash and ctb.token_contract_address_hash == tb.token_contract_address_hash and - ctb.block_number == tb.block_number and ((is_nil(ctb.token_id) and is_nil(tb.token_id)) or ctb.token_id == tb.token_id), where: is_nil(ctb.value) or is_nil(ctb.value_fetched_at), - where: not is_nil(tb.value) and not is_nil(tb.value_fetched_at) + where: not is_nil(tb.value) and not is_nil(tb.value_fetched_at), + distinct: ctb.id, + order_by: [asc: ctb.id, desc: tb.block_number] ) end @impl FillingMigration - def update_batch(token_balance_ids) do - query = - from(tb in TokenBalance, - where: tb.id in ^token_balance_ids, - update: [set: [value: nil, value_fetched_at: nil]] - ) - - Repo.update_all(query, [], timeout: :infinity) + def update_batch(identifiers) do + {ctb_ids, tb_ids} = + Enum.reduce(identifiers, {[], []}, fn {ctb_id, tb_id}, {ctb_acc, tb_acc} -> + {[ctb_id | ctb_acc], [tb_id | tb_acc]} + end) + + Repo.transaction(fn -> + ctb_query = from(ctb in CurrentTokenBalance, where: ctb.id in ^ctb_ids) + + Repo.delete_all(ctb_query, timeout: :infinity) + + tb_query = + from(tb in TokenBalance, + where: tb.id in ^tb_ids, + update: [set: [value: nil, value_fetched_at: nil]] + ) + + Repo.update_all(tb_query, [], timeout: :infinity) + end) end @impl FillingMigration diff --git a/apps/explorer/priv/repo/migrations/20240923135258_reset_sanitize_missing_token_balances_migration_status.exs b/apps/explorer/priv/repo/migrations/20240923135258_reset_sanitize_missing_token_balances_migration_status.exs new file mode 100644 index 000000000000..d96bab5f2ace --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20240923135258_reset_sanitize_missing_token_balances_migration_status.exs @@ -0,0 +1,7 @@ +defmodule Explorer.Repo.Migrations.ResetSanitizeMissingTokenBalancesMigrationStatus do + use Ecto.Migration + + def change do + execute("DELETE FROM migrations_status WHERE migration_name = 'sanitize_missing_token_balances'") + 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 2ba66c34640c..ee68a91eeef8 100644 --- a/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs +++ b/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs @@ -219,7 +219,7 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do ] }} = run_block_consensus_change(block, true, options) - assert %{value: nil} = Repo.one(Address.CurrentTokenBalance) + assert count(Address.CurrentTokenBalance) == 0 end test "delete_address_current_token_balances does not delete rows with matching block number when consensus is false", @@ -238,6 +238,100 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do assert count(Address.CurrentTokenBalance) == count end + test "derive_address_current_token_balances inserts rows if there is an address_token_balance left for the rows deleted by delete_address_current_token_balances", + %{consensus_block: %{number: block_number} = block, options: options} do + token = insert(:token) + token_contract_address_hash = token.contract_address_hash + + %Address{hash: address_hash} = + insert_address_with_token_balances(%{ + previous: %{value: 1}, + current: %{block_number: block_number, value: 2}, + token_contract_address_hash: token_contract_address_hash + }) + + # Token must exist with non-`nil` `holder_count` for `blocks_update_token_holder_counts` to update + update_holder_count!(token_contract_address_hash, 1) + + assert count(Address.TokenBalance) == 2 + assert count(Address.CurrentTokenBalance) == 1 + + previous_block_number = block_number - 1 + + insert(:block, number: block_number, consensus: true) + + assert {:ok, + %{ + delete_address_current_token_balances: [ + %{ + address_hash: ^address_hash, + token_contract_address_hash: ^token_contract_address_hash + } + ], + delete_address_token_balances: [ + %{ + address_hash: ^address_hash, + token_contract_address_hash: ^token_contract_address_hash, + block_number: ^block_number + } + ], + derive_address_current_token_balances: [ + %{ + address_hash: ^address_hash, + token_contract_address_hash: ^token_contract_address_hash, + block_number: ^previous_block_number + } + ], + # no updates because it both deletes and derives a holder + blocks_update_token_holder_counts: [] + }} = run_block_consensus_change(block, true, options) + + assert count(Address.TokenBalance) == 1 + assert count(Address.CurrentTokenBalance) == 1 + + previous_value = Decimal.new(1) + + assert %Address.CurrentTokenBalance{block_number: ^previous_block_number, value: ^previous_value} = + Repo.get_by(Address.CurrentTokenBalance, + address_hash: address_hash, + token_contract_address_hash: token_contract_address_hash + ) + end + + test "a non-holder reverting to a holder increases the holder_count", + %{consensus_block: %{hash: block_hash, miner_hash: miner_hash, number: block_number}, options: options} do + token = insert(:token) + token_contract_address_hash = token.contract_address_hash + + non_holder_reverts_to_holder(%{ + current: %{block_number: block_number}, + token_contract_address_hash: token_contract_address_hash + }) + + # Token must exist with non-`nil` `holder_count` for `blocks_update_token_holder_counts` to update + update_holder_count!(token_contract_address_hash, 0) + + insert(:block, number: block_number, consensus: true) + + block_params = params_for(:block, hash: block_hash, miner_hash: miner_hash, number: block_number, consensus: true) + + %Ecto.Changeset{valid?: true, changes: block_changes} = Block.changeset(%Block{}, block_params) + changes_list = [block_changes] + + assert {:ok, + %{ + blocks_update_token_holder_counts: [ + %{ + contract_address_hash: ^token_contract_address_hash, + holder_count: 1 + } + ] + }} = + Multi.new() + |> Blocks.run(changes_list, options) + |> Repo.transaction() + end + test "a holder reverting to a non-holder decreases the holder_count", %{consensus_block: %{hash: block_hash, miner_hash: miner_hash, number: block_number}, options: options} do token = insert(:token) diff --git a/apps/explorer/test/explorer/chain/import_test.exs b/apps/explorer/test/explorer/chain/import_test.exs index d08b7595237e..d79e0869b289 100644 --- a/apps/explorer/test/explorer/chain/import_test.exs +++ b/apps/explorer/test/explorer/chain/import_test.exs @@ -2145,11 +2145,12 @@ defmodule Explorer.Chain.ImportTest do } }) - assert %{value: nil} = + assert is_nil( Repo.get_by(Address.CurrentTokenBalance, address_hash: address_hash, token_contract_address_hash: token_contract_address_hash ) + ) assert is_nil( Repo.get_by(Address.TokenBalance, @@ -2159,5 +2160,186 @@ defmodule Explorer.Chain.ImportTest do ) ) end + + test "address_current_token_balances is derived during reorgs" do + %Block{number: block_number} = insert(:block, consensus: true) + previous_block_number = block_number - 1 + + %Address.TokenBalance{ + address_hash: address_hash, + token_contract_address_hash: token_contract_address_hash, + value: previous_value, + block_number: previous_block_number + } = insert(:token_balance, block_number: previous_block_number) + + address = Repo.get(Address, address_hash) + + %Address.TokenBalance{ + address_hash: ^address_hash, + token_contract_address_hash: token_contract_address_hash, + value: current_value, + block_number: ^block_number + } = + insert(:token_balance, + address: address, + token_contract_address_hash: token_contract_address_hash, + block_number: block_number + ) + + refute current_value == previous_value + + %Address.CurrentTokenBalance{ + address_hash: ^address_hash, + token_contract_address_hash: ^token_contract_address_hash, + block_number: ^block_number + } = + insert(:address_current_token_balance, + address: address, + token_contract_address_hash: token_contract_address_hash, + block_number: block_number, + value: current_value + ) + + miner_hash_after = address_hash() + from_address_hash_after = address_hash() + block_hash_after = block_hash() + + assert {:ok, _} = + Import.all(%{ + addresses: %{ + params: [ + %{hash: miner_hash_after}, + %{hash: from_address_hash_after} + ] + }, + blocks: %{ + params: [ + %{ + consensus: true, + difficulty: 1, + gas_limit: 1, + gas_used: 1, + hash: block_hash_after, + miner_hash: miner_hash_after, + nonce: 1, + number: block_number, + parent_hash: block_hash(), + size: 1, + timestamp: Timex.parse!("2019-01-01T02:00:00Z", "{ISO:Extended:Z}"), + total_difficulty: 1 + } + ] + } + }) + + assert %Address.CurrentTokenBalance{block_number: ^previous_block_number, value: ^previous_value} = + Repo.get_by(Address.CurrentTokenBalance, + address_hash: address_hash, + token_contract_address_hash: token_contract_address_hash + ) + + assert is_nil( + Repo.get_by(Address.TokenBalance, + address_hash: address_hash, + token_contract_address_hash: token_contract_address_hash, + block_number: block_number + ) + ) + end + + test "address_token_balances and address_current_token_balances can be replaced during reorgs" do + %Block{number: block_number} = insert(:block, consensus: true) + value_before = Decimal.new(1) + + %Address{hash: address_hash} = address = insert(:address) + + %Address.TokenBalance{ + address_hash: ^address_hash, + token_contract_address_hash: token_contract_address_hash, + block_number: ^block_number + } = insert(:token_balance, address: address, block_number: block_number, value: value_before) + + %Address.CurrentTokenBalance{ + address_hash: ^address_hash, + token_contract_address_hash: ^token_contract_address_hash, + block_number: ^block_number + } = + insert(:address_current_token_balance, + address: address, + token_contract_address_hash: token_contract_address_hash, + block_number: block_number, + value: value_before + ) + + miner_hash_after = address_hash() + from_address_hash_after = address_hash() + block_hash_after = block_hash() + value_after = Decimal.add(value_before, 1) + + assert {:ok, _} = + Import.all(%{ + addresses: %{ + params: [ + %{hash: address_hash}, + %{hash: token_contract_address_hash}, + %{hash: miner_hash_after}, + %{hash: from_address_hash_after} + ] + }, + address_token_balances: %{ + params: [ + %{ + address_hash: address_hash, + token_contract_address_hash: token_contract_address_hash, + block_number: block_number, + value: value_after, + token_type: "ERC-20" + } + ] + }, + address_current_token_balances: %{ + params: [ + %{ + address_hash: address_hash, + token_contract_address_hash: token_contract_address_hash, + block_number: block_number, + value: value_after, + token_type: "ERC-20" + } + ] + }, + blocks: %{ + params: [ + %{ + consensus: true, + difficulty: 1, + gas_limit: 1, + gas_used: 1, + hash: block_hash_after, + miner_hash: miner_hash_after, + nonce: 1, + number: block_number, + parent_hash: block_hash(), + size: 1, + timestamp: Timex.parse!("2019-01-01T02:00:00Z", "{ISO:Extended:Z}"), + total_difficulty: 1 + } + ] + } + }) + + assert %Address.CurrentTokenBalance{value: ^value_after} = + Repo.get_by(Address.CurrentTokenBalance, + address_hash: address_hash, + token_contract_address_hash: token_contract_address_hash + ) + + assert %Address.TokenBalance{value: ^value_after} = + Repo.get_by(Address.TokenBalance, + address_hash: address_hash, + token_contract_address_hash: token_contract_address_hash, + block_number: block_number + ) + end end end diff --git a/apps/explorer/test/explorer/migrator/sanitize_missing_token_balances_test.exs b/apps/explorer/test/explorer/migrator/sanitize_missing_token_balances_test.exs index 18a6ffe40b73..e8c990e2011e 100644 --- a/apps/explorer/test/explorer/migrator/sanitize_missing_token_balances_test.exs +++ b/apps/explorer/test/explorer/migrator/sanitize_missing_token_balances_test.exs @@ -1,7 +1,7 @@ defmodule Explorer.Migrator.SanitizeMissingTokenBalancesTest do use Explorer.DataCase, async: false - alias Explorer.Chain.Address.TokenBalance + alias Explorer.Chain.Address.{CurrentTokenBalance, TokenBalance} alias Explorer.Migrator.{SanitizeMissingTokenBalances, MigrationStatus} alias Explorer.Repo @@ -10,11 +10,16 @@ defmodule Explorer.Migrator.SanitizeMissingTokenBalancesTest do Enum.each(0..10, fn _x -> token_balance = insert(:token_balance) + insert(:token_balance, + address: token_balance.address, + token_contract_address_hash: token_balance.token_contract_address_hash, + token_id: token_balance.token_id + ) + insert(:address_current_token_balance, address: token_balance.address, token_contract_address_hash: token_balance.token_contract_address_hash, token_id: token_balance.token_id, - block_number: token_balance.block_number, value: nil, value_fetched_at: nil ) @@ -30,10 +35,17 @@ defmodule Explorer.Migrator.SanitizeMissingTokenBalancesTest do TokenBalance |> Repo.all() - |> Enum.each(fn tb -> - assert %{value: nil, value_fetched_at: nil} = tb + |> Enum.group_by(&{&1.address_hash, &1.token_contract_address_hash, &1.token_id}) + |> Enum.each(fn {_, tbs} -> + assert [%{value: nil, value_fetched_at: nil}, %{value: old_value, value_fetched_at: old_value_fetched_at}] = + Enum.sort_by(tbs, & &1.block_number, &>=/2) + + refute is_nil(old_value) + refute is_nil(old_value_fetched_at) end) + assert Repo.all(CurrentTokenBalance) == [] + assert MigrationStatus.get_status("sanitize_missing_token_balances") == "completed" end end From f5aecf16f31512aa7973ee9b124d20f15fa373d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 13:59:28 +0300 Subject: [PATCH 175/363] chore(deps): bump dataloader from 2.0.0 to 2.0.1 (#10781) Bumps [dataloader](https://github.com/absinthe-graphql/dataloader) from 2.0.0 to 2.0.1. - [Changelog](https://github.com/absinthe-graphql/dataloader/blob/main/CHANGELOG.md) - [Commits](https://github.com/absinthe-graphql/dataloader/compare/v2.0.0...v2.0.1) --- updated-dependencies: - dependency-name: dataloader dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 7e93f8121c76..da993fb50e3f 100644 --- a/mix.lock +++ b/mix.lock @@ -31,7 +31,7 @@ "cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"}, "credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"}, "csv": {:hex, :csv, "2.5.0", "c47b5a5221bf2e56d6e8eb79e77884046d7fd516280dc7d9b674251e0ae46246", [:mix], [{:parallel_stream, "~> 1.0.4 or ~> 1.1.0", [hex: :parallel_stream, repo: "hexpm", optional: false]}], "hexpm", "e821f541487045c7591a1963eeb42afff0dfa99bdcdbeb3410795a2f59c77d34"}, - "dataloader": {:hex, :dataloader, "2.0.0", "49b42d60b9bb06d761a71d7b034c4b34787957e713d4fae15387a25fcd639112", [:mix], [{:ecto, ">= 3.4.3 and < 4.0.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:opentelemetry_process_propagator, "~> 0.2.1", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "09d61781b76ce216e395cdbc883ff00d00f46a503e215c22722dba82507dfef0"}, + "dataloader": {:hex, :dataloader, "2.0.1", "fa06b057b432b993203003fbff5ff040b7f6483a77e732b7dfc18f34ded2634f", [:mix], [{:ecto, ">= 3.4.3 and < 4.0.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:opentelemetry_process_propagator, "~> 0.2.1 or ~> 0.3", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "da7ff00890e1b14f7457419b9508605a8e66ae2cc2d08c5db6a9f344550efa11"}, "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "decorator": {:hex, :decorator, "1.4.0", "a57ac32c823ea7e4e67f5af56412d12b33274661bb7640ec7fc882f8d23ac419", [:mix], [], "hexpm", "0a07cedd9083da875c7418dea95b78361197cf2bf3211d743f6f7ce39656597f"}, From 5e4b8d856d72c4ecff717634f2df848109d59726 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 27 Sep 2024 13:23:08 +0300 Subject: [PATCH 176/363] chore: Remove old UI from base Docker image (#10828) * chore: Remove old UI from base Docker image * Rename Dockerfile with old UI --- .github/workflows/release.yml | 29 ++++++++++ docker/Dockerfile | 19 +------ docker/oldUI.Dockerfile | 104 ++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 17 deletions(-) create mode 100644 docker/oldUI.Dockerfile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 427836ce3880..2057477e2e25 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -203,6 +203,35 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + - name: Build & Push Docker image with an old UI (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/oldUI.Dockerfile + push: true + cache-from: type=registry,ref=blockscout/blockscout:buildcache + cache-to: type=registry,ref=blockscout/blockscout:buildcache,mode=max + tags: blockscout/blockscout:${{ env.RELEASE_VERSION }}-with-old-ui + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + DECODE_NOT_A_CONTRACT_CALLS=false + MIXPANEL_URL= + MIXPANEL_TOKEN= + AMPLITUDE_URL= + AMPLITUDE_API_KEY= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + # - name: Send release announcement to Slack workflow # id: slack # uses: slackapi/slack-github-action@v1.24.0 diff --git a/docker/Dockerfile b/docker/Dockerfile index 834c57ff4319..edcc99047162 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -46,25 +46,11 @@ ADD config ./config ADD rel ./rel ADD *.exs ./ -RUN apk add --update nodejs npm - -# Run forderground build and phoenix digest -RUN mix compile && npm install npm@latest - -# Add blockscout npm deps -RUN cd apps/block_scout_web/assets/ && \ - npm install && \ - npm run deploy && \ - cd /app/apps/explorer/ && \ - npm install && \ - apk update && \ - apk del --force-broken-world alpine-sdk gmp-dev automake libtool inotify-tools autoconf python3 - +# Run backend compilation +RUN mix compile RUN apk add --update git make -RUN mix phx.digest - RUN mkdir -p /opt/release \ && mix release blockscout \ && mv _build/${MIX_ENV}/rel/blockscout /opt/release @@ -94,7 +80,6 @@ RUN apk --no-cache --update add jq curl && \ WORKDIR /app COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /opt/release/blockscout . -COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/apps/explorer/node_modules ./node_modules COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/config/config_helper.exs ./config/config_helper.exs COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/config/config_helper.exs /app/releases/${RELEASE_VERSION}/config_helper.exs COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/config/assets/precompiles-arbitrum.json ./config/assets/precompiles-arbitrum.json diff --git a/docker/oldUI.Dockerfile b/docker/oldUI.Dockerfile new file mode 100644 index 000000000000..8ee713ac2b4d --- /dev/null +++ b/docker/oldUI.Dockerfile @@ -0,0 +1,104 @@ +FROM hexpm/elixir:1.16.3-erlang-26.2.5.1-alpine-3.18.7 AS builder + +WORKDIR /app + +ENV MIX_ENV="prod" + +RUN apk --no-cache --update add alpine-sdk gmp-dev automake libtool inotify-tools autoconf python3 file gcompat + +RUN set -ex && \ + apk --update add libstdc++ curl ca-certificates gcompat + +ARG CACHE_EXCHANGE_RATES_PERIOD +ARG API_V1_READ_METHODS_DISABLED +ARG DISABLE_WEBAPP +ARG API_V1_WRITE_METHODS_DISABLED +ARG CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED +ARG ADMIN_PANEL_ENABLED +ARG CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL +ARG SESSION_COOKIE_DOMAIN +ARG MIXPANEL_TOKEN +ARG MIXPANEL_URL +ARG AMPLITUDE_API_KEY +ARG AMPLITUDE_URL +ARG CHAIN_TYPE +ENV CHAIN_TYPE=${CHAIN_TYPE} +ARG BRIDGED_TOKENS_ENABLED +ENV BRIDGED_TOKENS_ENABLED=${BRIDGED_TOKENS_ENABLED} +ARG MUD_INDEXER_ENABLED +ENV MUD_INDEXER_ENABLED=${MUD_INDEXER_ENABLED} +ARG SHRINK_INTERNAL_TRANSACTIONS_ENABLED +ENV SHRINK_INTERNAL_TRANSACTIONS_ENABLED=${SHRINK_INTERNAL_TRANSACTIONS_ENABLED} + +# Cache elixir deps +ADD mix.exs mix.lock ./ +ADD apps/block_scout_web/mix.exs ./apps/block_scout_web/ +ADD apps/explorer/mix.exs ./apps/explorer/ +ADD apps/ethereum_jsonrpc/mix.exs ./apps/ethereum_jsonrpc/ +ADD apps/indexer/mix.exs ./apps/indexer/ + +ENV MIX_HOME=/opt/mix +RUN mix local.hex --force +RUN mix do deps.get, local.rebar --force, deps.compile + +ADD apps ./apps +ADD config ./config +ADD rel ./rel +ADD *.exs ./ + +RUN apk add --update nodejs npm + +# Run backend compilation and install latest npm +RUN mix compile && npm install npm@latest + +# Add blockscout npm deps +RUN cd apps/block_scout_web/assets/ && \ + npm install && \ + npm run deploy && \ + cd /app/apps/explorer/ && \ + npm install && \ + apk update && \ + apk del --force-broken-world alpine-sdk gmp-dev automake libtool inotify-tools autoconf python3 + + +RUN apk add --update git make + +RUN mix phx.digest + +RUN mkdir -p /opt/release \ + && mix release blockscout \ + && mv _build/${MIX_ENV}/rel/blockscout /opt/release + +############################################################## +FROM hexpm/elixir:1.16.3-erlang-26.2.5.1-alpine-3.18.7 + +ARG RELEASE_VERSION +ENV RELEASE_VERSION=${RELEASE_VERSION} +ARG CHAIN_TYPE +ENV CHAIN_TYPE=${CHAIN_TYPE} +ARG BRIDGED_TOKENS_ENABLED +ENV BRIDGED_TOKENS_ENABLED=${BRIDGED_TOKENS_ENABLED} +ARG SHRINK_INTERNAL_TRANSACTIONS_ENABLED +ENV SHRINK_INTERNAL_TRANSACTIONS_ENABLED=${SHRINK_INTERNAL_TRANSACTIONS_ENABLED} +ARG BLOCKSCOUT_VERSION +ENV BLOCKSCOUT_VERSION=${BLOCKSCOUT_VERSION} +ARG BLOCKSCOUT_USER=blockscout +ARG BLOCKSCOUT_GROUP=blockscout +ARG BLOCKSCOUT_UID=10001 +ARG BLOCKSCOUT_GID=10001 + +RUN apk --no-cache --update add jq curl && \ + addgroup --system --gid ${BLOCKSCOUT_GID} ${BLOCKSCOUT_GROUP} && \ + adduser --system --uid ${BLOCKSCOUT_UID} --ingroup ${BLOCKSCOUT_GROUP} --disabled-password ${BLOCKSCOUT_USER} + +WORKDIR /app + +COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /opt/release/blockscout . +COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/apps/explorer/node_modules ./node_modules +COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/config/config_helper.exs ./config/config_helper.exs +COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/config/config_helper.exs /app/releases/${RELEASE_VERSION}/config_helper.exs +COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/config/assets/precompiles-arbitrum.json ./config/assets/precompiles-arbitrum.json + +RUN chown -R ${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app + +USER ${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} From e897070d84497a284d4f01056d24d2dedc29b2ff Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 27 Sep 2024 13:23:46 +0300 Subject: [PATCH 177/363] fix: Repair /metrics endpoint (#10813) * fix: Repair /metrics endpoint * Return back change in the application.ex --- .../controllers/metrics_contoller_test.exs | 11 +++++ apps/indexer/lib/indexer/application.ex | 21 --------- ...in_pending_address_operations_collector.ex | 44 ++++++++++--------- 3 files changed, 34 insertions(+), 42 deletions(-) create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/metrics_contoller_test.exs diff --git a/apps/block_scout_web/test/block_scout_web/controllers/metrics_contoller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/metrics_contoller_test.exs new file mode 100644 index 000000000000..e9ea7e7e55b4 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/metrics_contoller_test.exs @@ -0,0 +1,11 @@ +defmodule BlockScoutWeb.MetricsControllerTest do + use BlockScoutWeb.ConnCase + + describe "/metrics page" do + test "renders /metrics page", %{conn: conn} do + conn = get(conn, "/metrics") + + assert text_response(conn, 200) + end + end +end diff --git a/apps/indexer/lib/indexer/application.ex b/apps/indexer/lib/indexer/application.ex index 78d05b37b044..cb7b9a99f83b 100644 --- a/apps/indexer/lib/indexer/application.ex +++ b/apps/indexer/lib/indexer/application.ex @@ -14,29 +14,8 @@ defmodule Indexer.Application do alias Indexer.Memory alias Prometheus.Registry - @default_prometheus_collectors [ - Indexer.Prometheus.Collector.PendingBlockOperations - ] - - case Application.compile_env(:explorer, :chain_type) do - :filecoin -> - @chain_type_prometheus_collectors [ - Indexer.Prometheus.Collector.FilecoinPendingAddressOperations - ] - - _ -> - @chain_type_prometheus_collectors [] - end - - @prometheus_collectors @default_prometheus_collectors ++ - @chain_type_prometheus_collectors - @impl Application def start(_type, _args) do - for collector <- @prometheus_collectors do - Registry.register_collector(collector) - end - memory_monitor_options = case Application.get_env(:indexer, :memory_limit) do nil -> %{} diff --git a/apps/indexer/lib/indexer/prometheus/collector/filecoin_pending_address_operations_collector.ex b/apps/indexer/lib/indexer/prometheus/collector/filecoin_pending_address_operations_collector.ex index 207841257542..207990ca456b 100644 --- a/apps/indexer/lib/indexer/prometheus/collector/filecoin_pending_address_operations_collector.ex +++ b/apps/indexer/lib/indexer/prometheus/collector/filecoin_pending_address_operations_collector.ex @@ -1,29 +1,31 @@ -defmodule Indexer.Prometheus.Collector.FilecoinPendingAddressOperations do - @moduledoc """ - Custom collector to count number of records in filecoin_pending_address_operations table. - """ +if Application.compile_env(:explorer, :chain_type) == :filecoin do + defmodule Indexer.Prometheus.Collector.FilecoinPendingAddressOperations do + @moduledoc """ + Custom collector to count number of records in filecoin_pending_address_operations table. + """ - use Prometheus.Collector + use Prometheus.Collector - alias Explorer.Chain.Filecoin.PendingAddressOperation - alias Explorer.Repo - alias Prometheus.Model + alias Explorer.Chain.Filecoin.PendingAddressOperation + alias Explorer.Repo + alias Prometheus.Model - def collect_mf(_registry, callback) do - callback.( - create_gauge( - :filecoin_pending_address_operations, - "Number of records in filecoin_pending_address_operations table", - Repo.aggregate(PendingAddressOperation, :count, timeout: :infinity) + def collect_mf(_registry, callback) do + callback.( + create_gauge( + :filecoin_pending_address_operations, + "Number of records in filecoin_pending_address_operations table", + Repo.aggregate(PendingAddressOperation, :count, timeout: :infinity) + ) ) - ) - end + end - def collect_metrics(:filecoin_pending_address_operations, count) do - Model.gauge_metrics([{count}]) - end + def collect_metrics(:filecoin_pending_address_operations, count) do + Model.gauge_metrics([{count}]) + end - defp create_gauge(name, help, data) do - Model.create_mf(name, help, :gauge, __MODULE__, data) + defp create_gauge(name, help, data) do + Model.create_mf(name, help, :gauge, __MODULE__, data) + end end end From fae80f7501645ae4ea01235cba62512c43a1485e Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Fri, 27 Sep 2024 13:49:48 +0300 Subject: [PATCH 178/363] feat: (celo) include token information in API response for address epoch rewards (#10831) * chore: add missing spec and doc * feat: include token information in API response for address epoch rewards --- .../block_scout_web/views/api/v2/celo_view.ex | 9 ++- .../chain/cache/celo_core_contracts.ex | 45 +++++++++++- .../explorer/chain/celo/election_reward.ex | 73 ++++++++++++++++++- .../lib/explorer/chain/celo/reader.ex | 1 + 4 files changed, 123 insertions(+), 5 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex index ac22f8ae1318..99241c6d4efd 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex @@ -221,7 +221,7 @@ defmodule BlockScoutWeb.API.V2.CeloView do } end - defp prepare_election_reward(%ElectionReward{} = reward) do + defp prepare_election_reward(%ElectionReward{token: %Token{}} = reward) do %{ amount: reward.amount, block_number: reward.block.number, @@ -237,7 +237,12 @@ defmodule BlockScoutWeb.API.V2.CeloView do reward.associated_account_address, reward.associated_account_address_hash ), - type: reward.type + type: reward.type, + token: + TokenView.render("token.json", %{ + token: reward.token, + contract_address_hash: reward.token.contract_address_hash + }) } end diff --git a/apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex b/apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex index 42e86fc2035d..b0c75b1ccd74 100644 --- a/apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex +++ b/apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex @@ -161,7 +161,33 @@ defmodule Explorer.Chain.Cache.CeloCoreContracts do end end - defp get_address_updates(contract_atom) do + @doc """ + Retrieves all address updates for a specified core contract. + + ## Parameters + + - `contract_atom` (`atom()`): The atom representing the core contract (e.g., + `:accounts`, `:validators`). + + ## Returns + + - `{:ok, [map()]}`: On success, returns a list of maps containing address + updates for the contract. + - `{:error, reason}`: Returns an error tuple with one of the following + reasons: `:contract_atom_not_found`, `:contract_name_not_found` + + ## Examples + + iex> Explorer.Chain.Cache.CeloCoreContracts.get_address_updates(:validators) + {:ok, [%{"address" => "0x123...", "updated_at_block_number" => 1000000}, ...]} + + iex> Explorer.Chain.Cache.CeloCoreContracts.get_address_updates(:unknown_contract) + {:error, :contract_atom_not_found} + + """ + @spec get_address_updates(atom()) :: + {:ok, [map()]} | {:error, :contract_atom_not_found | :contract_name_not_found} + def get_address_updates(contract_atom) do with {:atom, {:ok, contract_name}} <- {:atom, Map.fetch(@atom_to_contract_name, contract_atom)}, {:addresses, {:ok, contract_name_to_addresses}} <- @@ -223,6 +249,23 @@ defmodule Explorer.Chain.Cache.CeloCoreContracts do end end + @doc """ + Retrieves the block number of the first address update for a given core + contract. + + ## Parameters + + - `contract_atom` (`atom()`): The atom representing the core contract. + + ## Returns + + - `{:ok, Block.block_number()}`: The block number of the first update. + - `{:error, :contract_atom_not_found}`: If the contract atom is not + recognized. + """ + @spec get_first_update_block_number(atom()) :: + {:ok, Block.block_number() | nil} + | {:error, :contract_atom_not_found} def get_first_update_block_number(contract_atom) do with {:ok, address_updates} <- get_address_updates(contract_atom), %{"updated_at_block_number" => updated_at_block_number} <- diff --git a/apps/explorer/lib/explorer/chain/celo/election_reward.ex b/apps/explorer/lib/explorer/chain/celo/election_reward.ex index f31898bf9d1e..e45d563f2b35 100644 --- a/apps/explorer/lib/explorer/chain/celo/election_reward.ex +++ b/apps/explorer/lib/explorer/chain/celo/election_reward.ex @@ -32,8 +32,9 @@ defmodule Explorer.Chain.Celo.ElectionReward do import Ecto.Query, only: [from: 2, where: 3] import Explorer.Helper, only: [safe_parse_non_negative_integer: 1] + alias Explorer.Chain.Cache.CeloCoreContracts alias Explorer.{Chain, PagingOptions} - alias Explorer.Chain.{Address, Block, Hash, Wei} + alias Explorer.Chain.{Address, Block, Hash, Token, Wei} @type type :: :voter | :validator | :group | :delegated_payment @types_enum ~w(voter validator group delegated_payment)a @@ -96,6 +97,8 @@ defmodule Explorer.Chain.Celo.ElectionReward do null: false ) + field(:token, :any, virtual: true) :: Token.t() | nil + timestamps() end @@ -243,10 +246,76 @@ defmodule Explorer.Chain.Celo.ElectionReward do ) end + @doc """ + Joins the token table to the query based on the reward type. + + ## Parameters + - `query` (`Ecto.Query.t()`): The query to join the token table. + + ## Returns + - An Ecto query with the token table joined. + """ + @spec join_token(Ecto.Query.t()) :: Ecto.Query.t() + def join_token(query) do + # This match should never fail + %{ + voter: [voter_token_address_hash], + validator: [validator_token_address_hash], + group: [group_token_address_hash], + delegated_payment: [delegated_payment_token_address_hash] + } = + Map.new( + @reward_type_atom_to_token_atom, + fn {type, token_atom} -> + addresses = + token_atom + |> CeloCoreContracts.get_address_updates() + |> case do + {:ok, addresses} -> addresses + _ -> [] + end + |> Enum.map(fn %{"address" => address_hash_string} -> + {:ok, address_hash} = Hash.Address.cast(address_hash_string) + address_hash + end) + + {type, addresses} + end + ) + + from( + r in query, + join: t in Token, + on: + t.contract_address_hash == + fragment( + """ + CASE ? + WHEN ? THEN ?::bytea + WHEN ? THEN ?::bytea + WHEN ? THEN ?::bytea + WHEN ? THEN ?::bytea + ELSE NULL + END + """, + r.type, + ^"voter", + ^voter_token_address_hash.bytes, + ^"validator", + ^validator_token_address_hash.bytes, + ^"group", + ^group_token_address_hash.bytes, + ^"delegated_payment", + ^delegated_payment_token_address_hash.bytes + ), + select_merge: %{token: t} + ) + end + @doc """ Makes Explorer.PagingOptions map for election rewards. """ - @spec address_paging_options(map()) :: [Chain.paging_options()] + @spec block_paging_options(map()) :: [Chain.paging_options()] def block_paging_options(params) do with %{ "amount" => amount_string, diff --git a/apps/explorer/lib/explorer/chain/celo/reader.ex b/apps/explorer/lib/explorer/chain/celo/reader.ex index 35a534e9e55d..ac49b8787b4a 100644 --- a/apps/explorer/lib/explorer/chain/celo/reader.ex +++ b/apps/explorer/lib/explorer/chain/celo/reader.ex @@ -52,6 +52,7 @@ defmodule Explorer.Chain.Celo.Reader do address_hash |> ElectionReward.address_hash_to_ordered_rewards_query() + |> ElectionReward.join_token() |> ElectionReward.paginate(paging_options) |> limit(^paging_options.page_size) |> join_associations(necessity_by_association) From 23b994dbc8b24731fda9387f9a6e5903ca013f17 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:05:20 +0400 Subject: [PATCH 179/363] fix: Sanitize replaced transactions migration (#10784) --- apps/explorer/config/config.exs | 1 + apps/explorer/config/runtime/test.exs | 1 + apps/explorer/lib/explorer/application.ex | 3 +- .../sanitize_replaced_transactions.ex | 46 +++++++++++++++++++ 4 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 apps/explorer/lib/explorer/migrator/sanitize_replaced_transactions.ex diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index 233186aa4745..9503b7e0a619 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -129,6 +129,7 @@ config :explorer, Explorer.Migrator.TransactionBlockConsensus, enabled: true config :explorer, Explorer.Migrator.TokenTransferBlockConsensus, enabled: true config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, enabled: true config :explorer, Explorer.Migrator.SanitizeMissingTokenBalances, enabled: true +config :explorer, Explorer.Migrator.SanitizeReplacedTransactions, enabled: true config :explorer, Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand, enabled: true diff --git a/apps/explorer/config/runtime/test.exs b/apps/explorer/config/runtime/test.exs index eeae8772a77c..01874f4bbfb2 100644 --- a/apps/explorer/config/runtime/test.exs +++ b/apps/explorer/config/runtime/test.exs @@ -50,6 +50,7 @@ config :explorer, Explorer.Migrator.TokenTransferBlockConsensus, enabled: false config :explorer, Explorer.Migrator.ShrinkInternalTransactions, enabled: false config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, enabled: false config :explorer, Explorer.Migrator.SanitizeMissingTokenBalances, enabled: false +config :explorer, Explorer.Migrator.SanitizeReplacedTransactions, 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 96685ea646cf..b5b4fcee2fbc 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -146,7 +146,8 @@ defmodule Explorer.Application do configure_mode_dependent_process(Explorer.Migrator.ShrinkInternalTransactions, :indexer), configure_chain_type_dependent_process(Explorer.Chain.Cache.BlackfortValidatorsCounters, :blackfort), configure_chain_type_dependent_process(Explorer.Chain.Cache.StabilityValidatorsCounters, :stability), - configure_mode_dependent_process(Explorer.Migrator.SanitizeMissingTokenBalances, :indexer) + configure_mode_dependent_process(Explorer.Migrator.SanitizeMissingTokenBalances, :indexer), + configure_mode_dependent_process(Explorer.Migrator.SanitizeReplacedTransactions, :indexer) ] |> List.flatten() diff --git a/apps/explorer/lib/explorer/migrator/sanitize_replaced_transactions.ex b/apps/explorer/lib/explorer/migrator/sanitize_replaced_transactions.ex new file mode 100644 index 000000000000..5b92f7176d3d --- /dev/null +++ b/apps/explorer/lib/explorer/migrator/sanitize_replaced_transactions.ex @@ -0,0 +1,46 @@ +defmodule Explorer.Migrator.SanitizeReplacedTransactions do + @moduledoc """ + Cleans the transactions that are related to non-consensus blocks. + """ + + use Explorer.Migrator.FillingMigration + + import Ecto.Query + + alias Explorer.Chain.Transaction + alias Explorer.Migrator.FillingMigration + alias Explorer.Repo + + @migration_name "sanitize_replaced_transactions" + + @impl FillingMigration + def migration_name, do: @migration_name + + @impl FillingMigration + def last_unprocessed_identifiers(state) do + limit = batch_size() * concurrency() + + ids = + unprocessed_data_query() + |> select([t], t.hash) + |> limit(^limit) + |> Repo.all(timeout: :infinity) + + {ids, state} + end + + @impl FillingMigration + def unprocessed_data_query do + from(t in Transaction, where: t.block_consensus == false) + end + + @impl FillingMigration + def update_batch(transaction_hashes) do + query = from(t in Transaction, where: t.hash in ^transaction_hashes) + + Repo.delete_all(query, timeout: :infinity) + end + + @impl FillingMigration + def update_cache, do: :ok +end From 435bba786fe2ec45db2b36be0ec04b91b5fef0f2 Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Fri, 27 Sep 2024 14:41:10 +0300 Subject: [PATCH 180/363] fix: filecoin stucked pending address operations (#10832) * chore: log database related errors * fix: do not buffer failed operations * fix: do not hardcode Beryx API Filecoin mainnet base url --- .../explorer/chain/filecoin/pending_address_operation.ex | 4 +++- apps/indexer/lib/indexer/fetcher/filecoin/address_info.ex | 8 +++++++- apps/indexer/lib/indexer/fetcher/filecoin/beryx_api.ex | 2 +- config/runtime.exs | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/filecoin/pending_address_operation.ex b/apps/explorer/lib/explorer/chain/filecoin/pending_address_operation.ex index 0fa166e0fb6d..63e61ab61547 100644 --- a/apps/explorer/lib/explorer/chain/filecoin/pending_address_operation.ex +++ b/apps/explorer/lib/explorer/chain/filecoin/pending_address_operation.ex @@ -19,7 +19,8 @@ defmodule Explorer.Chain.Filecoin.PendingAddressOperation do @typedoc """ * `address_hash` - the hash of the address that is pending to be fetched. * `http_status_code` - the unsuccessful (non-200) http code returned by Beryx - API if the fetcher failed to fetch the address. + API if the fetcher failed to fetch the address, set to `nil` if the fetcher + did not attempt to fetch the address yet. """ @primary_key false typed_schema "filecoin_pending_address_operations" do @@ -63,6 +64,7 @@ defmodule Explorer.Chain.Filecoin.PendingAddressOperation do from( op in __MODULE__, select: op, + where: is_nil(op.http_status_code), order_by: [desc: op.address_hash] ) diff --git a/apps/indexer/lib/indexer/fetcher/filecoin/address_info.ex b/apps/indexer/lib/indexer/fetcher/filecoin/address_info.ex index 83e5f7cdd193..1d7a9d66ba29 100644 --- a/apps/indexer/lib/indexer/fetcher/filecoin/address_info.ex +++ b/apps/indexer/lib/indexer/fetcher/filecoin/address_info.ex @@ -108,7 +108,9 @@ defmodule Indexer.Fetcher.Filecoin.AddressInfo do else _ -> Logger.error("Could not fetch Filecoin address info: #{to_string(address_hash)}") - :retry + # TODO: We should consider implementing retry logic when fetching + # becomes more stable + :ok end end @@ -169,6 +171,10 @@ defmodule Indexer.Fetcher.Filecoin.AddressInfo do end ) |> Repo.transaction() + |> tap(fn + {:ok, _} -> :ok + error -> Logger.error("Error updating address and removing pending operation: #{inspect(error)}") + end) end @spec fetch_address_info_using_beryx_api(PendingAddressOperation.t()) :: diff --git a/apps/indexer/lib/indexer/fetcher/filecoin/beryx_api.ex b/apps/indexer/lib/indexer/fetcher/filecoin/beryx_api.ex index f4e7f212c9ae..494880c7a7a9 100644 --- a/apps/indexer/lib/indexer/fetcher/filecoin/beryx_api.ex +++ b/apps/indexer/lib/indexer/fetcher/filecoin/beryx_api.ex @@ -27,7 +27,7 @@ defmodule Indexer.Fetcher.Filecoin.BeryxAPI do base_url = config |> Keyword.get(:base_url) |> String.trim_trailing("/") api_token = config[:api_token] - url = "#{base_url}/mainnet/account/info/#{eth_address_hash}" + url = "#{base_url}/account/info/#{eth_address_hash}" headers = [ {"Authorization", "Bearer #{api_token}"}, diff --git a/config/runtime.exs b/config/runtime.exs index 7875f4c47658..4ef037d05c71 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -1065,7 +1065,7 @@ config :indexer, Indexer.Fetcher.Celo.EpochBlockOperations.Supervisor, disabled?: not celo_epoch_fetchers_enabled? config :indexer, Indexer.Fetcher.Filecoin.BeryxAPI, - base_url: ConfigHelper.safe_get_env("BERYX_API_BASE_URL", "https://api.zondax.ch/fil/data/v3"), + base_url: ConfigHelper.safe_get_env("BERYX_API_BASE_URL", "https://api.zondax.ch/fil/data/v3/mainnet"), api_token: System.get_env("BERYX_API_TOKEN") filecoin_native_address_fetcher_enabled? = From cffc5360baeefec373fefd0c985ee5525a528a63 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:38:24 +0400 Subject: [PATCH 181/363] chore: Reindex incorrect internal transactions migration (#10654) --- apps/explorer/config/config.exs | 1 + apps/explorer/config/runtime/test.exs | 1 + apps/explorer/lib/explorer/application.ex | 3 +- ...l_transactions_with_incompatible_status.ex | 83 +++++++++++++++++++ ...sactions_with_incompatible_status_test.exs | 73 ++++++++++++++++ 5 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 apps/explorer/lib/explorer/migrator/reindex_internal_transactions_with_incompatible_status.ex create mode 100644 apps/explorer/test/explorer/migrator/reindex_internal_transactions_with_incompatible_status_test.exs diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index 9503b7e0a619..3fd5e07120f1 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -130,6 +130,7 @@ config :explorer, Explorer.Migrator.TokenTransferBlockConsensus, enabled: true config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, enabled: true config :explorer, Explorer.Migrator.SanitizeMissingTokenBalances, enabled: true config :explorer, Explorer.Migrator.SanitizeReplacedTransactions, enabled: true +config :explorer, Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, enabled: true config :explorer, Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand, enabled: true diff --git a/apps/explorer/config/runtime/test.exs b/apps/explorer/config/runtime/test.exs index 01874f4bbfb2..a46bd5004fcb 100644 --- a/apps/explorer/config/runtime/test.exs +++ b/apps/explorer/config/runtime/test.exs @@ -51,6 +51,7 @@ config :explorer, Explorer.Migrator.ShrinkInternalTransactions, enabled: false config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, enabled: false config :explorer, Explorer.Migrator.SanitizeMissingTokenBalances, enabled: false config :explorer, Explorer.Migrator.SanitizeReplacedTransactions, enabled: false +config :explorer, Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, 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 b5b4fcee2fbc..c5ed03e9292b 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -147,7 +147,8 @@ defmodule Explorer.Application do configure_chain_type_dependent_process(Explorer.Chain.Cache.BlackfortValidatorsCounters, :blackfort), configure_chain_type_dependent_process(Explorer.Chain.Cache.StabilityValidatorsCounters, :stability), configure_mode_dependent_process(Explorer.Migrator.SanitizeMissingTokenBalances, :indexer), - configure_mode_dependent_process(Explorer.Migrator.SanitizeReplacedTransactions, :indexer) + configure_mode_dependent_process(Explorer.Migrator.SanitizeReplacedTransactions, :indexer), + configure_mode_dependent_process(Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, :indexer) ] |> List.flatten() diff --git a/apps/explorer/lib/explorer/migrator/reindex_internal_transactions_with_incompatible_status.ex b/apps/explorer/lib/explorer/migrator/reindex_internal_transactions_with_incompatible_status.ex new file mode 100644 index 000000000000..94a6a26ca53c --- /dev/null +++ b/apps/explorer/lib/explorer/migrator/reindex_internal_transactions_with_incompatible_status.ex @@ -0,0 +1,83 @@ +defmodule Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus do + @moduledoc """ + Searches for all failed transactions for which all internal transactions are successful + and adds block numbers of these transactions to pending_block_operations. + """ + + use Explorer.Migrator.FillingMigration + + import Ecto.Query + + alias Explorer.Chain.{Block, InternalTransaction, PendingBlockOperation, Transaction} + alias Explorer.Migrator.FillingMigration + alias Explorer.Repo + + @migration_name "reindex_internal_transactions_with_incompatible_status" + + @impl FillingMigration + def migration_name, do: @migration_name + + @impl FillingMigration + def last_unprocessed_identifiers(state) do + limit = batch_size() * concurrency() + + ids = + unprocessed_data_query() + |> select([t], t.block_number) + |> distinct(true) + |> limit(^limit) + |> Repo.all(timeout: :infinity) + + {ids, state} + end + + @impl FillingMigration + def unprocessed_data_query do + pbo_query = + from( + pbo in PendingBlockOperation, + where: pbo.block_number == parent_as(:transaction).block_number + ) + + it_query = + from( + it in InternalTransaction, + where: parent_as(:transaction).hash == it.transaction_hash and it.index > 0, + select: 1 + ) + + it_error_query = + from( + it in InternalTransaction, + where: parent_as(:transaction).hash == it.transaction_hash and not is_nil(it.error) and it.index > 0, + select: 1 + ) + + from( + t in Transaction, + as: :transaction, + where: t.status == ^:error, + where: not exists(pbo_query), + where: exists(it_query), + where: not exists(it_error_query) + ) + end + + @impl FillingMigration + def update_batch(block_numbers) do + now = DateTime.utc_now() + + params = + Block + |> where([b], b.number in ^block_numbers) + |> select([b], %{block_hash: b.hash, block_number: b.number}) + |> Repo.all() + |> Enum.uniq_by(& &1.block_number) + |> Enum.map(&Map.merge(&1, %{inserted_at: now, updated_at: now})) + + Repo.insert_all(PendingBlockOperation, params, on_conflict: :nothing) + end + + @impl FillingMigration + def update_cache, do: :ok +end diff --git a/apps/explorer/test/explorer/migrator/reindex_internal_transactions_with_incompatible_status_test.exs b/apps/explorer/test/explorer/migrator/reindex_internal_transactions_with_incompatible_status_test.exs new file mode 100644 index 000000000000..4ca3037e4729 --- /dev/null +++ b/apps/explorer/test/explorer/migrator/reindex_internal_transactions_with_incompatible_status_test.exs @@ -0,0 +1,73 @@ +defmodule Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatusTest do + use Explorer.DataCase, async: false + + alias Explorer.Chain.PendingBlockOperation + alias Explorer.Migrator.{ReindexInternalTransactionsWithIncompatibleStatus, MigrationStatus} + alias Explorer.Repo + + describe "Migrate incorrect internal transactions" do + test "Adds new pbo for incorrect internal transactions" do + incorrect_block_numbers = + Enum.map(1..5, fn i -> + block = insert(:block) + transaction = :transaction |> insert() |> with_block(block, status: :error) + + insert(:internal_transaction, + index: 10, + transaction: transaction, + block: block, + block_number: block.number, + block_index: i, + error: nil + ) + + block.number + end) + + Enum.each(1..5, fn i -> + block = insert(:block) + transaction = :transaction |> insert() |> with_block(block, status: :error) + + insert(:internal_transaction, + index: 10, + transaction: transaction, + block: block, + block_number: block.number, + block_index: i, + error: "error", + output: nil + ) + end) + + Enum.each(1..5, fn i -> + block = insert(:block) + transaction = :transaction |> insert() |> with_block(block, status: :ok) + + insert(:internal_transaction, + index: 10, + transaction: transaction, + block: block, + block_number: block.number, + block_index: i, + error: nil + ) + end) + + assert MigrationStatus.get_status("reindex_internal_transactions_with_incompatible_status") == nil + assert Repo.all(PendingBlockOperation) == [] + + ReindexInternalTransactionsWithIncompatibleStatus.start_link([]) + Process.sleep(100) + + pbo_block_numbers = + PendingBlockOperation + |> Repo.all() + |> Enum.map(& &1.block_number) + |> Enum.sort() + + assert incorrect_block_numbers == pbo_block_numbers + + assert MigrationStatus.get_status("reindex_internal_transactions_with_incompatible_status") == "completed" + end + end +end From 2e6ce2c222fdee705b26aa4272d1cc25fc7d6362 Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Fri, 27 Sep 2024 18:05:17 +0400 Subject: [PATCH 182/363] chore: bump elixir to 1.17.3 and Erlang OTP to 27.1 (#10284) * fix: qemu arm build * ci: remove arm build workaround * chore: bump elixir stack to 1.17.1 * chore: fix dialyzer * chore: update pr ref * chore: bump elixir stack to 1.17.2 * chore: fix range warnings * fix: test * chore: bump elixir stack to 1.17.3 * chore: fix warnings --- .dialyzer-ignore | 8 +------- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- .github/workflows/config.yml | 4 ++-- .tool-versions | 4 ++-- .../controllers/address_validation_controller.ex | 3 --- .../controllers/api/rpc/contract_controller.ex | 14 -------------- .../controllers/smart_contract_controller.ex | 2 +- .../controllers/transaction_controller.ex | 12 ------------ .../controllers/transaction_state_controller.ex | 6 ------ .../transaction_token_transfer_controller.ex | 3 --- .../address_token_balance/_token_balances.html.eex | 2 +- .../block_scout_web/views/api/v2/optimism_view.ex | 2 +- apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex | 2 +- apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/ipc.ex | 1 - .../lib/ethereum_jsonrpc/utility/ranges_helper.ex | 12 ++++++------ .../lib/explorer/account/watchlist_address.ex | 3 --- apps/explorer/lib/explorer/chain.ex | 8 ++++---- .../explorer/chain/cache/celo_core_contracts.ex | 3 --- .../chain/import/runner/internal_transactions.ex | 2 +- apps/explorer/lib/explorer/chain/transaction.ex | 2 +- .../explorer/third_party_integrations/sourcify.ex | 2 +- .../utility/address_contract_code_fetch_attempt.ex | 2 +- .../lib/explorer/utility/missing_block_range.ex | 6 +++--- apps/explorer/lib/fetch_celo_core_contracts.ex | 4 ++-- apps/explorer/test/explorer/chain/address_test.exs | 2 +- .../explorer/chain_spec/parity/importer_test.exs | 4 ++-- apps/indexer/lib/indexer/block/catchup/fetcher.ex | 8 ++++---- .../block/catchup/missing_ranges_collector.ex | 4 ++-- apps/indexer/lib/indexer/block/fetcher.ex | 2 +- apps/indexer/lib/indexer/bound_interval.ex | 2 +- .../indexer/fetcher/celo/validator_group_votes.ex | 6 +++--- .../lib/indexer/fetcher/optimism/txn_batch.ex | 5 +++-- apps/indexer/lib/indexer/helper.ex | 2 +- docker/Dockerfile | 4 ++-- 34 files changed, 49 insertions(+), 99 deletions(-) diff --git a/.dialyzer-ignore b/.dialyzer-ignore index 258b2525b406..c0bea4bf4985 100644 --- a/.dialyzer-ignore +++ b/.dialyzer-ignore @@ -4,10 +4,4 @@ lib/explorer/smart_contract/vyper/publisher_worker.ex:1 lib/explorer/smart_contract/solidity/publisher_worker.ex:8 lib/explorer/smart_contract/vyper/publisher_worker.ex:8 lib/phoenix/router.ex:402 -lib/explorer/smart_contract/reader.ex:435 -lib/explorer/exchange_rates/source.ex:139 -lib/explorer/exchange_rates/source.ex:142 -lib/block_scout_web/cldr.ex:1 -lib/block_scout_web/views/api/v2/transaction_view.ex:431 -lib/block_scout_web/views/api/v2/transaction_view.ex:472 -lib/explorer/chain/transaction.ex:171 +lib/explorer/chain/cache/celo_core_contracts.ex:162 diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 858bb8587c21..5fa188d46862 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -81,7 +81,7 @@ body: attributes: label: Elixir & Erlang/OTP versions description: Elixir & Erlang/OTP versions. - placeholder: Elixir 1.16.3 (compiled with Erlang/OTP 26) + placeholder: Elixir 1.17.3 (compiled with Erlang/OTP 27) validations: required: true diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml index 17cb27192c87..03d8e8c4b154 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/config.yml @@ -32,8 +32,8 @@ on: env: MIX_ENV: test - OTP_VERSION: ${{ github.ref_name == '9256/merge' && '26.2.5.1' || vars.OTP_VERSION }} - ELIXIR_VERSION: ${{ github.ref_name == '9256/merge' && '1.16.3' || vars.ELIXIR_VERSION }} + OTP_VERSION: ${{ github.ref_name == '10284/merge' && '27.1' || vars.OTP_VERSION }} + ELIXIR_VERSION: ${{ github.ref_name == '10284/merge' && '1.17.3' || vars.ELIXIR_VERSION }} ACCOUNT_AUTH0_DOMAIN: "blockscoutcom.us.auth0.com" jobs: diff --git a/.tool-versions b/.tool-versions index 15355e529467..270ab12e48f5 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,3 +1,3 @@ -elixir 1.16.3-otp-26 -erlang 26.2.5.1 +elixir 1.17.3-otp-27 +erlang 27.1 nodejs 18.17.1 diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex index 48442d0adbbd..b0cec84c05b0 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex @@ -91,9 +91,6 @@ defmodule BlockScoutWeb.AddressValidationController do :error -> unprocessable_entity(conn) - - {:error, :not_found} -> - not_found(conn) end end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex index e9d113a205e1..4392d51b9e50 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex @@ -101,17 +101,6 @@ defmodule BlockScoutWeb.API.RPC.ContractController do render(conn, :error, error: "#{@smth_went_wrong}: #{inspect(error.errors)}") - {:publish, error} -> - Logger.error(fn -> - [ - @smth_went_wrong, - ": ", - inspect(error) - ] - end) - - render(conn, :error, error: @smth_went_wrong) - {:format, :error} -> render(conn, :error, error: @invalid_address) @@ -142,9 +131,6 @@ defmodule BlockScoutWeb.API.RPC.ContractController do else {:error, error} -> render(conn, :error, error: error) - - _ -> - render(conn, :error, error: "Invalid body") end end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex index 87eaf6bd7824..ee2d59ae8ff5 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex @@ -217,7 +217,7 @@ defmodule BlockScoutWeb.SmartContractController do :error -> unprocessable_entity(conn) - :not_found -> + {:error, :not_found} -> not_found(conn) _ -> diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex index a31baf1ea923..166566e35536 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex @@ -174,12 +174,6 @@ defmodule BlockScoutWeb.TransactionController do ) ) else - :not_found -> - set_not_found_view(conn, id) - - :error -> - unprocessable_entity(conn) - {:error, :not_found} -> set_not_found_view(conn, id) @@ -209,12 +203,6 @@ defmodule BlockScoutWeb.TransactionController do ) ) else - :not_found -> - set_not_found_view(conn, id) - - :error -> - unprocessable_entity(conn) - {:error, :not_found} -> set_not_found_view(conn, id) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex index f978e7647b28..b071a2e09992 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex @@ -73,9 +73,6 @@ defmodule BlockScoutWeb.TransactionStateController do {:error, :not_found} -> TransactionController.set_not_found_view(conn, transaction_hash_string) - - :not_found -> - TransactionController.set_not_found_view(conn, transaction_hash_string) end end @@ -115,9 +112,6 @@ defmodule BlockScoutWeb.TransactionStateController do current_user: current_user(conn) ) else - :not_found -> - TransactionController.set_not_found_view(conn, transaction_hash_string) - :error -> unprocessable_entity(conn) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex index 061fde450ad4..437b8515c3aa 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex @@ -120,9 +120,6 @@ defmodule BlockScoutWeb.TransactionTokenTransferController do ) ) else - :not_found -> - TransactionController.set_not_found_view(conn, transaction_hash_string) - :error -> unprocessable_entity(conn) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex index 807b11b99609..2096d38a7cb7 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex @@ -43,7 +43,7 @@ :token_search, :name, class: "w-100 dropdown-search-field", - 'data-filter-dropdown-tokens': true, + "data-filter-dropdown-tokens": true, placeholder: gettext("Search tokens") ) %>
diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex index 1b021718d1cd..2c64837ee141 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex @@ -60,7 +60,7 @@ defmodule BlockScoutWeb.API.V2.OptimismView do items = batches |> Enum.map(fn batch -> - from..to = batch.l2_block_range + from..to//_ = batch.l2_block_range %{ "internal_id" => batch.id, diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex index c2ac95cb8730..ec98c66673a9 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex @@ -270,7 +270,7 @@ defmodule EthereumJSONRPC do Fetches blocks by block number range. """ @spec fetch_blocks_by_range(Range.t(), json_rpc_named_arguments) :: {:ok, Blocks.t()} | {:error, reason :: term} - def fetch_blocks_by_range(_first.._last = range, json_rpc_named_arguments) do + def fetch_blocks_by_range(_first.._last//_ = range, json_rpc_named_arguments) do range |> Enum.map(fn number -> %{number: number} end) |> fetch_blocks_by_params(&Block.ByNumber.request/1, json_rpc_named_arguments) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/ipc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/ipc.ex index 7f1ff401b053..b030a6abf35c 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/ipc.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/ipc.ex @@ -84,7 +84,6 @@ defmodule EthereumJSONRPC.IPC do else {:error, %Jason.DecodeError{data: ""}} -> {:error, :empty_response} {:error, error} -> {:error, {:invalid_json, error}} - {:error, error} -> {:error, error} end end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/ranges_helper.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/ranges_helper.ex index f53a8b14f13e..5657fe77299b 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/ranges_helper.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/ranges_helper.ex @@ -58,7 +58,7 @@ defmodule EthereumJSONRPC.Utility.RangesHelper do defp number_in_ranges?(number, ranges) do Enum.reduce_while(ranges, false, fn - _from.._to = range, _acc -> if number in range, do: {:halt, true}, else: {:cont, false} + _from.._to//_ = range, _acc -> if number in range, do: {:halt, true}, else: {:cont, false} num_to_latest, _acc -> if number >= num_to_latest, do: {:halt, true}, else: {:cont, false} end) end @@ -78,7 +78,7 @@ defmodule EthereumJSONRPC.Utility.RangesHelper do |> Enum.reject(&is_nil/1) |> Enum.sort_by( fn - from.._to -> from + from.._to//_ -> from el -> el end, :asc @@ -86,10 +86,10 @@ defmodule EthereumJSONRPC.Utility.RangesHelper do |> Enum.chunk_while( nil, fn - _from.._to = chunk, nil -> + _from.._to//_ = chunk, nil -> {:cont, chunk} - _ch_from..ch_to = chunk, acc_from..acc_to = acc -> + _ch_from..ch_to//_ = chunk, acc_from..acc_to//_ = acc -> if Range.disjoint?(chunk, acc), do: {:cont, acc, chunk}, else: {:cont, acc_from..max(ch_to, acc_to)} @@ -97,7 +97,7 @@ defmodule EthereumJSONRPC.Utility.RangesHelper do num, nil -> {:halt, num} - num, acc_from.._ = acc -> + num, acc_from.._//_ = acc -> if Range.disjoint?(num..num, acc), do: {:cont, acc, num}, else: {:halt, acc_from} _, num -> @@ -113,7 +113,7 @@ defmodule EthereumJSONRPC.Utility.RangesHelper do @spec split([Range.t()], integer) :: [Range.t()] def split(ranges, size) do ranges - |> Enum.reduce([], fn from..to = range, acc -> + |> Enum.reduce([], fn from..to//_ = range, acc -> range_size = Range.size(range) if range_size > size do diff --git a/apps/explorer/lib/explorer/account/watchlist_address.ex b/apps/explorer/lib/explorer/account/watchlist_address.ex index 14302eb0d557..529186dd36fb 100644 --- a/apps/explorer/lib/explorer/account/watchlist_address.ex +++ b/apps/explorer/lib/explorer/account/watchlist_address.ex @@ -112,9 +112,6 @@ defmodule Explorer.Account.WatchlistAddress do else {:error, reason} -> add_error(changeset, :address_hash, reason) - - _ -> - add_error(changeset, :address_hash, "Address error") end end diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index c9307fedde47..8c64622cb89a 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2281,15 +2281,15 @@ defmodule Explorer.Chain do iex> insert(:block, number: 0) iex> insert(:block, number: 2) iex> insert(:block, number: 5) - iex> Explorer.Chain.missing_block_number_ranges(5..0) - [4..3, 1..1] + iex> Explorer.Chain.missing_block_number_ranges(5..0//-1) + [4..3//-1, 1..1] If only non-consensus blocks exist for a number, the number still counts as missing. iex> insert(:block, number: 0) iex> insert(:block, number: 1, consensus: false) iex> insert(:block, number: 2) - iex> Explorer.Chain.missing_block_number_ranges(2..0) + iex> Explorer.Chain.missing_block_number_ranges(2..0//-1) [1..1] if range starts with non-consensus block in the middle of the chain, it returns missing numbers. @@ -2311,7 +2311,7 @@ defmodule Explorer.Chain do @spec missing_block_number_ranges(Range.t()) :: [Range.t()] def missing_block_number_ranges(range) - def missing_block_number_ranges(range_start..range_end) do + def missing_block_number_ranges(range_start..range_end//_) do range_min = min(range_start, range_end) range_max = max(range_start, range_end) diff --git a/apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex b/apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex index b0c75b1ccd74..1e617d3f6b66 100644 --- a/apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex +++ b/apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex @@ -142,9 +142,6 @@ defmodule Explorer.Chain.Cache.CeloCoreContracts do {:error, :event_name_not_found} - nil -> - {:error, :event_does_not_exist} - {:contract_address, :error} -> Logger.error(fn -> [ 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 1446ec927acf..da81e2df5109 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex @@ -809,7 +809,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do block_ranges = RangesHelper.get_trace_block_ranges() Enum.reduce(block_ranges, dynamic([_], false), fn - _from.._to = range, acc -> dynamic([block], ^acc or block.number in ^range) + _from.._to//_ = range, acc -> dynamic([block], ^acc or block.number in ^range) num_to_latest, acc -> dynamic([block], ^acc or block.number >= ^num_to_latest) end) else diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index 59f0ec68f40b..1e687e5c28ee 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -1971,7 +1971,7 @@ defmodule Explorer.Chain.Transaction do Only consensus blocks are taken into account. """ @spec tx_count_for_block_range(Range.t()) :: non_neg_integer() - def tx_count_for_block_range(from..to) do + def tx_count_for_block_range(from..to//_) do Repo.replica().aggregate( from( t in Transaction, diff --git a/apps/explorer/lib/explorer/third_party_integrations/sourcify.ex b/apps/explorer/lib/explorer/third_party_integrations/sourcify.ex index 5604324d07db..d23491a94cd4 100644 --- a/apps/explorer/lib/explorer/third_party_integrations/sourcify.ex +++ b/apps/explorer/lib/explorer/third_party_integrations/sourcify.ex @@ -399,7 +399,7 @@ defmodule Explorer.ThirdPartyIntegrations.Sourcify do trimmed_path = path |> String.split("/") - |> Enum.slice(9..-1) + |> Enum.slice(9..-1//-1) |> Enum.join("/") %{ diff --git a/apps/explorer/lib/explorer/utility/address_contract_code_fetch_attempt.ex b/apps/explorer/lib/explorer/utility/address_contract_code_fetch_attempt.ex index 89f5672dacab..951a401a08a1 100644 --- a/apps/explorer/lib/explorer/utility/address_contract_code_fetch_attempt.ex +++ b/apps/explorer/lib/explorer/utility/address_contract_code_fetch_attempt.ex @@ -25,7 +25,7 @@ defmodule Explorer.Utility.AddressContractCodeFetchAttempt do @doc """ Gets retries number and updated_at for the Explorer.Chain.Address """ - @spec get_retries_number(Hash.Address.t()) :: {non_neg_integer(), DateTime.t()} + @spec get_retries_number(Hash.Address.t()) :: {non_neg_integer(), DateTime.t()} | nil def get_retries_number(address_hash) do __MODULE__ |> where([address_contract_code_fetch_attempt], address_contract_code_fetch_attempt.address_hash == ^address_hash) diff --git a/apps/explorer/lib/explorer/utility/missing_block_range.ex b/apps/explorer/lib/explorer/utility/missing_block_range.ex index 9bcc6c9224d6..16b37b29f2d2 100644 --- a/apps/explorer/lib/explorer/utility/missing_block_range.ex +++ b/apps/explorer/lib/explorer/utility/missing_block_range.ex @@ -55,7 +55,7 @@ defmodule Explorer.Utility.MissingBlockRange do |> save_batch() end - def save_range(from..to) do + def save_range(from..to//_) do min_number = min(from, to) max_number = max(from, to) @@ -84,7 +84,7 @@ defmodule Explorer.Utility.MissingBlockRange do end end - def delete_range(from..to) do + def delete_range(from..to//_) do min_number = min(from, to) max_number = max(from, to) @@ -294,7 +294,7 @@ defmodule Explorer.Utility.MissingBlockRange do number, nil -> {:cont, number..number} - number, first..last when number == last + 1 -> + number, first..last//_ when number == last + 1 -> {:cont, first..number} number, range -> diff --git a/apps/explorer/lib/fetch_celo_core_contracts.ex b/apps/explorer/lib/fetch_celo_core_contracts.ex index a1a12884ab15..6d720350aa9c 100644 --- a/apps/explorer/lib/fetch_celo_core_contracts.ex +++ b/apps/explorer/lib/fetch_celo_core_contracts.ex @@ -156,10 +156,10 @@ defmodule Mix.Tasks.FetchCeloCoreContracts do IO.puts("CELO_CORE_CONTRACTS=#{core_contracts_json}") end - defp fetch_logs_by_chunks(from_block..to_block, requests_func, json_rpc_named_arguments) do + defp fetch_logs_by_chunks(from_block..to_block//_, requests_func, json_rpc_named_arguments) do from_block..to_block |> IndexerHelper.range_chunk_every(@chunk_size) - |> Enum.reduce([], fn chunk_start..chunk_end, acc -> + |> Enum.reduce([], fn chunk_start..chunk_end//_, acc -> IndexerHelper.log_blocks_chunk_handling(chunk_start, chunk_end, 0, to_block, nil, :L1) requests = requests_func.(chunk_start, chunk_end) diff --git a/apps/explorer/test/explorer/chain/address_test.exs b/apps/explorer/test/explorer/chain/address_test.exs index c6ca32494652..ca3f13f3aa50 100644 --- a/apps/explorer/test/explorer/chain/address_test.exs +++ b/apps/explorer/test/explorer/chain/address_test.exs @@ -72,7 +72,7 @@ defmodule Explorer.Chain.AddressTest do test "with top addresses in order" do address_hashes = - 4..1 + 4..1//-1 |> Enum.map(&insert(:address, fetched_coin_balance: &1)) |> Enum.map(& &1.hash) diff --git a/apps/explorer/test/explorer/chain_spec/parity/importer_test.exs b/apps/explorer/test/explorer/chain_spec/parity/importer_test.exs index e610fd661dcd..24ce1da121b6 100644 --- a/apps/explorer/test/explorer/chain_spec/parity/importer_test.exs +++ b/apps/explorer/test/explorer/chain_spec/parity/importer_test.exs @@ -108,9 +108,9 @@ defmodule Explorer.ChainSpec.Parity.ImporterTest do assert %{ address_hash: %Hash{ byte_count: 20, - bytes: <<167, 105, 41, 137, 10, 123, 71, 251, 133, 145, 150, 1, 108, 111, 221, 130, 137, 206, 183, 85>> + bytes: <<25, 104, 125, 170, 57, 195, 104, 19, 155, 110, 123, 230, 13, 193, 117, 58, 159, 12, 190, 163>> }, - value: 5_000_000_000_000_000_000_000, + value: 8_000_000_000_000_000_000_000, contract_code: nil, nonce: 0 } == diff --git a/apps/indexer/lib/indexer/block/catchup/fetcher.ex b/apps/indexer/lib/indexer/block/catchup/fetcher.ex index ea5454b5a214..11a7de7f3af4 100644 --- a/apps/indexer/lib/indexer/block/catchup/fetcher.ex +++ b/apps/indexer/lib/indexer/block/catchup/fetcher.ex @@ -58,8 +58,8 @@ defmodule Indexer.Block.Catchup.Fetcher do } missing_ranges -> - first.._ = List.first(missing_ranges) - _..last = List.last(missing_ranges) + first.._//_ = List.first(missing_ranges) + _..last//_ = List.last(missing_ranges) Logger.metadata(first_block_number: first, last_block_number: last) @@ -165,7 +165,7 @@ defmodule Indexer.Block.Catchup.Fetcher do ) defp fetch_and_import_missing_range( %__MODULE__{block_fetcher: %Block.Fetcher{} = block_fetcher}, - first..last = range + first..last//_ = range ) do Logger.metadata(fetcher: :block_catchup, first_block_number: first, last_block_number: last) Process.flag(:trap_exit, true) @@ -270,7 +270,7 @@ defmodule Indexer.Block.Catchup.Fetcher do number, nil -> {:cont, number..number} - number, first..last when number == last - 1 -> + number, first..last//_ when number == last - 1 -> {:cont, first..number} number, range -> diff --git a/apps/indexer/lib/indexer/block/catchup/missing_ranges_collector.ex b/apps/indexer/lib/indexer/block/catchup/missing_ranges_collector.ex index 43b79e7d148d..ebd10dc41d6b 100644 --- a/apps/indexer/lib/indexer/block/catchup/missing_ranges_collector.ex +++ b/apps/indexer/lib/indexer/block/catchup/missing_ranges_collector.ex @@ -60,7 +60,7 @@ defmodule Indexer.Block.Catchup.MissingRangesCollector do ranges |> Enum.reverse() - |> Enum.flat_map(fn f..l -> Chain.missing_block_number_ranges(l..f) end) + |> Enum.flat_map(fn f..l//_ -> Chain.missing_block_number_ranges(l..f) end) |> MissingRangesManipulator.save_batch() if not is_nil(max_fetched_block_number) do @@ -246,7 +246,7 @@ defmodule Indexer.Block.Catchup.MissingRangesCollector do |> RangesHelper.sanitize_ranges() case List.last(ranges) do - _from.._to -> + _from.._to//_ -> {:finite_ranges, ranges} nil -> diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index b8a4d716b201..533fd068b826 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -137,7 +137,7 @@ defmodule Indexer.Block.Fetcher do callback_module: callback_module, json_rpc_named_arguments: json_rpc_named_arguments } = state, - _.._ = range, + _.._//_ = range, additional_options \\ %{} ) when callback_module != nil do diff --git a/apps/indexer/lib/indexer/bound_interval.ex b/apps/indexer/lib/indexer/bound_interval.ex index 2b09b5c0972e..27c8b559d17c 100644 --- a/apps/indexer/lib/indexer/bound_interval.ex +++ b/apps/indexer/lib/indexer/bound_interval.ex @@ -8,7 +8,7 @@ defmodule Indexer.BoundInterval do current: 1, maximum: nil - def within(minimum..maximum) when is_integer(minimum) and is_integer(maximum) and minimum <= maximum do + def within(minimum..maximum//_) when is_integer(minimum) and is_integer(maximum) and minimum <= maximum do %__MODULE__{minimum: minimum, current: minimum, maximum: maximum} end diff --git a/apps/indexer/lib/indexer/fetcher/celo/validator_group_votes.ex b/apps/indexer/lib/indexer/fetcher/celo/validator_group_votes.ex index f51e736e00b4..0369dfba071d 100644 --- a/apps/indexer/lib/indexer/fetcher/celo/validator_group_votes.ex +++ b/apps/indexer/lib/indexer/fetcher/celo/validator_group_votes.ex @@ -133,7 +133,7 @@ defmodule Indexer.Fetcher.Celo.ValidatorGroupVotes do end end - defp process_chunk(_..chunk_to_block = chunk, block_range, json_rpc_named_arguments) do + defp process_chunk(_..chunk_to_block//_ = chunk, block_range, json_rpc_named_arguments) do validator_group_votes = chunk |> fetch_logs_chunk(block_range, json_rpc_named_arguments) @@ -156,8 +156,8 @@ defmodule Indexer.Fetcher.Celo.ValidatorGroupVotes do end defp fetch_logs_chunk( - chunk_from_block..chunk_to_block, - from_block..to_block, + chunk_from_block..chunk_to_block//_, + from_block..to_block//_, json_rpc_named_arguments ) do IndexerHelper.log_blocks_chunk_handling(chunk_from_block, chunk_to_block, from_block, to_block, nil, :L1) diff --git a/apps/indexer/lib/indexer/fetcher/optimism/txn_batch.ex b/apps/indexer/lib/indexer/fetcher/optimism/txn_batch.ex index 5f70a79f706e..f3373ad57961 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/txn_batch.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/txn_batch.ex @@ -96,7 +96,8 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do {:reorg_monitor_started, !is_nil(Process.whereis(RollupL1ReorgMonitor))}, {:rpc_l1_undefined, false} <- {:rpc_l1_undefined, is_nil(optimism_l1_rpc)}, json_rpc_named_arguments = Optimism.json_rpc_named_arguments(optimism_l1_rpc), - {start_block_l1, batch_inbox, batch_submitter} = read_system_config(system_config, json_rpc_named_arguments), + {:system_config_read, {start_block_l1, batch_inbox, batch_submitter}} <- + {:system_config_read, read_system_config(system_config, json_rpc_named_arguments)}, {:batch_inbox_valid, true} <- {:batch_inbox_valid, Helper.address_correct?(batch_inbox)}, {:batch_submitter_valid, true} <- {:batch_submitter_valid, Helper.address_correct?(batch_submitter)}, @@ -179,7 +180,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do {:stop, :normal, state} - nil -> + {:system_config_read, nil} -> Logger.error("Cannot read SystemConfig contract.") {:stop, :normal, state} diff --git a/apps/indexer/lib/indexer/helper.ex b/apps/indexer/lib/indexer/helper.ex index 512e3716785a..b7e110100cd3 100644 --- a/apps/indexer/lib/indexer/helper.ex +++ b/apps/indexer/lib/indexer/helper.ex @@ -220,7 +220,7 @@ defmodule Indexer.Helper do [1..3, 4..6, 7..9, 10..10] """ @spec range_chunk_every(Range.t(), non_neg_integer()) :: Enum.t() - def range_chunk_every(from..to, chunk_size) do + def range_chunk_every(from..to//_, chunk_size) do chunks_number = floor((to - from + 1) / chunk_size) 0..chunks_number diff --git a/docker/Dockerfile b/docker/Dockerfile index edcc99047162..faa74bd25f53 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM hexpm/elixir:1.16.3-erlang-26.2.5.1-alpine-3.18.7 AS builder +FROM hexpm/elixir:1.17.3-erlang-27.1-alpine-3.20.3 AS builder WORKDIR /app @@ -56,7 +56,7 @@ RUN mkdir -p /opt/release \ && mv _build/${MIX_ENV}/rel/blockscout /opt/release ############################################################## -FROM hexpm/elixir:1.16.3-erlang-26.2.5.1-alpine-3.18.7 +FROM hexpm/elixir:1.17.3-erlang-27.1-alpine-3.20.3 ARG RELEASE_VERSION ENV RELEASE_VERSION=${RELEASE_VERSION} From e66d345b96a2d3b2cdece4ad5fbd996cef44e099 Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Fri, 27 Sep 2024 17:17:05 +0300 Subject: [PATCH 183/363] feat: add verbosity to GraphQL token transfers query (#10770) * feat: GraphQL query for token transfers with verbose token, transaction, and block info * feat: extend GraphQL schema to support legacy `tokenTransferTxs` query * fix: cspell warnings * chore: add spec and doc for `token_transfers_by_address_hash/4` * chore: remove duplicate function declaration * fix: do not treat examples as doctests --- .../graphql/celo/resolvers/token_transfer.ex | 22 +++ .../celo/resolvers/token_transfer_tx.ex | 34 +++++ .../graphql/celo/schema/query_fields.ex | 28 ++++ .../graphql/celo/schema/types.ex | 87 +++++++++++ .../graphql/resolvers/block.ex | 15 +- .../graphql/resolvers/token.ex | 19 +++ .../graphql/resolvers/token_transfer.ex | 15 +- .../graphql/resolvers/transaction.ex | 18 ++- .../lib/block_scout_web/graphql/schema.ex | 11 ++ .../block_scout_web/graphql/schema/types.ex | 135 ++++++++++++----- apps/explorer/lib/explorer/chain.ex | 4 +- .../lib/explorer/chain/token_transfer.ex | 43 +++++- apps/explorer/lib/explorer/graphql.ex | 13 ++ apps/explorer/lib/explorer/graphql/celo.ex | 139 ++++++++++++++++++ 14 files changed, 538 insertions(+), 45 deletions(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer_tx.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/query_fields.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/types.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token.ex create mode 100644 apps/explorer/lib/explorer/graphql/celo.ex diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer.ex b/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer.ex new file mode 100644 index 000000000000..31e4a252c3f5 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer.ex @@ -0,0 +1,22 @@ +defmodule BlockScoutWeb.GraphQL.Celo.Resolvers.TokenTransfer do + @moduledoc """ + Resolvers for token transfers, used in the CELO schema. + """ + + alias Absinthe.Relay.Connection + alias Explorer.GraphQL.Celo, as: GraphQL + alias Explorer.Repo + + def get_by(_, args, _) do + connection_args = Map.take(args, [:after, :before, :first, :last]) + + GraphQL.token_tx_transfers_query() + |> Connection.from_query(&Repo.all/1, connection_args, options(args)) + end + + defp options(%{before: _}), do: [] + + defp options(%{count: count}), do: [count: count] + + defp options(_), do: [] +end diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer_tx.ex b/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer_tx.ex new file mode 100644 index 000000000000..557b30dde0a1 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer_tx.ex @@ -0,0 +1,34 @@ +defmodule BlockScoutWeb.GraphQL.Celo.Resolvers.TokenTransferTx do + @moduledoc false + + alias Absinthe.Relay.Connection + alias Explorer.GraphQL.Celo, as: GraphQL + alias Explorer.Repo + + def get_by(_, %{address_hash: address_hash, first: limit} = args, _) do + connection_args = Map.take(args, [:after, :before, :first, :last]) + + offset = + case Connection.offset(args) do + {:ok, offset} when is_integer(offset) -> offset + _ -> 0 + end + + address_hash + |> GraphQL.token_tx_transfers_query_for_address(offset, limit) + |> Connection.from_query(&Repo.all/1, connection_args, options(args)) + end + + def get_by(_, args, _) do + connection_args = Map.take(args, [:after, :before, :first, :last]) + + GraphQL.token_tx_transfers_query() + |> Connection.from_query(&Repo.all/1, connection_args, options(args)) + end + + defp options(%{before: _}), do: [] + + defp options(%{count: count}), do: [count: count] + + defp options(_), do: [] +end diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/query_fields.ex b/apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/query_fields.ex new file mode 100644 index 000000000000..f24fa7b6354d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/query_fields.ex @@ -0,0 +1,28 @@ +defmodule BlockScoutWeb.GraphQL.Celo.QueryFields do + @moduledoc """ + Query fields for the CELO schema. + """ + + alias BlockScoutWeb.GraphQL.Celo.Resolvers.TokenTransferTx + + use Absinthe.Schema.Notation + use Absinthe.Relay.Schema, :modern + + defmacro generate do + quote do + @desc "Gets token transfer transactions." + connection field(:token_transfer_txs, node_type: :transfer_tx) do + arg(:address_hash, :address_hash) + arg(:count, :integer) + + resolve(&TokenTransferTx.get_by/3) + + complexity(fn + %{first: first}, child_complexity -> first * child_complexity + %{last: last}, child_complexity -> last * child_complexity + %{}, _child_complexity -> 0 + end) + end + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/types.ex b/apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/types.ex new file mode 100644 index 000000000000..6d65f185f0b9 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/types.ex @@ -0,0 +1,87 @@ +defmodule BlockScoutWeb.GraphQL.Celo.Schema.Types do + @moduledoc false + + use Absinthe.Schema.Notation + use Absinthe.Relay.Schema.Notation, :modern + + alias BlockScoutWeb.GraphQL.Celo.Resolvers.TokenTransfer + + @desc """ + Represents a CELO or usd token transfer between addresses. + """ + node object(:celo_transfer, id_fetcher: &celo_transfer_id_fetcher/2) do + field(:value, :decimal) + field(:token, :string) + field(:token_address, :string) + field(:token_type, :string) + field(:token_id, :decimal) + field(:block_number, :integer) + field(:from_address_hash, :address_hash) + field(:to_address_hash, :address_hash) + field(:transaction_hash, :full_hash) + + field(:log_index, :integer) + + field(:gas_price, :wei) + field(:gas_used, :decimal) + field(:input, :string) + field(:timestamp, :datetime) + field(:comment, :string) + + field(:to_account_hash, :address_hash) + field(:from_account_hash, :address_hash) + end + + @desc """ + Represents a CELO token transfer between addresses. + """ + node object(:transfer_tx, id_fetcher: &transfer_tx_id_fetcher/2) do + field(:gateway_fee_recipient, :address_hash) + field(:gateway_fee, :address_hash) + field(:fee_currency, :address_hash) + field(:fee_token, :string) + field(:address_hash, :address_hash) + field(:transaction_hash, :full_hash) + field(:block_number, :integer) + field(:gas_price, :wei) + field(:gas_used, :decimal) + field(:input, :string) + field(:timestamp, :datetime) + + connection field(:token_transfer, node_type: :celo_transfer) do + arg(:count, :integer) + resolve(&TokenTransfer.get_by/3) + + complexity(fn + %{first: first}, child_complexity -> + first * child_complexity + + %{last: last}, child_complexity -> + last * child_complexity + end) + end + end + + connection(node_type: :transfer_tx) + connection(node_type: :celo_transfer) + + defp transfer_tx_id_fetcher( + %{transaction_hash: transaction_hash, address_hash: address_hash}, + _ + ) do + Jason.encode!(%{ + transaction_hash: to_string(transaction_hash), + address_hash: to_string(address_hash) + }) + end + + defp celo_transfer_id_fetcher( + %{transaction_hash: transaction_hash, log_index: log_index}, + _ + ) do + Jason.encode!(%{ + transaction_hash: to_string(transaction_hash), + log_index: log_index + }) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/block.ex b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/block.ex index 913ae80586d9..24de1e62599d 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/block.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/block.ex @@ -3,14 +3,27 @@ defmodule BlockScoutWeb.GraphQL.Resolvers.Block do alias BlockScoutWeb.GraphQL.Resolvers.Helper alias Explorer.Chain + alias Explorer.Chain.Transaction + + @api_true [api?: true] def get_by(_, %{number: number}, resolution) do with {:api_enabled, true} <- {:api_enabled, resolution.context.api_enabled}, - {:ok, _} = result <- Chain.number_to_block(number) do + {:ok, _} = result <- Chain.number_to_block(number, @api_true) do result else {:api_enabled, false} -> {:error, Helper.api_is_disabled()} {:error, :not_found} -> {:error, "Block number #{number} was not found."} end end + + def get_by(%Transaction{block_hash: hash}, _, resolution) do + with {:api_enabled, true} <- {:api_enabled, resolution.context.api_enabled}, + {:ok, _} = result <- Chain.hash_to_block(hash, @api_true) do + result + else + {:api_enabled, false} -> {:error, Helper.api_is_disabled()} + {:error, :not_found} -> {:error, "Block hash #{to_string(hash)} was not found."} + end + end end diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token.ex b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token.ex new file mode 100644 index 000000000000..a830537b3853 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token.ex @@ -0,0 +1,19 @@ +defmodule BlockScoutWeb.GraphQL.Resolvers.Token do + @moduledoc false + + alias BlockScoutWeb.GraphQL.Resolvers.Helper + alias Explorer.Chain.TokenTransfer + alias Explorer.GraphQL + + def get_by( + %TokenTransfer{token_contract_address_hash: token_contract_address_hash}, + _, + resolution + ) do + if resolution.context.api_enabled do + GraphQL.get_token(%{contract_address_hash: token_contract_address_hash}) + else + {:error, Helper.api_is_disabled()} + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token_transfer.ex b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token_transfer.ex index 2fe2a05a6521..2302befd7150 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token_transfer.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token_transfer.ex @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.GraphQL.Resolvers.TokenTransfer do alias Absinthe.Relay.Connection alias BlockScoutWeb.GraphQL.Resolvers.Helper + alias Explorer.Chain.{Address, TokenTransfer} alias Explorer.{GraphQL, Repo} def get_by(%{transaction_hash: _, log_index: _} = args, resolution) do @@ -19,7 +20,19 @@ defmodule BlockScoutWeb.GraphQL.Resolvers.TokenTransfer do token_contract_address_hash |> GraphQL.list_token_transfers_query() - |> Connection.from_query(&Repo.all/1, connection_args, options(args)) + |> Connection.from_query(&Repo.replica().all/1, connection_args, options(args)) + else + {:error, Helper.api_is_disabled()} + end + end + + def get_by(%Address{hash: address_hash}, args, resolution) do + if resolution.context.api_enabled do + connection_args = Map.take(args, [:after, :before, :first, :last]) + + address_hash + |> TokenTransfer.token_transfers_by_address_hash(nil, [], nil) + |> Connection.from_query(&Repo.replica().all/1, connection_args, options(args)) else {:error, Helper.api_is_disabled()} end diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/transaction.ex b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/transaction.ex index 2dd299abae1a..3dbeb9924f66 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/transaction.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/transaction.ex @@ -4,11 +4,13 @@ defmodule BlockScoutWeb.GraphQL.Resolvers.Transaction do alias Absinthe.Relay.Connection alias BlockScoutWeb.GraphQL.Resolvers.Helper alias Explorer.{Chain, GraphQL, Repo} - alias Explorer.Chain.Address + alias Explorer.Chain.{Address, TokenTransfer} + + @api_true [api?: true] def get_by(_, %{hash: hash}, resolution) do with {:api_enabled, true} <- {:api_enabled, resolution.context.api_enabled}, - {:ok, transaction} <- Chain.hash_to_transaction(hash) do + {:ok, transaction} <- Chain.hash_to_transaction(hash, @api_true) do {:ok, transaction} else {:api_enabled, false} -> {:error, Helper.api_is_disabled()} @@ -22,12 +24,22 @@ defmodule BlockScoutWeb.GraphQL.Resolvers.Transaction do if resolution.context.api_enabled do address_hash |> GraphQL.address_to_transactions_query(args.order) - |> Connection.from_query(&Repo.all/1, connection_args, options(args)) + |> Connection.from_query(&Repo.replica().all/1, connection_args, options(args)) else {:error, Helper.api_is_disabled()} end end + def get_by(%TokenTransfer{transaction_hash: hash}, _, resolution) do + with {:api_enabled, true} <- {:api_enabled, resolution.context.api_enabled}, + {:ok, transaction} <- Chain.hash_to_transaction(hash, @api_true) do + {:ok, transaction} + else + {:api_enabled, false} -> {:error, Helper.api_is_disabled()} + {:error, :not_found} -> {:error, "Transaction not found."} + end + end + defp options(%{before: _}), do: [] defp options(%{count: count}), do: [count: count] diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/schema.ex b/apps/block_scout_web/lib/block_scout_web/graphql/schema.ex index 91b11a695900..1bfee0f8a48a 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/schema.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/schema.ex @@ -22,6 +22,10 @@ defmodule BlockScoutWeb.GraphQL.Schema do import_types(BlockScoutWeb.GraphQL.Schema.Types) + if Application.compile_env(:explorer, :chain_type) == :celo do + import_types(BlockScoutWeb.GraphQL.Celo.Schema.Types) + end + node interface do resolve_type(fn %ExplorerChainInternalTransaction{}, _ -> @@ -100,6 +104,13 @@ defmodule BlockScoutWeb.GraphQL.Schema do arg(:hash, non_null(:full_hash)) resolve(&Transaction.get_by/3) end + + if Application.compile_env(:explorer, :chain_type) == :celo do + require BlockScoutWeb.GraphQL.Celo.QueryFields + alias BlockScoutWeb.GraphQL.Celo.QueryFields + + QueryFields.generate() + end end subscription do diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex b/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex index 192d38d2f170..6cfde913b422 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex @@ -1,3 +1,66 @@ +defmodule BlockScoutWeb.GraphQL.Schema.Transaction do + @moduledoc false + + alias BlockScoutWeb.GraphQL.Resolvers.{Block, InternalTransaction} + + case Application.compile_env(:explorer, :chain_type) do + :celo -> + @chain_type_fields quote( + do: [ + field(:gas_token_contract_address_hash, :address_hash) + ] + ) + + _ -> + @chain_type_fields quote(do: []) + end + + defmacro generate do + quote do + node object(:transaction, id_fetcher: &transaction_id_fetcher/2) do + field(:cumulative_gas_used, :decimal) + field(:error, :string) + field(:gas, :decimal) + field(:gas_price, :wei) + field(:gas_used, :decimal) + field(:hash, :full_hash) + field(:index, :integer) + field(:input, :string) + field(:nonce, :nonce_hash) + field(:r, :decimal) + field(:s, :decimal) + field(:status, :status) + field(:v, :decimal) + field(:value, :wei) + field(:block_hash, :full_hash) + field(:block_number, :integer) + field(:from_address_hash, :address_hash) + field(:to_address_hash, :address_hash) + field(:created_contract_address_hash, :address_hash) + field(:earliest_processing_start, :datetime) + field(:revert_reason, :string) + field(:max_priority_fee_per_gas, :wei) + field(:max_fee_per_gas, :wei) + field(:type, :integer) + field(:has_error_in_internal_txs, :boolean) + + field :block, :block do + resolve(&Block.get_by/3) + end + + connection field(:internal_transactions, node_type: :internal_transaction) do + arg(:count, :integer) + resolve(&InternalTransaction.get_by/3) + + complexity(fn params, child_complexity -> process_complexity(params, child_complexity) end) + end + + unquote_splicing(@chain_type_fields) + end + end + end +end + defmodule BlockScoutWeb.GraphQL.Schema.SmartContracts do @moduledoc false case Application.compile_env(:explorer, :chain_type) do @@ -42,7 +105,7 @@ end defmodule BlockScoutWeb.GraphQL.Schema.Types do @moduledoc false - require BlockScoutWeb.GraphQL.Schema.SmartContracts + require BlockScoutWeb.GraphQL.Schema.{Transaction, SmartContracts} use Absinthe.Schema.Notation use Absinthe.Relay.Schema.Notation, :modern @@ -50,11 +113,13 @@ defmodule BlockScoutWeb.GraphQL.Schema.Types do import Absinthe.Resolution.Helpers alias BlockScoutWeb.GraphQL.Resolvers.{ - InternalTransaction, + Token, + TokenTransfer, Transaction } alias BlockScoutWeb.GraphQL.Schema.SmartContracts, as: SmartContractsSchema + alias BlockScoutWeb.GraphQL.Schema.Transaction, as: TransactionSchema import_types(Absinthe.Type.Custom) import_types(BlockScoutWeb.GraphQL.Schema.Scalars) @@ -87,6 +152,13 @@ defmodule BlockScoutWeb.GraphQL.Schema.Types do complexity(fn params, child_complexity -> process_complexity(params, child_complexity) end) end + + connection field(:token_transfers, node_type: :token_transfer) do + arg(:count, :integer) + resolve(&TokenTransfer.get_by/3) + + complexity(fn params, child_complexity -> process_complexity(params, child_complexity) end) + end end @desc """ @@ -160,46 +232,37 @@ defmodule BlockScoutWeb.GraphQL.Schema.Types do field(:to_address_hash, :address_hash) field(:token_contract_address_hash, :address_hash) field(:transaction_hash, :full_hash) + + field :transaction, :transaction do + resolve(&Transaction.get_by/3) + end + + field :token, :token do + resolve(&Token.get_by/3) + end end @desc """ - Models a Web3 transaction. + Represents a token. """ - node object(:transaction, id_fetcher: &transaction_id_fetcher/2) do - field(:cumulative_gas_used, :decimal) - field(:error, :string) - field(:gas, :decimal) - field(:gas_price, :wei) - field(:gas_used, :decimal) - field(:hash, :full_hash) - field(:index, :integer) - field(:input, :string) - field(:nonce, :nonce_hash) - field(:r, :decimal) - field(:s, :decimal) - field(:status, :status) - field(:v, :decimal) - field(:value, :wei) - field(:block_hash, :full_hash) - field(:block_number, :integer) - field(:from_address_hash, :address_hash) - field(:to_address_hash, :address_hash) - field(:created_contract_address_hash, :address_hash) - field(:earliest_processing_start, :datetime) - field(:revert_reason, :string) - field(:max_priority_fee_per_gas, :wei) - field(:max_fee_per_gas, :wei) - field(:type, :integer) - field(:has_error_in_internal_txs, :boolean) - - connection field(:internal_transactions, node_type: :internal_transaction) do - arg(:count, :integer) - resolve(&InternalTransaction.get_by/3) - - complexity(fn params, child_complexity -> process_complexity(params, child_complexity) end) - end + object :token do + field(:name, :string) + field(:symbol, :string) + field(:total_supply, :decimal) + field(:decimals, :decimal) + field(:type, :string) + field(:holder_count, :integer) + field(:circulating_market_cap, :decimal) + field(:icon_url, :string) + field(:volume_24h, :decimal) + field(:contract_address_hash, :address_hash) end + @desc """ + Models a Web3 transaction. + """ + TransactionSchema.generate() + def token_transfer_id_fetcher(%{transaction_hash: transaction_hash, log_index: log_index}, _) do Jason.encode!(%{transaction_hash: to_string(transaction_hash), log_index: log_index}) end diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 8c64622cb89a..216d42c70075 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -317,8 +317,8 @@ defmodule Explorer.Chain do filters = Keyword.get(options, :token_type) necessity_by_association = Keyword.get(options, :necessity_by_association) - direction - |> TokenTransfer.token_transfers_by_address_hash(address_hash, filters, paging_options) + address_hash + |> TokenTransfer.token_transfers_by_address_hash(direction, filters, paging_options) |> join_associations(necessity_by_association) |> select_repo(options).all() end diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index bafd52e39cbc..8bf41f8c88d3 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -442,7 +442,46 @@ defmodule Explorer.Chain.TokenTransfer do |> order_by([tt], desc: tt.block_number, desc: tt.log_index) end - def token_transfers_by_address_hash(direction, address_hash, token_types, paging_options) do + @doc """ + Retrieves token transfers associated with a given address, optionally filtered + by direction and token types. + + ## Parameters + + - `address_hash` (`Hash.Address.t()`): The address hash for which to retrieve + token transfers. + - `direction` (`nil | :to | :from`): The direction of the transfers to filter. + - `:to` - transfers where `to_address` matches `address_hash`. + - `:from` - transfers where `from_address` matches `address_hash`. + - `nil` - includes both incoming and outgoing transfers. + - `token_types` (`[binary()]`): The token types to filter, e.g `["ERC20", "ERC721"]`. + - `paging_options` (`nil | Explorer.PagingOptions.t()`): Pagination options to + limit the result set. + + ## Returns + + An `Ecto.Query` for `TokenTransfer.t()`. + + ## Examples + + Fetch all incoming ERC20 token transfers for a specific address: + + # iex> query = token_transfers_by_address_hash(address_hash, :to, ["ERC20"], paging_options) + # iex> Repo.all(query) + + Fetch both incoming and outgoing token transfers for a specific address + without pagination, token type filtering, and direction filtering: + + # iex> query = token_transfers_by_address_hash(address_hash, nil, [], nil) + # iex> Repo.all(query) + """ + @spec token_transfers_by_address_hash( + Hash.Address.t(), + nil | :to | :from, + [binary()], + nil | Explorer.PagingOptions.t() + ) :: Ecto.Query.t() + def token_transfers_by_address_hash(address_hash, direction, token_types, paging_options) do if direction == :to || direction == :from do only_consensus_transfers_query() |> filter_by_direction(direction, address_hash) @@ -474,7 +513,7 @@ defmodule Explorer.Chain.TokenTransfer do |> union(^from_address_hash_query) |> Chain.wrapped_union_subquery() |> order_by([tt], desc: tt.block_number, desc: tt.log_index) - |> limit(^paging_options.page_size) + |> handle_paging_options(paging_options) end end diff --git a/apps/explorer/lib/explorer/graphql.ex b/apps/explorer/lib/explorer/graphql.ex index feb318fde3ec..e275631615f6 100644 --- a/apps/explorer/lib/explorer/graphql.ex +++ b/apps/explorer/lib/explorer/graphql.ex @@ -14,6 +14,7 @@ defmodule Explorer.GraphQL do alias Explorer.Chain.{ Hash, InternalTransaction, + Token, TokenTransfer, Transaction } @@ -83,6 +84,18 @@ defmodule Explorer.GraphQL do end end + @doc """ + Returns a token for a given contract address hash. + """ + @spec get_token(map()) :: {:ok, Token.t()} | {:error, String.t()} + def get_token(%{contract_address_hash: _} = clauses) do + if token = Repo.replica().get_by(Token, clauses) do + {:ok, token} + else + {:error, "Token not found."} + end + end + @doc """ Returns a query to fetch token transfers for a token contract address hash. diff --git a/apps/explorer/lib/explorer/graphql/celo.ex b/apps/explorer/lib/explorer/graphql/celo.ex new file mode 100644 index 000000000000..3b92d12082a3 --- /dev/null +++ b/apps/explorer/lib/explorer/graphql/celo.ex @@ -0,0 +1,139 @@ +defmodule Explorer.GraphQL.Celo do + @moduledoc """ + Defines Ecto queries to fetch Celo blockchain data for the legacy GraphQL + schema. + + Includes functions to construct queries for token transfers and transactions. + """ + + import Ecto.Query, + only: [from: 2, order_by: 3, where: 3, subquery: 1] + + alias Explorer.Chain.{ + Block, + Hash, + Token, + TokenTransfer, + Transaction + } + + @doc """ + Constructs a paginated query for token transfers involving a specific address. + """ + @spec token_tx_transfers_query_for_address(Hash.Address.t(), integer(), integer()) :: Ecto.Query.t() + def token_tx_transfers_query_for_address(address_hash, offset, limit) do + page = floor(offset / limit) + 1 + growing_limit = limit * (page + 1) + + tokens = + from( + tt in TokenTransfer, + where: not is_nil(tt.transaction_hash), + where: tt.to_address_hash == ^address_hash, + or_where: tt.from_address_hash == ^address_hash, + select: %{ + transaction_hash: tt.transaction_hash, + block_number: tt.block_number, + to_address_hash: tt.to_address_hash, + from_address_hash: tt.from_address_hash + }, + distinct: [desc: tt.block_number, desc: tt.transaction_hash], + order_by: [ + desc: tt.block_number, + desc: tt.transaction_hash, + desc: tt.from_address_hash, + desc: tt.to_address_hash + ], + limit: ^growing_limit + ) + + query = + from( + tt in subquery(tokens), + as: :token_transfer, + inner_join: tx in Transaction, + as: :transaction, + on: tx.hash == tt.transaction_hash, + inner_join: b in Block, + on: tx.block_hash == b.hash, + left_join: token in Token, + on: tx.gas_token_contract_address_hash == token.contract_address_hash, + select: %{ + transaction_hash: tt.transaction_hash, + to_address_hash: tt.to_address_hash, + from_address_hash: tt.from_address_hash, + gas_used: tx.gas_used, + gas_price: tx.gas_price, + fee_currency: tx.gas_token_contract_address_hash, + fee_token: fragment("coalesce(?, 'CELO')", token.symbol), + # gateway_fee: tx.gateway_fee, + # gateway_fee_recipient: tx.gas_fee_recipient_hash, + timestamp: b.timestamp, + input: tx.input, + nonce: tx.nonce, + block_number: tt.block_number + } + ) + + query + |> order_by([transaction: t], + desc: t.block_number, + desc: t.hash, + asc: t.nonce, + desc: t.from_address_hash, + desc: t.to_address_hash + ) + end + + @doc """ + Constructs a query for token transfers filtered by a specific address. + """ + @spec token_tx_transfers_query_by_address(Hash.Address.t()) :: Ecto.Query.t() + def token_tx_transfers_query_by_address(address_hash) do + token_tx_transfers_query() + |> where([t], t.from_address_hash == ^address_hash or t.to_address_hash == ^address_hash) + |> order_by([transaction: t], desc: t.block_number, asc: t.nonce) + end + + @doc """ + Constructs a query to fetch detailed token transfer information. + """ + @spec token_tx_transfers_query() :: Ecto.Query.t() + def token_tx_transfers_query do + from( + tt in TokenTransfer, + inner_join: tx in Transaction, + as: :transaction, + on: tx.hash == tt.transaction_hash, + inner_join: b in Block, + on: tt.block_number == b.number, + # left_join: wf in CeloWalletAccounts, + # on: tt.from_address_hash == wf.wallet_address_hash, + # left_join: wt in CeloWalletAccounts, + # on: tt.to_address_hash == wt.wallet_address_hash, + left_join: token in Token, + on: tt.token_contract_address_hash == token.contract_address_hash, + select: %{ + gas_used: tx.gas_used, + gas_price: tx.gas_price, + timestamp: b.timestamp, + input: tx.input, + transaction_hash: tt.transaction_hash, + from_address_hash: tt.from_address_hash, + to_address_hash: tt.to_address_hash, + # from_account_hash: wf.account_address_hash, + # to_account_hash: wt.account_address_hash, + log_index: tt.log_index, + value: tt.amount, + # comment: tt.comment, + token: token.symbol, + token_address: token.contract_address_hash, + nonce: tx.nonce, + block_number: tt.block_number, + token_type: token.type, + token_id: fragment("(COALESCE(?, ARRAY[]::Decimal[]))[1]", tt.token_ids) + }, + order_by: [desc: tt.block_number, desc: tt.amount, desc: tt.log_index] + ) + end +end From 12517dbde551f1a19eeba2ed45631c935037d6eb Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 27 Sep 2024 18:59:49 +0300 Subject: [PATCH 184/363] feat: Address scam badge flag (#10763) * Address badges * Pass badges preload in all controllers related to address * Process review comments: redesign routes * Changes to fit specified requirements * Hide scam addresses from search based on the flag at the backend * Refactoring based on review comments * Update apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_badge_controller.ex Co-authored-by: nikitosing <32202610+nikitosing@users.noreply.github.com> * Update apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_badge_controller.ex Co-authored-by: nikitosing <32202610+nikitosing@users.noreply.github.com> * Add addresses filtering * Hide scam tokens from the lists --------- Co-authored-by: nikitosing <32202610+nikitosing@users.noreply.github.com> --- apps/block_scout_web/.sobelow-conf | 3 +- .../channels/address_channel.ex | 18 +++- .../api/v2/address_badge_controller.ex | 90 +++++++++++++++++++ .../controllers/api/v2/address_controller.ex | 19 ++-- .../controllers/api/v2/block_controller.ex | 10 +-- .../controllers/api/v2/fallback_controller.ex | 7 ++ .../api/v2/main_page_controller.ex | 4 +- .../api/v2/transaction_controller.ex | 26 ++++-- .../transaction_interpretation.ex | 10 +-- .../models/transaction_state_helper.ex | 10 +-- .../lib/block_scout_web/notifier.ex | 8 +- .../routers/address_badges_v2_router.ex | 58 ++++++++++++ .../lib/block_scout_web/routers/api_router.ex | 11 ++- .../views/api/v2/address_badge_view.ex | 33 +++++++ .../block_scout_web/views/api/v2/helper.ex | 7 ++ apps/explorer/lib/explorer/chain/address.ex | 4 + .../chain/address/scam_badge_to_address.ex | 79 ++++++++++++++++ .../lib/explorer/chain/advanced_filter.ex | 12 +-- .../lib/explorer/chain/celo/epoch_reward.ex | 4 +- apps/explorer/lib/explorer/chain/search.ex | 12 ++- .../lib/explorer/chain/smart_contract.ex | 2 + apps/explorer/lib/explorer/chain/token.ex | 2 + .../lib/explorer/chain/token_transfer.ex | 4 +- apps/explorer/lib/explorer/helper.ex | 38 +++++++- ...240910095635_add_address_badges_tables.exs | 14 +++ config/runtime.exs | 1 + cspell.json | 1 + docker-compose/envs/common-blockscout.env | 5 +- 28 files changed, 431 insertions(+), 61 deletions(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_badge_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/routers/address_badges_v2_router.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/address_badge_view.ex create mode 100644 apps/explorer/lib/explorer/chain/address/scam_badge_to_address.ex create mode 100644 apps/explorer/priv/repo/migrations/20240910095635_add_address_badges_tables.exs diff --git a/apps/block_scout_web/.sobelow-conf b/apps/block_scout_web/.sobelow-conf index 70a8e7b0104f..45fc3453b994 100644 --- a/apps/block_scout_web/.sobelow-conf +++ b/apps/block_scout_web/.sobelow-conf @@ -9,6 +9,7 @@ ignore_files: [ "apps/block_scout_web/lib/block_scout_web/routers/smart_contracts_api_v2_router.ex", "apps/block_scout_web/lib/block_scout_web/routers/tokens_api_v2_router.ex", - "apps/block_scout_web/lib/block_scout_web/routers/utils_api_v2_router.ex" + "apps/block_scout_web/lib/block_scout_web/routers/utils_api_v2_router.ex", + "apps/block_scout_web/lib/block_scout_web/routers/address_badges_v2_router.ex" ] ] diff --git a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex index 7fd6932a41df..b22306431b24 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex @@ -50,8 +50,18 @@ defmodule BlockScoutWeb.AddressChannel do @transaction_associations [ from_address: [:names, :smart_contract, :proxy_implementations], - to_address: [:names, :smart_contract, :proxy_implementations], - created_contract_address: [:names, :smart_contract, :proxy_implementations] + to_address: [ + :scam_badge, + :names, + :smart_contract, + :proxy_implementations + ], + created_contract_address: [ + :scam_badge, + :names, + :smart_contract, + :proxy_implementations + ] ] ++ @chain_type_transaction_associations @@ -404,8 +414,8 @@ defmodule BlockScoutWeb.AddressChannel do token_transfers |> Repo.preload([ [ - from_address: [:names, :smart_contract, :proxy_implementations], - to_address: [:names, :smart_contract, :proxy_implementations] + from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], + to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] ] ]), conn: nil diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_badge_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_badge_controller.ex new file mode 100644 index 000000000000..5a3d58d572ea --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_badge_controller.ex @@ -0,0 +1,90 @@ +defmodule BlockScoutWeb.API.V2.AddressBadgeController do + require Logger + use BlockScoutWeb, :controller + + alias Explorer.Chain + alias Explorer.Chain.Address.ScamBadgeToAddress + alias Plug.Conn + + @api_true [api?: true] + + action_fallback(BlockScoutWeb.API.V2.FallbackController) + + def assign_badge_to_address( + conn, + %{ + "address_hashes" => address_hashes + } = params + ) + when is_list(address_hashes) do + with :ok <- check_sensitive_endpoint_api_key(params["api_key"]), + valid_address_hashes = filter_address_hashes(address_hashes), + {_num_of_inserted, badge_to_address_list} <- ScamBadgeToAddress.add(valid_address_hashes) do + conn + |> put_status(200) + |> render(:badge_to_address, %{ + badge_to_address_list: badge_to_address_list, + status: if(Enum.empty?(badge_to_address_list), do: "update skipped", else: "added") + }) + end + end + + def assign_badge_to_address(_, _), do: {:error, :not_found} + + def unassign_badge_from_address( + conn, + %{ + "address_hashes" => address_hashes + } = params + ) + when is_list(address_hashes) do + with :ok <- check_sensitive_endpoint_api_key(params["api_key"]), + valid_address_hashes = filter_address_hashes(address_hashes), + {_num_of_deleted, badge_to_address_list} <- ScamBadgeToAddress.delete(valid_address_hashes) do + conn + |> put_status(200) + |> render(:badge_to_address, %{ + badge_to_address_list: badge_to_address_list, + status: if(Enum.empty?(badge_to_address_list), do: "update skipped", else: "removed") + }) + end + end + + def unassign_badge_from_address(_, _), do: {:error, :not_found} + + def show_badge_addresses(conn, _) do + with {:ok, body, _conn} <- Conn.read_body(conn, []), + {:ok, %{"api_key" => provided_api_key}} <- Jason.decode(body), + :ok <- check_sensitive_endpoint_api_key(provided_api_key) do + badge_to_address_list = ScamBadgeToAddress.get(@api_true) + + conn + |> put_status(200) + |> render(:badge_to_address, %{ + badge_to_address_list: badge_to_address_list + }) + else + _ -> + {:error, :not_found} + end + end + + defp check_sensitive_endpoint_api_key(provided_api_key) do + with {:sensitive_endpoints_api_key, api_key} when not is_nil(api_key) <- + {:sensitive_endpoints_api_key, Application.get_env(:block_scout_web, :sensitive_endpoints_api_key)}, + {:api_key, ^api_key} <- {:api_key, provided_api_key} do + :ok + end + end + + defp filter_address_hashes(address_hashes) do + address_hashes + |> Enum.uniq() + |> Enum.filter(fn potential_address_hash -> + case Chain.string_to_address_hash(potential_address_hash) do + {:ok, _address_hash} -> true + _ -> false + end + end) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index 59afca890e2c..447d830313c4 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -52,9 +52,9 @@ defmodule BlockScoutWeb.API.V2.AddressController do @transaction_necessity_by_association [ necessity_by_association: %{ - [created_contract_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, :block => :optional } |> Map.merge(@chain_type_transaction_necessity_by_association), @@ -63,8 +63,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do @token_transfer_necessity_by_association [ necessity_by_association: %{ - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, :block => :optional, :transaction => :optional, :token => :optional @@ -75,6 +75,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do @address_options [ necessity_by_association: %{ :names => :optional, + :scam_badge => :optional, :token => :optional, :proxy_implementations => :optional }, @@ -212,8 +213,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do options = [ necessity_by_association: %{ - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, :block => :optional, :token => :optional, :transaction => :optional @@ -284,9 +285,9 @@ defmodule BlockScoutWeb.API.V2.AddressController do full_options = [ necessity_by_association: %{ - [created_contract_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional + [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional } ] |> Keyword.merge(paging_options(params)) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex index ab2fed6b17c6..f440de46f83f 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex @@ -83,9 +83,9 @@ defmodule BlockScoutWeb.API.V2.BlockController do @transaction_necessity_by_association [ necessity_by_association: %{ - [created_contract_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, :block => :optional } |> Map.merge(@chain_type_transaction_necessity_by_association) @@ -93,9 +93,9 @@ defmodule BlockScoutWeb.API.V2.BlockController do @internal_transaction_necessity_by_association [ necessity_by_association: %{ - [created_contract_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional + [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional } ] diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex index 7f2544b453ec..f6c90431e3fa 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex @@ -133,6 +133,13 @@ defmodule BlockScoutWeb.API.V2.FallbackController do |> render(:changeset_errors, changeset: changeset) end + def call(conn, {:error, :badge_creation_failed}) do + conn + |> put_status(:unprocessable_entity) + |> put_view(UserView) + |> render(:message, %{message: "Badge creation failed"}) + end + def call(conn, {:restricted_access, true}) do Logger.error(fn -> ["#{@restricted_access}"] diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex index 79a89f8edda7..ec99ba4422c6 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex @@ -24,9 +24,9 @@ defmodule BlockScoutWeb.API.V2.MainPageController do necessity_by_association: %{ :block => :required, - [created_contract_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional } |> Map.merge(@chain_type_transaction_necessity_by_association), paging_options: %PagingOptions{page_size: 6}, diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex index 43179c26c823..ce5e5bbed9c6 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex @@ -70,6 +70,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do :block => :optional, [ created_contract_address: [ + :scam_badge, :names, :token, :smart_contract, @@ -78,26 +79,33 @@ defmodule BlockScoutWeb.API.V2.TransactionController do ] => :optional, [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional + [ + to_address: [ + :scam_badge, + :names, + :smart_contract, + :proxy_implementations + ] + ] => :optional } |> Map.merge(@chain_type_transaction_necessity_by_association) @token_transfers_necessity_by_association %{ - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional } @token_transfers_in_tx_necessity_by_association %{ - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, token: :required } @internal_transaction_necessity_by_association [ necessity_by_association: %{ - [created_contract_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional + [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional } ] @@ -515,7 +523,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do validate_transaction(transaction_hash_string, params, necessity_by_association: %{ [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional }, api?: true ) do diff --git a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex index 591598fd42d7..94a0eb27e682 100644 --- a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex +++ b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex @@ -19,9 +19,9 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do @items_limit 50 @internal_transaction_necessity_by_association [ necessity_by_association: %{ - [created_contract_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional + [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional } ] @@ -108,9 +108,9 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do Chain.select_repo(@api_true).preload(transaction, [ :transaction_actions, :block, - to_address: [:names, :smart_contract], + to_address: [:scam_badge, :names, :smart_contract], from_address: [:names, :smart_contract], - created_contract_address: [:names, :token, :smart_contract] + created_contract_address: [:scam_badge, :names, :token, :smart_contract] ]) skip_sig_provider? = false diff --git a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex index 3d96d69ff19b..7774d28759ed 100644 --- a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex @@ -69,16 +69,16 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do |> Enum.find(&(&1.hash == transaction.hash)) |> Repo.preload( token_transfers: [ - from_address: [:names, :smart_contract, :proxy_implementations], - to_address: [:names, :smart_contract, :proxy_implementations] + from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], + to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] ], internal_transactions: [ - from_address: [:names, :smart_contract, :proxy_implementations], - to_address: [:names, :smart_contract, :proxy_implementations] + from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], + to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] ], block: [miner: [:names, :smart_contract, :proxy_implementations]], from_address: [:names, :smart_contract, :proxy_implementations], - to_address: [:names, :smart_contract, :proxy_implementations] + to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] ) previous_block_number = BlockNumberHelper.previous_block_number(transaction.block_number) diff --git a/apps/block_scout_web/lib/block_scout_web/notifier.ex b/apps/block_scout_web/lib/block_scout_web/notifier.ex index a25f81212020..089bc27de3fb 100644 --- a/apps/block_scout_web/lib/block_scout_web/notifier.ex +++ b/apps/block_scout_web/lib/block_scout_web/notifier.ex @@ -181,8 +181,8 @@ defmodule BlockScoutWeb.Notifier do DenormalizationHelper.extend_transaction_preload([ :token, :transaction, - from_address: [:names, :smart_contract, :proxy_implementations], - to_address: [:names, :smart_contract, :proxy_implementations] + from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], + to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] ]) ) @@ -205,9 +205,9 @@ defmodule BlockScoutWeb.Notifier do def handle_event({:chain_event, :transactions, :realtime, transactions}) do base_preloads = [ :block, - created_contract_address: [:names, :smart_contract, :proxy_implementations], + created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], from_address: [:names, :smart_contract, :proxy_implementations], - to_address: [:names, :smart_contract, :proxy_implementations] + to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] ] preloads = if API_V2.enabled?(), do: [:token_transfers | base_preloads], else: base_preloads diff --git a/apps/block_scout_web/lib/block_scout_web/routers/address_badges_v2_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/address_badges_v2_router.ex new file mode 100644 index 000000000000..36edce1b8c70 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/routers/address_badges_v2_router.ex @@ -0,0 +1,58 @@ +# This file in ignore list of `sobelow`, be careful while adding new endpoints here +defmodule BlockScoutWeb.Routers.AddressBadgesApiV2Router do + @moduledoc """ + Router for /api/v2/scam-badge-addresses. This route has separate router in order to ignore sobelow's warning about missing CSRF protection + """ + use BlockScoutWeb, :router + alias BlockScoutWeb.API.V2 + alias BlockScoutWeb.Plug.{CheckApiV2, RateLimit} + + @max_query_string_length 5_000 + + pipeline :api_v2 do + plug( + Plug.Parsers, + parsers: [:urlencoded, :multipart, :json], + query_string_length: @max_query_string_length, + pass: ["*/*"], + json_decoder: Poison + ) + + plug(BlockScoutWeb.Plug.Logger, application: :api_v2) + plug(:accepts, ["json"]) + plug(CheckApiV2) + plug(:fetch_session) + plug(:protect_from_forgery) + plug(RateLimit) + end + + pipeline :api_v2_no_forgery_protect do + plug( + Plug.Parsers, + parsers: [:urlencoded, :multipart, :json], + length: 20_000_000, + query_string_length: 5_000, + pass: ["*/*"], + json_decoder: Poison + ) + + plug(BlockScoutWeb.Plug.Logger, application: :api_v2) + plug(:accepts, ["json"]) + plug(CheckApiV2) + plug(RateLimit) + plug(:fetch_session) + end + + scope "/", as: :api_v2 do + pipe_through(:api_v2_no_forgery_protect) + + post("/", V2.AddressBadgeController, :assign_badge_to_address) + delete("/", V2.AddressBadgeController, :unassign_badge_from_address) + end + + scope "/", as: :api_v2 do + pipe_through(:api_v2) + + get("/", V2.AddressBadgeController, :show_badge_addresses) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index 2a987813fcaf..772527d2ec97 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -14,7 +14,15 @@ defmodule BlockScoutWeb.Routers.ApiRouter do """ use BlockScoutWeb, :router alias BlockScoutWeb.AddressTransactionController - alias BlockScoutWeb.Routers.{APIKeyV2Router, SmartContractsApiV2Router, TokensApiV2Router, UtilsApiV2Router} + + alias BlockScoutWeb.Routers.{ + AddressBadgesApiV2Router, + APIKeyV2Router, + SmartContractsApiV2Router, + TokensApiV2Router, + UtilsApiV2Router + } + alias BlockScoutWeb.Plug.{CheckApiV2, RateLimit} alias BlockScoutWeb.Routers.AccountRouter @@ -25,6 +33,7 @@ defmodule BlockScoutWeb.Routers.ApiRouter do forward("/v2/key", APIKeyV2Router) forward("/v2/utils", UtilsApiV2Router) + forward("/v2/scam-badge-addresses", AddressBadgesApiV2Router) pipeline :api do plug( diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_badge_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_badge_view.ex new file mode 100644 index 000000000000..d3eda4545f71 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_badge_view.ex @@ -0,0 +1,33 @@ +defmodule BlockScoutWeb.API.V2.AddressBadgeView do + use BlockScoutWeb, :view + + def render("badge_to_address.json", %{badge_to_address_list: badge_to_address_list, status: status}) do + prepare_badge_to_address(badge_to_address_list, status) + end + + def render("badge_to_address.json", %{badge_to_address_list: badge_to_address_list}) do + prepare_badge_to_address(badge_to_address_list) + end + + defp prepare_badge_to_address(badge_to_address_list) do + %{ + badge_to_address_list: format_badge_to_address_list(badge_to_address_list) + } + end + + defp prepare_badge_to_address(badge_to_address_list, status) do + %{ + badge_to_address_list: format_badge_to_address_list(badge_to_address_list), + status: status + } + end + + defp format_badge_to_address_list(badge_to_address_list) do + badge_to_address_list + |> Enum.map(fn badge_to_address -> + %{ + address_hash: "0x" <> Base.encode16(badge_to_address.address_hash.bytes) + } + end) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex index 8df0acb81b16..d0ffdc96085a 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex @@ -84,6 +84,7 @@ defmodule BlockScoutWeb.API.V2.Helper do "hash" => Address.checksum(address), "is_contract" => smart_contract?, "name" => address_name(address), + "is_scam" => address_marked_as_scam?(address), "proxy_type" => proxy_type, "implementations" => Proxy.proxy_object_info(implementation_address_hashes, implementation_names), "is_verified" => verified?(address) || verified_minimal_proxy?(proxy_implementations), @@ -158,6 +159,12 @@ defmodule BlockScoutWeb.API.V2.Helper do def address_name(_), do: nil + def address_marked_as_scam?(%Address{scam_badge: scam_badge}) when not is_nil(scam_badge) do + true + end + + def address_marked_as_scam?(_), do: false + def verified?(%Address{smart_contract: nil}), do: false def verified?(%Address{smart_contract: %{metadata_from_verified_bytecode_twin: true}}), do: false def verified?(%Address{smart_contract: %NotLoaded{}}), do: nil diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index 52c1d662a951..700cbb126c24 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -103,6 +103,7 @@ defmodule Explorer.Chain.Address.Schema do ) has_many(:names, Address.Name, foreign_key: :address_hash, references: :hash) + has_one(:scam_badge, Address.ScamBadgeToAddress, foreign_key: :address_hash, references: :hash) has_many(:decompiled_smart_contracts, DecompiledSmartContract, foreign_key: :address_hash, references: :hash) has_many(:withdrawals, Withdrawal, foreign_key: :address_hash, references: :hash) @@ -126,6 +127,7 @@ defmodule Explorer.Chain.Address do alias Ecto.Association.NotLoaded alias Ecto.Changeset + alias Explorer.Helper, as: ExplorerHelper alias Explorer.Chain.Cache.{Accounts, NetVersion} alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.SmartContract.Proxy.Models.Implementation @@ -181,6 +183,7 @@ defmodule Explorer.Chain.Address do Solidity source code is in `smart_contract` `t:Explorer.Chain.SmartContract.t/0` `contract_source_code` *if* the contract has been verified * `names` - names known for the address + * `badges` - badges applied for the address * `inserted_at` - when this address was inserted * `updated_at` - when this address was last updated * `ens_domain_name` - virtual field for ENS domain name passing @@ -454,6 +457,7 @@ defmodule Explorer.Chain.Address do ) base_query + |> ExplorerHelper.maybe_hide_scam_addresses(:hash) |> page_addresses(paging_options) |> limit(^paging_options.page_size) |> Chain.select_repo(options).all() diff --git a/apps/explorer/lib/explorer/chain/address/scam_badge_to_address.ex b/apps/explorer/lib/explorer/chain/address/scam_badge_to_address.ex new file mode 100644 index 000000000000..085de60e44c7 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/address/scam_badge_to_address.ex @@ -0,0 +1,79 @@ +defmodule Explorer.Chain.Address.ScamBadgeToAddress do + @moduledoc """ + Defines Address.ScamBadgeToAddress.t() mapping with Address.t() + """ + + use Explorer.Schema + + import Ecto.Changeset + + alias Explorer.{Chain, Repo} + alias Explorer.Chain.{Address, Hash} + + import Ecto.Query, only: [from: 2] + + @typedoc """ + * `address` - the `t:Explorer.Chain.Address.t/0`. + * `address_hash` - foreign key for `address`. + """ + @primary_key false + typed_schema "scam_address_badge_mappings" do + belongs_to(:address, Address, foreign_key: :address_hash, references: :hash, type: Hash.Address, null: false) + + timestamps() + end + + @required_fields ~w(address_hash)a + @allowed_fields @required_fields + + def changeset(%__MODULE__{} = struct, params \\ %{}) do + struct + |> cast(params, @allowed_fields) + |> validate_required(@required_fields) + |> foreign_key_constraint(:address_hash) + end + + @doc """ + Adds Address.ScamBadgeToAddress.t() by the list of Hash.Address.t() + """ + @spec add([Hash.Address.t()]) :: {non_neg_integer(), [__MODULE__.t()]} + def add(address_hashes) do + now = DateTime.utc_now() + + insert_params = + address_hashes + |> Enum.map(fn address_hash_string -> + case Chain.string_to_address_hash(address_hash_string) do + {:ok, address_hash} -> %{address_hash: address_hash, inserted_at: now, updated_at: now} + :error -> nil + end + end) + |> Enum.filter(&(!is_nil(&1))) + + Repo.insert_all(__MODULE__, insert_params, on_conflict: :nothing, returning: [:address_hash]) + end + + @doc """ + Deletes Address.ScamBadgeToAddress.t() by the list of Hash.Address.t() + """ + @spec delete([Hash.Address.t()]) :: {non_neg_integer(), [__MODULE__.t()]} + def delete(address_hashes) do + query = + from( + bta in __MODULE__, + where: bta.address_hash in ^address_hashes, + select: bta + ) + + Repo.delete_all(query) + end + + @doc """ + Gets the list of Address.ScamBadgeToAddress.t() + """ + @spec get([Chain.necessity_by_association_option() | Chain.api?()]) :: [__MODULE__.t()] + def get(options) do + __MODULE__ + |> Chain.select_repo(options).all() + end +end diff --git a/apps/explorer/lib/explorer/chain/advanced_filter.ex b/apps/explorer/lib/explorer/chain/advanced_filter.ex index 1161030be1a2..ec7b9305b868 100644 --- a/apps/explorer/lib/explorer/chain/advanced_filter.ex +++ b/apps/explorer/lib/explorer/chain/advanced_filter.ex @@ -252,8 +252,8 @@ defmodule Explorer.Chain.AdvancedFilter do preload: [ :block, from_address: [:names, :smart_contract, :proxy_implementations], - to_address: [:names, :smart_contract, :proxy_implementations], - created_contract_address: [:names, :smart_contract, :proxy_implementations] + to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], + created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] ], order_by: [ desc: transaction.block_number, @@ -289,8 +289,8 @@ defmodule Explorer.Chain.AdvancedFilter do as: :transaction, preload: [ from_address: [:names, :smart_contract, :proxy_implementations], - to_address: [:names, :smart_contract, :proxy_implementations], - created_contract_address: [:names, :smart_contract, :proxy_implementations], + to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], + created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], transaction: transaction ], order_by: [ @@ -703,8 +703,8 @@ defmodule Explorer.Chain.AdvancedFilter do preload: [ :transaction, :token, - from_address: [:names, :smart_contract, :proxy_implementations], - to_address: [:names, :smart_contract, :proxy_implementations] + from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], + to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] ], select_merge: %{ token_ids: [token_transfer.token_id], diff --git a/apps/explorer/lib/explorer/chain/celo/epoch_reward.ex b/apps/explorer/lib/explorer/chain/celo/epoch_reward.ex index 8356456f25a9..f08d59f33623 100644 --- a/apps/explorer/lib/explorer/chain/celo/epoch_reward.ex +++ b/apps/explorer/lib/explorer/chain/celo/epoch_reward.ex @@ -101,8 +101,8 @@ defmodule Explorer.Chain.Celo.EpochReward do select: {tt.log_index, tt}, preload: [ :token, - [from_address: [:names, :smart_contract, :proxy_implementations]], - [to_address: [:names, :smart_contract, :proxy_implementations]] + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]], + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] ] ) diff --git a/apps/explorer/lib/explorer/chain/search.ex b/apps/explorer/lib/explorer/chain/search.ex index 3405753499ca..5731f94cd889 100644 --- a/apps/explorer/lib/explorer/chain/search.ex +++ b/apps/explorer/lib/explorer/chain/search.ex @@ -83,10 +83,12 @@ defmodule Explorer.Chain.Search do end def base_joint_query(string, term) do - tokens_query = search_token_query(string, term) - contracts_query = search_contract_query(term) + tokens_query = + string |> search_token_query(term) |> ExplorerHelper.maybe_hide_scam_addresses(:contract_address_hash) + + contracts_query = term |> search_contract_query() |> ExplorerHelper.maybe_hide_scam_addresses(:address_hash) labels_query = search_label_query(term) - address_query = search_address_query(string) + address_query = string |> search_address_query() |> ExplorerHelper.maybe_hide_scam_addresses(:hash) block_query = search_block_query(string) basic_query = @@ -161,6 +163,7 @@ defmodule Explorer.Chain.Search do tokens_result = search_query |> search_token_query(term) + |> ExplorerHelper.maybe_hide_scam_addresses(:contract_address_hash) |> order_by([token], desc_nulls_last: token.circulating_market_cap, desc_nulls_last: token.fiat_value, @@ -175,6 +178,7 @@ defmodule Explorer.Chain.Search do contracts_result = term |> search_contract_query() + |> ExplorerHelper.maybe_hide_scam_addresses(:address_hash) |> order_by([items], asc: items.name, desc: items.inserted_at) |> limit(^paging_options.page_size) |> select_repo(options).all() @@ -216,6 +220,7 @@ defmodule Explorer.Chain.Search do address_result = if query = search_address_query(search_query) do query + |> ExplorerHelper.maybe_hide_scam_addresses(:hash) |> select_repo(options).all() else [] @@ -602,6 +607,7 @@ defmodule Explorer.Chain.Search do [ result[:address_hash] |> search_address_query() + |> ExplorerHelper.maybe_hide_scam_addresses(:hash) |> select_repo(options).all() |> merge_address_search_result_with_ens_info(result) ] diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index 3e80fbfdf2b4..c79d2fe278b8 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -113,6 +113,7 @@ defmodule Explorer.Chain.SmartContract do alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.SmartContract.Proxy.Models.Implementation + alias Explorer.Helper, as: ExplorerHelper alias Explorer.SmartContract.Helper alias Explorer.SmartContract.Solidity.Verifier @@ -1273,6 +1274,7 @@ defmodule Explorer.Chain.SmartContract do query = from(contract in __MODULE__) query + |> ExplorerHelper.maybe_hide_scam_addresses(:address_hash) |> filter_contracts(filter) |> search_contracts(search_string) |> SortingHelper.apply_sorting(sorting_options, @default_sorting) diff --git a/apps/explorer/lib/explorer/chain/token.ex b/apps/explorer/lib/explorer/chain/token.ex index b756d2f301bb..f97e0f511316 100644 --- a/apps/explorer/lib/explorer/chain/token.ex +++ b/apps/explorer/lib/explorer/chain/token.ex @@ -81,6 +81,7 @@ defmodule Explorer.Chain.Token do alias Ecto.Changeset alias Explorer.{Chain, SortingHelper} alias Explorer.Chain.{BridgedToken, Hash, Search, Token} + alias Explorer.Helper, as: ExplorerHelper alias Explorer.Repo alias Explorer.SmartContract.Helper @@ -210,6 +211,7 @@ defmodule Explorer.Chain.Token do sorted_paginated_query = query + |> ExplorerHelper.maybe_hide_scam_addresses(:contract_address_hash) |> apply_filter(token_type) |> SortingHelper.apply_sorting(sorting, @default_sorting) |> SortingHelper.page_with_sorting(paging_options, sorting, @default_sorting) diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index 8bf41f8c88d3..77cace2455c1 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -240,7 +240,7 @@ defmodule Explorer.Chain.TokenTransfer do :transaction, :token, [from_address: [:names, :smart_contract, :proxy_implementations]], - [to_address: [:names, :smart_contract, :proxy_implementations]] + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] ]) only_consensus_transfers_query() @@ -267,7 +267,7 @@ defmodule Explorer.Chain.TokenTransfer do :transaction, :token, [from_address: [:names, :smart_contract, :proxy_implementations]], - [to_address: [:names, :smart_contract, :proxy_implementations]] + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] ]) only_consensus_transfers_query() diff --git a/apps/explorer/lib/explorer/helper.ex b/apps/explorer/lib/explorer/helper.ex index e4d2bc6e4689..f31081578edd 100644 --- a/apps/explorer/lib/explorer/helper.ex +++ b/apps/explorer/lib/explorer/helper.ex @@ -7,7 +7,7 @@ defmodule Explorer.Helper do alias Explorer.Chain alias Explorer.Chain.Data - import Ecto.Query, only: [where: 3] + import Ecto.Query, only: [join: 5, where: 3] import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] @max_safe_integer round(:math.pow(2, 63)) - 1 @@ -212,4 +212,40 @@ defmodule Explorer.Helper do true -> :eq end end + + @doc """ + Conditionally hides scam addresses in the given query. + + ## Parameters + + - query: The Ecto query to be modified. + - address_hash_key: The key used to identify address hash field in the query to join with base query table on. + + ## Returns + + The modified query with scam addresses hidden, if applicable. + """ + @spec maybe_hide_scam_addresses(nil | Ecto.Query.t(), atom()) :: Ecto.Query.t() + def maybe_hide_scam_addresses(nil, _address_hash_key), do: nil + + def maybe_hide_scam_addresses(query, address_hash_key) do + if Application.get_env(:block_scout_web, :hide_scam_addresses) do + query + |> join( + :inner, + [q], + q2 in fragment(""" + ( + SELECT hash + FROM addresses a + WHERE NOT EXISTS + (SELECT 1 FROM scam_address_badge_mappings sabm WHERE sabm.address_hash=a.hash) + ) + """), + on: field(q, ^address_hash_key) == q2.hash + ) + else + query + end + end end diff --git a/apps/explorer/priv/repo/migrations/20240910095635_add_address_badges_tables.exs b/apps/explorer/priv/repo/migrations/20240910095635_add_address_badges_tables.exs new file mode 100644 index 000000000000..ba7bbc7db20d --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20240910095635_add_address_badges_tables.exs @@ -0,0 +1,14 @@ +defmodule Explorer.Repo.Migrations.AddAddressBadgesTables do + use Ecto.Migration + + def change do + create table(:scam_address_badge_mappings, primary_key: false) do + add(:address_hash, references(:addresses, column: :hash, type: :bytea, on_delete: :delete_all), + null: false, + primary_key: true + ) + + timestamps(null: false, type: :utc_datetime_usec) + end + end +end diff --git a/config/runtime.exs b/config/runtime.exs index 4ef037d05c71..bcd6580bcc3c 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -33,6 +33,7 @@ config :block_scout_web, permanent_light_mode_enabled: ConfigHelper.parse_bool_env_var("PERMANENT_LIGHT_MODE_ENABLED"), display_token_icons: ConfigHelper.parse_bool_env_var("DISPLAY_TOKEN_ICONS"), hide_block_miner: ConfigHelper.parse_bool_env_var("HIDE_BLOCK_MINER"), + hide_scam_addresses: ConfigHelper.parse_bool_env_var("HIDE_SCAM_ADDRESSES"), show_tenderly_link: ConfigHelper.parse_bool_env_var("SHOW_TENDERLY_LINK"), sensitive_endpoints_api_key: System.get_env("API_SENSITIVE_ENDPOINTS_KEY"), disable_api?: disable_api? diff --git a/cspell.json b/cspell.json index 22872a9c5637..3e2c5a3b11fc 100644 --- a/cspell.json +++ b/cspell.json @@ -461,6 +461,7 @@ "rollups", "RPC's", "RPCs", + "sabm", "safelow", "savechives", "Secon", diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index fd2f85e1ffc2..7c82e9e21c32 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -65,12 +65,12 @@ EXCHANGE_RATES_COIN= # EXCHANGE_RATES_CRYPTORANK_COIN_ID= # EXCHANGE_RATES_CRYPTORANK_LIMIT= # TOKEN_EXCHANGE_RATES_SOURCE= -POOL_SIZE=80 # EXCHANGE_RATES_COINGECKO_PLATFORM_ID= # TOKEN_EXCHANGE_RATE_INTERVAL= # TOKEN_EXCHANGE_RATE_REFETCH_INTERVAL= # TOKEN_EXCHANGE_RATE_MAX_BATCH_SIZE= # DISABLE_TOKEN_EXCHANGE_RATE= +POOL_SIZE=80 POOL_SIZE_API=10 ECTO_USE_SSL=false # DATADOG_HOST= @@ -262,6 +262,7 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_ARBITRUM_BRIDGE_MESSAGES_TRACKING_ENABLED= # INDEXER_ARBITRUM_TRACKING_MESSAGES_ON_L1_RECHECK_INTERVAL= # INDEXER_ARBITRUM_MISSED_MESSAGES_RECHECK_INTERVAL= +# INDEXER_ARBITRUM_MISSED_MESSAGES_BLOCKS_DEPTH= # CELO_CORE_CONTRACTS= # INDEXER_CELO_VALIDATOR_GROUP_VOTES_BATCH_SIZE=200000 # INDEXER_DISABLE_CELO_EPOCH_FETCHER=false @@ -273,7 +274,6 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # FILECOIN_PENDING_ADDRESS_OPERATIONS_MIGRATION_CONCURRENCY= # INDEXER_DISABLE_FILECOIN_ADDRESS_INFO_FETCHER=false # INDEXER_FILECOIN_ADDRESS_INFO_CONCURRENCY=1 -# INDEXER_ARBITRUM_MISSED_MESSAGES_BLOCKS_DEPTH= # INDEXER_REALTIME_FETCHER_MAX_GAP= # INDEXER_FETCHER_INIT_QUERY_LIMIT= # INDEXER_TOKEN_BALANCES_FETCHER_INIT_QUERY_LIMIT= @@ -342,6 +342,7 @@ MAINTENANCE_ALERT_MESSAGE= CHAIN_ID= MAX_SIZE_UNLESS_HIDE_ARRAY=50 HIDE_BLOCK_MINER=false +# HIDE_SCAM_ADDRESSES= DISPLAY_TOKEN_ICONS=false RE_CAPTCHA_SECRET_KEY= RE_CAPTCHA_CLIENT_KEY= From ad1ce1fc077b09e32f28e8d40f14b1f19b82a034 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Fri, 27 Sep 2024 19:42:51 +0300 Subject: [PATCH 185/363] Update base docker image at oldUI.Dockerfile --- docker/oldUI.Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/oldUI.Dockerfile b/docker/oldUI.Dockerfile index 8ee713ac2b4d..1a2b1da1755a 100644 --- a/docker/oldUI.Dockerfile +++ b/docker/oldUI.Dockerfile @@ -1,4 +1,4 @@ -FROM hexpm/elixir:1.16.3-erlang-26.2.5.1-alpine-3.18.7 AS builder +FROM hexpm/elixir:1.17.3-erlang-27.1-alpine-3.20.3 AS builder WORKDIR /app @@ -70,7 +70,7 @@ RUN mkdir -p /opt/release \ && mv _build/${MIX_ENV}/rel/blockscout /opt/release ############################################################## -FROM hexpm/elixir:1.16.3-erlang-26.2.5.1-alpine-3.18.7 +FROM hexpm/elixir:1.17.3-erlang-27.1-alpine-3.20.3 ARG RELEASE_VERSION ENV RELEASE_VERSION=${RELEASE_VERSION} From 083966a58a0cdf5f4c0ec54cce540c4df2c86c49 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 30 Sep 2024 13:26:32 +0300 Subject: [PATCH 186/363] fix: Decode revert reason by decoding candidates from the DB (#10827) --- apps/explorer/lib/explorer/chain/transaction.ex | 10 +++++++++- apps/explorer/test/explorer/chain/transaction_test.exs | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index 1e687e5c28ee..b6f2f734546f 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -773,6 +773,7 @@ defmodule Explorer.Chain.Transaction do proxy_implementation_addresses_map \\ %{} ) + # skip decoding if there is no to_address def decoded_input_data( %__MODULE__{to_address: nil}, _, @@ -783,9 +784,11 @@ defmodule Explorer.Chain.Transaction do ), do: {{:error, :no_to_address}, full_abi_acc, methods_acc} + # skip decoding if transaction is not loaded def decoded_input_data(%NotLoaded{}, _, _, full_abi_acc, methods_acc, _proxy_implementation_addresses_map), do: {{:error, :not_loaded}, full_abi_acc, methods_acc} + # skip decoding if input is empty def decoded_input_data( %__MODULE__{input: %{bytes: bytes}}, _, @@ -798,6 +801,7 @@ defmodule Explorer.Chain.Transaction do {{:error, :no_input_data}, full_abi_acc, methods_acc} end + # skip decoding if to_address is not a contract unless DECODE_NOT_A_CONTRACT_CALLS is set if not Application.compile_env(:explorer, :decode_not_a_contract_calls) do def decoded_input_data( %__MODULE__{to_address: %{contract_code: nil}}, @@ -810,6 +814,7 @@ defmodule Explorer.Chain.Transaction do do: {{:error, :not_a_contract_call}, full_abi_acc, methods_acc} end + # if to_address's smart_contract is nil reduce to the case when to_address is not loaded def decoded_input_data( %__MODULE__{ to_address: %{smart_contract: nil}, @@ -836,6 +841,7 @@ defmodule Explorer.Chain.Transaction do ) end + # if to_address's smart_contract is not loaded reduce to the case when to_address is not loaded def decoded_input_data( %__MODULE__{ to_address: %{smart_contract: %NotLoaded{}}, @@ -862,6 +868,7 @@ defmodule Explorer.Chain.Transaction do ) end + # if to_address is not loaded try decoding by method candidates in the DB def decoded_input_data( %__MODULE__{ to_address: %NotLoaded{}, @@ -899,6 +906,7 @@ defmodule Explorer.Chain.Transaction do full_abi_acc, methods_acc} end + # if to_address is not loaded and input is not a method call return error def decoded_input_data( %__MODULE__{to_address: %NotLoaded{}}, _, @@ -931,7 +939,7 @@ defmodule Explorer.Chain.Transaction do proxy_implementation_addresses_map ) do # In some cases transactions use methods of some unpredictable contracts, so we can try to look up for method in a whole DB - {{:error, :could_not_decode}, full_abi_acc} -> + {{:error, error}, full_abi_acc} when error in [:could_not_decode, :no_matching_function] -> case decoded_input_data( %__MODULE__{ to_address: %NotLoaded{}, diff --git a/apps/explorer/test/explorer/chain/transaction_test.exs b/apps/explorer/test/explorer/chain/transaction_test.exs index fdb514ce7ba6..febe9e703f9a 100644 --- a/apps/explorer/test/explorer/chain/transaction_test.exs +++ b/apps/explorer/test/explorer/chain/transaction_test.exs @@ -258,7 +258,7 @@ defmodule Explorer.Chain.TransactionTest do test "that a contract call transaction that has no verified contract returns a commensurate error" do transaction = :transaction - |> insert(to_address: insert(:contract_address)) + |> insert(to_address: insert(:contract_address), input: "0x1234567891") |> Repo.preload(to_address: :smart_contract) assert {{:error, :contract_not_verified, []}, _, _} = Transaction.decoded_input_data(transaction, []) From 57a027f6cf35842d500ecdd5ea623dd6b45e0269 Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Mon, 30 Sep 2024 17:30:35 +0300 Subject: [PATCH 187/363] fix: dialyzer warning (#10845) --- .dialyzer-ignore | 1 - apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex | 2 +- apps/indexer/lib/indexer/application.ex | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.dialyzer-ignore b/.dialyzer-ignore index c0bea4bf4985..056ffa415ce2 100644 --- a/.dialyzer-ignore +++ b/.dialyzer-ignore @@ -4,4 +4,3 @@ lib/explorer/smart_contract/vyper/publisher_worker.ex:1 lib/explorer/smart_contract/solidity/publisher_worker.ex:8 lib/explorer/smart_contract/vyper/publisher_worker.ex:8 lib/phoenix/router.ex:402 -lib/explorer/chain/cache/celo_core_contracts.ex:162 diff --git a/apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex b/apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex index 1e617d3f6b66..d822a5fae709 100644 --- a/apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex +++ b/apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex @@ -11,7 +11,7 @@ defmodule Explorer.Chain.Cache.CeloCoreContracts do For details on the structure of the `CELO_CORE_CONTRACTS` environment variable, see `app/explorer/lib/fetch_celo_core_contracts.ex`. """ - @dialyzer :no_match + @dialyzer {:nowarn_function, get_address_updates: 1} require Logger diff --git a/apps/indexer/lib/indexer/application.ex b/apps/indexer/lib/indexer/application.ex index cb7b9a99f83b..14ee265fbe8b 100644 --- a/apps/indexer/lib/indexer/application.ex +++ b/apps/indexer/lib/indexer/application.ex @@ -12,7 +12,6 @@ defmodule Indexer.Application do alias Indexer.Fetcher.OnDemand.TokenTotalSupply, as: TokenTotalSupplyOnDemand alias Indexer.Memory - alias Prometheus.Registry @impl Application def start(_type, _args) do From 6882d3d88ce28042762f2b287a4916e1da11f070 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 21:46:02 +0300 Subject: [PATCH 188/363] chore(deps-dev): bump credo from 1.7.7 to 1.7.8 (#10850) Bumps [credo](https://github.com/rrrene/credo) from 1.7.7 to 1.7.8. - [Release notes](https://github.com/rrrene/credo/releases) - [Changelog](https://github.com/rrrene/credo/blob/master/CHANGELOG.md) - [Commits](https://github.com/rrrene/credo/compare/v1.7.7...v1.7.8) --- updated-dependencies: - dependency-name: credo dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index da993fb50e3f..b1c8fad6b11e 100644 --- a/mix.lock +++ b/mix.lock @@ -29,7 +29,7 @@ "cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"}, - "credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"}, + "credo": {:hex, :credo, "1.7.8", "9722ba1681e973025908d542ec3d95db5f9c549251ba5b028e251ad8c24ab8c5", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cb9e87cc64f152f3ed1c6e325e7b894dea8f5ef2e41123bd864e3cd5ceb44968"}, "csv": {:hex, :csv, "2.5.0", "c47b5a5221bf2e56d6e8eb79e77884046d7fd516280dc7d9b674251e0ae46246", [:mix], [{:parallel_stream, "~> 1.0.4 or ~> 1.1.0", [hex: :parallel_stream, repo: "hexpm", optional: false]}], "hexpm", "e821f541487045c7591a1963eeb42afff0dfa99bdcdbeb3410795a2f59c77d34"}, "dataloader": {:hex, :dataloader, "2.0.1", "fa06b057b432b993203003fbff5ff040b7f6483a77e732b7dfc18f34ded2634f", [:mix], [{:ecto, ">= 3.4.3 and < 4.0.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:opentelemetry_process_propagator, "~> 0.2.1 or ~> 0.3", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "da7ff00890e1b14f7457419b9508605a8e66ae2cc2d08c5db6a9f344550efa11"}, "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, From 41703b6bdbcfa125992c2809705ae48ce69cb3c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 21:46:32 +0300 Subject: [PATCH 189/363] chore(deps): bump bamboo from 2.3.0 to 2.3.1 (#10849) Bumps [bamboo](https://github.com/thoughtbot/bamboo) from 2.3.0 to 2.3.1. - [Release notes](https://github.com/thoughtbot/bamboo/releases) - [Changelog](https://github.com/beam-community/bamboo/blob/main/CHANGELOG.md) - [Commits](https://github.com/thoughtbot/bamboo/compare/v2.3.0...v2.3.1) --- updated-dependencies: - dependency-name: bamboo dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index b1c8fad6b11e..b486297fc463 100644 --- a/mix.lock +++ b/mix.lock @@ -4,7 +4,7 @@ "absinthe_plug": {:git, "https://github.com/blockscout/absinthe_plug.git", "90a8188e94e2650f13259fb16462075a87f98e18", [tag: "1.5.8"]}, "absinthe_relay": {:hex, :absinthe_relay, "1.5.2", "cfb8aed70f4e4c7718d3f1c212332d2ea728f17c7fc0f68f1e461f0f5f0c4b9a", [:mix], [{:absinthe, "~> 1.5.0 or ~> 1.6.0 or ~> 1.7.0", [hex: :absinthe, repo: "hexpm", optional: false]}, {:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "0587ee913afa31512e1457a5064ee88427f8fe7bcfbeeecd41c71d9cff0b62b6"}, "accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm", "11b18c220bcc2eab63b5470c038ef10eb6783bcb1fcdb11aa4137defa5ac1bb8"}, - "bamboo": {:hex, :bamboo, "2.3.0", "d2392a2cabe91edf488553d3c70638b532e8db7b76b84b0a39e3dfe492ffd6fc", [:mix], [{:hackney, ">= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.4 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "dd0037e68e108fd04d0e8773921512c940e35d981e097b5793543e3b2f9cd3f6"}, + "bamboo": {:hex, :bamboo, "2.3.1", "85029339f01c3dd59071cfd2b7b18e826aa7fc172cc324d75603bb99d95b6544", [:mix], [{:hackney, ">= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.4 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "895b2993ed195b2b0fa79c0d5a1d36aa529e817b6df257e4a10745459048d505"}, "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.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"}, "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"}, From 36e6f8307bd74386fdd44c106bcab5f3037b5e4a Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:19:14 +0400 Subject: [PATCH 190/363] fix: Disable archive balances only if latest block is available (#10851) --- apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex | 6 +++--- apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex index ec98c66673a9..8b4dd4f51b7b 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex @@ -189,8 +189,8 @@ defmodule EthereumJSONRPC do def fetch_balances(params_list, json_rpc_named_arguments, chunk_size \\ nil) when is_list(params_list) and is_list(json_rpc_named_arguments) do filtered_params = - if Application.get_env(:ethereum_jsonrpc, :disable_archive_balances?) do - {:ok, max_block_number} = fetch_block_number_by_tag("latest", json_rpc_named_arguments) + with true <- Application.get_env(:ethereum_jsonrpc, :disable_archive_balances?), + {:ok, max_block_number} <- fetch_block_number_by_tag("latest", json_rpc_named_arguments) do window = Application.get_env(:ethereum_jsonrpc, :archive_balances_window) params_list @@ -200,7 +200,7 @@ defmodule EthereumJSONRPC do _ -> false end) else - params_list + _ -> params_list end filtered_params_in_range = diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs index 0409dc1c6492..52bbf7bd4631 100644 --- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs @@ -1058,7 +1058,7 @@ defmodule EthereumJSONRPCSyncTest do ]} end) - Application.put_env(:ethereum_jsonrpc, :disable_archive_balances?, "true") + Application.put_env(:ethereum_jsonrpc, :disable_archive_balances?, true) Application.put_env(:ethereum_jsonrpc, :archive_balances_window, 1) assert EthereumJSONRPC.fetch_balances( From ea9e806bd5fee90408fba1dd5d364fdd338e712e Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Tue, 1 Oct 2024 11:51:55 +0300 Subject: [PATCH 191/363] refactor: use middleware to check if GraphQL API is enabled (#10772) --- .../graphql/middleware/api_enabled.ex | 19 +++++++++ .../graphql/resolvers/address.ex | 25 ++++-------- .../graphql/resolvers/block.ex | 33 +++++++++------- .../graphql/resolvers/helper.ex | 9 ----- .../graphql/resolvers/internal_transaction.ex | 21 +++------- .../graphql/resolvers/token.ex | 13 +------ .../graphql/resolvers/token_transfer.ex | 37 ++++++------------ .../graphql/resolvers/transaction.ex | 39 +++++-------------- .../lib/block_scout_web/graphql/schema.ex | 10 ++++- apps/explorer/lib/explorer/graphql.ex | 17 +++++++- 10 files changed, 98 insertions(+), 125 deletions(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/graphql/middleware/api_enabled.ex delete mode 100644 apps/block_scout_web/lib/block_scout_web/graphql/resolvers/helper.ex diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/middleware/api_enabled.ex b/apps/block_scout_web/lib/block_scout_web/graphql/middleware/api_enabled.ex new file mode 100644 index 000000000000..dfe2bdb57564 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/graphql/middleware/api_enabled.ex @@ -0,0 +1,19 @@ +defmodule BlockScoutWeb.GraphQL.Middleware.ApiEnabled do + @moduledoc """ + Middleware to check if the GraphQL API is enabled. + """ + alias Absinthe.Resolution + + @behaviour Absinthe.Middleware + + @api_is_disabled "GraphQL API is disabled." + + def call(resolution, _config) do + if resolution.context.api_enabled do + resolution + else + resolution + |> Resolution.put_result({:error, @api_is_disabled}) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/address.ex b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/address.ex index 4a9449979b4c..1cbc324a1076 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/address.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/address.ex @@ -1,28 +1,19 @@ defmodule BlockScoutWeb.GraphQL.Resolvers.Address do @moduledoc false - alias BlockScoutWeb.GraphQL.Resolvers.Helper alias Explorer.Chain - def get_by(_, %{hashes: hashes}, resolution) do - if resolution.context.api_enabled do - case Chain.hashes_to_addresses(hashes) do - [] -> {:error, "Addresses not found."} - result -> {:ok, result} - end - else - {:error, Helper.api_is_disabled()} + def get_by(_, %{hashes: hashes}, _) do + case Chain.hashes_to_addresses(hashes) do + [] -> {:error, "Addresses not found."} + result -> {:ok, result} end end - def get_by(_, %{hash: hash}, resolution) do - if resolution.context.api_enabled do - case Chain.hash_to_address(hash) do - {:error, :not_found} -> {:error, "Address not found."} - {:ok, _} = result -> result - end - else - {:error, Helper.api_is_disabled()} + def get_by(_, %{hash: hash}, _) do + case Chain.hash_to_address(hash) do + {:error, :not_found} -> {:error, "Address not found."} + {:ok, _} = result -> result end end end diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/block.ex b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/block.ex index 24de1e62599d..1b03f48dfc88 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/block.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/block.ex @@ -1,29 +1,32 @@ defmodule BlockScoutWeb.GraphQL.Resolvers.Block do @moduledoc false - alias BlockScoutWeb.GraphQL.Resolvers.Helper alias Explorer.Chain alias Explorer.Chain.Transaction @api_true [api?: true] - def get_by(_, %{number: number}, resolution) do - with {:api_enabled, true} <- {:api_enabled, resolution.context.api_enabled}, - {:ok, _} = result <- Chain.number_to_block(number, @api_true) do - result - else - {:api_enabled, false} -> {:error, Helper.api_is_disabled()} - {:error, :not_found} -> {:error, "Block number #{number} was not found."} + def get_by(_, %{number: number}, _) do + number + |> Chain.number_to_block(@api_true) + |> case do + {:ok, _} = result -> + result + + {:error, :not_found} -> + {:error, "Block number #{number} was not found."} end end - def get_by(%Transaction{block_hash: hash}, _, resolution) do - with {:api_enabled, true} <- {:api_enabled, resolution.context.api_enabled}, - {:ok, _} = result <- Chain.hash_to_block(hash, @api_true) do - result - else - {:api_enabled, false} -> {:error, Helper.api_is_disabled()} - {:error, :not_found} -> {:error, "Block hash #{to_string(hash)} was not found."} + def get_by(%Transaction{block_hash: hash}, _, _) do + hash + |> Chain.hash_to_block(@api_true) + |> case do + {:ok, _} = result -> + result + + {:error, :not_found} -> + {:error, "Block hash #{to_string(hash)} was not found."} end end end diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/helper.ex b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/helper.ex deleted file mode 100644 index 215e713acff9..000000000000 --- a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/helper.ex +++ /dev/null @@ -1,9 +0,0 @@ -defmodule BlockScoutWeb.GraphQL.Resolvers.Helper do - @moduledoc """ - Helper functions for BlockScoutWeb.GraphQL.Resolvers modules - """ - - @api_is_disabled "GraphQL API is disabled." - - def api_is_disabled, do: @api_is_disabled -end diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/internal_transaction.ex b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/internal_transaction.ex index 647ae0b36134..14e0093eef47 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/internal_transaction.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/internal_transaction.ex @@ -2,26 +2,17 @@ defmodule BlockScoutWeb.GraphQL.Resolvers.InternalTransaction do @moduledoc false alias Absinthe.Relay.Connection - alias BlockScoutWeb.GraphQL.Resolvers.Helper alias Explorer.Chain.Transaction alias Explorer.{GraphQL, Repo} - def get_by(%{transaction_hash: _, index: _} = args, resolution) do - if resolution.context.api_enabled do - GraphQL.get_internal_transaction(args) - else - {:error, Helper.api_is_disabled()} - end + def get_by(%{transaction_hash: _, index: _} = args, _) do + GraphQL.get_internal_transaction(args) end - def get_by(%Transaction{} = transaction, args, resolution) do - if resolution.context.api_enabled do - transaction - |> GraphQL.transaction_to_internal_transactions_query() - |> Connection.from_query(&Repo.all/1, args, options(args)) - else - {:error, Helper.api_is_disabled()} - end + def get_by(%Transaction{} = transaction, args, _) do + transaction + |> GraphQL.transaction_to_internal_transactions_query() + |> Connection.from_query(&Repo.all/1, args, options(args)) end defp options(%{before: _}), do: [] diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token.ex b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token.ex index a830537b3853..55092ec02e75 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token.ex @@ -1,19 +1,10 @@ defmodule BlockScoutWeb.GraphQL.Resolvers.Token do @moduledoc false - alias BlockScoutWeb.GraphQL.Resolvers.Helper alias Explorer.Chain.TokenTransfer alias Explorer.GraphQL - def get_by( - %TokenTransfer{token_contract_address_hash: token_contract_address_hash}, - _, - resolution - ) do - if resolution.context.api_enabled do - GraphQL.get_token(%{contract_address_hash: token_contract_address_hash}) - else - {:error, Helper.api_is_disabled()} - end + def get_by(%TokenTransfer{token_contract_address_hash: token_contract_address_hash}, _, _) do + GraphQL.get_token(%{contract_address_hash: token_contract_address_hash}) end end diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token_transfer.ex b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token_transfer.ex index 2302befd7150..74fb50b52f34 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token_transfer.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/token_transfer.ex @@ -2,40 +2,27 @@ defmodule BlockScoutWeb.GraphQL.Resolvers.TokenTransfer do @moduledoc false alias Absinthe.Relay.Connection - alias BlockScoutWeb.GraphQL.Resolvers.Helper alias Explorer.Chain.{Address, TokenTransfer} alias Explorer.{GraphQL, Repo} - def get_by(%{transaction_hash: _, log_index: _} = args, resolution) do - if resolution.context.api_enabled do - GraphQL.get_token_transfer(args) - else - {:error, Helper.api_is_disabled()} - end + def get_by(%{transaction_hash: _, log_index: _} = args, _) do + GraphQL.get_token_transfer(args) end - def get_by(_, %{token_contract_address_hash: token_contract_address_hash} = args, resolution) do - if resolution.context.api_enabled do - connection_args = Map.take(args, [:after, :before, :first, :last]) + def get_by(_, %{token_contract_address_hash: token_contract_address_hash} = args, _) do + connection_args = Map.take(args, [:after, :before, :first, :last]) - token_contract_address_hash - |> GraphQL.list_token_transfers_query() - |> Connection.from_query(&Repo.replica().all/1, connection_args, options(args)) - else - {:error, Helper.api_is_disabled()} - end + token_contract_address_hash + |> GraphQL.list_token_transfers_query() + |> Connection.from_query(&Repo.replica().all/1, connection_args, options(args)) end - def get_by(%Address{hash: address_hash}, args, resolution) do - if resolution.context.api_enabled do - connection_args = Map.take(args, [:after, :before, :first, :last]) + def get_by(%Address{hash: address_hash}, args, _) do + connection_args = Map.take(args, [:after, :before, :first, :last]) - address_hash - |> TokenTransfer.token_transfers_by_address_hash(nil, [], nil) - |> Connection.from_query(&Repo.replica().all/1, connection_args, options(args)) - else - {:error, Helper.api_is_disabled()} - end + address_hash + |> TokenTransfer.token_transfers_by_address_hash(nil, [], nil) + |> Connection.from_query(&Repo.replica().all/1, connection_args, options(args)) end defp options(%{before: _}), do: [] diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/transaction.ex b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/transaction.ex index 3dbeb9924f66..c1406eed85fc 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/transaction.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/resolvers/transaction.ex @@ -2,43 +2,22 @@ defmodule BlockScoutWeb.GraphQL.Resolvers.Transaction do @moduledoc false alias Absinthe.Relay.Connection - alias BlockScoutWeb.GraphQL.Resolvers.Helper - alias Explorer.{Chain, GraphQL, Repo} + alias Explorer.{GraphQL, Repo} alias Explorer.Chain.{Address, TokenTransfer} - @api_true [api?: true] + def get_by(_, %{hash: hash}, _), + do: GraphQL.get_transaction_by_hash(hash) - def get_by(_, %{hash: hash}, resolution) do - with {:api_enabled, true} <- {:api_enabled, resolution.context.api_enabled}, - {:ok, transaction} <- Chain.hash_to_transaction(hash, @api_true) do - {:ok, transaction} - else - {:api_enabled, false} -> {:error, Helper.api_is_disabled()} - {:error, :not_found} -> {:error, "Transaction not found."} - end - end - - def get_by(%Address{hash: address_hash}, args, resolution) do + def get_by(%Address{hash: address_hash}, args, _) do connection_args = Map.take(args, [:after, :before, :first, :last]) - if resolution.context.api_enabled do - address_hash - |> GraphQL.address_to_transactions_query(args.order) - |> Connection.from_query(&Repo.replica().all/1, connection_args, options(args)) - else - {:error, Helper.api_is_disabled()} - end + address_hash + |> GraphQL.address_to_transactions_query(args.order) + |> Connection.from_query(&Repo.replica().all/1, connection_args, options(args)) end - def get_by(%TokenTransfer{transaction_hash: hash}, _, resolution) do - with {:api_enabled, true} <- {:api_enabled, resolution.context.api_enabled}, - {:ok, transaction} <- Chain.hash_to_transaction(hash, @api_true) do - {:ok, transaction} - else - {:api_enabled, false} -> {:error, Helper.api_is_disabled()} - {:error, :not_found} -> {:error, "Transaction not found."} - end - end + def get_by(%TokenTransfer{transaction_hash: hash}, _, _), + do: GraphQL.get_transaction_by_hash(hash) defp options(%{before: _}), do: [] diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/schema.ex b/apps/block_scout_web/lib/block_scout_web/graphql/schema.ex index 1bfee0f8a48a..2a01ac5b8f29 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/schema.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/schema.ex @@ -4,9 +4,11 @@ defmodule BlockScoutWeb.GraphQL.Schema do use Absinthe.Schema use Absinthe.Relay.Schema, :modern - alias Absinthe.Middleware.Dataloader, as: AbsintheMiddlewareDataloader + alias Absinthe.Middleware.Dataloader, as: AbsintheDataloaderMiddleware alias Absinthe.Plugin, as: AbsinthePlugin + alias BlockScoutWeb.GraphQL.Middleware.ApiEnabled, as: ApiEnabledMiddleware + alias BlockScoutWeb.GraphQL.Resolvers.{ Address, Block, @@ -136,7 +138,11 @@ defmodule BlockScoutWeb.GraphQL.Schema do end end + def middleware(middleware, _field, _object) do + [ApiEnabledMiddleware | middleware] + end + def plugins do - [AbsintheMiddlewareDataloader] ++ AbsinthePlugin.defaults() + [AbsintheDataloaderMiddleware] ++ AbsinthePlugin.defaults() end end diff --git a/apps/explorer/lib/explorer/graphql.ex b/apps/explorer/lib/explorer/graphql.ex index e275631615f6..1492e68142b4 100644 --- a/apps/explorer/lib/explorer/graphql.ex +++ b/apps/explorer/lib/explorer/graphql.ex @@ -11,6 +11,8 @@ defmodule Explorer.GraphQL do where: 3 ] + alias Explorer.{Chain, Repo} + alias Explorer.Chain.{ Hash, InternalTransaction, @@ -19,7 +21,7 @@ defmodule Explorer.GraphQL do Transaction } - alias Explorer.Repo + @api_true [api?: true] @doc """ Returns a query to fetch transactions with a matching `to_address_hash`, @@ -96,6 +98,19 @@ defmodule Explorer.GraphQL do end end + @doc """ + Returns a transaction for a given hash. + """ + @spec get_transaction_by_hash(Hash.t()) :: {:ok, Transaction.t()} | {:error, String.t()} + def get_transaction_by_hash(hash) do + hash + |> Chain.hash_to_transaction(@api_true) + |> case do + {:ok, _} = result -> result + {:error, :not_found} -> {:error, "Transaction not found."} + end + end + @doc """ Returns a query to fetch token transfers for a token contract address hash. From 6647fe38d3c69782619fba546cf463c986c1145a Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Tue, 1 Oct 2024 11:52:17 +0300 Subject: [PATCH 192/363] fix: set `API_GRAPHQL_MAX_COMPLEXITY` in build action (#10843) --- .github/workflows/publish-docker-image-for-celo.yml | 1 + docker/Dockerfile | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/workflows/publish-docker-image-for-celo.yml b/.github/workflows/publish-docker-image-for-celo.yml index ace16f10edf4..2efe98d266f1 100644 --- a/.github/workflows/publish-docker-image-for-celo.yml +++ b/.github/workflows/publish-docker-image-for-celo.yml @@ -36,6 +36,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | + API_GRAPHQL_MAX_COMPLEXITY=6650 CACHE_EXCHANGE_RATES_PERIOD= API_V1_READ_METHODS_DISABLED=false DISABLE_WEBAPP=false diff --git a/docker/Dockerfile b/docker/Dockerfile index faa74bd25f53..8d1f4cd8b813 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -29,6 +29,8 @@ ARG MUD_INDEXER_ENABLED ENV MUD_INDEXER_ENABLED=${MUD_INDEXER_ENABLED} ARG SHRINK_INTERNAL_TRANSACTIONS_ENABLED ENV SHRINK_INTERNAL_TRANSACTIONS_ENABLED=${SHRINK_INTERNAL_TRANSACTIONS_ENABLED} +ARG API_GRAPHQL_MAX_COMPLEXITY +ENV API_GRAPHQL_MAX_COMPLEXITY=${API_GRAPHQL_MAX_COMPLEXITY} # Cache elixir deps ADD mix.exs mix.lock ./ From 2277fbeeb1101666bc0d8f5564d60fc6ce13bf8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 11:51:05 +0300 Subject: [PATCH 193/363] chore(deps-dev): bump eslint in /apps/block_scout_web/assets (#10863) Bumps [eslint](https://github.com/eslint/eslint) from 8.57.0 to 8.57.1. - [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.57.0...v8.57.1) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 64 ++++++++++--------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index bff2686cf1be..a9b9eb853a79 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": "^12.0.2", "css-loader": "^7.1.2", "css-minimizer-webpack-plugin": "^7.0.0", - "eslint": "^8.57.0", + "eslint": "^8.57.1", "eslint-config-standard": "^17.1.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-node": "^11.1.0", @@ -2117,9 +2117,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2145,12 +2145,13 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -2172,9 +2173,10 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", "dev": true }, "node_modules/@istanbuljs/load-nyc-config": { @@ -6533,16 +6535,16 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -17438,9 +17440,9 @@ } }, "@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true }, "@ethereumjs/rlp": { @@ -17454,12 +17456,12 @@ "integrity": "sha512-60G28ke/sXdtS9KZCpZSHHkCbdsOGEhIUGlwq6yhY74UpTiToIh8np7A8yphhM4BWsvNFtIvLpi4co+h9Mr9Ow==" }, "@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" } @@ -17471,9 +17473,9 @@ "dev": true }, "@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, "@istanbuljs/load-nyc-config": { @@ -20860,16 +20862,16 @@ } }, "eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 64ab6c6a49c2..213c533f48a1 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -90,7 +90,7 @@ "copy-webpack-plugin": "^12.0.2", "css-loader": "^7.1.2", "css-minimizer-webpack-plugin": "^7.0.0", - "eslint": "^8.57.0", + "eslint": "^8.57.1", "eslint-config-standard": "^17.1.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-node": "^11.1.0", From 95d1397b12f24be9d9d35965e43cbbd347bbf8d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 11:51:21 +0300 Subject: [PATCH 194/363] chore(deps): bump solc from 0.8.26 to 0.8.27 in /apps/explorer (#10864) Bumps [solc](https://github.com/ethereum/solc-js) from 0.8.26 to 0.8.27. - [Commits](https://github.com/ethereum/solc-js/compare/v0.8.26...v0.8.27) --- updated-dependencies: - dependency-name: solc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/explorer/package-lock.json | 14 +++++++------- apps/explorer/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/explorer/package-lock.json b/apps/explorer/package-lock.json index e62ea298b99f..64c4a8c66375 100644 --- a/apps/explorer/package-lock.json +++ b/apps/explorer/package-lock.json @@ -7,7 +7,7 @@ "name": "blockscout", "license": "GPL-3.0", "dependencies": { - "solc": "0.8.26" + "solc": "0.8.27" }, "engines": { "node": "18.x", @@ -76,9 +76,9 @@ } }, "node_modules/solc": { - "version": "0.8.26", - "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", - "integrity": "sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==", + "version": "0.8.27", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.27.tgz", + "integrity": "sha512-BNxMol2tUAbkH7HKlXBcBqrGi2aqgv+uMHz26mJyTtlVgWmBA4ktiw0qVKHfkjf2oaHbwtbtaSeE2dhn/gTAKw==", "dependencies": { "command-exists": "^1.2.8", "commander": "^8.1.0", @@ -144,9 +144,9 @@ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" }, "solc": { - "version": "0.8.26", - "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", - "integrity": "sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==", + "version": "0.8.27", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.27.tgz", + "integrity": "sha512-BNxMol2tUAbkH7HKlXBcBqrGi2aqgv+uMHz26mJyTtlVgWmBA4ktiw0qVKHfkjf2oaHbwtbtaSeE2dhn/gTAKw==", "requires": { "command-exists": "^1.2.8", "commander": "^8.1.0", diff --git a/apps/explorer/package.json b/apps/explorer/package.json index effecd6187bd..9389c1f2e266 100644 --- a/apps/explorer/package.json +++ b/apps/explorer/package.json @@ -13,6 +13,6 @@ }, "scripts": {}, "dependencies": { - "solc": "0.8.26" + "solc": "0.8.27" } } From ba5a90079c020b3bdff6827d46b987ecafe4c5ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 11:52:14 +0300 Subject: [PATCH 195/363] chore(deps-dev): bump sass in /apps/block_scout_web/assets (#10862) Bumps [sass](https://github.com/sass/dart-sass) from 1.77.8 to 1.79.4. - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.77.8...1.79.4) --- updated-dependencies: - dependency-name: sass dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 144 ++++-------------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 33 insertions(+), 113 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index a9b9eb853a79..1d0db2b53c5e 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -89,7 +89,7 @@ "mini-css-extract-plugin": "^2.9.0", "postcss": "^8.4.42", "postcss-loader": "^8.1.1", - "sass": "^1.77.8", + "sass": "^1.79.4", "sass-loader": "^14.2.1", "style-loader": "^4.0.0", "webpack": "^5.94.0", @@ -4561,15 +4561,6 @@ "node": "*" } }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/blakejs": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.1.tgz", @@ -5030,42 +5021,18 @@ } }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" + "node": ">= 14.16.0" }, - "engines": { - "node": ">= 6" + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/chrome-trace-event": { @@ -8646,18 +8613,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -13320,15 +13275,16 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz", + "integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==", "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, "engines": { - "node": ">=8.10.0" + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/rechoir": { @@ -13737,12 +13693,12 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sass": { - "version": "1.77.8", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", - "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", + "version": "1.79.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.4.tgz", + "integrity": "sha512-K0QDSNPXgyqO4GZq2HO5Q70TLxTH6cIT59RdoCHMivrC8rqzaTw5ab9prjz9KUN1El4FLXrBXJhik61JR4HcGg==", "dev": true, "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", + "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, @@ -19382,12 +19338,6 @@ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==" }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, "blakejs": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.1.tgz", @@ -19731,30 +19681,12 @@ } }, "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dev": true, "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } + "readdirp": "^4.0.1" } }, "chrome-trace-event": { @@ -22486,15 +22418,6 @@ "has-bigints": "^1.0.1" } }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, "is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -25950,13 +25873,10 @@ } }, "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz", + "integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==", + "dev": true }, "rechoir": { "version": "0.8.0", @@ -26277,12 +26197,12 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { - "version": "1.77.8", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", - "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", + "version": "1.79.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.4.tgz", + "integrity": "sha512-K0QDSNPXgyqO4GZq2HO5Q70TLxTH6cIT59RdoCHMivrC8rqzaTw5ab9prjz9KUN1El4FLXrBXJhik61JR4HcGg==", "dev": true, "requires": { - "chokidar": ">=3.0.0 <4.0.0", + "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" } diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 213c533f48a1..e7c8e4f7a1b2 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -101,7 +101,7 @@ "mini-css-extract-plugin": "^2.9.0", "postcss": "^8.4.42", "postcss-loader": "^8.1.1", - "sass": "^1.77.8", + "sass": "^1.79.4", "sass-loader": "^14.2.1", "style-loader": "^4.0.0", "webpack": "^5.94.0", From 675089534418734f70bcdcde4798ebbf4b670bfb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 11:55:03 +0300 Subject: [PATCH 196/363] chore(deps-dev): bump autoprefixer in /apps/block_scout_web/assets (#10854) Bumps [autoprefixer](https://github.com/postcss/autoprefixer) from 10.4.19 to 10.4.20. - [Release notes](https://github.com/postcss/autoprefixer/releases) - [Changelog](https://github.com/postcss/autoprefixer/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/autoprefixer/compare/10.4.19...10.4.20) --- updated-dependencies: - dependency-name: autoprefixer dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 26 +++++++++---------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 1d0db2b53c5e..4f230eb81b33 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -73,7 +73,7 @@ "devDependencies": { "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.4", - "autoprefixer": "^10.4.19", + "autoprefixer": "^10.4.20", "babel-loader": "^9.1.3", "copy-webpack-plugin": "^12.0.2", "css-loader": "^7.1.2", @@ -4158,9 +4158,9 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "node_modules/autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "dev": true, "funding": [ { @@ -4177,11 +4177,11 @@ } ], "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -19054,16 +19054,16 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "dev": true, "requires": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" } }, diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index e7c8e4f7a1b2..c04496c0d487 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -85,7 +85,7 @@ "devDependencies": { "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.4", - "autoprefixer": "^10.4.19", + "autoprefixer": "^10.4.20", "babel-loader": "^9.1.3", "copy-webpack-plugin": "^12.0.2", "css-loader": "^7.1.2", From 9b4e0c2bb017f81ef1f7c71d7bc761c8c5c18cc4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 11:58:28 +0300 Subject: [PATCH 197/363] chore(deps): bump sweetalert2 in /apps/block_scout_web/assets (#10857) Bumps [sweetalert2](https://github.com/sweetalert2/sweetalert2) from 11.12.4 to 11.14.1. - [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.12.4...v11.14.1) --- updated-dependencies: - dependency-name: sweetalert2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 4f230eb81b33..29f1b334c569 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -61,7 +61,7 @@ "redux": "^5.0.1", "stream-browserify": "^3.0.0", "stream-http": "^3.1.1", - "sweetalert2": "^11.12.4", + "sweetalert2": "^11.14.1", "urijs": "^1.19.11", "url": "^0.11.3", "util": "^0.12.5", @@ -14382,9 +14382,9 @@ } }, "node_modules/sweetalert2": { - "version": "11.12.4", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.12.4.tgz", - "integrity": "sha512-ZSpyaLbAmn4b7xjnV9x9BFD1UOrCAhIzm1D8dZ443kGxtVKqbTIA5SgXs4xeEtmFfEXUyC3RBgpSlu1AXmCiHA==", + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.14.1.tgz", + "integrity": "sha512-xadhfcA4STGMh8nC5zHFFWURhRpWc4zyI3GdMDFH/m3hGWZeQQNWhX9xcG4lI9gZYsi/IlazKbwvvje3juL3Xg==", "funding": { "type": "individual", "url": "https://github.com/sponsors/limonte" @@ -26678,9 +26678,9 @@ } }, "sweetalert2": { - "version": "11.12.4", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.12.4.tgz", - "integrity": "sha512-ZSpyaLbAmn4b7xjnV9x9BFD1UOrCAhIzm1D8dZ443kGxtVKqbTIA5SgXs4xeEtmFfEXUyC3RBgpSlu1AXmCiHA==" + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.14.1.tgz", + "integrity": "sha512-xadhfcA4STGMh8nC5zHFFWURhRpWc4zyI3GdMDFH/m3hGWZeQQNWhX9xcG4lI9gZYsi/IlazKbwvvje3juL3Xg==" }, "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 c04496c0d487..30b1b02625b5 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -73,7 +73,7 @@ "redux": "^5.0.1", "stream-browserify": "^3.0.0", "stream-http": "^3.1.1", - "sweetalert2": "^11.12.4", + "sweetalert2": "^11.14.1", "urijs": "^1.19.11", "url": "^0.11.3", "util": "^0.12.5", From ebd97a20a5166c110a61d8e0d3493d96f4dee516 Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Wed, 2 Oct 2024 12:35:44 +0300 Subject: [PATCH 198/363] feat: support CoinMarketCap format in token supply stats (#10853) --- .../controllers/api/rpc/stats_controller.ex | 23 ++++++++++++++++++- .../api/rpc/stats_controller_test.exs | 18 +++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex index 8b766c7715b8..985be306e460 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex @@ -5,11 +5,23 @@ defmodule BlockScoutWeb.API.RPC.StatsController do alias Explorer.Chain.Cache.{AddressSum, AddressSumMinusBurnt} alias Explorer.Chain.Wei + @cmc_token_supply_precision 9 + def tokensupply(conn, params) do with {:contractaddress_param, {:ok, contractaddress_param}} <- fetch_contractaddress(params), {:format, {:ok, address_hash}} <- to_address_hash(contractaddress_param), {:token, {:ok, token}} <- {:token, Chain.token_from_address_hash(address_hash)} do - render(conn, "tokensupply.json", total_supply: token.total_supply && Decimal.to_string(token.total_supply)) + if Map.get(params, "cmc") == "true" do + conn + |> put_resp_content_type("text/plain") + |> send_resp(200, token.total_supply && to_cmc_total_supply(token.total_supply)) + else + conn + |> render( + "tokensupply.json", + total_supply: token.total_supply && Decimal.to_string(token.total_supply) + ) + end else {:contractaddress_param, :error} -> render(conn, :error, error: "Query parameter contract address is required") @@ -77,6 +89,15 @@ defmodule BlockScoutWeb.API.RPC.StatsController do {:format, Chain.string_to_address_hash(address_hash_string)} end + @spec to_cmc_total_supply(Decimal.t()) :: String.t() + defp to_cmc_total_supply(total_supply) do + total_supply + |> Wei.from(:wei) + |> Wei.to(:ether) + |> Decimal.round(@cmc_token_supply_precision) + |> Decimal.to_string() + end + def totalfees(conn, params) do case Map.fetch(params, "date") do {:ok, date} -> diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs index 6ec7c2e4b7c7..6312896f7d9d 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs @@ -83,6 +83,24 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do assert response["message"] == "OK" assert :ok = ExJsonSchema.Validator.validate(tokensupply_schema(), response) end + + test "with valid contract address and cmc format", %{conn: conn} do + token = insert(:token, total_supply: 110_052_089_716_627_912_057_222_572) + + params = %{ + "module" => "stats", + "action" => "tokensupply", + "contractaddress" => to_string(token.contract_address_hash), + "cmc" => "true" + } + + assert response = + conn + |> get("/api", params) + |> text_response(200) + + assert response == "110052089.716627912" + end end describe "ethsupplyexchange" do From b5f4ab8a7cda093c223193e560449c00736f5646 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:37:06 +0300 Subject: [PATCH 199/363] chore(deps-dev): bump mini-css-extract-plugin (#10859) Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 2.9.0 to 2.9.1. - [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases) - [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v2.9.0...v2.9.1) --- updated-dependencies: - dependency-name: mini-css-extract-plugin dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 29f1b334c569..fdc8cb28f8da 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -86,7 +86,7 @@ "file-loader": "^6.2.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "mini-css-extract-plugin": "^2.9.0", + "mini-css-extract-plugin": "^2.9.1", "postcss": "^8.4.42", "postcss-loader": "^8.1.1", "sass": "^1.79.4", @@ -11556,9 +11556,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", - "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.1.tgz", + "integrity": "sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ==", "dev": true, "dependencies": { "schema-utils": "^4.0.0", @@ -24682,9 +24682,9 @@ } }, "mini-css-extract-plugin": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", - "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.1.tgz", + "integrity": "sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ==", "dev": true, "requires": { "schema-utils": "^4.0.0", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 30b1b02625b5..84e91ba44f73 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -98,7 +98,7 @@ "file-loader": "^6.2.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "mini-css-extract-plugin": "^2.9.0", + "mini-css-extract-plugin": "^2.9.1", "postcss": "^8.4.42", "postcss-loader": "^8.1.1", "sass": "^1.79.4", From 360375ac412919450dedaf4701b8b49085b19426 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:37:25 +0300 Subject: [PATCH 200/363] chore(deps): bump url in /apps/block_scout_web/assets (#10858) Bumps [url](https://github.com/defunctzombie/node-url) from 0.11.3 to 0.11.4. - [Commits](https://github.com/defunctzombie/node-url/compare/v0.11.3...v0.11.4) --- updated-dependencies: - dependency-name: url dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 37 ++++++++++--------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index fdc8cb28f8da..991cc899cc27 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -63,7 +63,7 @@ "stream-http": "^3.1.1", "sweetalert2": "^11.14.1", "urijs": "^1.19.11", - "url": "^0.11.3", + "url": "^0.11.4", "util": "^0.12.5", "viewerjs": "^1.11.6", "web3": "^4.12.1", @@ -14855,12 +14855,15 @@ "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" }, "node_modules/url": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz", - "integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==", + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", + "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", "dependencies": { "punycode": "^1.4.1", - "qs": "^6.11.2" + "qs": "^6.12.3" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/url-parse": { @@ -14879,11 +14882,11 @@ "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" }, "node_modules/url/node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -27013,12 +27016,12 @@ "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" }, "url": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz", - "integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==", + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", + "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", "requires": { "punycode": "^1.4.1", - "qs": "^6.11.2" + "qs": "^6.12.3" }, "dependencies": { "punycode": { @@ -27027,11 +27030,11 @@ "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" }, "qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "requires": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" } } } diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 84e91ba44f73..6bf7d19f3aae 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -75,7 +75,7 @@ "stream-http": "^3.1.1", "sweetalert2": "^11.14.1", "urijs": "^1.19.11", - "url": "^0.11.3", + "url": "^0.11.4", "util": "^0.12.5", "viewerjs": "^1.11.6", "web3": "^4.12.1", From d5a2a0ac42fb7bd100cfabec8dea49b5ea5e5025 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:32:01 +0300 Subject: [PATCH 201/363] chore(deps-dev): bump webpack in /apps/block_scout_web/assets (#10860) Bumps [webpack](https://github.com/webpack/webpack) from 5.94.0 to 5.95.0. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.94.0...v5.95.0) --- updated-dependencies: - dependency-name: webpack dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 991cc899cc27..e27ffa7172a4 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -92,7 +92,7 @@ "sass": "^1.79.4", "sass-loader": "^14.2.1", "style-loader": "^4.0.0", - "webpack": "^5.94.0", + "webpack": "^5.95.0", "webpack-cli": "^5.1.4" }, "engines": { @@ -15486,9 +15486,9 @@ } }, "node_modules/webpack": { - "version": "5.94.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", - "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", + "version": "5.95.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz", + "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==", "dev": true, "dependencies": { "@types/estree": "^1.0.5", @@ -27545,9 +27545,9 @@ "dev": true }, "webpack": { - "version": "5.94.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", - "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", + "version": "5.95.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz", + "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==", "dev": true, "requires": { "@types/estree": "^1.0.5", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 6bf7d19f3aae..ca29e7a0adad 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -104,7 +104,7 @@ "sass": "^1.79.4", "sass-loader": "^14.2.1", "style-loader": "^4.0.0", - "webpack": "^5.94.0", + "webpack": "^5.95.0", "webpack-cli": "^5.1.4" }, "jest": { From c3765c94c786328e5c97b080ff7dd1aeaff18bbc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:32:16 +0300 Subject: [PATCH 202/363] chore(deps-dev): bump postcss in /apps/block_scout_web/assets (#10855) Bumps [postcss](https://github.com/postcss/postcss) from 8.4.42 to 8.4.47. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.4.42...8.4.47) --- updated-dependencies: - dependency-name: postcss dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 e27ffa7172a4..2621d4c72f9f 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -87,7 +87,7 @@ "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "mini-css-extract-plugin": "^2.9.1", - "postcss": "^8.4.42", + "postcss": "^8.4.47", "postcss-loader": "^8.1.1", "sass": "^1.79.4", "sass-loader": "^14.2.1", @@ -12142,9 +12142,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" }, "node_modules/picomatch": { "version": "2.3.0", @@ -12296,9 +12296,9 @@ } }, "node_modules/postcss": { - "version": "8.4.42", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.42.tgz", - "integrity": "sha512-hywKUQB9Ra4dR1mGhldy5Aj1X3MWDSIA1cEi+Uy0CjheLvP6Ual5RlwMCh8i/X121yEDLDIKBsrCQ8ba3FDMfQ==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -12316,8 +12316,8 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -14023,9 +14023,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -25121,9 +25121,9 @@ "integrity": "sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==" }, "picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" }, "picomatch": { "version": "2.3.0", @@ -25218,14 +25218,14 @@ "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" }, "postcss": { - "version": "8.4.42", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.42.tgz", - "integrity": "sha512-hywKUQB9Ra4dR1mGhldy5Aj1X3MWDSIA1cEi+Uy0CjheLvP6Ual5RlwMCh8i/X121yEDLDIKBsrCQ8ba3FDMfQ==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "requires": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" } }, "postcss-calc": { @@ -26435,9 +26435,9 @@ "dev": true }, "source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true }, "source-map-support": { diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index ca29e7a0adad..4a1cb3c0f097 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -99,7 +99,7 @@ "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "mini-css-extract-plugin": "^2.9.1", - "postcss": "^8.4.42", + "postcss": "^8.4.47", "postcss-loader": "^8.1.1", "sass": "^1.79.4", "sass-loader": "^14.2.1", From afdcc0a27e928fb274adcbebbe648025e0e6e20b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:52:39 +0300 Subject: [PATCH 203/363] chore(deps): bump mixpanel-browser in /apps/block_scout_web/assets (#10861) Bumps [mixpanel-browser](https://github.com/mixpanel/mixpanel-js) from 2.54.1 to 2.55.1. - [Release notes](https://github.com/mixpanel/mixpanel-js/releases) - [Changelog](https://github.com/mixpanel/mixpanel-js/blob/master/CHANGELOG.md) - [Commits](https://github.com/mixpanel/mixpanel-js/compare/v2.54.1...v2.55.1) --- updated-dependencies: - dependency-name: mixpanel-browser dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 2621d4c72f9f..11dac710fa3d 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -46,7 +46,7 @@ "lodash.reduce": "^4.6.0", "luxon": "^3.5.0", "malihu-custom-scrollbar-plugin": "3.1.5", - "mixpanel-browser": "^2.54.1", + "mixpanel-browser": "^2.55.1", "moment": "^2.30.1", "nanomorph": "^5.4.0", "numeral": "^2.0.6", @@ -11609,9 +11609,9 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" }, "node_modules/mixpanel-browser": { - "version": "2.54.1", - "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.54.1.tgz", - "integrity": "sha512-1FDs/E0UHPJLry/J0r3pBXrnEhJnz7H2CqzWWZgmdDAm3WiMva69X/qFIqcW3TOmHPaprJVfgwzqt6MJavMrHQ==", + "version": "2.55.1", + "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.55.1.tgz", + "integrity": "sha512-NSEPdFSJxoR1OCKWKHbtqd3BeH1c9NjXbEt0tN5TgBEO1nSDji6niU9n4MopAXOP0POET9spjpQKxZtLZKTJwA==", "dependencies": { "rrweb": "2.0.0-alpha.13" } @@ -24725,9 +24725,9 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" }, "mixpanel-browser": { - "version": "2.54.1", - "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.54.1.tgz", - "integrity": "sha512-1FDs/E0UHPJLry/J0r3pBXrnEhJnz7H2CqzWWZgmdDAm3WiMva69X/qFIqcW3TOmHPaprJVfgwzqt6MJavMrHQ==", + "version": "2.55.1", + "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.55.1.tgz", + "integrity": "sha512-NSEPdFSJxoR1OCKWKHbtqd3BeH1c9NjXbEt0tN5TgBEO1nSDji6niU9n4MopAXOP0POET9spjpQKxZtLZKTJwA==", "requires": { "rrweb": "2.0.0-alpha.13" } diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 4a1cb3c0f097..8bdc16465740 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -58,7 +58,7 @@ "lodash.reduce": "^4.6.0", "luxon": "^3.5.0", "malihu-custom-scrollbar-plugin": "3.1.5", - "mixpanel-browser": "^2.54.1", + "mixpanel-browser": "^2.55.1", "moment": "^2.30.1", "nanomorph": "^5.4.0", "numeral": "^2.0.6", From c876863008697e7f09f4bf01baff10f99145d9f3 Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Thu, 3 Oct 2024 02:30:54 -0600 Subject: [PATCH 204/363] feat: Arbitrum L1-to-L2 messages with hashed message id (#10751) * initial implementation * Finalized approach to handle messages with hashed id * Documentation updated * code review comments addressed * Clarify plain message ID check documentation --- .../lib/ethereum_jsonrpc/transaction.ex | 6 +- .../lib/explorer/chain/arbitrum/reader.ex | 32 +- apps/indexer/lib/indexer/block/fetcher.ex | 15 +- apps/indexer/lib/indexer/buffered_task.ex | 1043 +++++++++++++++-- .../arbitrum/messages_to_l2_matcher.ex | 347 ++++++ .../lib/indexer/fetcher/arbitrum/messaging.ex | 139 ++- .../arbitrum/rollup_messages_catchup.ex | 29 +- .../lib/indexer/fetcher/arbitrum/utils/db.ex | 11 + .../indexer/fetcher/arbitrum/utils/helper.ex | 55 + .../workers/historical_messages_on_l2.ex | 122 +- apps/indexer/lib/indexer/supervisor.ex | 2 + .../indexer/transform/arbitrum/messaging.ex | 17 +- config/runtime.exs | 3 + 13 files changed, 1602 insertions(+), 219 deletions(-) create mode 100644 apps/indexer/lib/indexer/fetcher/arbitrum/messages_to_l2_matcher.ex diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex index f835c4fb6fbf..0287f699b03a 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex @@ -66,7 +66,7 @@ defmodule EthereumJSONRPC.Transaction do :arbitrum -> @chain_type_fields quote( do: [ - request_id: non_neg_integer() + request_id: EthereumJSONRPC.hash() ] ) @@ -662,7 +662,7 @@ defmodule EthereumJSONRPC.Transaction do # # "txType": to avoid FunctionClauseError when indexing Wanchain defp entry_to_elixir({key, value}) - when key in ~w(blockHash condition creates from hash input jsonrpc publicKey raw to txType executionNode requestRecord blobVersionedHashes), + when key in ~w(blockHash condition creates from hash input jsonrpc publicKey raw to txType executionNode requestRecord blobVersionedHashes requestId), do: {key, value} # specific to Nethermind client @@ -670,7 +670,7 @@ defmodule EthereumJSONRPC.Transaction do do: {"input", value} defp entry_to_elixir({key, quantity}) - when key in ~w(gas gasPrice nonce r s standardV v value type maxPriorityFeePerGas maxFeePerGas maxFeePerBlobGas requestId) and + when key in ~w(gas gasPrice nonce r s standardV v value type maxPriorityFeePerGas maxFeePerGas maxFeePerBlobGas) and quantity != nil do {key, quantity_to_integer(quantity)} end diff --git a/apps/explorer/lib/explorer/chain/arbitrum/reader.ex b/apps/explorer/lib/explorer/chain/arbitrum/reader.ex index 881ebe357e38..abfc5b420a39 100644 --- a/apps/explorer/lib/explorer/chain/arbitrum/reader.ex +++ b/apps/explorer/lib/explorer/chain/arbitrum/reader.ex @@ -24,8 +24,10 @@ defmodule Explorer.Chain.Arbitrum.Reader do # https://github.com/OffchainLabs/go-ethereum/blob/dff302de66598c36b964b971f72d35a95148e650/core/types/transaction.go#L44C2-L50 @message_to_l2_eth_deposit 100 @message_to_l2_submit_retryable_tx 105 - - @zero_wei 0 + @to_l2_messages_transaction_types [ + @message_to_l2_eth_deposit, + @message_to_l2_submit_retryable_tx + ] @doc """ Retrieves the number of the latest L1 block where an L1-to-L2 message was discovered. @@ -838,10 +840,6 @@ defmodule Explorer.Chain.Arbitrum.Reader do # table. A message is considered missed if there is a transaction without a # matching message record. # - # For transactions that could be considered ETH deposits, it checks - # that the message value is not zero, as transactions with a zero value - # cannot be a deposit. - # # ## Returns # - A query to retrieve missed L1-to-L2 messages. @spec missed_messages_to_l2_query() :: Ecto.Query.t() @@ -849,10 +847,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do from(rollup_tx in Transaction, left_join: msg in Message, on: rollup_tx.hash == msg.completion_transaction_hash and msg.direction == :to_l2, - where: - (rollup_tx.type == ^@message_to_l2_submit_retryable_tx or - (rollup_tx.type == ^@message_to_l2_eth_deposit and rollup_tx.value != ^@zero_wei)) and - is_nil(msg.completion_transaction_hash) + where: rollup_tx.type in @to_l2_messages_transaction_types and is_nil(msg.completion_transaction_hash) ) end @@ -1337,4 +1332,21 @@ defmodule Explorer.Chain.Arbitrum.Reader do |> Chain.join_associations(%{:transactions => :optional}) |> Repo.all() end + + @doc """ + Retrieves the message IDs of uncompleted L1-to-L2 messages. + + ## Returns + - A list of the message IDs of uncompleted L1-to-L2 messages. + """ + @spec get_uncompleted_l1_to_l2_messages_ids() :: [non_neg_integer()] + def get_uncompleted_l1_to_l2_messages_ids do + query = + from(msg in Message, + where: msg.direction == :to_l2 and is_nil(msg.completion_transaction_hash), + select: msg.message_id + ) + + Repo.all(query) + end end diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 533fd068b826..c86d5443f12c 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -17,6 +17,7 @@ defmodule Indexer.Block.Fetcher do alias Explorer.Chain.Filecoin.PendingAddressOperation, as: FilecoinPendingAddressOperation alias Explorer.Chain.{Address, Block, Hash, Import, Transaction, Wei} alias Indexer.Block.Fetcher.Receipts + alias Indexer.Fetcher.Arbitrum.MessagesToL2Matcher, as: ArbitrumMessagesToL2Matcher alias Indexer.Fetcher.Celo.EpochBlockOperations, as: CeloEpochBlockOperations alias Indexer.Fetcher.Celo.EpochLogs, as: CeloEpochLogs alias Indexer.Fetcher.CoinBalance.Catchup, as: CoinBalanceCatchup @@ -186,7 +187,8 @@ defmodule Indexer.Block.Fetcher do do: PolygonZkevmBridge.parse(blocks, logs), else: [] ), - arbitrum_xlevel_messages = ArbitrumMessaging.parse(transactions_with_receipts, logs), + {arbitrum_xlevel_messages, arbitrum_txs_for_further_handling} = + ArbitrumMessaging.parse(transactions_with_receipts, logs), %FetchedBeneficiaries{params_set: beneficiary_params_set, errors: beneficiaries_errors} = fetch_beneficiaries(blocks, transactions_with_receipts, json_rpc_named_arguments), addresses = @@ -265,6 +267,9 @@ defmodule Indexer.Block.Fetcher do update_addresses_cache(inserted[:addresses]) update_uncles_cache(inserted[:block_second_degree_relations]) update_withdrawals_cache(inserted[:withdrawals]) + + async_match_arbitrum_messages_to_l2(arbitrum_txs_for_further_handling) + result else {step, {:error, reason}} -> {:error, {step, reason}} @@ -739,4 +744,12 @@ defmodule Indexer.Block.Fetcher do Map.put(token_transfer, :token, token) end) end + + # Asynchronously schedules matching of Arbitrum L1-to-L2 messages where the message ID is hashed. + @spec async_match_arbitrum_messages_to_l2([map()]) :: :ok + defp async_match_arbitrum_messages_to_l2([]), do: :ok + + defp async_match_arbitrum_messages_to_l2(txs_with_messages_from_l1) do + ArbitrumMessagesToL2Matcher.async_discover_match(txs_with_messages_from_l1) + end end diff --git a/apps/indexer/lib/indexer/buffered_task.ex b/apps/indexer/lib/indexer/buffered_task.ex index 3c9095d2cb71..22fb518bf313 100644 --- a/apps/indexer/lib/indexer/buffered_task.ex +++ b/apps/indexer/lib/indexer/buffered_task.ex @@ -1,51 +1,301 @@ defmodule Indexer.BufferedTask do @moduledoc """ - Provides a behaviour for batched task running with retries. + Provides a behaviour for batched task running with retries and memory-aware buffering. + + This module implements a generic buffered task system that allows for efficient + processing of data in batches. It manages a queue of entries, processes them + concurrently up to a specified limit, and provides mechanisms for retrying + failed tasks. The module is designed to be memory-aware, with the ability to + shrink its memory usage when requested by a memory monitor. + + The `BufferedTask` operates as follows: + + 1. Initialization: + When a module implementing the `BufferedTask` behaviour is started as part of + a supervision tree, it must use `BufferedTask.child_spec/1` to define its + child specification. This ensures that `BufferedTask.start_link/2` is called + correctly, initializing the `BufferedTask` process with the implementing + module's options. During this initialization, the periodic flushing of + buffers to the processing queue is also set up, establishing an automated + cycle of data processing. + + 2. Initial Data Streaming: + Upon initialization, the `BufferedTask` sends itself an `:initial_stream` + message. This triggers a call to the `init/3` callback of the implementing + module, populating the queue with initial data. + + 3. Data Processing Flow: + a. Data Entry: + - External processes can add entries to the buffer using the `buffer/4` + function. This function allows for both synchronous and asynchronous + buffering of entries, with an option to prioritize entries. + b. Flushing: + - Periodically, based on the `flush_interval`, or when manually triggered, + the buffered entries are moved to the main processing queue. This + flushing process automatically triggers the processing of the next + batch of queue data, ensuring continuous data handling without manual + intervention. + c. Batch Processing: + - As concurrency becomes available (controlled by `max_concurrency`), + batches of entries (size determined by `max_batch_size`) are taken from + the queue and processed by spawning tasks that call the `run/2` callback + of the implementing module. + d. Retry Mechanism: + - If a task fails or explicitly requests a retry, the entries are + re-added to the queue for another processing attempt. + + The entire process of periodic flushing, batch processing, and retry handling + is automated, requiring no manual management once the `BufferedTask` is + initialized. This design ensures efficient and continuous data processing with + minimal overhead. + + The `BufferedTask` is designed to be memory-aware and can interact with a + memory monitor to adjust its memory usage when system resources are constrained. + It also provides debugging capabilities to monitor its internal state and + performance. + + ## Initialization Parameters + + The following parameters are passed to `BufferedTask.init/1` through + `BufferedTask.child_spec/1` and `BufferedTask.start_link/2`: + + - `:callback_module`: Required. The module implementing the `BufferedTask` + behaviour. + - `:task_supervisor`: Required. The name of the `Task.Supervisor` to spawn + tasks under. + - `:flush_interval`: Required. The interval in milliseconds between automatic + queue flushes. Set to `:infinity` to disable automatic flushing. + - `:max_batch_size`: Required. The maximum number of entries to be processed + in a single batch. + - `:max_concurrency`: Required. The maximum number of concurrent processing + tasks allowed. + - `:memory_monitor`: Optional. The `Indexer.Memory.Monitor` server to register + with for memory-aware operations. + - `:poll`: Optional. Boolean flag to enable/disable polling for new data after + processing all current entries. Defaults to `true`. + - `:state`: Required. Initial state for the callback module. This can include + any parameters or initial values required for proper functioning of `init` + and `run` callbacks. + - `:metadata`: Optional. Logger metadata to set in the `BufferedTask` process + and its child processes. - ## Named Arguments + ## Callbacks - Named arguments are required and are passed in the list that is the second element of the tuple. + ### `c:init/3` - * `:flush_interval` - The interval in milliseconds to flush the buffer. - * `:max_concurrency` - The maximum number of tasks to run concurrently at any given time. - * `:poll` - poll for new records when all records are processed - * `:max_batch_size` - The maximum batch passed to `c:run/2`. - * `:memory_monitor` - The `Indexer.Memory.Monitor` `t:GenServer.server/0` to register as - `Indexer.Memory.Monitor.shrinkable/0` with. - * `:task_supervisor` - The `Task.Supervisor` name to spawn tasks under. + @callback init(initial, reducer, state) :: {final, state} - ## Options + This callback is invoked during the initial streaming process to populate the + queue with initial data. It runs in a separate task, allowing for long-running + operations without blocking the main `BufferedTask` process. - Options are optional and are passed in the list that is second element of the tuple. + - `initial`: An opaque value representing the initial accumulator. Its structure + and content are fully controlled by the `reducer` function, so there's no need + to handle this parameter specifically within the `init/3` callback. + - `reducer`: A function that accumulates entries into the `BufferedTask`'s + internal buffers. + - `state`: The initial state provided during initialization. - * `:name` - The registered name for the new process. + The `init/3` callback should use the `reducer` function to add entries to the + `BufferedTask`'s buffers. The `BufferedTask` will automatically manage these + entries, flushing them to the main processing queue and initiating batch + processing as needed. - ## Callbacks + ### `c:run/2` + + @callback run(entries, state) :: :ok | {:ok, state} | :retry | {:retry, new_entries :: list} | {:retry, new_entries :: list, state} + + This callback is invoked as concurrency becomes available to process batches + of entries from the queue. It is called within a task spawned by the + `Task.Supervisor` specified during initialization. + + - `entries`: A list of entries to be processed, with a maximum length of + `:max_batch_size`. + - `state`: The current state of the callback module. + + The `run/2` callback processes the given entries and returns one of the following + possible results: + + - `:ok`: Indicates successful processing. + - `{:ok, state}`: Indicates successful processing and requests an update to + the callback module state. + - `:retry`: Signals that the entire batch should be retried. + - `{:retry, new_entries}`: Specifies a new list of entries to be retried. This + can be a completely new list of entries or a subset of entries which were not + successfully handled by `run/2` in this iteration. + - `{:retry, new_entries, state}`: Specifies a new list of entries to be retried + and requests an update to the callback module state. + + If the callback crashes, the `BufferedTask` will automatically retry the batch. + The retry mechanism ensures resilience in data processing, allowing for + temporary failures or resource unavailability to be handled gracefully. + + The `BufferedTask` manages concurrency, ensuring that no more than + `:max_concurrency` `run/2` callbacks are executing simultaneously. This + provides controlled parallelism while preventing system overload. + + ## Examples - `c:init/2` is used for a task to populate its buffer on boot with an initial set of entries. + ### Typical Usage - For example, the following callback would buffer all unfetched account balances on startup: + Here's a comprehensive example of a module implementing the BufferedTask behaviour + for processing random integers: - def init(initial, reducer) do - Chain.stream_unfetched_balances([:hash], initial, fn %{hash: hash}, acc -> - reducer.(Hash.to_string(hash), acc) - end) + defmodule NumberProcessor do + # Will generate the Supervisor and TaskSupervisor for NumberProcessor + use Indexer.Fetcher, restart: :permanent + + alias Indexer.BufferedTask + + def child_spec([init_options, gen_server_options]) do + state = %{initial_count: 50} + + buffered_task_init_options = + [ + poll: false, + flush_interval: 5000, + max_batch_size: 10, + max_concurrency: 2, + task_supervisor: NumberProcessor.TaskSupervisor, + metadata: [task: :number_processor] + ] + |> Keyword.merge(init_options) + |> Keyword.put(:state, state) + + Supervisor.child_spec( + {BufferedTask, [{__MODULE__, buffered_task_init_options}, gen_server_options]}, + id: __MODULE__ + ) + end + + @impl BufferedTask + def init(initial, reducer, %{initial_count: count}) do + Enum.reduce(1..count, initial, fn _, acc -> + number = :rand.uniform(1000) + reducer.(number, acc) + end) + end + + @impl BufferedTask + def run(numbers, _state) do + Enum.each(numbers, fn number -> + result = if rem(number, 2) == 0, do: "even", else: "odd" + IO.puts("Number \#{number} is \#{result}") + end) + :ok + end + + def add_numbers(count, high_priority?) do + numbers = Enum.map(1..count, fn _ -> :rand.uniform(1000) end) + BufferedTask.buffer(__MODULE__, numbers, high_priority?) + end end - `c:init/2` may be long-running and allows concurrent calls to `Explorer.BufferedTask.buffer/2` for on-demand entries. - As concurrency becomes available, `c:run/2` of the task is invoked, with a list of batched entries to be processed. + To use this module in your application's supervision tree: + + children = [ + {NumberProcessor.Supervisor, [[memory_monitor: memory_monitor]]} + ] + + Supervisor.init(children, strategy: :one_for_one) + + This setup assumes you have a memory_monitor defined elsewhere in your application. + + To add more numbers to the buffer after initialization: + + NumberProcessor.add_numbers(20, false) # Add 20 numbers with normal priority + NumberProcessor.add_numbers(5, true) # Add 5 numbers with high priority + + ### Minimal Init and Retry Example + + This example demonstrates two key features of BufferedTask: a minimal `init/3` + callback that simply passes through the initial state and use of `{:retry, new_entries}` + in the `run/2` callback for task rescheduling. + + defmodule TaskScheduler do + use Indexer.Fetcher, restart: :permanent + + alias Indexer.BufferedTask - For example, the `c:run/2` for above `c:init/2` could be written: + def child_spec([init_options, gen_server_options]) do + state = %{max_retries: 3} - def run(string_hashes, _state) do - case EthereumJSONRPC.fetch_balances_by_hash(string_hashes) do - {:ok, results} -> :ok = update_balances(results) - {:error, _reason} -> :retry + buffered_task_init_options = + [ + poll: false, + flush_interval: 1000, + max_batch_size: 10, + max_concurrency: 5, + task_supervisor: TaskScheduler.TaskSupervisor, + metadata: [fetcher: :task_scheduler] + ] + |> Keyword.merge(init_options) + |> Keyword.put(:state, state) + + Supervisor.child_spec( + {BufferedTask, [{__MODULE__, buffered_task_init_options}, gen_server_options]}, + id: __MODULE__ + ) + end + + @impl BufferedTask + def init(initial, _reducer, _state) do + initial + end + + @impl BufferedTask + def run(tasks, %{max_retries: max_retries} = state) do + now = System.system_time(:millisecond) + {due_tasks, future_tasks} = Enum.split_with(tasks, &(&1.at <= now)) + + new_future_tasks = Enum.map(due_tasks, fn task -> + case execute_task(task) do + :ok -> nil + :error -> + retry_count = Map.get(task, :retry_count, 0) + if retry_count < max_retries do + %{task | retry_count: retry_count + 1} + end + end + end) + |> Enum.reject(&is_nil/1) + + case future_tasks ++ new_future_tasks do + [] -> :ok + remaining_tasks -> {:retry, remaining_tasks} + end + end + + defp execute_task(%{fun: fun, args: args}) do + try do + apply(fun, args) + :ok + rescue + _ -> :error + end + end + + def schedule_task(at, fun, args) do + task = %{at: at, fun: fun, args: args} + BufferedTask.buffer(__MODULE__, [task], false) end end - If a task crashes, it will be retried automatically. Tasks may also be programmatically retried by returning `:retry` - from `c:run/2`. + To use this module in your application's supervision tree: + + children = [ + {TaskScheduler.Supervisor, [[memory_monitor: memory_monitor]]} + ] + + Supervisor.init(children, strategy: :one_for_one) + + To schedule a new task: + + TaskScheduler.schedule_task( + System.system_time(:millisecond) + 5000, + fn -> IO.puts("Hello from the future!") end, + [] + ) """ use GenServer @@ -54,7 +304,7 @@ defmodule Indexer.BufferedTask do import Indexer.Logger, only: [process: 1] - alias Indexer.{BoundQueue, BufferedTask, Memory} + alias Indexer.{BoundQueue, Memory} @enforce_keys [ :callback_module, @@ -78,6 +328,78 @@ defmodule Indexer.BufferedTask do bound_queue: %BoundQueue{}, task_ref_to_batch: %{} + @typedoc """ + BufferedTask struct: + * `init_task` - reference to the initial streaming task. This field holds the + `Task.t()` for the initial data population process. It's used to track the + completion of the initial data streaming. + * `flush_timer` - reference to the timer for periodic buffer flushing. This + field stores the timer reference returned by `Process.send_after/3`, which + is scheduled using the `flush_interval`. When the timer triggers, it sends + a `:flush` message to the process, initiating a buffer flush operation. + This field is managed internally and doesn't need to be set by the user. + * `callback_module` - module implementing the `BufferedTask` behaviour. This + field must be set when initializing the `BufferedTask`. It specifies the + module that defines the `init/3` and `run/2` callbacks, which are crucial + for the operation of the buffered task. + * `callback_module_state` - state maintained by the callback module. This + field holds any state that the callback module needs to persist between + calls to its callbacks. It's passed to and potentially updated by the + `run/2` callback. + * `task_supervisor` - name of the `Task.Supervisor` for spawning tasks. This + field must be set during initialization. It's used to spawn concurrent + tasks for processing batches of data, allowing for controlled + concurrency. + * `flush_interval` - interval in milliseconds between buffer flushes. This + field must be set during initialization. It determines how often the + buffer is automatically flushed, ensuring that data is processed even + if the buffer doesn't fill up quickly. If set to `:infinity`, no automatic + flushing occurs - it can be used for when manual flushing is preferred by + sending a `:flush` message to the process. + * `max_batch_size` - maximum number of entries in a batch for processing. + This field must be set during initialization. It controls the size of + batches sent to the `run/2` callback, allowing for optimized processing + of data. + * `max_concurrency` - maximum number of concurrent processing tasks. This + field must be set during initialization. It limits the number of + simultaneous `run/2` callback executions, preventing system overload. + * `poll` - boolean flag to enable/disable polling for new records. This field + has a default value of true. When true, the module will continue to call + `init/3` to fetch new data after processing all current entries. + * `metadata` - list of metadata for logging purposes. This field can be set + during initialization. It's used to add context to log messages, + improving the traceability of the buffered task's operations. + * `current_buffer` - list of entries waiting to be processed. This field is + used internally to store incoming entries before they're moved to the + bound_queue for processing. Acts as a temporary holding area for + incoming data. + * `current_front_buffer` - list of high-priority entries for processing. These + entries are moved to the front of the bound_queue during flushing. + * `bound_queue` - queue with a maximum size limit for storing entries. This + field uses a `BoundQueue` struct to efficiently manage entries while + respecting memory constraints. + * `task_ref_to_batch` - map of task references to their corresponding batches. + This field is used internally to keep track of which batch is being + processed by each spawned task, facilitating proper handling of task + results and retries. + """ + @type t :: %__MODULE__{ + init_task: Task.t() | :complete | nil, + flush_timer: reference() | nil, + callback_module: module(), + callback_module_state: term(), + task_supervisor: GenServer.name(), + flush_interval: timeout() | :infinity, + max_batch_size: non_neg_integer(), + max_concurrency: non_neg_integer(), + poll: boolean(), + metadata: Logger.metadata(), + current_buffer: [term(), ...], + current_front_buffer: [term(), ...], + bound_queue: BoundQueue.t(term()), + task_ref_to_batch: %{reference() => [term(), ...]} + } + @typedoc """ Entry passed to `t:reducer/2` in `c:init/2` and grouped together into a list as `t:entries/0` passed to `c:run/2`. """ @@ -112,55 +434,106 @@ defmodule Indexer.BufferedTask do @type state :: term() @doc """ - Populates a task's buffer on boot with an initial set of entries. - - For example, the following callback would buffer all unfetched account balances on startup: - - def init(initial, reducer, state) do - final = Chain.stream_unfetched_balances([:hash], initial, fn %{hash: hash}, acc -> - reducer.(Hash.to_string(hash), acc) - end) - - {final, state} - end - - The `init/2` operation may be long-running as it is run in a separate process and allows concurrent calls to - `Explorer.BufferedTask.buffer/2` for on-demand entries. + This callback is invoked during the initial streaming process to populate the + queue with initial data. It runs in a separate task, allowing for long-running + operations without blocking the main `BufferedTask` process. + + - `initial`: An opaque value representing the initial accumulator. Its structure + and content are fully controlled by the `reducer` function, so there's no need + to handle this parameter specifically within the `init/3` callback. + - `reducer`: A function that accumulates entries into the `BufferedTask`'s + internal buffers. + - `state`: The initial state provided during initialization. + + The `init/3` callback should use the `reducer` function to add entries to the + `BufferedTask`'s buffers. The `BufferedTask` will automatically manage these + entries, flushing them to the main processing queue and initiating batch + processing as needed. """ @callback init(initial, reducer, state) :: accumulator @doc """ - Invoked as concurrency becomes available with a list of batched entries to be processed. - - For example, the `c:run/2` callback for the example `c:init/2` callback could be written: - - def run(string_hashes, _state) do - case EthereumJSONRPC.fetch_balances_by_hash(string_hashes) do - {:ok, results} -> :ok = update_balances(results) - {:error, _reason} -> :retry - end - end - - If a task crashes, it will be retried automatically. Tasks may also be programmatically retried by returning `:retry` - from `c:run/2`. - - ## Returns - - * `:ok` - run was successful - * `:retry` - run should be retried after it failed - * `{:retry, new_entries :: list}` - run should be retried with `new_entries` - + This callback is invoked as concurrency becomes available to process batches + of entries from the queue. It is called within a task spawned by the + `Task.Supervisor` specified during initialization. + + - `entries`: A list of entries to be processed, with a maximum length of + `:max_batch_size`. + - `state`: The current state of the callback module. + + The `run/2` callback processes the given entries and returns one of the following + possible results: + + - `:ok`: Indicates successful processing. + - `{:ok, state}`: Indicates successful processing and requests an update to + the callback module state. + - `:retry`: Signals that the entire batch should be retried. + - `{:retry, new_entries}`: Specifies a new list of entries to be retried. This + can be a completely new list of entries or a subset of entries which were not + successfully handled by `run/2` in this iteration. + - `{:retry, new_entries, state}`: Specifies a new list of entries to be retried + and requests an update to the callback module state. + + If the callback crashes, the `BufferedTask` will automatically retry the batch. + The retry mechanism ensures resilience in data processing, allowing for + temporary failures or resource unavailability to be handled gracefully. + + The `BufferedTask` manages concurrency, ensuring that no more than + `:max_concurrency` `run/2` callbacks are executing simultaneously. This + provides controlled parallelism while preventing system overload. """ - @callback run(entries, state) :: :ok | :retry | {:retry, new_entries :: list} + @callback run(entries, state) :: + :ok | {:ok, state} | :retry | {:retry, new_entries :: list} | {:retry, new_entries :: list, state} @doc """ - Buffers list of entries for future async execution. + Buffers a list of entries for future execution. + + This function sends a message to the specified BufferedTask process to add the + given entries to one of two internal buffers: + 1. The regular buffer, if `front?` is `false`. + 2. The front buffer, if `front?` is `true`. + + When the buffers are later flushed asynchronously: + - Entries from the regular buffer are added to the end of the processing queue. + - Entries from the front buffer are added to the beginning of the processing queue. + + Note: If the total number of elements in the buffers exceeds the maximum queue + size (which is undefined by default) when flushed, excess elements will be + dropped without notification to the calling process. + + ## Parameters + - `server`: The name or PID of the BufferedTask process. + - `entries`: A list of entries to be buffered. + - `front?`: If `true`, entries are added to the front buffer; if `false`, + they are added to the regular buffer. + - `timeout`: The maximum time to wait for a reply, in milliseconds. Defaults to + 5000 ms. + + ## Returns + - `:ok` if the entries were successfully added to the appropriate buffer. """ + @spec buffer(GenServer.name(), entries(), boolean()) :: :ok @spec buffer(GenServer.name(), entries(), boolean(), timeout()) :: :ok def buffer(server, entries, front?, timeout \\ 5000) when is_list(entries) do GenServer.call(server, {:buffer, entries, front?}, timeout) end + @doc """ + Defines a child specification for a BufferedTask to be used in a supervision tree. + + ## Parameters + - `[init_arguments]` or `[init_arguments, gen_server_options]`: + - `init_arguments`: A list of initialization arguments for the BufferedTask. + - `gen_server_options`: An optional list of GenServer options. + + ## Returns + A child specification map suitable for use in a supervision tree. + + ## Usage + This function is typically called indirectly as part of the `child_spec/1` + function of a module implementing the BufferedTask behavior. It's not intended + to be called directly in application code. + """ def child_spec([init_arguments]) do child_spec([init_arguments, []]) end @@ -174,33 +547,64 @@ defmodule Indexer.BufferedTask do Supervisor.child_spec(default, []) end - @doc false + @doc """ + Retrieves debug information about the current state of the BufferedTask. + + Returns a map containing the total number of entries in buffers and queue, + and the number of active tasks. This function is useful for monitoring and + debugging the BufferedTask's internal state. + + ## Parameters + - `server`: The name or PID of the BufferedTask process. + + ## Returns + A map with keys `:buffer` (total entries count) and `:tasks` (active tasks count). + """ + @spec debug_count(GenServer.name()) :: %{buffer: non_neg_integer(), tasks: non_neg_integer()} def debug_count(server) do GenServer.call(server, :debug_count) end @doc """ - Starts `callback_module` as a buffered task. + Starts a `BufferedTask` process for the given callback module. - Takes a tuple of the callback module and list of named arguments and options, much like the format accepted for - `Supervisor.start_link/2`, `Supervisor.init/2` and `Supervisor.child_spec/2`. + This function is generally not called directly in application code. Instead, + it's used in the context of a supervision tree, typically invoked through + the `child_spec/1` function of a module implementing the BufferedTask behavior. + It initializes a BufferedTask process and ultimately leads to calling + `BufferedTask.init/1`. - ## Named Arguments + ## Parameters + The function takes a tuple with two elements: + 1. `callback_module`: The module implementing the `BufferedTask` behavior. + 2. A keyword list of options, which is a merge of application-wide defaults + and task-specific options. - Named arguments are required and are passed in the list that is the second element of the tuple. + ### Named Arguments + These are required and should be included in the options list: * `:flush_interval` - The interval in milliseconds to flush the buffer. - * `:max_concurrency` - The maximum number of tasks to run concurrently at any given time. - * `:max_batch_size` - The maximum batch passed to `c:run/2`. + * `:max_concurrency` - The maximum number of tasks to run concurrently. + * `:max_batch_size` - The maximum batch size passed to `c:run/2`. * `:task_supervisor` - The `Task.Supervisor` name to spawn tasks under. + * `:state` - Initial state for the callback module. - ## Options - - Options are optional and are passed in the list that is second element of the tuple. + ### Options + These are optional and can be included in the options list: * `:name` - The registered name for the new process. - * `:metadata` - `Logger.metadata/1` to det in teh `Indexer.BufferedTask` process and any child processes. - + * `:metadata` - `Logger.metadata/1` to set in the `BufferedTask` process + and any child processes. + * `:memory_monitor` - The memory monitor process name, if applicable. + + ## Returns + * `{:ok, pid()}` if the process starts successfully. + * `{:error, {:already_started, pid()}}` if the process is already started. + + ## Note + The options passed to this function are a merge of application-wide defaults + (retrieved from `Application.get_all_env(:indexer)`) and the task-specific + options provided when setting up the fetcher. """ @spec start_link( {callback_module :: module, @@ -220,15 +624,47 @@ defmodule Indexer.BufferedTask do GenServer.start_link(__MODULE__, {module, init_opts}, genserver_opts) end + @doc """ + Initializes the BufferedTask process. + + This function accepts parameters passed from `start_link/2`, sends an + `:initial_stream` message to self to start the initial streaming process, + sets up the process as shrinkable if a memory monitor is provided, sets + Logger metadata, and configures the initial state of `BufferedTask` + including the state of a module implementing the BufferedTask behavior. + + ## Parameters + - `{callback_module, opts}`: A tuple containing: + - `callback_module`: The module implementing the BufferedTask behavior. + - `opts`: A keyword list of options for initializing the BufferedTask. + + ## Options + - `:state`: Required. The initial state for the callback module. + - `:task_supervisor`: Required. The name of the Task.Supervisor. + - `:flush_interval`: Required. The interval for flushing the buffer to the + processing queue. + - `:max_batch_size`: Required. The maximum size of the queue's data batch + to be processed at once. + - `:max_concurrency`: Required. The maximum number of concurrent tasks to + process the queue's data. + - `:poll`: Optional. Whether to poll for new data from the stream after + processing all current data. Defaults to `true`. + - `:metadata`: Optional. Logger metadata. Defaults to an empty list. + + ## Returns + `{:ok, state}` where `state` is the initialized BufferedTask struct. + """ def init({callback_module, opts}) do send(self(), :initial_stream) + # Allows the memory monitor to shrink the amount of elements in the queue + # when extensive memory usage is detected. shrinkable(opts) metadata = Keyword.get(opts, :metadata, []) Logger.metadata(metadata) - state = %BufferedTask{ + state = %__MODULE__{ callback_module: callback_module, callback_module_state: Keyword.fetch!(opts, :state), poll: Keyword.get(opts, :poll, true), @@ -242,54 +678,102 @@ defmodule Indexer.BufferedTask do {:ok, state} end + # Initiates the initial data streaming process in response to the :initial_stream + # message. This message is self-sent during initialization to start populating + # the queue with initial data using the `init/3` function of the callback module. def handle_info(:initial_stream, state) do {:noreply, do_initial_stream(state)} end + # Handles the periodic flush message to process buffered entries. + # This message is scheduled by the flush_interval to ensure regular processing of + # accumulated data, pushing it to the queue and triggering batch processing. def handle_info(:flush, state) do {:noreply, flush(state)} end - def handle_info({ref, :ok}, %{init_task: ref} = state) do + # Handles the successful completion of the initial streaming task. + def handle_info({ref, :ok}, %__MODULE__{init_task: ref} = state) do {:noreply, state} end + # Handles the successful completion of a task processing queue data, removes the + # reference to the task, and triggers processing of the next batch if queue + # contains data. def handle_info({ref, :ok}, state) do {:noreply, drop_task(state, ref)} end + # Handles the successful completion of a task processing queue data, updated the + # callback module state, removes the reference to the task, and triggers processing + # of the next batch if queue contains data. + def handle_info({ref, {:ok, new_callback_module_state}}, state) do + {:noreply, drop_task(%__MODULE__{state | callback_module_state: new_callback_module_state}, ref)} + end + + # Handles a retry request for a task processing queue data. The original batch + # is added back to the queue and processing of the next batch is triggered. + # Useful when all data from the batch needs to be reprocessed. def handle_info({ref, :retry}, state) do {:noreply, drop_task_and_retry(state, ref)} end + # Handles a retry request for a task processing queue data with specified + # retryable entries. These entries are added to the queue and processing of + # the next batch is triggered. Useful when only part of the original batch + # needs to be reprocessed. def handle_info({ref, {:retry, retryable_entries}}, state) do {:noreply, drop_task_and_retry(state, ref, retryable_entries)} end - def handle_info({:DOWN, ref, :process, _pid, :normal}, %BufferedTask{init_task: ref} = state) do - {:noreply, %{state | init_task: :complete}} + # Handles a retry request for a task processing queue data with specified + # retryable entries. If the task modified the state, the call back module + # state is updated. These entries are added to the queue and processing of + # the next batch is triggered. + # If all entries are needed to be retried, the `retryable_entries` should + # be `nil`. + def handle_info({ref, {:retry, retryable_entries, new_callback_module_state}}, state) do + {:noreply, + drop_task_and_retry(%__MODULE__{state | callback_module_state: new_callback_module_state}, ref, retryable_entries)} + end + + # Handles the normal termination of the initial streaming task, marking it as complete. + def handle_info({:DOWN, ref, :process, _pid, :normal}, %__MODULE__{init_task: ref} = state) do + {:noreply, %__MODULE__{state | init_task: :complete}} end + # Handles the normal termination of a non-initial task, no action needed. def handle_info({:DOWN, _ref, :process, _pid, :normal}, state) do {:noreply, state} end + # Handles abnormal termination of a task processing queue data. The task's batch + # is re-added to the queue and processing of the next batch is triggered. def handle_info({:DOWN, ref, :process, _pid, _reason}, state) do {:noreply, drop_task_and_retry(state, ref)} end + # Handles asynchronous buffering of entries. + # Adds the provided entries to either the front or back buffer without waiting for a response. + # This is used for non-blocking buffering operations. def handle_info({:buffer, entries, front?}, state) do {:noreply, buffer_entries(state, entries, front?)} end + # Handles synchronous buffering of entries. + # Adds the provided entries to either the front or back buffer and waits for the operation to complete. + # This is used when the caller needs confirmation that the entries have been buffered. def handle_call({:buffer, entries, front?}, _from, state) do {:reply, :ok, buffer_entries(state, entries, front?)} end + # Provides debug information about the current state of the BufferedTask. + # Returns a count of entries in buffers and queue, and the number of active tasks. + # This is useful for monitoring and debugging the BufferedTask's internal state. def handle_call( :debug_count, _from, - %BufferedTask{ + %__MODULE__{ current_buffer: current_buffer, current_front_buffer: current_front_buffer, bound_queue: bound_queue, @@ -302,6 +786,10 @@ defmodule Indexer.BufferedTask do {:reply, %{buffer: count, tasks: Enum.count(task_ref_to_batch)}, state} end + # Retrieves the full internal state of the BufferedTask. + # This handler provides complete access to the BufferedTask's state, + # primarily for advanced debugging, testing, or custom runtime introspection. + # Use with caution as it exposes internal implementation details. def handle_call( :state, _from, @@ -310,6 +798,8 @@ defmodule Indexer.BufferedTask do {:reply, state, state} end + # Adds entries to the back of the queue and initiates processing of the next + # batch of queue's data by the callback module. def handle_call({:push_back, entries}, _from, state) when is_list(entries) do new_state = state @@ -319,6 +809,8 @@ defmodule Indexer.BufferedTask do {:reply, :ok, new_state} end + # Adds entries to the front of the queue and initiates processing of the next + # batch of queue's data by the callback module. def handle_call({:push_front, entries}, _from, state) when is_list(entries) do new_state = state @@ -328,6 +820,8 @@ defmodule Indexer.BufferedTask do {:reply, :ok, new_state} end + # Attempts to shrink the bound queue in response to high memory usage detection. + # Called by the Memory Monitor when this process is identified as a high memory consumer. def handle_call(:shrink, _from, %__MODULE__{bound_queue: bound_queue} = state) do {reply, shrunk_state} = case BoundQueue.shrink(bound_queue) do @@ -341,42 +835,133 @@ defmodule Indexer.BufferedTask do {:reply, reply, shrunk_state, :hibernate} end + # Checks if the bound queue has been previously shrunk. + # Used by the Memory Monitor to track which processes have been shrunk and may be eligible for expansion. def handle_call(:shrunk?, _from, %__MODULE__{bound_queue: bound_queue} = state) do {:reply, BoundQueue.shrunk?(bound_queue), state} end + # Expands the previously shrunk bound queue to its original size. + # Called by the Memory Monitor when overall system memory usage has decreased sufficiently. def handle_call(:expand, _from, %__MODULE__{bound_queue: bound_queue} = state) do - {:reply, :ok, %{state | bound_queue: BoundQueue.expand(bound_queue)}} + {:reply, :ok, %__MODULE__{state | bound_queue: BoundQueue.expand(bound_queue)}} end + # Removes a task from the state and initiates the next batch processing. + # + # This function is called to remove a task reference from the state, regardless + # of whether the task completed successfully, failed, or needs to be retried. + # After removing the task, it attempts to spawn the next batch of queue's data + # for processing. + # + # ## Parameters + # - `state`: The current state of the BufferedTask. + # - `ref`: The reference of the task to be removed. + # + # ## Returns + # Updated state after removing the task and potentially spawning a new data + # portion for processing. + @spec drop_task(t(), reference()) :: t() defp drop_task(state, ref) do - spawn_next_batch(%BufferedTask{state | task_ref_to_batch: Map.delete(state.task_ref_to_batch, ref)}) + spawn_next_batch(%__MODULE__{state | task_ref_to_batch: Map.delete(state.task_ref_to_batch, ref)}) end - defp drop_task_and_retry(%BufferedTask{task_ref_to_batch: task_ref_to_batch} = state, ref, new_batch \\ nil) do + # Removes a task from the state and schedules it (or another chunk of data) for retry. + # + # This function is called when a task needs to be retried, either due to failure + # or explicit retry request. It removes the task reference from the state and + # pushes either a new batch of entries (if provided) or the original batch back + # to the queue for reprocessing. + # + # ## Parameters + # - `state`: The current state of the BufferedTask. + # - `ref`: The reference of the task to be removed and retried. + # - `new_batch`: Optional. A new batch of entries to be processed instead of + # the original batch. Defaults to nil. + # + # ## Returns + # Updated state after removing the task and pushing entries for retry. + @spec drop_task_and_retry( + %__MODULE__{task_ref_to_batch: %{reference() => [term(), ...]}}, + reference(), + entries() | nil + ) :: t() + defp drop_task_and_retry(%__MODULE__{task_ref_to_batch: task_ref_to_batch} = state, ref, new_batch \\ nil) do batch = Map.fetch!(task_ref_to_batch, ref) + # Question: should we push the data to the queue first and then spawn the next batch? state |> drop_task(ref) |> push_back(new_batch || batch) end + # Adds new entries to the appropriate buffer in the current state. + # + # This function has the following behaviors depending on the input: + # 1. If `front?` is true, it prepends the entries to the current front buffer. + # 2. If `front?` is false, it prepends the entries to the current buffer. + # 3. If entries is empty, it returns the original state unchanged. + # + # ## Parameters + # - `state`: The current state of the BufferedTask. + # - `entries`: A list of new entries to be added to a buffer. + # - `front?`: A boolean indicating whether to add to the front buffer (true) or + # the regular buffer (false). + # + # ## Returns + # An updated state with the new entries added to the appropriate buffer, or + # the original state if entries is empty. + # + # ## Notes + # When entries are added, they are prepended as a single list element to + # the existing buffer, maintaining the order of batches. + @spec buffer_entries(t(), [], boolean()) :: t() defp buffer_entries(state, [], _front?), do: state + @spec buffer_entries(t(), nonempty_list(), true) :: t() defp buffer_entries(state, entries, true) do - %{state | current_front_buffer: [entries | state.current_front_buffer]} + %__MODULE__{state | current_front_buffer: [entries | state.current_front_buffer]} end + @spec buffer_entries(t(), nonempty_list(), false) :: t() defp buffer_entries(state, entries, false) do - %{state | current_buffer: [entries | state.current_buffer]} + %__MODULE__{state | current_buffer: [entries | state.current_buffer]} end - defp do_initial_stream(%BufferedTask{init_task: init_task} = state) when is_reference(init_task) do + # Initiates the initial streaming process for the BufferedTask. + # + # This function has two clauses: + # 1. When an init_task is already in progress, it schedules the next buffer flush. + # 2. When no init_task is in progress, it starts a new async task to initialize + # the stream. + # + # The initialization process: + # - Calls the `init/3` function of the callback module. + # - Accumulates entries into chunks up to the maximum batch size. + # - Pushes full chunks to the queue, triggering processing of queue data. + # - Processes any remaining entries after initialization. These are entries that + # were accumulated but didn't form a complete chunk. They are pushed to the + # queue, ensuring no data is lost, and trigger processing of queue data (which + # may include these entries). + # + # ## Parameters + # - `state`: The current state of the BufferedTask. + # + # ## Returns + # - Updated `state` with the new `init_task` reference and scheduled buffer flush. + @spec do_initial_stream(%__MODULE__{ + init_task: Task.t() | :complete | nil, + callback_module: module(), + max_batch_size: pos_integer(), + task_supervisor: GenServer.name(), + metadata: Logger.metadata() + }) :: t() + defp do_initial_stream(%__MODULE__{init_task: init_task} = state) when is_reference(init_task) do schedule_next_buffer_flush(state) end defp do_initial_stream( - %BufferedTask{ + %__MODULE__{ callback_module: callback_module, callback_module_state: callback_module_state, max_batch_size: max_batch_size, @@ -392,6 +977,12 @@ defmodule Indexer.BufferedTask do {0, []} |> callback_module.init( + # It accumulates entries into chunks until the maximum chunk size is + # reached, then pushes the chunk to the queue and trigger processing + # of the next batch of the queue's data. The chunk size is set to + # ensure it doesn't exceed the batch size used in `spawn_next_batch`, + # guaranteeing that all data in the queue can be included in + # processing batches. fn entry, {len, acc} when len + 1 >= max_batch_size -> entries = Enum.reverse([entry | acc]) @@ -407,9 +998,15 @@ defmodule Indexer.BufferedTask do |> catchup_remaining(max_batch_size, parent) end) - schedule_next_buffer_flush(%BufferedTask{state | init_task: task.ref}) + schedule_next_buffer_flush(%__MODULE__{state | init_task: task.ref}) end + # Processes any remaining entries after the initial streaming operation by + # pushing them to the back of the queue and triggering processing of a batch of + # the queue's data. + @spec catchup_remaining({non_neg_integer(), list()}, pos_integer(), pid()) :: :ok + defp catchup_remaining(chunk_with_length, max_batch_size, pid) + defp catchup_remaining({0, []}, _max_batch_size, _pid), do: :ok defp catchup_remaining({len, batch}, max_batch_size, pid) @@ -419,19 +1016,68 @@ defmodule Indexer.BufferedTask do :ok end + # Pushes entries to the back of the queue. + # + # This function has two behaviors depending on the input: + # 1. If given a PID, it sends a :push_back message to the specified process. + # 2. If given the current state, it adds entries to the back of the bound queue. + # + # ## Parameters + # - `pid`: A PID of a BufferedTask process. + # - `state`: The current state of the BufferedTask. + # - `entries`: A list of entries to be pushed to the back of the queue. + # + # ## Returns + # - When given a PID: The result of the GenServer call (any term). + # - When given the current state: The updated state with a potentially + # modified bound queue. + @spec push_back(pid(), list()) :: term() defp push_back(pid, entries) when is_pid(pid) and is_list(entries) do GenServer.call(pid, {:push_back, entries}) end - defp push_back(%BufferedTask{} = state, entries), do: push(state, entries, false) - + @spec push_back(t(), list()) :: t() + defp push_back(%__MODULE__{} = state, entries), do: push(state, entries, false) + + # Pushes entries to the front of the queue. + # + # This function has two behaviors depending on the input: + # 1. If given a PID, it sends a :push_front message to the specified process. + # 2. If given the current state, it adds entries to the front of the bound queue. + # + # ## Parameters + # - `pid`: A PID of a BufferedTask process. + # - `state`: The current state of the BufferedTask. + # - `entries`: A list of entries to be pushed to the front of the queue. + # + # ## Returns + # - When given a PID: The result of the GenServer call (any term). + # - When given the current state: The updated state with a potentially + # modified bound queue. + @spec push_front(pid(), list()) :: term() defp push_front(pid, entries) when is_pid(pid) and is_list(entries) do GenServer.call(pid, {:push_front, entries}) end - defp push_front(%BufferedTask{} = state, entries), do: push(state, entries, true) - - defp push(%BufferedTask{bound_queue: bound_queue} = state, entries, front?) when is_list(entries) do + @spec push_front(t(), list()) :: t() + defp push_front(%__MODULE__{} = state, entries), do: push(state, entries, true) + + # Pushes a list of entries into the bound queue of the state. + # + # If all entries are successfully added, the function simply updates the state + # with the new bound queue. If the bound queue reaches its maximum size before + # all entries can be added, the function discards the remaining entries and + # logs a warning. + # + # ## Parameters + # - `state`: The current state of the BufferedTask. + # - `entries`: A list of entries to be pushed into the bound queue. + # - `front?`: A boolean flag. If true, pushes to the front; if false, pushes to the back. + # + # ## Returns + # The updated state with the new entries added to the bound queue. + @spec push(%__MODULE__{bound_queue: BoundQueue.t(term())}, list(), boolean()) :: t() + defp push(%__MODULE__{bound_queue: bound_queue} = state, entries, front?) when is_list(entries) do new_bound_queue = case push_until_maximum_size(bound_queue, entries, front?) do {new_bound_queue, []} -> @@ -453,24 +1099,88 @@ defmodule Indexer.BufferedTask do new_bound_queue end - %BufferedTask{state | bound_queue: new_bound_queue} + %__MODULE__{state | bound_queue: new_bound_queue} end + # Pushes entries into a BoundQueue until its maximum size is reached. + # + # This function attempts to add entries to either the front or back of the + # BoundQueue, depending on the boolean flag. It continues adding entries + # until the queue reaches its maximum size or all entries have been added. + # + # The order of entries is preserved during the push operation. When pushing + # to the front, the first entry in the list becomes the first in the queue. + # Conversely, when pushing to the back, the last entry in the list becomes + # the last in the queue. + # + # ## Parameters + # - `bound_queue`: The BoundQueue to push entries into. + # - `entries`: A list of entries to be pushed into the queue. + # - `front?`: A boolean flag. If true, pushes to the front; if false, pushes to the back. + # + # ## Returns + # A tuple containing: + # - The updated BoundQueue with new entries added. + # - A list of remaining entries that couldn't be pushed due to size limitations. + @spec push_until_maximum_size(BoundQueue.t(term()), list(), boolean()) :: {BoundQueue.t(term()), list()} defp push_until_maximum_size(bound_queue, entries, true), do: BoundQueue.push_front_until_maximum_size(bound_queue, entries) defp push_until_maximum_size(bound_queue, entries, false), do: BoundQueue.push_back_until_maximum_size(bound_queue, entries) - defp take_batch(%BufferedTask{bound_queue: bound_queue, max_batch_size: max_batch_size} = state) do + # Takes a batch of entries from the current state or BoundQueue. + # + # This function has three implementations to handle different input types + # and use cases: + # + # 1. When given the current state of the BufferedTask: + # - Takes a batch of entries based on the `max_batch_size`. + # - Returns the batch and an updated state. + # + # 2. When given a BoundQueue and `max_batch_size`: + # - Initializes the recursive batch-taking process. + # - Returns the batch and the updated BoundQueue. + # + # 3. Recursive implementation (private): + # - Recursively takes entries from the BoundQueue. + # - Accumulates entries until the requested number is reached or the queue is empty. + # + # ## Parameters + # - `state`: The current state of the BufferedTask. + # - `bound_queue`: The BoundQueue to take entries from. + # - `max_batch_size`: The maximum number of entries to take. + # - `remaining`: The number of entries still to be taken (in recursive calls). + # - `acc`: The accumulator for entries already taken (in recursive calls). + # + # ## Returns + # Depending on the implementation: + # 1. `{batch, updated_state}` + # 2. `{batch, updated_bound_queue}` + # 3. `{reversed_batch, updated_bound_queue}` + # + # Where: + # - `batch`: A list of taken entries in their original order. + # - `updated_state`: The state with an updated BoundQueue. + # - `updated_bound_queue`: The BoundQueue with taken entries removed. + # + # ## Notes + # - The function ensures that no more than `max_batch_size` entries are taken. + # - If the queue becomes empty before taking all requested entries, the function + # returns with whatever entries it has accumulated so far. + @spec take_batch(%__MODULE__{bound_queue: BoundQueue.t(term()), max_batch_size: non_neg_integer()}) :: + {entries(), t()} + defp take_batch(%__MODULE__{bound_queue: bound_queue, max_batch_size: max_batch_size} = state) do {batch, new_bound_queue} = take_batch(bound_queue, max_batch_size) - {batch, %BufferedTask{state | bound_queue: new_bound_queue}} + {batch, %__MODULE__{state | bound_queue: new_bound_queue}} end + @spec take_batch(BoundQueue.t(term()), non_neg_integer()) :: {entries(), BoundQueue.t(term())} defp take_batch(%BoundQueue{} = bound_queue, max_batch_size) do take_batch(bound_queue, max_batch_size, []) end + @spec take_batch(BoundQueue.t(term()), non_neg_integer(), list()) :: {entries(), BoundQueue.t(term())} defp take_batch(%BoundQueue{} = bound_queue, 0, acc) do {Enum.reverse(acc), bound_queue} end @@ -485,26 +1195,73 @@ defmodule Indexer.BufferedTask do end end - # get more work from `init/2` - defp schedule_next(%BufferedTask{poll: true, bound_queue: %BoundQueue{size: 0}, task_ref_to_batch: tasks} = state) + # Schedules the next operation based on the current state of the BufferedTask. + # + # This function is called after triggering processing a batch of queue's data + # to determine the next action, helping maintain a continuous flow of work in + # the BufferedTask. + # + # This function has two clauses: + # 1. When the queue is empty, there are no ongoing tasks, and polling is enabled, + # it re-initializes the stream to fetch more work. + # 2. In all other cases, it schedules the next buffer flush. + # + # ## Parameters + # - `state`: The current state of the BufferedTask. + # + # ## Returns + # - Updated `state` after scheduling the next operation. + @spec schedule_next(%__MODULE__{ + poll: boolean(), + bound_queue: BoundQueue.t(term()), + task_ref_to_batch: %{reference() => [term(), ...]} + }) :: t() + defp schedule_next(%__MODULE__{poll: true, bound_queue: %BoundQueue{size: 0}, task_ref_to_batch: tasks} = state) when tasks == %{} do do_initial_stream(state) end - # was not shrunk or not out of work - defp schedule_next(%BufferedTask{} = state) do + defp schedule_next(%__MODULE__{} = state) do schedule_next_buffer_flush(state) end + # Schedules the next buffer flush based on the flush_interval. + # + # If the flush_interval is set to `:infinity`, no flush is scheduled and the + # state is returned unchanged. Otherwise, a timer is set to send a `:flush` + # message after the specified interval. + # + # ## Parameters + # - `state`: The current state of the BufferedTask. Must include `:flush_interval`. + # + # ## Returns + # - The updated state with the new flush_timer if a flush was scheduled, + # or the unchanged state if flush_interval is :infinity. + @spec schedule_next_buffer_flush(%__MODULE__{flush_interval: timeout() | :infinity}) :: t() defp schedule_next_buffer_flush(state) do if state.flush_interval == :infinity do state else timer = Process.send_after(self(), :flush, state.flush_interval) - %{state | flush_timer: timer} + %__MODULE__{state | flush_timer: timer} end end + # Registers the current process as shrinkable with the memory monitor if one is provided. + # + # This function checks the options for a `:memory_monitor`. If present, it registers + # the current process with the monitor as a shrinkable process. This allows the + # memory monitor to request the process to reduce its memory usage if needed. + # + # ## Parameters + # - `options`: A keyword list of options that may include :memory_monitor. + # + # ## Returns + # - `:ok` if no memory monitor is provided or after successful registration. + # + # ## Side Effects + # - If a memory monitor is provided, the current process is registered as + # shrinkable with that monitor. defp shrinkable(options) do case Keyword.get(options, :memory_monitor) do nil -> :ok @@ -512,8 +1269,31 @@ defmodule Indexer.BufferedTask do end end + # Spawns the next batch processing task. + # + # This function checks if a new task can be spawned based on the current + # number of running tasks and the availability of entries in the bound queue. + # If conditions are met, it takes a batch from the bound queue and spawns + # a new task to process it. As soon as the task is spawned, the + # `task_ref_to_batch` map is updated to enable retrying the task if needed. + # + # ## Parameters + # - `state`: The current state of the BufferedTask. + # + # ## Returns + # - An updated state if a new task was spawned. Otherwise, the original state + # is returned. + @spec spawn_next_batch(%__MODULE__{ + bound_queue: BoundQueue.t(term()), + callback_module: module(), + callback_module_state: term(), + max_concurrency: non_neg_integer(), + task_ref_to_batch: %{reference() => [term(), ...]}, + task_supervisor: GenServer.name(), + metadata: Logger.metadata() + }) :: t() defp spawn_next_batch( - %BufferedTask{ + %__MODULE__{ bound_queue: bound_queue, callback_module: callback_module, callback_module_state: callback_module_state, @@ -536,14 +1316,39 @@ defmodule Indexer.BufferedTask do } ]) - %BufferedTask{new_state | task_ref_to_batch: Map.put(task_ref_to_batch, ref, batch)} + %__MODULE__{new_state | task_ref_to_batch: Map.put(task_ref_to_batch, ref, batch)} else state end end - # only public so that `Task.Supervisor.async_nolink` can call it - @doc false + @doc """ + Executes the run callback of the specified module. + + This function is designed to be called asynchronously by `Task.Supervisor`. It + invokes the `run/2` callback of the specified callback module. + + ## Parameters + - params: A map containing the following keys: + - `:metadata`: Keyword list of logging metadata to be set. + - `:callback_module`: The module that implements the `run/2` callback. + - `:batch`: A list of items to be processed by the callback. + - `:callback_module_state`: The current state of the callback module. + + ## Returns + Returns the result of calling `run/2` on the callback module. + + ## Notes + This function is public to allow it to be called by `Task.Supervisor`, but it's + not intended for direct use outside of the BufferedTask context. + """ + @spec log_run(%{ + metadata: Logger.metadata(), + callback_module: module(), + batch: entries(), + callback_module_state: term() + }) :: + any() def log_run(%{ metadata: metadata, callback_module: callback_module, @@ -554,17 +1359,31 @@ defmodule Indexer.BufferedTask do callback_module.run(batch, callback_module_state) end - defp flush(%BufferedTask{current_buffer: [], current_front_buffer: []} = state) do + # Initiates processing of the next batch of the queue's data after flushing the current buffers. + # + # This function ensures that all buffered entries are scheduled for + # processing by pushing both the regular and front buffers to the queue if they + # are not empty. Then, it initiates processing of the next batch of the queue's + # data by spawning a task that will call the `run/2` callback of the callback + # module, and schedules the next operation. + # + # ## Parameters + # - `state`: The current state of the BufferedTask. + # + # ## Returns + # - Updated `state` after flushing buffers and scheduling next operations. + @spec flush(%__MODULE__{current_buffer: list(), current_front_buffer: list}) :: t() + defp flush(%__MODULE__{current_buffer: [], current_front_buffer: []} = state) do state |> spawn_next_batch() |> schedule_next() end - defp flush(%BufferedTask{current_buffer: buffer, current_front_buffer: front_buffer} = state) do + defp flush(%__MODULE__{current_buffer: buffer, current_front_buffer: front_buffer} = state) do back_entries = List.flatten(buffer) front_entries = List.flatten(front_buffer) - %BufferedTask{state | current_buffer: [], current_front_buffer: []} + %__MODULE__{state | current_buffer: [], current_front_buffer: []} |> push_back(back_entries) |> push_front(front_entries) |> flush() diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/messages_to_l2_matcher.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/messages_to_l2_matcher.ex new file mode 100644 index 000000000000..9dab37bf16a4 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/messages_to_l2_matcher.ex @@ -0,0 +1,347 @@ +defmodule Indexer.Fetcher.Arbitrum.MessagesToL2Matcher do + @moduledoc """ + Matches and processes L1-to-L2 messages in the Arbitrum protocol. + + This module implements a buffered task system to handle the matching of + L1-to-L2 messages with hashed message IDs. It periodically attempts to match + unmatched messages, imports matched messages to the database, and reschedules + unmatched messages for future processing. + + The matcher operates asynchronously, allowing for efficient handling of + messages even when corresponding L1 transactions are not yet indexed. This + approach prevents blocking the discovery process and ensures eventual + consistency in message matching. + + Key features: + - Implements the `BufferedTask` behavior for efficient batch processing. + - Maintains a cache of uncompleted message IDs to optimize matching. + - Provides functionality to asynchronously schedule message matching. + - Automatically retries unmatched messages based on a configurable interval. + """ + + use Indexer.Fetcher, restart: :permanent + use Spandex.Decorators + + import Indexer.Fetcher.Arbitrum.Utils.Logging, only: [log_info: 1] + + require Logger + + alias Indexer.BufferedTask + alias Indexer.Fetcher.Arbitrum.MessagesToL2Matcher.Supervisor, as: MessagesToL2MatcherSupervisor + alias Indexer.Fetcher.Arbitrum.Messaging, as: MessagingUtils + alias Indexer.Fetcher.Arbitrum.Utils.Db + alias Indexer.Fetcher.Arbitrum.Utils.Helper, as: ArbitrumHelper + + @behaviour BufferedTask + + # Since the cache for DB responses is used, it is efficient to get rid of concurrent handling of the tasks. + @default_max_batch_size 10 + @default_max_concurrency 1 + + @flush_interval :timer.seconds(1) + + @typep min_transaction :: %{ + :hash => binary(), + :type => non_neg_integer(), + optional(:request_id) => non_neg_integer(), + optional(any()) => any() + } + + @doc """ + Defines the child specification for the MessagesToL2Matcher. + + This function creates a child specification for use in a supervision tree, + configuring a `BufferedTask` process for the MessagesToL2Matcher. It sets up + the initial state and options for the task, including the recheck interval + for matching L1-to-L2 messages. + + Using the same value for discovering new L1 messages interval and for the + unmatched L2 messages recheck interval ensures that message matching attempts + are synchronized with the rate of new L1 message discovery, optimizing the + process by avoiding unnecessary rechecks when no new L1 messages have been + added to the database. + + ## Parameters + - `init_options`: A keyword list of initial options for the BufferedTask. + - `gen_server_options`: A keyword list of options for the underlying GenServer. + + ## Returns + A child specification map suitable for use in a supervision tree, with the + following key properties: + - Uses `BufferedTask` as the module to start. + - Configures the MessagesToL2Matcher as the callback module for the BufferedTask. + - Sets the initial state with an empty cache of IDs of uncompleted messages and + the recheck interval from the Arbitrum.TrackingMessagesOnL1 configuration. + - Merges provided options with default options for the BufferedTask. + - Uses this module's name as the child's id in the supervision tree. + """ + def child_spec([init_options, gen_server_options]) do + messages_on_l1_interval = + Application.get_all_env(:indexer)[Indexer.Fetcher.Arbitrum.TrackingMessagesOnL1][:recheck_interval] + + buffered_task_init_options = + defaults() + |> Keyword.merge(init_options) + |> Keyword.merge( + state: %{ + uncompleted_messages: %{}, + recheck_interval: messages_on_l1_interval + } + ) + + Supervisor.child_spec({BufferedTask, [{__MODULE__, buffered_task_init_options}, gen_server_options]}, + id: __MODULE__ + ) + end + + @impl BufferedTask + def init(initial, _, _) do + initial + end + + @doc """ + Processes a batch of transactions with hashed message IDs for L1-to-L2 messages. + + This function, implementing the `BufferedTask` behavior, handles a list of + transactions with associated timeouts. It attempts to match hashed request IDs + with uncompleted L1-to-L2 messages, updates the transactions accordingly, and + imports any successfully matched messages to the database. + + The function performs the following steps: + 1. Separates transactions with expired timeouts from those still delayed. + 2. Attempts to update expired transactions by matching their hashed request IDs. + 3. Processes updated transactions to filter and import L1-to-L2 messages. + 4. Reschedules unmatched or delayed transactions for future processing. + + For unmatched transactions, new timeouts are set to the current time increased + by the value of the recheck interval. + + ## Parameters + - `txs_with_timeouts`: A list of tuples, each containing a timeout and a + transaction with a potentially hashed request ID. + - `state`: The current state of the task, including cached IDs of uncompleted + messages and the recheck interval. + + ## Returns + - `{:ok, updated_state}` if all transactions were processed successfully and + no retries are needed. + - `{:retry, txs_to_retry, updated_state}` if some transactions need to be + retried, either due to unmatched request IDs or unexpired timeouts. + + The returned state always includes an updated cache of IDs of uncompleted + messages. + """ + @impl BufferedTask + @spec run([{non_neg_integer(), min_transaction()}], %{ + :recheck_interval => non_neg_integer(), + :uncompleted_messages => %{binary() => binary()}, + optional(any()) => any() + }) :: + {:ok, %{:uncompleted_messages => %{binary() => binary()}, optional(any()) => any()}} + | {:retry, [{non_neg_integer(), min_transaction()}], + %{:uncompleted_messages => %{binary() => binary()}, optional(any()) => any()}} + def run(txs_with_timeouts, %{uncompleted_messages: cached_uncompleted_messages_ids, recheck_interval: _} = state) + when is_list(txs_with_timeouts) do + # For next handling only the transactions with expired timeouts are needed. + now = DateTime.to_unix(DateTime.utc_now(), :millisecond) + + {txs, delayed_txs} = + txs_with_timeouts + |> Enum.reduce({[], []}, fn {timeout, tx}, {txs, delayed_txs} -> + if timeout > now do + {txs, [{timeout, tx} | delayed_txs]} + else + {[tx | txs], delayed_txs} + end + end) + + # Check if the request Id of transactions with expired timeouts matches hashed + # ids of the uncompleted messages and update the transactions with the decoded + # request ids. If it required, the cache is updated. + # Possible outcomes: + # - no transactions were updated, because the txs list is empty, the cache is updated + # - no transactions were updated, because no matches in both cache and DB were found, the cache is updated + # - all matches were found in the cache, the cache is not updated + # - all matches were found in the DB, the cache is updated + # - some matches were found in the cache, but not all, the cache is not updated + {updated?, handled_txs, updated_cache} = update_txs_with_hashed_ids(txs, cached_uncompleted_messages_ids) + updated_state = %{state | uncompleted_messages: updated_cache} + + case {updated?, txs == []} do + {false, true} -> + # There were no transactions with expired timeouts, so counters of the transactions + # updated and the transactions are scheduled for retry. + {:retry, delayed_txs, updated_state} + + {false, false} -> + # Some of the transactions were with expired timeouts, but no matches were found + # for these transaction in the cache or the DB. Timeouts for such transactions + # are re-initialized and they are added to the list with transactions with + # updated counters. + txs_to_retry = + delayed_txs ++ initialize_timeouts(handled_txs, now + state.recheck_interval) + + {:retry, txs_to_retry, updated_state} + + {true, _} -> + {messages, txs_to_retry_wo_timeouts} = MessagingUtils.filter_l1_to_l2_messages(handled_txs) + + MessagingUtils.import_to_db(messages) + + if txs_to_retry_wo_timeouts == [] and delayed_txs == [] do + {:ok, updated_state} + else + # Either some of the transactions with expired timeouts don't have a matching + # request id in the cache or the DB, or there are transactions with non-expired + # timeouts. All these transactions are needed to be scheduled for retry. + txs_to_retry = + delayed_txs ++ initialize_timeouts(txs_to_retry_wo_timeouts, now + state.recheck_interval) + + {:retry, txs_to_retry, updated_state} + end + end + end + + @doc """ + Asynchronously schedules the discovery of matches for L1-to-L2 messages. + + This function schedules the processing of transactions with hashed message IDs that + require further matching. + + ## Parameters + - `txs_with_messages_from_l1`: A list of transactions containing L1-to-L2 + messages with hashed message IDs. + + ## Returns + - `:ok` + """ + @spec async_discover_match([min_transaction()]) :: :ok + def async_discover_match(txs_with_messages_from_l1) do + # Do nothing in case if the indexing chain is not Arbitrum or the feature is disabled. + if MessagesToL2MatcherSupervisor.disabled?() do + :ok + else + BufferedTask.buffer(__MODULE__, Enum.map(txs_with_messages_from_l1, &{0, &1}), false) + end + end + + # Retrieves and transforms uncompleted L1-to-L2 message IDs into a map of hashed IDs. + # + # This function fetches the IDs of uncompleted L1-to-L2 messages and creates a map + # where each key is the hashed hexadecimal string representation of a message ID, + # and the corresponding value is the original ID converted to a hexadecimal string. + # + # ## Returns + # A map where: + # - Keys are hashed message IDs as hexadecimal strings. + # - Values are original message IDs as 256-bit hexadecimal strings. + @spec get_hashed_ids_for_uncompleted_messages() :: %{binary() => binary()} + defp get_hashed_ids_for_uncompleted_messages do + Db.get_uncompleted_l1_to_l2_messages_ids() + |> Enum.reduce(%{}, fn id, acc -> + Map.put( + acc, + ArbitrumHelper.get_hashed_message_id_as_hex_str(id), + ArbitrumHelper.bytes_to_hex_str(<>) + ) + end) + end + + # Updates transactions with hashed request IDs, using cached or fresh data. + # + # This function attempts to replace hashed request IDs in transactions with their + # original IDs. It first tries using a cached set of uncompleted message IDs. If + # no matches are found in the cache, it fetches fresh data from the database. + # + # ## Parameters + # - `txs`: A list of transactions with potentially hashed request IDs. + # - `cached_uncompleted_messages_ids`: A map of cached hashed message IDs to their + # original forms. + # + # ## Returns + # A tuple containing: + # - A boolean indicating whether any transactions were updated. + # - An updated list of transactions, with some request IDs potentially replaced. + # - The map of uncompleted message IDs used for the update (either the cache or + # freshly fetched data). + # + # ## Notes + # - If the cache is used successfully, it's returned as-is, even if potentially + # outdated. + # - If the cache fails, fresh data is fetched and returned, updating the cache. + @spec update_txs_with_hashed_ids([min_transaction()], %{binary() => binary()}) :: + {boolean(), [min_transaction()], %{binary() => binary()}} + defp update_txs_with_hashed_ids([], cache), do: {false, [], cache} + + defp update_txs_with_hashed_ids(txs, cached_uncompleted_messages_ids) do + # Try to use the cached DB response first. That makes sense if historical + # messages are being processed (by catchup block fetcher or by the missing + # messages handler). Since amount of txs provided to this function is limited + # it OK to inspect the cache before making a DB request. + case revise_txs_with_hashed_ids(txs, cached_uncompleted_messages_ids, true) do + {_, false} -> + # If no matches were found in the cache, try to fetch uncompleted messages from the DB. + uncompleted_messages = get_hashed_ids_for_uncompleted_messages() + + {updated_txs, updated?} = revise_txs_with_hashed_ids(txs, uncompleted_messages, false) + + {updated?, updated_txs, uncompleted_messages} + + {updated_txs, _} -> + # There could be a case when some hashed ids were not found since the cache is outdated + # such txs will be scheduled for retry and the cache will be updated then. + {true, updated_txs, cached_uncompleted_messages_ids} + end + end + + # Attempts to replace hashed request IDs in transactions with their original IDs. + # + # This function iterates through a list of transactions, trying to match their + # hashed request IDs with entries in the provided map of uncompleted messages. + # If a match is found, the transaction's request ID is updated to its original + # (non-hashed) form. + # + # ## Parameters + # - `txs`: A list of transactions with potentially hashed request IDs. + # - `uncompleted_messages`: A map of hashed message IDs to their original forms. + # - `report?`: A boolean flag indicating whether to log decoding attempts. + # + # ## Returns + # A tuple containing: + # - An updated list of transactions, with some request IDs potentially replaced. + # - A boolean indicating whether any transactions were updated. + @spec revise_txs_with_hashed_ids([min_transaction()], %{binary() => binary()}, boolean()) :: + {[min_transaction()], boolean()} + defp revise_txs_with_hashed_ids(txs, uncompleted_messages, report?) do + txs + |> Enum.reduce({[], false}, fn tx, {updated_txs, updated?} -> + if report?, do: log_info("Attempting to decode the request id #{tx.request_id} in the tx #{tx.hash}") + + case Map.get(uncompleted_messages, tx.request_id) do + nil -> + {[tx | updated_txs], updated?} + + id -> + {[%{tx | request_id: id} | updated_txs], true} + end + end) + end + + # Assigns a uniform timeout to each transaction in the given list. + @spec initialize_timeouts([min_transaction()], non_neg_integer()) :: [{non_neg_integer(), min_transaction()}] + defp initialize_timeouts(txs_to_retry, timeout) do + txs_to_retry + |> Enum.map(&{timeout, &1}) + end + + defp defaults do + [ + flush_interval: @flush_interval, + 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, + metadata: [fetcher: :messages_to_l2_matcher] + ] + end +end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/messaging.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/messaging.ex index 29704aad8ca5..bbed0955b80f 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/messaging.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/messaging.ex @@ -14,10 +14,14 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do import Indexer.Fetcher.Arbitrum.Utils.Logging, only: [log_info: 1, log_debug: 1] + alias Explorer.Chain + alias Explorer.Chain.Arbitrum.Message alias Indexer.Fetcher.Arbitrum.Utils.Db require Logger + @zero_hex_prefix "0x" <> String.duplicate("0", 56) + @l2_to_l1_event_unindexed_params [ :address, {:uint, 256}, @@ -27,17 +31,6 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do :bytes ] - @type arbitrum_message :: %{ - direction: :to_l2 | :from_l2, - message_id: non_neg_integer(), - originator_address: binary(), - originating_transaction_hash: binary(), - origination_timestamp: DateTime.t(), - originating_transaction_block_number: non_neg_integer(), - completion_transaction_hash: binary(), - status: :initiated | :sent | :confirmed | :relayed - } - @typep min_transaction :: %{ :hash => binary(), :type => non_neg_integer(), @@ -60,40 +53,57 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do } @doc """ - Filters a list of rollup transactions to identify L1-to-L2 messages and composes a map for each with the related message information. + Filters rollup transactions to identify L1-to-L2 messages and categorizes them. - This function filters a list of rollup transactions, selecting those where - `request_id` is not nil and is below 2^31, indicating they are L1-to-L2 - message completions. These filtered transactions are then processed to - construct a detailed message structure for each. + This function processes a list of rollup transactions, identifying those with + non-nil `request_id` fields. It then separates these into two categories: + messages with plain message IDs and transactions with hashed message IDs. ## Parameters - `transactions`: A list of rollup transaction entries. - `report`: An optional boolean flag (default `true`) that, when `true`, logs - the number of processed L1-to-L2 messages if any are found. + the number of identified L1-to-L2 messages and transactions requiring + further processing. ## Returns - - A list of L1-to-L2 messages with detailed information and current status. Every - map in the list compatible with the database import operation. All messages in - this context are considered `:relayed` as they represent completed actions from - L1 to L2. + A tuple containing: + - A list of L1-to-L2 messages with detailed information, ready for database + import. All messages in this context are considered `:relayed` as they + represent completed actions from L1 to L2. + - A list of transactions with hashed message IDs that require further + processing for message ID matching. """ - @spec filter_l1_to_l2_messages([min_transaction()]) :: [arbitrum_message] - @spec filter_l1_to_l2_messages([min_transaction()], boolean()) :: [arbitrum_message] + @spec filter_l1_to_l2_messages([min_transaction()]) :: {[Message.to_import()], [min_transaction()]} + @spec filter_l1_to_l2_messages([min_transaction()], boolean()) :: {[Message.to_import()], [min_transaction()]} def filter_l1_to_l2_messages(transactions, report \\ true) when is_list(transactions) and is_boolean(report) do - messages = + {transactions_with_proper_message_id, transactions_with_hashed_message_id} = transactions |> Enum.filter(fn tx -> - tx[:request_id] != nil and Bitwise.bsr(tx[:request_id], 31) == 0 + tx[:request_id] != nil end) + |> Enum.split_with(fn tx -> + plain_message_id?(tx[:request_id]) + end) + + # Transform transactions with the plain message ID into messages + messages = + transactions_with_proper_message_id |> handle_filtered_l1_to_l2_messages() - if report && not (messages == []) do - log_info("#{length(messages)} completions of L1-to-L2 messages will be imported") + if report do + if not (messages == []) do + log_info("#{length(messages)} completions of L1-to-L2 messages will be imported") + end + + if not (transactions_with_hashed_message_id == []) do + log_info( + "#{length(transactions_with_hashed_message_id)} completions of L1-to-L2 messages require message ID matching discovery" + ) + end end - messages + {messages, transactions_with_hashed_message_id} end @doc """ @@ -110,7 +120,7 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do - A list of L2-to-L1 messages with detailed information and current status. Each map in the list is compatible with the database import operation. """ - @spec filter_l2_to_l1_messages(maybe_improper_list(min_log, [])) :: [arbitrum_message] + @spec filter_l2_to_l1_messages(maybe_improper_list(min_log, [])) :: [Message.to_import()] def filter_l2_to_l1_messages(logs) when is_list(logs) do arbsys_contract = Application.get_env(:indexer, __MODULE__)[:arbsys_contract] @@ -135,7 +145,7 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do in the list compatible with the database import operation. All messages in this context are considered `:relayed` as they represent completed actions from L1 to L2. """ - @spec handle_filtered_l1_to_l2_messages(maybe_improper_list(min_transaction, [])) :: [arbitrum_message] + @spec handle_filtered_l1_to_l2_messages(maybe_improper_list(min_transaction, [])) :: [Message.to_import()] def handle_filtered_l1_to_l2_messages([]) do [] end @@ -145,7 +155,12 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do |> Enum.map(fn tx -> log_debug("L1 to L2 message #{tx.hash} found with the type #{tx.type}") - %{direction: :to_l2, message_id: tx.request_id, completion_transaction_hash: tx.hash, status: :relayed} + %{ + direction: :to_l2, + message_id: quantity_to_integer(tx.request_id), + completion_transaction_hash: tx.hash, + status: :relayed + } |> complete_to_params() end) end @@ -168,8 +183,8 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do - A list of L2-to-L1 messages with detailed information and current status, ready for database import. """ - @spec handle_filtered_l2_to_l1_messages([min_log]) :: [arbitrum_message] - @spec handle_filtered_l2_to_l1_messages([min_log], module()) :: [arbitrum_message] + @spec handle_filtered_l2_to_l1_messages([min_log]) :: [Message.to_import()] + @spec handle_filtered_l2_to_l1_messages([min_log], module()) :: [Message.to_import()] def handle_filtered_l2_to_l1_messages(filtered_logs, caller \\ nil) def handle_filtered_l2_to_l1_messages([], _) do @@ -211,21 +226,40 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do # The check if messages are executed is required only for the case when l2-to-l1 # messages are found by block catchup fetcher - updated_messages_map = - case caller do - nil -> - messages_map - - _ -> - messages_map - |> find_and_update_executed_messages() - end - - updated_messages_map + caller + |> case do + nil -> + messages_map + + _ -> + messages_map + |> find_and_update_executed_messages() + end |> Map.values() end + @doc """ + Imports a list of messages into the database. + + ## Parameters + - `messages`: A list of messages to import into the database. + + ## Returns + N/A + """ + @spec import_to_db([Message.to_import()]) :: :ok + def import_to_db(messages) do + {:ok, _} = + Chain.import(%{ + arbitrum_messages: %{params: messages}, + timeout: :infinity + }) + + :ok + end + # Converts an incomplete message structure into a complete parameters map for database updates. + @spec complete_to_params(map()) :: Message.to_import() defp complete_to_params(incomplete) do [ :direction, @@ -243,6 +277,7 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do end # Parses an L2-to-L1 event, extracting relevant information from the event's data. + @spec l2_to_l1_event_parse(min_log()) :: {non_neg_integer(), binary(), non_neg_integer(), DateTime.t()} defp l2_to_l1_event_parse(event) do [ caller, @@ -260,6 +295,8 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do # Determines the status of an L2-to-L1 message based on its block number and the highest # committed and confirmed block numbers. + @spec status_l2_to_l1_message(non_neg_integer(), non_neg_integer(), non_neg_integer()) :: + :confirmed | :sent | :initiated defp status_l2_to_l1_message(msg_block, highest_committed_block, highest_confirmed_block) do cond do highest_confirmed_block >= msg_block -> :confirmed @@ -278,6 +315,9 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do # ## Returns # - The updated map of messages with the `completion_transaction_hash` and `status` fields updated # for messages that have been executed. + @spec find_and_update_executed_messages(%{non_neg_integer() => Message.to_import()}) :: %{ + non_neg_integer() => Message.to_import() + } defp find_and_update_executed_messages(messages) do messages |> Map.keys() @@ -292,4 +332,15 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do Map.put(messages_acc, execution.message_id, message) end) end + + # Checks if the given request ID is a plain message ID (starts with 56 zero + # characters that correspond to 28 zero bytes). + @spec plain_message_id?(non_neg_integer()) :: boolean() + defp plain_message_id?(request_id) when byte_size(request_id) == 66 do + String.starts_with?(request_id, @zero_hex_prefix) + end + + defp plain_message_id?(_) do + false + end end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/rollup_messages_catchup.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/rollup_messages_catchup.ex index 56db328eddcd..eb66752bf1f2 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/rollup_messages_catchup.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/rollup_messages_catchup.ex @@ -41,14 +41,17 @@ defmodule Indexer.Fetcher.Arbitrum.RollupMessagesCatchup do responsible for L1-to-L2 messages and then re-requests these transactions through RPC. Results are utilized to construct messages. These messages are marked as `:relayed`, indicating that they have been successfully received on - L2 and are considered completed, and are then imported into the database. This - approach is adopted because it parallels the action of re-indexing existing - transactions to include Arbitrum-specific fields, which are absent in the - currently indexed transactions. However, permanently adding these fields to the - database model for the sake of historical message catch-up is impractical. - Therefore, to avoid the extensive process of re-indexing and to minimize changes - to the database schema, fetching the required data directly from an external - node via RPC is preferred for historical message discovery. + L2 and are considered completed, and are then imported into the database. If + it is determined that a message cannot be constructed because of a hashed + message ID, the transaction is scheduled for further asynchronous processing to + match it with the corresponding L1 transaction. This approach is adopted + because it parallels the action of re-indexing existing transactions to include + Arbitrum-specific fields, which are absent in the currently indexed + transactions. However, permanently adding these fields to the database model + for the sake of historical message catch-up is impractical. Therefore, to avoid + the extensive process of re-indexing and to minimize changes to the database + schema, fetching the required data directly from an external node via RPC is + preferred for historical message discovery. """ use GenServer @@ -268,8 +271,14 @@ defmodule Indexer.Fetcher.Arbitrum.RollupMessagesCatchup do # `requestId` for every transaction. This RPC request is necessary because the # `requestId` field is not present in the transaction model of already indexed # transactions in the database. Results are used to construct messages, which are - # subsequently stored in the database. These imported messages are marked as - # `:relayed`, signifying that they represent completed actions from L1 to L2. + # subsequently stored in the database. + # + # Messages with plain (non-hashed) request IDs are imported into the database and + # marked as `:relayed`, representing completed actions from L1 to L2. + # + # For transactions where the `requestId` represents a hashed message ID, the + # function schedules asynchronous discovery to match them with corresponding L1 + # transactions. # # After importing the messages, the function immediately switches to the process # of choosing a delay prior to the next iteration of historical message discovery diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex index 9f7b6c8778c4..0b2dcd75df6b 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex @@ -892,6 +892,17 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do Reader.get_da_info_by_batch_number(batch_number) end + @doc """ + Retrieves the list of uncompleted L2-to-L1 messages IDs. + + ## Returns + - A list of the IDs of uncompleted L2-to-L1 messages. + """ + @spec get_uncompleted_l1_to_l2_messages_ids() :: [non_neg_integer()] + def get_uncompleted_l1_to_l2_messages_ids do + Reader.get_uncompleted_l1_to_l2_messages_ids() + end + @spec lifecycle_transaction_to_map(Arbitrum.LifecycleTransaction.t()) :: Arbitrum.LifecycleTransaction.to_import() defp lifecycle_transaction_to_map(tx) do [:id, :hash, :block_number, :timestamp, :status] diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/helper.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/helper.ex index d7abb6aa0080..7dcd56b76578 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/helper.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/helper.ex @@ -1,6 +1,7 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Helper do alias Explorer.Chain.Arbitrum.LifecycleTransaction + import EthereumJSONRPC, only: [quantity_to_integer: 1] import Indexer.Fetcher.Arbitrum.Utils.Logging, only: [log_info: 1] @moduledoc """ @@ -205,4 +206,58 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Helper do end) |> Enum.reverse() end + + @doc """ + Converts a message ID to its hashed hexadecimal string representation. + + This function takes a message ID (either as an integer or a hexadecimal string), + concatenates it with 256 zero bits, computes a hash of the concatenation, and + then converts the resulting hash to a hexadecimal string with a "0x" prefix. + + ## Parameters + - `message_id`: The message ID to be hashed and converted. Can be either a + non-negative integer or a "0x"-prefixed hexadecimal string. + + ## Returns + - A string representing the hashed message ID in hexadecimal format, prefixed + with "0x". + + ## Examples + + iex> get_hashed_message_id_as_hex_str(1490421) + "0x9d1614591a3e0ba8854206a716e49ffdffc679131820fa815b989fdef9e5554d" + + iex> get_hashed_message_id_as_hex_str("0x000000000000000000000000000000000000000000000000000000000016bdf5") + "0x9d1614591a3e0ba8854206a716e49ffdffc679131820fa815b989fdef9e5554d" + """ + @spec get_hashed_message_id_as_hex_str(non_neg_integer() | binary()) :: String.t() + def get_hashed_message_id_as_hex_str(message_id) do + message_id + |> hash_for_message_id() + |> bytes_to_hex_str() + end + + # Calculates the hash for a given message ID. + # + # This function computes a 256-bit Keccak hash of the message ID. For integer + # inputs, it concatenates the 256-bit message ID with 256 zero bits before + # hashing. For hexadecimal string inputs, it first converts the string to an + # integer. + # + # ## Parameters + # - `message_id`: Either a non-negative integer or a "0x"-prefixed hexadecimal + # string of 66 characters (including the "0x" prefix). + # + # ## Returns + # - A binary representing the 256-bit Keccak hash of the processed message ID. + @spec hash_for_message_id(non_neg_integer() | binary()) :: binary() + defp hash_for_message_id(message_id) when is_integer(message_id) do + # As per https://github.com/OffchainLabs/nitro/blob/849348e10cf1d9c023f4748dc1211bd363422485/arbos/parse_l2.go#L40 + (<> <> <<0::size(256)>>) + |> ExKeccak.hash_256() + end + + defp hash_for_message_id(message_id) when is_binary(message_id) and byte_size(message_id) == 66 do + hash_for_message_id(quantity_to_integer(message_id)) + end end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex index 659a787d3a7e..e1421ff08454 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex @@ -18,8 +18,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do alias EthereumJSONRPC.Transaction, as: TransactionByRPC - alias Explorer.Chain - + alias Indexer.Fetcher.Arbitrum.MessagesToL2Matcher, as: ArbitrumMessagesToL2Matcher alias Indexer.Fetcher.Arbitrum.Messaging alias Indexer.Fetcher.Arbitrum.Utils.{Db, Rpc} @@ -127,7 +126,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do logs |> Messaging.handle_filtered_l2_to_l1_messages(__MODULE__) - import_to_db(messages) + Messaging.import_to_db(messages) end {:ok, start_block} @@ -143,9 +142,14 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do then their bodies are re-requested through RPC because already indexed transactions from the database cannot be utilized; the `requestId` field is not included in the transaction model. The function ensures that the block range - has been indexed before proceeding with message discovery and import. The - imported messages are marked as `:relayed`, as they represent completed actions - from L1 to L2. + has been indexed before proceeding with message discovery and import. + + Messages with plain (non-hashed) request IDs are imported into the database and + marked as `:relayed`, representing completed actions from L1 to L2. + + For transactions where the `requestId` represents a hashed message ID, the + function schedules asynchronous discovery to match them with corresponding L1 + transactions. ## Parameters - `end_block`: The ending block number for the discovery operation. If `nil` or @@ -213,25 +217,33 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do # Discovers and processes historical messages sent from L1 to L2 within a # specified rollup block range. # - # This function identifies which of already indexed transactions within the - # block range contains L1-to-L2 messages and makes RPC calls to fetch - # transaction data. These transactions are then processed to construct proper - # message structures, which are imported into the database. The imported - # messages are marked as `:relayed` as they represent completed actions from L1 - # to L2. + # This function identifies already indexed transactions within the block range + # that potentially contain L1-to-L2 messages. It then makes RPC calls to fetch + # complete transaction data, as the database doesn't include the Arbitrum-specific + # `requestId` field. # - # Note: Already indexed transactions from the database cannot be used because - # the `requestId` field is not included in the transaction model. + # The fetched transactions are processed to construct proper message structures. + # Messages with plain (non-hashed) request IDs are imported into the database + # and marked as `:relayed`, representing completed actions from L1 to L2. + # + # For transactions where the `requestId` represents a hashed message ID, the + # function schedules asynchronous discovery to match them with corresponding L1 + # transactions. + # + # The function processes transactions in chunks to manage memory usage and + # network load efficiently. # # ## Parameters # - `start_block`: The starting block number for the discovery range. # - `end_block`: The ending block number for the discovery range. - # - `config`: The configuration map containing settings for RPC communication - # and chunk size. + # - `config`: A map containing configuration settings, including: + # - `:rollup_rpc`: A map with RPC settings: + # - `:chunk_size`: The number of transactions to process in each chunk. + # - `:json_rpc_named_arguments`: Arguments for JSON-RPC communication. # # ## Returns # - `{:ok, start_block}`: A tuple indicating successful processing, returning - # the initial starting block number. + # the initial starting block number. @spec do_discover_historical_messages_to_l2(non_neg_integer(), non_neg_integer(), %{ :rollup_rpc => %{ :chunk_size => non_neg_integer(), @@ -253,10 +265,10 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do if transactions_length > 0 do log_debug("#{transactions_length} historical messages to L2 discovered") - messages = + {messages, txs_for_further_handling} = transactions |> Enum.chunk_every(chunk_size) - |> Enum.reduce([], fn chunk, messages_acc -> + |> Enum.reduce({[], []}, fn chunk, {messages_acc, txs_acc} -> # Since DB does not contain the field RequestId specific to Arbitrum # all transactions will be requested from the rollup RPC endpoint. # The catchup process intended to be run once and only for the BS instance @@ -264,19 +276,17 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do # the new field in DB requests = build_transaction_requests(chunk) - messages = + {messages, txs_with_hashed_message_id} = requests |> Rpc.make_chunked_request(json_rpc_named_arguments, "eth_getTransactionByHash") |> Enum.map(&transaction_json_to_map/1) |> Messaging.filter_l1_to_l2_messages(false) - messages ++ messages_acc + {messages ++ messages_acc, txs_with_hashed_message_id ++ txs_acc} end) - # Logging of zero messages is left by intent to reveal potential cases when - # not all transactions are recognized as completed L1-to-L2 messages. - log_info("#{length(messages)} completions of L1-to-L2 messages will be imported") - import_to_db(messages) + handle_messages(messages) + handle_txs_with_hashed_message_id(txs_for_further_handling) end {:ok, start_block} @@ -301,12 +311,60 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do |> TransactionByRPC.elixir_to_params() end - # Imports a list of messages into the database. - defp import_to_db(messages) do - {:ok, _} = - Chain.import(%{ - arbitrum_messages: %{params: messages}, - timeout: :infinity - }) + # Processes and imports completed L1-to-L2 messages. + # + # This function handles a list of completed L1-to-L2 messages, logging the number + # of messages to be imported and then importing them into the database. The + # function intentionally logs even when there are zero messages to import, which + # helps identify potential cases where not all transactions are recognized as + # completed L1-to-L2 messages. + # + # ## Parameters + # - `messages`: A list of completed L1-to-L2 messages ready for import. + # + # ## Returns + # - `:ok` + @spec handle_messages([Explorer.Chain.Arbitrum.Message.to_import()]) :: :ok + defp handle_messages(messages) do + log_info("#{length(messages)} completions of L1-to-L2 messages will be imported") + Messaging.import_to_db(messages) + end + + # Processes transactions with hashed message IDs for L1-to-L2 message completion. + # + # This function asynchronously handles transactions that contain L1-to-L2 + # messages with hashed message IDs. + # + # The asynchronous handling is beneficial because: + # - If the corresponding L1 transaction is already indexed, the message will be + # imported after the next flush of the queued tasks buffer. + # - If the corresponding L1 transaction is not yet indexed, it will be awaited by + # the queued tasks handler. + # + # Asynchronous processing prevents locking the discovery process, which would + # occur if we waited synchronously for L1 transactions to be indexed. Another + # approach for synchronous handling is to skip a message without importing it to + # the DB when an L1 transaction is not found; the absence of the message will be + # discovered after a Blockscout instance restart. In the current asynchronous + # implementation, even if the awaiting of an L1 transaction in the queued tasks + # is terminated due to a Blockscout instance shutdown, the absence of the message + # will be discovered after the restart. The system will then attempt to match it + # with the corresponding L1 message again. + # + # ## Parameters + # - `txs_with_hashed_message_id`: A list of transactions containing L1-to-L2 + # messages with hashed message IDs. + # + # ## Returns + # - `:ok` + @spec handle_txs_with_hashed_message_id([map()]) :: :ok + defp handle_txs_with_hashed_message_id([]), do: :ok + + defp handle_txs_with_hashed_message_id(txs_with_hashed_message_id) do + log_info( + "#{length(txs_with_hashed_message_id)} completions of L1-to-L2 messages require message ID matching discovery" + ) + + ArbitrumMessagesToL2Matcher.async_discover_match(txs_with_hashed_message_id) end end diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex index 70ea093532fe..a75c196ea42d 100644 --- a/apps/indexer/lib/indexer/supervisor.ex +++ b/apps/indexer/lib/indexer/supervisor.ex @@ -47,6 +47,7 @@ defmodule Indexer.Supervisor do Withdrawal } + alias Indexer.Fetcher.Arbitrum.MessagesToL2Matcher, as: ArbitrumMessagesToL2Matcher alias Indexer.Fetcher.Arbitrum.RollupMessagesCatchup, as: ArbitrumRollupMessagesCatchup alias Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses, as: ArbitrumTrackingBatchesStatuses alias Indexer.Fetcher.Arbitrum.TrackingMessagesOnL1, as: ArbitrumTrackingMessagesOnL1 @@ -190,6 +191,7 @@ defmodule Indexer.Supervisor do configure(ArbitrumRollupMessagesCatchup.Supervisor, [ [json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor] ]), + {ArbitrumMessagesToL2Matcher.Supervisor, [[memory_monitor: memory_monitor]]}, configure(Indexer.Fetcher.Celo.ValidatorGroupVotes.Supervisor, [ [json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor] ]), diff --git a/apps/indexer/lib/indexer/transform/arbitrum/messaging.ex b/apps/indexer/lib/indexer/transform/arbitrum/messaging.ex index f33c327082eb..e8c328f5bc2d 100644 --- a/apps/indexer/lib/indexer/transform/arbitrum/messaging.ex +++ b/apps/indexer/lib/indexer/transform/arbitrum/messaging.ex @@ -3,6 +3,7 @@ defmodule Indexer.Transform.Arbitrum.Messaging do Helper functions for transforming data for Arbitrum cross-chain messages. """ + alias Explorer.Chain.Arbitrum.Message alias Indexer.Fetcher.Arbitrum.Messaging, as: ArbitrumMessages require Logger @@ -11,25 +12,27 @@ defmodule Indexer.Transform.Arbitrum.Messaging do Parses and combines lists of rollup transactions and logs to identify and process both L1-to-L2 and L2-to-L1 messages. This function utilizes two filtering operations: one that identifies L1-to-L2 - message completions from a list of transactions and another that identifies - L2-to-L1 message initiations from a list of logs. Each filter constructs - a detailed message structure for the respective direction. The function then - combines these messages into a single list suitable for database import. + message completions from a list of transactions, as well as the transactions + suspected of containing messages but requiring additional handling due to + hashed message IDs; and another that identifies L2-to-L1 message initiations + from a list of logs. ## Parameters - `transactions`: A list of rollup transaction entries to filter for L1-to-L2 messages. - `logs`: A list of log entries to filter for L2-to-L1 messages. ## Returns + A tuple containing: - A combined list of detailed message maps from both L1-to-L2 completions and L2-to-L1 initiations, ready for database import. + - A list of transactions with hashed message IDs that require further processing. """ - @spec parse(list(), list()) :: list() + @spec parse([map()], [map()]) :: {[Message.to_import()], [map()]} def parse(transactions, logs) do prev_metadata = Logger.metadata() Logger.metadata(fetcher: :arbitrum_bridge_l2) - l1_to_l2_completion_ops = + {l1_to_l2_completion_ops, transactions_with_hashed_message_id} = transactions |> ArbitrumMessages.filter_l1_to_l2_messages() @@ -39,6 +42,6 @@ defmodule Indexer.Transform.Arbitrum.Messaging do Logger.reset_metadata(prev_metadata) - l1_to_l2_completion_ops ++ l2_to_l1_initiating_ops + {l1_to_l2_completion_ops ++ l2_to_l1_initiating_ops, transactions_with_hashed_message_id} end end diff --git a/config/runtime.exs b/config/runtime.exs index bcd6580bcc3c..3bf15bdd5fc5 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -973,6 +973,9 @@ config :indexer, Indexer.Fetcher.Arbitrum.RollupMessagesCatchup, config :indexer, Indexer.Fetcher.Arbitrum.RollupMessagesCatchup.Supervisor, enabled: ConfigHelper.parse_bool_env_var("INDEXER_ARBITRUM_BRIDGE_MESSAGES_TRACKING_ENABLED") +config :indexer, Indexer.Fetcher.Arbitrum.MessagesToL2Matcher.Supervisor, + disabled?: not ConfigHelper.parse_bool_env_var("INDEXER_ARBITRUM_BRIDGE_MESSAGES_TRACKING_ENABLED") + config :indexer, Indexer.Fetcher.RootstockData.Supervisor, disabled?: ConfigHelper.chain_type() != :rsk || ConfigHelper.parse_bool_env_var("INDEXER_DISABLE_ROOTSTOCK_DATA_FETCHER") From e16236243195deb06d6d4b9e0bb67c534b2f04ad Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 3 Oct 2024 15:03:36 +0300 Subject: [PATCH 205/363] chore: add version bump script (#10871) --- .github/workflows/pre-release-arbitrum.yml | 2 +- .github/workflows/pre-release-blackfort.yml | 2 +- .github/workflows/pre-release-celo.yml | 2 +- .github/workflows/pre-release-eth.yml | 2 +- .github/workflows/pre-release-optimism.yml | 2 +- .github/workflows/pre-release-redstone.yml | 2 +- .github/workflows/pre-release-shibarium.yml | 2 +- .github/workflows/pre-release-zksync.yml | 2 +- .github/workflows/pre-release.yml | 2 +- .../publish-docker-image-every-push.yml | 2 +- .../publish-docker-image-for-arbitrum.yml | 2 +- .../publish-docker-image-for-blackfort.yml | 2 +- .../publish-docker-image-for-celo.yml | 2 +- .../publish-docker-image-for-core.yml | 2 +- .../publish-docker-image-for-eth-sepolia.yml | 2 +- .../publish-docker-image-for-eth.yml | 2 +- .../publish-docker-image-for-filecoin.yml | 2 +- .../publish-docker-image-for-fuse.yml | 2 +- .../publish-docker-image-for-gnosis-chain.yml | 2 +- .../publish-docker-image-for-l2-staging.yml | 2 +- .../publish-docker-image-for-lukso.yml | 2 +- .../publish-docker-image-for-optimism.yml | 2 +- .../publish-docker-image-for-polygon-edge.yml | 2 +- .../publish-docker-image-for-redstone.yml | 2 +- .../publish-docker-image-for-rootstock.yml | 2 +- .../publish-docker-image-for-shibarium.yml | 2 +- .../publish-docker-image-for-stability.yml | 2 +- .../publish-docker-image-for-suave.yml | 2 +- .../publish-docker-image-for-zetachain.yml | 2 +- .../publish-docker-image-for-zkevm.yml | 2 +- .../publish-docker-image-for-zksync.yml | 2 +- ...publish-docker-image-staging-on-demand.yml | 2 +- ...publish-regular-docker-image-on-demand.yml | 2 +- .github/workflows/release-arbitrum.yml | 2 +- .github/workflows/release-blackfort.yml | 2 +- .github/workflows/release-celo.yml | 2 +- .github/workflows/release-eth.yml | 2 +- .github/workflows/release-filecoin.yml | 2 +- .github/workflows/release-fuse.yml | 2 +- .github/workflows/release-gnosis.yml | 2 +- .github/workflows/release-optimism.yml | 2 +- .github/workflows/release-polygon-edge.yml | 2 +- .github/workflows/release-polygon-zkevm.yml | 2 +- .github/workflows/release-redstone.yml | 2 +- .github/workflows/release-rootstock.yml | 2 +- .github/workflows/release-shibarium.yml | 2 +- .github/workflows/release-stability.yml | 2 +- .github/workflows/release-suave.yml | 2 +- .github/workflows/release-zetachain.yml | 2 +- .github/workflows/release-zksync.yml | 2 +- .github/workflows/release.yml | 2 +- bin/version_bump.sh | 76 +++++++++++++++++++ 52 files changed, 127 insertions(+), 51 deletions(-) create mode 100755 bin/version_bump.sh diff --git a/.github/workflows/pre-release-arbitrum.yml b/.github/workflows/pre-release-arbitrum.yml index 3bc6cd2c669b..3e60ea497347 100644 --- a/.github/workflows/pre-release-arbitrum.yml +++ b/.github/workflows/pre-release-arbitrum.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-blackfort.yml b/.github/workflows/pre-release-blackfort.yml index 66e730b6427e..0444d82badf7 100644 --- a/.github/workflows/pre-release-blackfort.yml +++ b/.github/workflows/pre-release-blackfort.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-celo.yml b/.github/workflows/pre-release-celo.yml index c493692792ea..a1fd1a2ebdb9 100644 --- a/.github/workflows/pre-release-celo.yml +++ b/.github/workflows/pre-release-celo.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-eth.yml b/.github/workflows/pre-release-eth.yml index 07c436f5727c..6655abb71e7e 100644 --- a/.github/workflows/pre-release-eth.yml +++ b/.github/workflows/pre-release-eth.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-optimism.yml b/.github/workflows/pre-release-optimism.yml index 44683dc335c9..29b735be0ed1 100644 --- a/.github/workflows/pre-release-optimism.yml +++ b/.github/workflows/pre-release-optimism.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-redstone.yml b/.github/workflows/pre-release-redstone.yml index 180ab79ce3b7..f7b17cd518bf 100644 --- a/.github/workflows/pre-release-redstone.yml +++ b/.github/workflows/pre-release-redstone.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-shibarium.yml b/.github/workflows/pre-release-shibarium.yml index 8258205844b7..f78af6c42ae2 100644 --- a/.github/workflows/pre-release-shibarium.yml +++ b/.github/workflows/pre-release-shibarium.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-zksync.yml b/.github/workflows/pre-release-zksync.yml index dc5b3b582100..402c48559b18 100644 --- a/.github/workflows/pre-release-zksync.yml +++ b/.github/workflows/pre-release-zksync.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index 2a83968a043e..dc5b20d14116 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/publish-docker-image-every-push.yml b/.github/workflows/publish-docker-image-every-push.yml index dea446dd47ee..1099b747d88b 100644 --- a/.github/workflows/publish-docker-image-every-push.yml +++ b/.github/workflows/publish-docker-image-every-push.yml @@ -11,7 +11,7 @@ on: env: OTP_VERSION: ${{ vars.OTP_VERSION }} ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 jobs: push_to_registry: diff --git a/.github/workflows/publish-docker-image-for-arbitrum.yml b/.github/workflows/publish-docker-image-for-arbitrum.yml index 19757155583c..45ced4c1a5ef 100644 --- a/.github/workflows/publish-docker-image-for-arbitrum.yml +++ b/.github/workflows/publish-docker-image-for-arbitrum.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: arbitrum steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-blackfort.yml b/.github/workflows/publish-docker-image-for-blackfort.yml index b0fbf41b3be3..6051b3e63d75 100644 --- a/.github/workflows/publish-docker-image-for-blackfort.yml +++ b/.github/workflows/publish-docker-image-for-blackfort.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: blackfort steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-celo.yml b/.github/workflows/publish-docker-image-for-celo.yml index 2efe98d266f1..6843e949f22a 100644 --- a/.github/workflows/publish-docker-image-for-celo.yml +++ b/.github/workflows/publish-docker-image-for-celo.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: celo steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-core.yml b/.github/workflows/publish-docker-image-for-core.yml index 07db87141aa3..3499b3e18d62 100644 --- a/.github/workflows/publish-docker-image-for-core.yml +++ b/.github/workflows/publish-docker-image-for-core.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: poa steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-eth-sepolia.yml b/.github/workflows/publish-docker-image-for-eth-sepolia.yml index 2b7976927c3f..8b9a57a66268 100644 --- a/.github/workflows/publish-docker-image-for-eth-sepolia.yml +++ b/.github/workflows/publish-docker-image-for-eth-sepolia.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: eth-sepolia steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-eth.yml b/.github/workflows/publish-docker-image-for-eth.yml index 0f35962eeec2..980552d75058 100644 --- a/.github/workflows/publish-docker-image-for-eth.yml +++ b/.github/workflows/publish-docker-image-for-eth.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: mainnet steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-filecoin.yml b/.github/workflows/publish-docker-image-for-filecoin.yml index efc5fcfbd324..caea5a953417 100644 --- a/.github/workflows/publish-docker-image-for-filecoin.yml +++ b/.github/workflows/publish-docker-image-for-filecoin.yml @@ -9,7 +9,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: filecoin steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-fuse.yml b/.github/workflows/publish-docker-image-for-fuse.yml index 9f19e1ef7b58..52bb4a5901b0 100644 --- a/.github/workflows/publish-docker-image-for-fuse.yml +++ b/.github/workflows/publish-docker-image-for-fuse.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: fuse steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-gnosis-chain.yml b/.github/workflows/publish-docker-image-for-gnosis-chain.yml index 88fff9f709b6..068822401f82 100644 --- a/.github/workflows/publish-docker-image-for-gnosis-chain.yml +++ b/.github/workflows/publish-docker-image-for-gnosis-chain.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: xdai steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-l2-staging.yml b/.github/workflows/publish-docker-image-for-l2-staging.yml index c3bdc7522ad6..a39648c4235e 100644 --- a/.github/workflows/publish-docker-image-for-l2-staging.yml +++ b/.github/workflows/publish-docker-image-for-l2-staging.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: optimism-l2-advanced steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-lukso.yml b/.github/workflows/publish-docker-image-for-lukso.yml index 01f5238a733b..b85f6be962d6 100644 --- a/.github/workflows/publish-docker-image-for-lukso.yml +++ b/.github/workflows/publish-docker-image-for-lukso.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: lukso steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-optimism.yml b/.github/workflows/publish-docker-image-for-optimism.yml index cb2c75f96a1c..5361c91dd4a4 100644 --- a/.github/workflows/publish-docker-image-for-optimism.yml +++ b/.github/workflows/publish-docker-image-for-optimism.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: optimism steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-polygon-edge.yml b/.github/workflows/publish-docker-image-for-polygon-edge.yml index 4bd600b778fa..8625850763f1 100644 --- a/.github/workflows/publish-docker-image-for-polygon-edge.yml +++ b/.github/workflows/publish-docker-image-for-polygon-edge.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: polygon-edge steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-redstone.yml b/.github/workflows/publish-docker-image-for-redstone.yml index c64c06b0e3eb..56e28fee69ac 100644 --- a/.github/workflows/publish-docker-image-for-redstone.yml +++ b/.github/workflows/publish-docker-image-for-redstone.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: redstone steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-rootstock.yml b/.github/workflows/publish-docker-image-for-rootstock.yml index 910c9ba2d1b7..335f4f475b56 100644 --- a/.github/workflows/publish-docker-image-for-rootstock.yml +++ b/.github/workflows/publish-docker-image-for-rootstock.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: rsk steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-shibarium.yml b/.github/workflows/publish-docker-image-for-shibarium.yml index 32d6cfb3b588..fcf2bf62838a 100644 --- a/.github/workflows/publish-docker-image-for-shibarium.yml +++ b/.github/workflows/publish-docker-image-for-shibarium.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: shibarium steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-stability.yml b/.github/workflows/publish-docker-image-for-stability.yml index d4bfd64a27b2..c106d4ea90a8 100644 --- a/.github/workflows/publish-docker-image-for-stability.yml +++ b/.github/workflows/publish-docker-image-for-stability.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: stability steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-suave.yml b/.github/workflows/publish-docker-image-for-suave.yml index fccbaee55ccb..bc968c303974 100644 --- a/.github/workflows/publish-docker-image-for-suave.yml +++ b/.github/workflows/publish-docker-image-for-suave.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: suave steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-zetachain.yml b/.github/workflows/publish-docker-image-for-zetachain.yml index d04c69973293..cef02097bc53 100644 --- a/.github/workflows/publish-docker-image-for-zetachain.yml +++ b/.github/workflows/publish-docker-image-for-zetachain.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: zetachain steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-zkevm.yml b/.github/workflows/publish-docker-image-for-zkevm.yml index 30270a10c6a9..8df71dbffd30 100644 --- a/.github/workflows/publish-docker-image-for-zkevm.yml +++ b/.github/workflows/publish-docker-image-for-zkevm.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: zkevm steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-zksync.yml b/.github/workflows/publish-docker-image-for-zksync.yml index 508355156091..5b397c729883 100644 --- a/.github/workflows/publish-docker-image-for-zksync.yml +++ b/.github/workflows/publish-docker-image-for-zksync.yml @@ -9,7 +9,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: zksync steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-staging-on-demand.yml b/.github/workflows/publish-docker-image-staging-on-demand.yml index f06df3348ba7..2fb56610ff1c 100644 --- a/.github/workflows/publish-docker-image-staging-on-demand.yml +++ b/.github/workflows/publish-docker-image-staging-on-demand.yml @@ -12,7 +12,7 @@ on: env: OTP_VERSION: ${{ vars.OTP_VERSION }} ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 jobs: push_to_registry: diff --git a/.github/workflows/publish-regular-docker-image-on-demand.yml b/.github/workflows/publish-regular-docker-image-on-demand.yml index 54b76994bbca..b78f41e02842 100644 --- a/.github/workflows/publish-regular-docker-image-on-demand.yml +++ b/.github/workflows/publish-regular-docker-image-on-demand.yml @@ -5,7 +5,7 @@ on: env: OTP_VERSION: ${{ vars.OTP_VERSION }} ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 jobs: push_to_registry: diff --git a/.github/workflows/release-arbitrum.yml b/.github/workflows/release-arbitrum.yml index 0d701ff27288..b5cce835bb37 100644 --- a/.github/workflows/release-arbitrum.yml +++ b/.github/workflows/release-arbitrum.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-blackfort.yml b/.github/workflows/release-blackfort.yml index 76e22b0fde1f..74752040c981 100644 --- a/.github/workflows/release-blackfort.yml +++ b/.github/workflows/release-blackfort.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-celo.yml b/.github/workflows/release-celo.yml index fd503e9e4497..0ec60c449303 100644 --- a/.github/workflows/release-celo.yml +++ b/.github/workflows/release-celo.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-eth.yml b/.github/workflows/release-eth.yml index 310e20f185fe..e5452d3978d2 100644 --- a/.github/workflows/release-eth.yml +++ b/.github/workflows/release-eth.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-filecoin.yml b/.github/workflows/release-filecoin.yml index 2992a3c2a930..def2f3a88345 100644 --- a/.github/workflows/release-filecoin.yml +++ b/.github/workflows/release-filecoin.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-fuse.yml b/.github/workflows/release-fuse.yml index 0315d76c6545..6805b6d8ee99 100644 --- a/.github/workflows/release-fuse.yml +++ b/.github/workflows/release-fuse.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-gnosis.yml b/.github/workflows/release-gnosis.yml index 3755089991af..e2b6f23afeaa 100644 --- a/.github/workflows/release-gnosis.yml +++ b/.github/workflows/release-gnosis.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-optimism.yml b/.github/workflows/release-optimism.yml index 44daccfe17ed..dc1735617f43 100644 --- a/.github/workflows/release-optimism.yml +++ b/.github/workflows/release-optimism.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-polygon-edge.yml b/.github/workflows/release-polygon-edge.yml index b31ff15ac713..1174367b6eaf 100644 --- a/.github/workflows/release-polygon-edge.yml +++ b/.github/workflows/release-polygon-edge.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-polygon-zkevm.yml b/.github/workflows/release-polygon-zkevm.yml index fd4f6ff21c44..976e6a8e3a0d 100644 --- a/.github/workflows/release-polygon-zkevm.yml +++ b/.github/workflows/release-polygon-zkevm.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-redstone.yml b/.github/workflows/release-redstone.yml index d85d7c74dfd6..38a0e546c1d8 100644 --- a/.github/workflows/release-redstone.yml +++ b/.github/workflows/release-redstone.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-rootstock.yml b/.github/workflows/release-rootstock.yml index a7ea06531804..3f50374a9b19 100644 --- a/.github/workflows/release-rootstock.yml +++ b/.github/workflows/release-rootstock.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-shibarium.yml b/.github/workflows/release-shibarium.yml index 2147eb436fc3..7c6d4d674a7b 100644 --- a/.github/workflows/release-shibarium.yml +++ b/.github/workflows/release-shibarium.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-stability.yml b/.github/workflows/release-stability.yml index aa82ac6d391f..9c7264bfc2db 100644 --- a/.github/workflows/release-stability.yml +++ b/.github/workflows/release-stability.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-suave.yml b/.github/workflows/release-suave.yml index e71101f495c1..e04521b86b00 100644 --- a/.github/workflows/release-suave.yml +++ b/.github/workflows/release-suave.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-zetachain.yml b/.github/workflows/release-zetachain.yml index 6d497fd0536f..a4345fa633ec 100644 --- a/.github/workflows/release-zetachain.yml +++ b/.github/workflows/release-zetachain.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-zksync.yml b/.github/workflows/release-zksync.yml index 15439457312c..cf277a0f0df2 100644 --- a/.github/workflows/release-zksync.yml +++ b/.github/workflows/release-zksync.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2057477e2e25..ce6821992079 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.8.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/bin/version_bump.sh b/bin/version_bump.sh new file mode 100755 index 000000000000..0359e289243f --- /dev/null +++ b/bin/version_bump.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +# Path to the mix.exs file +MIX_FILES=( + "$(pwd)/mix.exs" + "$(pwd)/apps/block_scout_web/mix.exs" + "$(pwd)/apps/explorer/mix.exs" + "$(pwd)/apps/indexer/mix.exs" + "$(pwd)/apps/ethereum_jsonrpc/mix.exs" +) +CONFIG_FILE="$(pwd)/rel/config.exs" +DOCKER_COMPOSE_FILE="$(pwd)/docker-compose/docker-compose.yml" +MAKE_FILE="$(pwd)/docker/Makefile" +WORKFLOW_FILES=($(find "$(pwd)/.github/workflows" -type f \( -name "pre-release-*" -o -name "release-*" -o -name "publish-docker-image-*" \))) + +# Function to bump version +bump_version() { + local type=$1 + local custom_version=$2 + + # Extract the current version + MIX_FILE="${MIX_FILES[0]}" + current_version=$(grep -o 'version: "[0-9]\+\.[0-9]\+\.[0-9]\+"' "$MIX_FILE" | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') + echo "Current version: $current_version" + + # Split the version into its components + IFS='.' read -r -a version_parts <<< "$current_version" + + # Check if the --patch flag is provided + if [[ "$type" == "--patch" ]]; then + # Increment the patch version + version_parts[2]=$((version_parts[2] + 1)) + elif [[ "$type" == "--minor" ]]; then + # Increment the minor version and reset the patch version + version_parts[1]=$((version_parts[1] + 1)) + version_parts[2]=0 + elif [[ "$type" == "--major" ]]; then + # Increment the major version and reset the minor and patch versions + version_parts[0]=$((version_parts[0] + 1)) + version_parts[1]=0 + version_parts[2]=0 + elif [[ "$type" == "--update-to-version" ]]; then + # Apply the version from the 3rd argument + if [[ -z "$2" ]]; then + echo "Error: No version specified for --update-to-version." + exit 1 + fi + new_version="$custom_version" + IFS='.' read -r -a version_parts <<< "$new_version" + else + echo "No --patch flag provided. Exiting." + exit 1 + fi + + # Join the version parts back together + new_version="${version_parts[0]}.${version_parts[1]}.${version_parts[2]}" + + # Replace the old version with the new version in the mix.exs files + for MIX_FILE in "${MIX_FILES[@]}"; do + sed -i '' "s/version: \"$current_version\"/version: \"$new_version\"/" "$MIX_FILE" + done + + sed -i '' "s/version: \"$current_version/version: \"$new_version/" "$CONFIG_FILE" + sed -i '' "s/RELEASE_VERSION: $current_version/RELEASE_VERSION: $new_version/" "$DOCKER_COMPOSE_FILE" + sed -i '' "s/RELEASE_VERSION ?= '$current_version'/RELEASE_VERSION ?= '$new_version'/" "$MAKE_FILE" + + # Replace the old version with the new version in the GitHub workflows files + for WORKFLOW_FILE in "${WORKFLOW_FILES[@]}"; do + sed -i '' "s/RELEASE_VERSION: $current_version/RELEASE_VERSION: $new_version/" "$WORKFLOW_FILE" + done + + echo "Version bumped from $current_version to $new_version" +} + +# Call the function +bump_version "$1" "$2" \ No newline at end of file From 2c17f344094fab093369dfa00a1c41635f509a60 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Thu, 3 Oct 2024 19:42:44 +0400 Subject: [PATCH 206/363] fix: Fix get current user in app template (#10844) --- .../lib/block_scout_web/templates/layout/app.html.eex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex index 57a85d771e08..e3ea973fa791 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex @@ -94,7 +94,7 @@ <% end %> <% end %> - <% session = Explorer.Account.enabled?() && Plug.Conn.get_session(@conn, :current_user) %> + <% session = Explorer.Account.enabled?() && Map.get(@conn.private, :plug_session) && Plug.Conn.get_session(@conn, :current_user) %> <%= render BlockScoutWeb.LayoutView, "_topnav.html", current_user: session, conn: @conn %> <%= if session && !session[:email_verified] do %> From e22b45f32eef96dbe87732f231d342f4c24bd113 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 4 Oct 2024 12:55:26 +0300 Subject: [PATCH 207/363] Remove unused block import stages (#10875) --- .../chain/import/stage/address_referencing.ex | 30 ------------------- .../explorer/chain/import/stage/addresses.ex | 26 ---------------- 2 files changed, 56 deletions(-) delete mode 100644 apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex delete mode 100644 apps/explorer/lib/explorer/chain/import/stage/addresses.ex diff --git a/apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex b/apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex deleted file mode 100644 index 72b62a2f9be1..000000000000 --- a/apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex +++ /dev/null @@ -1,30 +0,0 @@ -defmodule Explorer.Chain.Import.Stage.AddressReferencing do - @moduledoc """ - Imports any tables that reference `t:Explorer.Chain.Address.t/0` and that were imported by - `Explorer.Chain.Import.Stage.Addresses`. - """ - - alias Explorer.Chain.Import.{Runner, Stage} - - @behaviour Stage - - @impl Stage - def runners, - do: [ - Runner.Address.CoinBalances, - Runner.Blocks, - Runner.Address.CoinBalancesDaily - ] - - @impl Stage - def all_runners, - do: runners() - - @impl Stage - def multis(runner_to_changes_list, options) do - {final_multi, final_remaining_runner_to_changes_list} = - Stage.single_multi(runners(), runner_to_changes_list, options) - - {[final_multi], final_remaining_runner_to_changes_list} - end -end diff --git a/apps/explorer/lib/explorer/chain/import/stage/addresses.ex b/apps/explorer/lib/explorer/chain/import/stage/addresses.ex deleted file mode 100644 index fe91366bf74b..000000000000 --- a/apps/explorer/lib/explorer/chain/import/stage/addresses.ex +++ /dev/null @@ -1,26 +0,0 @@ -defmodule Explorer.Chain.Import.Stage.Addresses do - @moduledoc """ - Imports addresses before anything else that references them because an unused address is still valid and recoverable - if the other stage(s) don't commit. - """ - - alias Explorer.Chain.Import.{Runner, Stage} - - @behaviour Stage - - @runner Runner.Addresses - - @impl Stage - def runners, do: [@runner] - - @impl Stage - def all_runners, - do: runners() - - @chunk_size 50 - - @impl Stage - def multis(runner_to_changes_list, options) do - Stage.chunk_every(runner_to_changes_list, @runner, @chunk_size, options) - end -end From addc633799248f098124f1329a66220e87bada6a Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Fri, 4 Oct 2024 14:55:54 +0400 Subject: [PATCH 208/363] feat: api for querying mud systems abi (#10829) * feat: api for querying mud systems abi * chore: doc fixes --- .../controllers/api/v2/mud_controller.ex | 30 +++ .../lib/block_scout_web/routers/api_router.ex | 2 + .../lib/block_scout_web/views/address_view.ex | 8 - .../block_scout_web/views/api/v2/mud_view.ex | 26 ++ apps/explorer/lib/explorer/chain/mud.ex | 245 +++++++++++++++++- .../explorer/chain/smart_contract/proxy.ex | 10 - 6 files changed, 298 insertions(+), 23 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex index bf735edbaaa6..9f4b2f440d1f 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex @@ -16,6 +16,8 @@ defmodule BlockScoutWeb.API.V2.MudController do alias Explorer.Chain alias Explorer.Chain.{Address, Data, Hash, Mud, Mud.Schema.FieldSchema, Mud.Table} + @api_true [api?: true] + action_fallback(BlockScoutWeb.API.V2.FallbackController) @doc """ @@ -94,6 +96,34 @@ defmodule BlockScoutWeb.API.V2.MudController do end end + @doc """ + Function to handle GET requests to `/api/v2/mud/worlds/:world/systems` endpoint. + """ + @spec world_systems(Plug.Conn.t(), map()) :: Plug.Conn.t() + def world_systems(conn, %{"world" => world_param} = _params) do + with {:format, {:ok, world}} <- {:format, Hash.Address.cast(world_param)} do + systems = world |> Mud.world_systems() + + conn + |> put_status(200) + |> render(:systems, %{systems: systems}) + end + end + + @doc """ + Function to handle GET requests to `/api/v2/mud/worlds/:world/systems/:system` endpoint. + """ + @spec world_system(Plug.Conn.t(), map()) :: Plug.Conn.t() + def world_system(conn, %{"world" => world_param, "system" => system_param} = _params) do + with {:format, {:ok, world}} <- {:format, Hash.Address.cast(world_param)}, + {:format, {:ok, system}} <- {:format, Hash.Address.cast(system_param)}, + {:ok, system_id, abi} <- Mud.world_system(world, system, @api_true) do + conn + |> put_status(200) + |> render(:system, %{system_id: system_id, abi: abi}) + end + end + @doc """ Function to handle GET requests to `/api/v2/mud/worlds/:world/tables/count` endpoint. """ diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index 772527d2ec97..325b860bbece 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -367,6 +367,8 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/worlds", V2.MudController, :worlds) get("/worlds/count", V2.MudController, :worlds_count) get("/worlds/:world/tables", V2.MudController, :world_tables) + get("/worlds/:world/systems", V2.MudController, :world_systems) + get("/worlds/:world/systems/:system", V2.MudController, :world_system) get("/worlds/:world/tables/count", V2.MudController, :world_tables_count) get("/worlds/:world/tables/:table_id/records", V2.MudController, :world_table_records) get("/worlds/:world/tables/:table_id/records/count", V2.MudController, :world_table_records_count) diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex index a8016bdccade..fea2c32b5a2b 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex @@ -9,7 +9,6 @@ defmodule BlockScoutWeb.AddressView do alias Explorer.Chain.Address.Counters alias Explorer.Chain.{Address, Hash, InternalTransaction, Log, SmartContract, Token, TokenTransfer, Transaction, Wei} alias Explorer.Chain.Block.Reward - alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.ExchangeRates.Token, as: TokenExchangeRate alias Explorer.SmartContract.{Helper, Writer} @@ -438,13 +437,6 @@ defmodule BlockScoutWeb.AddressView do end end - def smart_contract_is_gnosis_safe_proxy?(%Address{smart_contract: %SmartContract{}} = address) do - address.smart_contract.name == "GnosisSafeProxy" && - Proxy.gnosis_safe_contract?(address.smart_contract.abi) - end - - def smart_contract_is_gnosis_safe_proxy?(_address), do: false - def tag_name_to_label(tag_name) do tag_name |> String.replace(" ", "-") diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/mud_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/mud_view.ex index 935646c129ad..c628d7994b63 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/mud_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/mud_view.ex @@ -32,6 +32,25 @@ defmodule BlockScoutWeb.API.V2.MudView do } end + @doc """ + Function to render GET requests to `/api/v2/mud/worlds/:world/systems` endpoint. + """ + def render("systems.json", %{systems: systems}) do + %{ + items: systems |> Enum.map(&prepare_system_for_list/1) + } + end + + @doc """ + Function to render GET requests to `/api/v2/mud/worlds/:world/systems/:system` endpoint. + """ + def render("system.json", %{system_id: system_id, abi: abi}) do + %{ + name: system_id |> Table.from() |> Map.get(:table_full_name), + abi: abi + } + end + @doc """ Function to render GET requests to `/api/v2/mud/worlds/:world/tables/:table_id/records` endpoint. """ @@ -69,6 +88,13 @@ defmodule BlockScoutWeb.API.V2.MudView do } end + defp prepare_system_for_list({system_id, system}) do + %{ + name: system_id |> Table.from() |> Map.get(:table_full_name), + address: system + } + end + defp format_record(nil, _schema, _blocks), do: nil defp format_record(record, schema, blocks) do diff --git a/apps/explorer/lib/explorer/chain/mud.ex b/apps/explorer/lib/explorer/chain/mud.ex index 0663acad3a52..c6eee86dbf05 100644 --- a/apps/explorer/lib/explorer/chain/mud.ex +++ b/apps/explorer/lib/explorer/chain/mud.ex @@ -10,10 +10,11 @@ defmodule Explorer.Chain.Mud do order_by: 3, select: 3, where: 3, - limit: 2 + limit: 2, + join: 5 ] - alias ABI.TypeDecoder + alias ABI.{FunctionSelector, TypeDecoder} alias Explorer.{Chain, PagingOptions, Repo, SortingHelper} alias Explorer.Chain.{ @@ -23,13 +24,12 @@ defmodule Explorer.Chain.Mud do Hash, Mud, Mud.Schema, - Mud.Schema.FieldSchema + Mud.Schema.FieldSchema, + SmartContract } require Logger - @schema_prefix "mud" - @store_tables_table_id Base.decode16!("746273746f72650000000000000000005461626c657300000000000000000000", case: :lower ) @@ -42,6 +42,42 @@ defmodule Explorer.Chain.Mud do value_names: ["fieldLayout", "keySchema", "valueSchema", "abiEncodedKeyNames", "abiEncodedValueNames"] } + @world_system_registry_table_id Base.decode16!("7462776f726c6400000000000000000053797374656d52656769737472790000", + case: :lower + ) + + # https://github.com/latticexyz/mud/blob/5a6c03c6bc02c980ca051dadd8e20560ac25c771/packages/world/src/codegen/tables/SystemRegistry.sol#L24-L32 + @world_system_registry_schema %Schema{ + key_schema: FieldSchema.from("0x0014010061000000000000000000000000000000000000000000000000000000"), + value_schema: FieldSchema.from("0x002001005f000000000000000000000000000000000000000000000000000000"), + key_names: ["system"], + value_names: ["systemId"] + } + + @world_function_selector_table_id Base.decode16!("7462776f726c6400000000000000000046756e6374696f6e53656c6563746f72", + case: :lower + ) + + # https://github.com/latticexyz/mud/blob/5a6c03c6bc02c980ca051dadd8e20560ac25c771/packages/world/src/codegen/tables/FunctionSelectors.sol#L24-L32 + @world_function_selector_schema %Schema{ + key_schema: FieldSchema.from("0x0004010043000000000000000000000000000000000000000000000000000000"), + value_schema: FieldSchema.from("0x002402005f430000000000000000000000000000000000000000000000000000"), + key_names: ["worldFunctionSelector"], + value_names: ["systemId", "systemFunctionSelector"] + } + + @world_function_signature_table_id Base.decode16!("6f74776f726c6400000000000000000046756e6374696f6e5369676e61747572", + case: :lower + ) + + # https://github.com/latticexyz/mud/blob/5a6c03c6bc02c980ca051dadd8e20560ac25c771/packages/world/src/codegen/tables/FunctionSignatures.sol#L21-L29 + @world_function_signature_schema %Schema{ + key_schema: FieldSchema.from("0x0004010043000000000000000000000000000000000000000000000000000000"), + value_schema: FieldSchema.from("0x00000001c5000000000000000000000000000000000000000000000000000000"), + key_names: ["functionSelector"], + value_names: ["functionSignature"] + } + @primary_key false typed_schema "records" do field(:address, Hash.Address, null: false) @@ -193,6 +229,128 @@ defmodule Explorer.Chain.Mud do |> Repo.Mud.all() end + @doc """ + Returns the list of first 1000 MUD systems registered for the given world. + """ + @spec world_systems(Hash.Address.t()) :: [{Hash.Full.t(), Hash.Address.t()}] + def world_systems(world) do + Mud + |> where([r], r.address == ^world and r.table_id == ^@world_system_registry_table_id and r.is_deleted == false) + |> limit(1000) + |> Repo.Mud.all() + |> Enum.map(&decode_record(&1, @world_system_registry_schema)) + |> Enum.map(fn s -> + with {:ok, system_id} <- Hash.Full.cast(s["systemId"]), + {:ok, system} <- Hash.Address.cast(s["system"]) do + {system_id, system} + end + end) + |> Enum.reject(&(&1 == :error)) + end + + @doc """ + Returns reconstructed ABI of the MUD system in the given world. + """ + @spec world_system(Hash.Address.t(), Hash.Address.t(), Keyword.t()) :: + {:ok, Hash.Full.t(), [FunctionSelector.t()]} | {:error, :not_found} + # credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity + def world_system(world, system, options \\ []) do + # pad to 32 bytes + padded_system_address_hash = %Data{bytes: <<0::size(96)>> <> system.bytes} + + # If we were to access MUD tables in SQL, it would look like: + # SELECT sr.*, fsl.*, fsg.* + # FROM tb.world.SystemRegistry sr + # JOIN tb.world.FunctionSelector fsl ON fsl.systemId = sr.systemId + # JOIN tb.world.FunctionSignature fsg ON fsg.functionSelector = fsl.worldFunctionSelector + # WHERE sr.system = $1 + {system_records, function_selector_signature_records} = + Mud + |> where( + [r], + r.address == ^world and r.table_id == ^@world_system_registry_table_id and r.is_deleted == false and + r.key_bytes == ^padded_system_address_hash + ) + |> join( + :left, + [r], + r2 in Mud, + on: + r2.address == ^world and r2.table_id == ^@world_function_selector_table_id and r2.is_deleted == false and + fragment("substring(? FOR 32)", r2.static_data) == r.static_data + ) + |> join( + :left, + [r, r2], + r3 in Mud, + on: + r3.address == ^world and r3.table_id == ^@world_function_signature_table_id and r3.is_deleted == false and + r3.key_bytes == r2.key_bytes + ) + |> select([r, r2, r3], {r, {r2, r3}}) + |> limit(1000) + |> Repo.Mud.all() + |> Enum.unzip() + + with false <- Enum.empty?(system_records), + system_record = Enum.at(system_records, 0), + {:ok, system_id} <- Hash.Full.cast(system_record.static_data.bytes) do + {:ok, system_id, reconstruct_system_abi(system, function_selector_signature_records, options)} + else + _ -> {:error, :not_found} + end + end + + @spec reconstruct_system_abi(Hash.Address.t(), [{Mud.t(), Mud.t()}], Keyword.t()) :: [FunctionSelector.t()] + defp reconstruct_system_abi(system, function_selector_signature_records, options) do + system_contract = SmartContract.address_hash_to_smart_contract(system, options) + + # fetch verified contract ABI, if any + verified_system_abi = + ((system_contract && system_contract.abi) || []) + |> ABI.parse_specification() + |> Enum.filter(&(&1.type == :function)) + |> Enum.into(%{}, fn selector -> {"0x" <> Base.encode16(selector.method_id, case: :lower), selector} end) + + function_selector_signature_records + |> Enum.reject(&(&1 == {nil, nil})) + |> Enum.map(fn {function_selector_record, function_signature_record} -> + function_selector = function_selector_record |> decode_record(@world_function_selector_schema) + + # if the external world function selector is present in the verified ABI, we use it + world_function_selector = + verified_system_abi |> Map.get(function_selector |> Map.get("worldFunctionSelector")) + + if world_function_selector do + world_function_selector + else + abi_method = parse_function_signature(function_signature_record) + + # if the internal system function selector is present in the verified ABI, + # then it has the same arguments as the external world function, but a different name, + # so we use it after replacing the function name accordingly. + # in case neither of the selectors were found in the verified ABI, we use the ABI crafted from the method signature + verified_system_abi + |> Map.get(function_selector |> Map.get("systemFunctionSelector"), abi_method) + |> Map.put(:function, abi_method.function) + end + end) + end + + @spec parse_function_signature(Mud.t()) :: FunctionSelector.t() + defp parse_function_signature(function_signature_record) do + raw_abi_method = + function_signature_record + |> decode_record(@world_function_signature_schema) + |> Map.get("functionSignature") + |> FunctionSelector.decode() + + raw_abi_method + |> Map.put(:type, :function) + |> Map.put(:state_mutability, :payable) + |> Map.put(:input_names, 0..(Enum.count(raw_abi_method.types) - 1) |> Enum.map(&"arg#{&1}")) + end + @doc """ Preloads last modification timestamps for the list of raw MUD records. @@ -407,3 +565,80 @@ defmodule Explorer.Chain.Mud do end end end + +defimpl Jason.Encoder, for: ABI.FunctionSelector do + alias Jason.Encode + + def encode(data, opts) do + function_inputs = encode_arguments(data.types, data.input_names) + + inputs = + if data.inputs_indexed do + function_inputs + |> Enum.zip(data.inputs_indexed) + |> Enum.map(fn {r, indexed} -> Map.put(r, "indexed", indexed) end) + else + function_inputs + end + + Encode.map( + %{ + "type" => data.type, + "name" => data.function, + "inputs" => inputs, + "outputs" => encode_arguments(data.returns, data.return_names), + "stateMutability" => encode_state_mutability(data.state_mutability) + }, + opts + ) + end + + defp encode_arguments(types, names) do + types + |> Enum.zip(names) + |> Enum.map(fn {type, name} -> + %{ + "type" => encode_type(type), + "name" => name, + "components" => encode_components(type) + } + |> Map.reject(fn {key, value} -> {key, value} == {"components", nil} end) + end) + end + + defp encode_type(:bool), do: "bool" + defp encode_type(:string), do: "string" + defp encode_type(:bytes), do: "bytes" + defp encode_type(:address), do: "address" + defp encode_type(:function), do: "function" + defp encode_type({:int, size}), do: "int#{size}" + defp encode_type({:uint, size}), do: "uint#{size}" + defp encode_type({:fixed, element_count, precision}), do: "fixed#{element_count}x#{precision}" + defp encode_type({:ufixed, element_count, precision}), do: "ufixed#{element_count}x#{precision}" + defp encode_type({:bytes, size}), do: "bytes#{size}" + defp encode_type({:array, type}), do: "#{encode_type(type)}[]" + defp encode_type({:array, type, element_count}), do: "#{encode_type(type)}[#{element_count}]" + defp encode_type({:tuple, _types}), do: "tuple" + + defp encode_components({:array, type}), do: encode_components(type) + defp encode_components({:array, type, _element_count}), do: encode_components(type) + + defp encode_components({:tuple, types}), + do: + types + |> Enum.with_index() + |> Enum.map(fn {type, index} -> + %{ + "type" => encode_type(type), + "name" => "arg#{index}" + } + end) + + defp encode_components(_), do: nil + + defp encode_state_mutability(:pure), do: "pure" + defp encode_state_mutability(:view), do: "view" + defp encode_state_mutability(:non_payable), do: "nonpayable" + defp encode_state_mutability(:payable), do: "payable" + defp encode_state_mutability(_), do: nil +end diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex index 52784c329aa5..50bafa094425 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex @@ -154,16 +154,6 @@ defmodule Explorer.Chain.SmartContract.Proxy do def get_implementation_abi_from_proxy(_, _), do: [] - @doc """ - Checks if the ABI of the smart-contract follows GnosisSafe proxy pattern - """ - @spec gnosis_safe_contract?([map()]) :: boolean() - def gnosis_safe_contract?(abi) when not is_nil(abi) do - if get_master_copy_pattern(abi), do: true, else: false - end - - def gnosis_safe_contract?(abi) when is_nil(abi), do: false - @doc """ Checks if the input of the smart-contract follows master-copy (or Safe) proxy pattern before fetching its implementation from 0x0 storage pointer From bbd20a85c9f95ebb8656e96ff8df78f888966cf2 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Fri, 4 Oct 2024 13:56:56 +0300 Subject: [PATCH 209/363] feat: Add metadata info to tx interpreter request (#10823) * feat: Add metadata info to tx interpreter request * Fix credo --- .../transaction_interpretation.ex | 111 ++++++++++++------ 1 file changed, 73 insertions(+), 38 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex index 94a0eb27e682..31a04b6d8630 100644 --- a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex +++ b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex @@ -9,6 +9,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do alias Explorer.Chain.{Data, InternalTransaction, Log, TokenTransfer, Transaction} alias HTTPoison.Response + import Explorer.MicroserviceInterfaces.Metadata, only: [maybe_preload_metadata: 1] import Explorer.Utility.Microservice, only: [base_url: 2, check_enabled: 2] require Logger @@ -106,13 +107,38 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do defp prepare_request_body(transaction) do transaction = Chain.select_repo(@api_true).preload(transaction, [ - :transaction_actions, :block, to_address: [:scam_badge, :names, :smart_contract], from_address: [:names, :smart_contract], created_contract_address: [:scam_badge, :names, :token, :smart_contract] ]) + token_transfers = transaction |> fetch_token_transfers() |> Enum.reverse() + internal_transactions = transaction |> fetch_internal_transactions() |> Enum.reverse() + logs = transaction |> fetch_logs() |> Enum.reverse() + + [transaction_with_meta | other_elements] = + ([transaction | token_transfers] ++ internal_transactions ++ logs) + |> maybe_preload_metadata() + + %{ + logs: logs_with_meta, + token_transfers: token_transfers_with_meta, + internal_transactions: internal_transactions_with_meta + } = + Enum.reduce(other_elements, %{logs: [], token_transfers: [], internal_transactions: []}, fn element, acc -> + case element do + %InternalTransaction{} -> + Map.put(acc, :internal_transactions, [element | acc.internal_transactions]) + + %TokenTransfer{} -> + Map.put(acc, :token_transfers, [element | acc.token_transfers]) + + %Log{} -> + Map.put(acc, :logs, [element | acc.logs]) + end + end) + skip_sig_provider? = false {decoded_input, _abi_acc, _methods_acc} = Transaction.decoded_input_data(transaction, skip_sig_provider?, @api_true) @@ -120,31 +146,31 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do %{ data: %{ - to: Helper.address_with_info(nil, transaction.to_address, transaction.to_address_hash, true), + to: + Helper.address_with_info(nil, transaction_with_meta.to_address, transaction_with_meta.to_address_hash, true), from: Helper.address_with_info( nil, - transaction.from_address, - transaction.from_address_hash, + transaction_with_meta.from_address, + transaction_with_meta.from_address_hash, true ), - hash: transaction.hash, - type: transaction.type, - value: transaction.value, - method: Transaction.method_name(transaction, Transaction.format_decoded_input(decoded_input)), - status: transaction.status, - actions: TransactionView.transaction_actions(transaction.transaction_actions), - tx_types: TransactionView.tx_types(transaction), - raw_input: transaction.input, + hash: transaction_with_meta.hash, + type: transaction_with_meta.type, + value: transaction_with_meta.value, + method: Transaction.method_name(transaction_with_meta, Transaction.format_decoded_input(decoded_input)), + status: transaction_with_meta.status, + tx_types: TransactionView.tx_types(transaction_with_meta), + raw_input: transaction_with_meta.input, decoded_input: decoded_input_data, - token_transfers: prepare_token_transfers(transaction, decoded_input), - internal_transactions: prepare_internal_transactions(transaction) + token_transfers: prepare_token_transfers(token_transfers_with_meta, decoded_input), + internal_transactions: prepare_internal_transactions(internal_transactions_with_meta, transaction_with_meta) }, - logs_data: %{items: prepare_logs(transaction)} + logs_data: %{items: prepare_logs(logs_with_meta, transaction_with_meta)} } end - defp prepare_token_transfers(transaction, decoded_input) do + defp fetch_token_transfers(transaction) do full_options = [ necessity_by_association: %{ @@ -158,10 +184,14 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do |> Chain.transaction_to_token_transfers(full_options) |> Chain.flat_1155_batch_token_transfers() |> Enum.take(@items_limit) + end + + defp prepare_token_transfers(token_transfers, decoded_input) do + token_transfers |> Enum.map(&TransactionView.prepare_token_transfer(&1, nil, decoded_input)) end - defp prepare_internal_transactions(transaction) do + defp fetch_internal_transactions(transaction) do full_options = @internal_transaction_necessity_by_association |> Keyword.merge(@api_true) @@ -169,9 +199,35 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do transaction.hash |> InternalTransaction.transaction_to_internal_transactions(full_options) |> Enum.take(@items_limit) + end + + defp prepare_internal_transactions(internal_transactions, transaction) do + internal_transactions |> Enum.map(&TransactionView.prepare_internal_transaction(&1, transaction.block)) end + defp fetch_logs(transaction) do + full_options = + [ + necessity_by_association: %{ + [address: [:names, :smart_contract, :proxy_implementations]] => :optional + } + ] + |> Keyword.merge(@api_true) + + transaction.hash + |> Chain.transaction_to_logs(full_options) + |> Enum.take(@items_limit) + end + + defp prepare_logs(logs, transaction) do + decoded_logs = TransactionView.decode_logs(logs, false) + + logs + |> Enum.zip(decoded_logs) + |> Enum.map(fn {log, decoded_log} -> TransactionView.prepare_log(log, transaction.hash, decoded_log, true) end) + end + defp user_op_to_logs_and_token_transfers(user_op, decoded_input) do log_options = [ @@ -213,27 +269,6 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do {prepared_logs, prepared_token_transfers} end - defp prepare_logs(transaction) do - full_options = - [ - necessity_by_association: %{ - [address: [:names, :smart_contract, :proxy_implementations]] => :optional - } - ] - |> Keyword.merge(@api_true) - - logs = - transaction.hash - |> Chain.transaction_to_logs(full_options) - |> Enum.take(@items_limit) - - decoded_logs = TransactionView.decode_logs(logs, false) - - logs - |> Enum.zip(decoded_logs) - |> Enum.map(fn {log, decoded_log} -> TransactionView.prepare_log(log, transaction.hash, decoded_log, true) end) - end - defp preload_template_variables({:ok, %{"success" => true, "data" => %{"summaries" => summaries} = data}}) do summaries_updated = Enum.map(summaries, fn %{"summary_template_variables" => summary_template_variables} = summary -> From c3005aa876e1fad707de6d70afea764363b2dc38 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:57:41 +0400 Subject: [PATCH 210/363] feat: Send archive balances requests to trace url (#10820) * feat: Send archive balances requests to trace url * Fix fetch coin balances tests --- apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex | 85 ++++++++++++++----- .../lib/ethereum_jsonrpc/fetched_balances.ex | 11 +++ .../ethereum_jsonrpc/utility/common_helper.ex | 13 +++ .../test/ethereum_jsonrpc_test.exs | 27 +++++- .../explorer/lib/explorer/chain/supply/rsk.ex | 2 +- .../indexer/fetcher/coin_balance/helper.ex | 4 +- .../lib/indexer/fetcher/contract_code.ex | 4 +- .../indexer/fetcher/on_demand/coin_balance.ex | 2 +- .../test/indexer/block/fetcher_test.exs | 6 +- .../fetcher/coin_balance/catchup_test.exs | 18 ++++ .../coin_balance_catchup_supervisor_case.ex | 10 ++- 11 files changed, 146 insertions(+), 36 deletions(-) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex index 8b4dd4f51b7b..3d5cc09968f2 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex @@ -38,6 +38,7 @@ defmodule EthereumJSONRPC do RequestCoordinator, Subscription, Transport, + Utility.CommonHelper, Utility.EndpointAvailabilityObserver, Utility.RangesHelper, Variant @@ -186,38 +187,53 @@ defmodule EthereumJSONRPC do [%{required(:block_quantity) => quantity, required(:hash_data) => data()}], json_rpc_named_arguments ) :: {:ok, FetchedBalances.t()} | {:error, reason :: term} - def fetch_balances(params_list, json_rpc_named_arguments, chunk_size \\ nil) + def fetch_balances(params_list, json_rpc_named_arguments, latest_block_number \\ 0, chunk_size \\ nil) when is_list(params_list) and is_list(json_rpc_named_arguments) do - filtered_params = - with true <- Application.get_env(:ethereum_jsonrpc, :disable_archive_balances?), - {:ok, max_block_number} <- fetch_block_number_by_tag("latest", json_rpc_named_arguments) do + latest_block_number_params = + case latest_block_number do + 0 -> fetch_block_number_by_tag("latest", json_rpc_named_arguments) + number -> {:ok, number} + end + + params_in_range = + params_list + |> Enum.filter(fn + %{block_quantity: block_quantity} -> + block_quantity |> quantity_to_integer() |> RangesHelper.traceable_block_number?() + end) + + trace_url_used? = !is_nil(json_rpc_named_arguments[:transport_options][:method_to_url][:eth_getBalance]) + archive_disabled? = Application.get_env(:ethereum_jsonrpc, :disable_archive_balances?) + + {latest_balances_params, archive_balance_params} = + with true <- not trace_url_used? or archive_disabled?, + {:ok, max_block_number} <- latest_block_number_params do window = Application.get_env(:ethereum_jsonrpc, :archive_balances_window) - params_list - |> Enum.filter(fn + Enum.split_with(params_in_range, fn %{block_quantity: "latest"} -> true %{block_quantity: block_quantity} -> quantity_to_integer(block_quantity) > max_block_number - window _ -> false end) else - _ -> params_list + _ -> {params_in_range, []} end - filtered_params_in_range = - filtered_params - |> Enum.filter(fn - %{block_quantity: block_quantity} -> - block_quantity |> quantity_to_integer() |> RangesHelper.traceable_block_number?() - end) - - id_to_params = id_to_params(filtered_params_in_range) - - with {:ok, responses} <- - id_to_params - |> FetchedBalances.requests() - |> chunk_requests(chunk_size) - |> json_rpc(json_rpc_named_arguments) do - {:ok, FetchedBalances.from_responses(responses, id_to_params)} + latest_id_to_params = id_to_params(latest_balances_params) + archive_id_to_params = id_to_params(archive_balance_params) + + with {:ok, latest_responses} <- do_balances_request(latest_id_to_params, chunk_size, json_rpc_named_arguments), + {:ok, archive_responses} <- + maybe_request_archive_balances( + archive_id_to_params, + trace_url_used?, + archive_disabled?, + chunk_size, + json_rpc_named_arguments + ) do + latest_fetched_balances = FetchedBalances.from_responses(latest_responses, latest_id_to_params) + archive_fetched_balances = FetchedBalances.from_responses(archive_responses, archive_id_to_params) + {:ok, FetchedBalances.merge(latest_fetched_balances, archive_fetched_balances)} end end @@ -471,6 +487,31 @@ defmodule EthereumJSONRPC do end end + defp do_balances_request(id_to_params, _chunk_size, _args) when id_to_params == %{}, do: {:ok, []} + + defp do_balances_request(id_to_params, chunk_size, json_rpc_named_arguments) do + id_to_params + |> FetchedBalances.requests() + |> chunk_requests(chunk_size) + |> json_rpc(json_rpc_named_arguments) + end + + defp archive_json_rpc_named_arguments(json_rpc_named_arguments) do + CommonHelper.put_in_keyword_nested( + json_rpc_named_arguments, + [:transport_options, :method_to_url, :eth_getBalance], + System.get_env("ETHEREUM_JSONRPC_TRACE_URL") + ) + end + + defp maybe_request_archive_balances(id_to_params, trace_url_used?, disabled?, chunk_size, json_rpc_named_arguments) do + if not trace_url_used? and not disabled? do + do_balances_request(id_to_params, chunk_size, archive_json_rpc_named_arguments(json_rpc_named_arguments)) + else + {:ok, []} + end + end + defp maybe_replace_url(url, _replace_url, EthereumJSONRPC.HTTP), do: url defp maybe_replace_url(url, replace_url, _), do: EndpointAvailabilityObserver.maybe_replace_url(url, replace_url, :ws) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_balances.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_balances.ex index da769b5e5a2d..d87bdfb00e3c 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_balances.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_balances.ex @@ -33,6 +33,17 @@ defmodule EthereumJSONRPC.FetchedBalances do ) end + @doc """ + Merges two `t/0` to one `t/0`. + """ + @spec merge(t(), t()) :: t() + def merge(%{params_list: params_list_1, errors: errors_1}, %{params_list: params_list_2, errors: errors_2}) do + %__MODULE__{ + params_list: params_list_1 ++ params_list_2, + errors: errors_1 ++ errors_2 + } + end + @doc """ `eth_getBalance` requests for `id_to_params`. """ diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/common_helper.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/common_helper.ex index 154edb78bd42..a6d0d55f2d7b 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/common_helper.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/common_helper.ex @@ -17,6 +17,19 @@ defmodule EthereumJSONRPC.Utility.CommonHelper do end end + @doc """ + Puts value under nested key in keyword. + Similar to `Kernel.put_in/3` but inserts values in the middle if they're missing + """ + @spec put_in_keyword_nested(Keyword.t(), [atom()], any()) :: Keyword.t() + def put_in_keyword_nested(keyword, [last_path], value) do + Keyword.put(keyword || [], last_path, value) + end + + def put_in_keyword_nested(keyword, [nearest_path | rest_path], value) do + Keyword.put(keyword || [], nearest_path, put_in_keyword_nested(keyword[nearest_path], rest_path, value)) + end + defp convert_to_ms(number, "s"), do: :timer.seconds(number) defp convert_to_ms(number, "m"), do: :timer.minutes(number) defp convert_to_ms(number, "h"), do: :timer.hours(number) diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs index 52bbf7bd4631..ecc2dbd148ba 100644 --- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs @@ -35,7 +35,8 @@ defmodule EthereumJSONRPCTest do [ %{block_quantity: "0x1", hash_data: hash} ], - json_rpc_named_arguments + json_rpc_named_arguments, + 1 ) == {:ok, %FetchedBalances{ @@ -49,6 +50,25 @@ defmodule EthereumJSONRPCTest do }} end + test "fetch latest block number from node if it wasn't provided", %{ + json_rpc_named_arguments: json_rpc_named_arguments + } do + if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do + expect(EthereumJSONRPC.Mox, :json_rpc, 2, fn + [%{id: id, method: "eth_getBlockByNumber"}], _options -> block_response(id, false, "0x1") + _json, _options -> {:ok, [%{id: 0, result: "0x1"}]} + end) + end + + assert {:ok, %FetchedBalances{}} = + EthereumJSONRPC.fetch_balances( + [ + %{block_quantity: "0x1", hash_data: "0x8bf38d4764929064f2d4d3a56520a76ab3df415b"} + ], + json_rpc_named_arguments + ) + end + test "with all invalid hash_data returns errors", %{json_rpc_named_arguments: json_rpc_named_arguments} do variant = Keyword.fetch!(json_rpc_named_arguments, :variant) @@ -90,7 +110,7 @@ defmodule EthereumJSONRPCTest do ], params_list: [] }} = - EthereumJSONRPC.fetch_balances([%{block_quantity: "0x1", hash_data: "0x0"}], json_rpc_named_arguments) + EthereumJSONRPC.fetch_balances([%{block_quantity: "0x1", hash_data: "0x0"}], json_rpc_named_arguments, 1) end test "with a mix of valid and invalid hash_data returns both", %{ @@ -163,7 +183,8 @@ defmodule EthereumJSONRPCTest do hash_data: "0x5" } ], - json_rpc_named_arguments + json_rpc_named_arguments, + 53 ) assert is_list(params_list) diff --git a/apps/explorer/lib/explorer/chain/supply/rsk.ex b/apps/explorer/lib/explorer/chain/supply/rsk.ex index 2eb9caa93350..14635a1c7eb6 100644 --- a/apps/explorer/lib/explorer/chain/supply/rsk.ex +++ b/apps/explorer/lib/explorer/chain/supply/rsk.ex @@ -113,7 +113,7 @@ defmodule Explorer.Chain.Supply.RSK do json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) - case EthereumJSONRPC.fetch_balances(params, json_rpc_named_arguments) do + case EthereumJSONRPC.fetch_balances(params, json_rpc_named_arguments, max_number) do {:ok, %FetchedBalances{ errors: [], diff --git a/apps/indexer/lib/indexer/fetcher/coin_balance/helper.ex b/apps/indexer/lib/indexer/fetcher/coin_balance/helper.ex index e4da98ed9aa8..759bd6eea46d 100644 --- a/apps/indexer/lib/indexer/fetcher/coin_balance/helper.ex +++ b/apps/indexer/lib/indexer/fetcher/coin_balance/helper.ex @@ -9,7 +9,7 @@ defmodule Indexer.Fetcher.CoinBalance.Helper do alias EthereumJSONRPC.{Blocks, FetchedBalances, Utility.RangesHelper} alias Explorer.Chain - alias Explorer.Chain.Cache.Accounts + alias Explorer.Chain.Cache.{Accounts, BlockNumber} alias Explorer.Chain.Hash alias Indexer.BufferedTask @@ -55,7 +55,7 @@ defmodule Indexer.Fetcher.CoinBalance.Helper do unique_filtered_entries |> Enum.map(&entry_to_params/1) - |> EthereumJSONRPC.fetch_balances(json_rpc_named_arguments) + |> EthereumJSONRPC.fetch_balances(json_rpc_named_arguments, BlockNumber.get_max()) |> case do {:ok, fetched_balances} -> run_fetched_balances(fetched_balances, fetcher_type) diff --git a/apps/indexer/lib/indexer/fetcher/contract_code.ex b/apps/indexer/lib/indexer/fetcher/contract_code.ex index bd2809da52e0..49778f375e26 100644 --- a/apps/indexer/lib/indexer/fetcher/contract_code.ex +++ b/apps/indexer/lib/indexer/fetcher/contract_code.ex @@ -12,7 +12,7 @@ defmodule Indexer.Fetcher.ContractCode do alias Explorer.Chain alias Explorer.Chain.{Block, Hash} - alias Explorer.Chain.Cache.Accounts + alias Explorer.Chain.Cache.{Accounts, BlockNumber} alias Indexer.{BufferedTask, Tracer} alias Indexer.Fetcher.CoinBalance.Helper, as: CoinBalanceHelper alias Indexer.Transform.Addresses @@ -120,7 +120,7 @@ defmodule Indexer.Fetcher.ContractCode do defp import_with_balances(addresses_params, entries, json_rpc_named_arguments) do entries |> coin_balances_request_params() - |> EthereumJSONRPC.fetch_balances(json_rpc_named_arguments) + |> EthereumJSONRPC.fetch_balances(json_rpc_named_arguments, BlockNumber.get_max()) |> case do {:ok, fetched_balances} -> balance_addresses_params = CoinBalanceHelper.balances_params_to_address_params(fetched_balances.params_list) diff --git a/apps/indexer/lib/indexer/fetcher/on_demand/coin_balance.ex b/apps/indexer/lib/indexer/fetcher/on_demand/coin_balance.ex index 626520b2b736..2170ee41eaaa 100644 --- a/apps/indexer/lib/indexer/fetcher/on_demand/coin_balance.ex +++ b/apps/indexer/lib/indexer/fetcher/on_demand/coin_balance.ex @@ -205,7 +205,7 @@ defmodule Indexer.Fetcher.OnDemand.CoinBalance do defp fetch_balances(block_number, address, json_rpc_named_arguments) do params = %{block_quantity: integer_to_quantity(block_number), hash_data: to_string(address.hash)} - EthereumJSONRPC.fetch_balances([params], json_rpc_named_arguments) + EthereumJSONRPC.fetch_balances([params], json_rpc_named_arguments, latest_block_number()) end defp do_import(%FetchedBalances{} = fetched_balances) do diff --git a/apps/indexer/test/indexer/block/fetcher_test.exs b/apps/indexer/test/indexer/block/fetcher_test.exs index 21416981d000..dc337b5e28c4 100644 --- a/apps/indexer/test/indexer/block/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/fetcher_test.exs @@ -49,7 +49,11 @@ defmodule Indexer.Block.FetcherTest do describe "import_range/2" do setup %{json_rpc_named_arguments: json_rpc_named_arguments} do - CoinBalanceCatchup.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + CoinBalanceCatchup.Supervisor.Case.start_supervised!( + json_rpc_named_arguments: json_rpc_named_arguments, + poll: false + ) + ContractCode.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) InternalTransaction.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) Token.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) diff --git a/apps/indexer/test/indexer/fetcher/coin_balance/catchup_test.exs b/apps/indexer/test/indexer/fetcher/coin_balance/catchup_test.exs index 358598a17da4..b330dccef57d 100644 --- a/apps/indexer/test/indexer/fetcher/coin_balance/catchup_test.exs +++ b/apps/indexer/test/indexer/fetcher/coin_balance/catchup_test.exs @@ -8,6 +8,7 @@ defmodule Indexer.Fetcher.CoinBalance.CatchupTest do import Mox alias Explorer.Chain.{Address, Hash, Wei} + alias Explorer.Chain.Cache.BlockNumber alias Indexer.Fetcher.CoinBalance.Catchup, as: CoinBalanceCatchup @moduletag :capture_log @@ -21,6 +22,13 @@ defmodule Indexer.Fetcher.CoinBalance.CatchupTest do setup do start_supervised!({Task.Supervisor, name: Indexer.TaskSupervisor}) + initial_config = Application.get_env(:explorer, Explorer.Chain.Cache.BlockNumber) + Application.put_env(:explorer, Explorer.Chain.Cache.BlockNumber, enabled: true) + + on_exit(fn -> + Application.put_env(:explorer, Explorer.Chain.Cache.BlockNumber, initial_config) + end) + :ok end @@ -225,6 +233,8 @@ defmodule Indexer.Fetcher.CoinBalance.CatchupTest do end) end + BlockNumber.set_max(block_number) + CoinBalanceCatchup.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) assert :ok = CoinBalanceCatchup.async_fetch_balances([%{address_hash: hash, block_number: block_number}]) @@ -318,6 +328,8 @@ defmodule Indexer.Fetcher.CoinBalance.CatchupTest do {:ok, [res2]} end) + BlockNumber.set_max(2) + case CoinBalanceCatchup.run(entries, json_rpc_named_arguments) do :ok -> balances = Repo.all(from(balance in Address.CoinBalance, where: balance.address_hash == ^hash_data)) @@ -373,6 +385,8 @@ defmodule Indexer.Fetcher.CoinBalance.CatchupTest do {:ok, [%{id: id, error: %{code: 1, message: "Bad"}}]} end) + BlockNumber.set_max(block_number()) + assert {:retry, ^entries} = CoinBalanceCatchup.run(entries, json_rpc_named_arguments) end @@ -401,6 +415,8 @@ defmodule Indexer.Fetcher.CoinBalance.CatchupTest do {:ok, [res]} end) + BlockNumber.set_max(block_number) + assert :ok = CoinBalanceCatchup.run(entries, json_rpc_named_arguments) end @@ -456,6 +472,8 @@ defmodule Indexer.Fetcher.CoinBalance.CatchupTest do {:ok, [res_good]} end) + BlockNumber.set_max(good_block_number) + assert {:retry, [{^address_hash_bytes, ^bad_block_number}]} = CoinBalanceCatchup.run( [{address_hash_bytes, good_block_number}, {address_hash_bytes, bad_block_number}], diff --git a/apps/indexer/test/support/indexer/fetcher/coin_balance_catchup_supervisor_case.ex b/apps/indexer/test/support/indexer/fetcher/coin_balance_catchup_supervisor_case.ex index 17831099c327..69ec7825616f 100644 --- a/apps/indexer/test/support/indexer/fetcher/coin_balance_catchup_supervisor_case.ex +++ b/apps/indexer/test/support/indexer/fetcher/coin_balance_catchup_supervisor_case.ex @@ -4,10 +4,12 @@ defmodule Indexer.Fetcher.CoinBalance.Catchup.Supervisor.Case do def start_supervised!(fetcher_arguments \\ []) when is_list(fetcher_arguments) do merged_fetcher_arguments = Keyword.merge( - fetcher_arguments, - flush_interval: 50, - max_batch_size: 1, - max_concurrency: 1 + [ + flush_interval: 50, + max_batch_size: 1, + max_concurrency: 1 + ], + fetcher_arguments ) [merged_fetcher_arguments] From 903cbe39011a55bc46037be35b8004840bd75e4b Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 4 Oct 2024 15:44:03 +0300 Subject: [PATCH 211/363] feat: Token transfers list API endpoint (#10801) * feat: Token transfers list API endpoint * Add filter by token type * Process review comments * Refactor to flatten batched token transfers * Enrich with tests for batch token transfers * Remove IO.inspect from the test --- .../api/v2/token_transfer_controller.ex | 79 ++++++++ .../transaction_interpretation.ex | 6 +- .../lib/block_scout_web/routers/api_router.ex | 4 + .../views/api/v2/advanced_filter_view.ex | 4 +- .../views/api/v2/stability_view.ex | 14 +- .../views/api/v2/token_transfer_view.ex | 89 +++++++++ .../views/api/v2/transaction_view.ex | 71 ++----- .../api/v2/token_transfer_controller_test.exs | 177 ++++++++++++++++++ .../lib/explorer/chain/token_transfer.ex | 43 +++++ 9 files changed, 417 insertions(+), 70 deletions(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_transfer_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/token_transfer_view.ex create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_transfer_controller_test.exs diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_transfer_controller.ex new file mode 100644 index 000000000000..05234b6ec791 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_transfer_controller.ex @@ -0,0 +1,79 @@ +defmodule BlockScoutWeb.API.V2.TokenTransferController do + use BlockScoutWeb, :controller + alias Explorer.{Chain, Helper, PagingOptions} + alias Explorer.Chain.{TokenTransfer, Transaction} + + import BlockScoutWeb.Chain, + only: [ + split_list_by_page: 1, + paging_options: 1, + token_transfers_next_page_params: 3 + ] + + import BlockScoutWeb.PagingHelper, + only: [ + delete_parameters_from_next_page_params: 1, + token_transfers_types_options: 1 + ] + + import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1] + import Explorer.MicroserviceInterfaces.Metadata, only: [maybe_preload_metadata: 1] + import Explorer.PagingOptions, only: [default_paging_options: 0] + + action_fallback(BlockScoutWeb.API.V2.FallbackController) + + @api_true [api?: true] + + @doc """ + Function to handle GET requests to `/api/v2/token-transfers` endpoint. + """ + @spec token_transfers(Plug.Conn.t(), map()) :: Plug.Conn.t() + def token_transfers(conn, params) do + paging_options = paging_options(params) + + options = + paging_options + |> Keyword.update(:paging_options, default_paging_options(), fn %PagingOptions{ + page_size: page_size + } = paging_options -> + mb_parsed_limit = Helper.parse_integer(params["limit"]) + %PagingOptions{paging_options | page_size: min(page_size, mb_parsed_limit && abs(mb_parsed_limit))} + end) + |> Keyword.merge(token_transfers_types_options(params)) + |> Keyword.merge(@api_true) + + result = + options + |> TokenTransfer.fetch() + |> Chain.flat_1155_batch_token_transfers() + |> Chain.paginate_1155_batch_token_transfers(paging_options) + |> split_list_by_page() + + {token_transfers, next_page} = result + + transactions = + token_transfers + |> Enum.map(fn token_transfer -> + token_transfer.transaction + end) + |> Enum.uniq() + + {decoded_transactions, _, _} = Transaction.decode_transactions(transactions, true, @api_true) + + decoded_transactions_map = + transactions + |> Enum.zip(decoded_transactions) + |> Enum.into(%{}, fn {%{hash: hash}, decoded_input} -> {hash, decoded_input} end) + + next_page_params = + next_page |> token_transfers_next_page_params(token_transfers, delete_parameters_from_next_page_params(params)) + + conn + |> put_status(200) + |> render(:token_transfers, %{ + token_transfers: token_transfers |> maybe_preload_ens() |> maybe_preload_metadata(), + decoded_transactions_map: decoded_transactions_map, + next_page_params: next_page_params + }) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex index 31a04b6d8630..11eb2458146d 100644 --- a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex +++ b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex @@ -3,7 +3,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do Module to interact with Transaction Interpretation Service """ - alias BlockScoutWeb.API.V2.{Helper, TokenView, TransactionView} + alias BlockScoutWeb.API.V2.{Helper, TokenTransferView, TokenView, TransactionView} alias Ecto.Association.NotLoaded alias Explorer.Chain alias Explorer.Chain.{Data, InternalTransaction, Log, TokenTransfer, Transaction} @@ -188,7 +188,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do defp prepare_token_transfers(token_transfers, decoded_input) do token_transfers - |> Enum.map(&TransactionView.prepare_token_transfer(&1, nil, decoded_input)) + |> Enum.map(&TokenTransferView.prepare_token_transfer(&1, nil, decoded_input)) end defp fetch_internal_transactions(transaction) do @@ -264,7 +264,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do |> TokenTransfer.logs_to_token_transfers(token_transfer_options) |> Chain.flat_1155_batch_token_transfers() |> Enum.take(@items_limit) - |> Enum.map(&TransactionView.prepare_token_transfer(&1, nil, decoded_input)) + |> Enum.map(&TokenTransferView.prepare_token_transfer(&1, nil, decoded_input)) {prepared_logs, prepared_token_transfers} end diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index 325b860bbece..2b0e32754beb 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -160,6 +160,10 @@ defmodule BlockScoutWeb.Routers.ApiRouter do end end + scope "/token-transfers" do + get("/", V2.TokenTransferController, :token_transfers) + end + scope "/blocks" do get("/", V2.BlockController, :blocks) get("/:block_hash_or_number", V2.BlockController, :block) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/advanced_filter_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/advanced_filter_view.ex index eb602e3412e9..cfa7c1ff6215 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/advanced_filter_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/advanced_filter_view.ex @@ -1,7 +1,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterView do use BlockScoutWeb, :view - alias BlockScoutWeb.API.V2.{Helper, TokenView, TransactionView} + alias BlockScoutWeb.API.V2.{Helper, TokenTransferView, TokenView} alias Explorer.Chain.{Address, Data, Transaction} alias Explorer.Market alias Explorer.Market.MarketHistory @@ -152,7 +152,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterView do value: advanced_filter.value, total: if(advanced_filter.type != "coin_transfer", - do: TransactionView.prepare_token_transfer_total(advanced_filter.token_transfer), + do: TokenTransferView.prepare_token_transfer_total(advanced_filter.token_transfer), else: nil ), token: diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/stability_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/stability_view.ex index f713428a3d08..2faa972485b7 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/stability_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/stability_view.ex @@ -1,6 +1,6 @@ defmodule BlockScoutWeb.API.V2.StabilityView do alias BlockScoutWeb.API.V2.{Helper, TokenView} - alias Explorer.Chain.{Hash, Log, Token, Transaction} + alias Explorer.Chain.{Log, Token, Transaction} @api_true [api?: true] @transaction_fee_event_signature "0x99e7b0ba56da2819c37c047f0511fd2bf6c9b4e27b4a979a19d6da0f74be8155" @@ -64,11 +64,12 @@ defmodule BlockScoutWeb.API.V2.StabilityView do "token" => TokenView.render("token.json", %{ token: transaction.transaction_fee_token, - contract_address_hash: bytes_to_address_hash(token_address_hash) + contract_address_hash: Transaction.bytes_to_address_hash(token_address_hash) }), "validator_address" => - Helper.address_with_info(nil, nil, bytes_to_address_hash(validator_address_hash), false), - "dapp_address" => Helper.address_with_info(nil, nil, bytes_to_address_hash(dapp_address_hash), false), + Helper.address_with_info(nil, nil, Transaction.bytes_to_address_hash(validator_address_hash), false), + "dapp_address" => + Helper.address_with_info(nil, nil, Transaction.bytes_to_address_hash(dapp_address_hash), false), "total_fee" => to_string(total_fee), "dapp_fee" => to_string(dapp_fee), "validator_fee" => to_string(validator_fee) @@ -95,7 +96,8 @@ defmodule BlockScoutWeb.API.V2.StabilityView do [{"token", "address", false, token_address_hash}, _, _, _, _, _] = mapping - {token, new_tokens_acc} = check_tokens_acc(bytes_to_address_hash(token_address_hash), tokens_acc) + {token, new_tokens_acc} = + check_tokens_acc(Transaction.bytes_to_address_hash(token_address_hash), tokens_acc) {%Transaction{transaction | transaction_fee_log: mapping, transaction_fee_token: token}, new_tokens_acc} @@ -121,6 +123,4 @@ defmodule BlockScoutWeb.API.V2.StabilityView do {token, Map.put(tokens_acc, token_address_hash, token)} end end - - defp bytes_to_address_hash(bytes), do: %Hash{byte_count: 20, bytes: bytes} end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_transfer_view.ex new file mode 100644 index 000000000000..a202137aba5e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_transfer_view.ex @@ -0,0 +1,89 @@ +defmodule BlockScoutWeb.API.V2.TokenTransferView do + use BlockScoutWeb, :view + + alias BlockScoutWeb.API.V2.{Helper, TokenView, TransactionView} + alias BlockScoutWeb.Tokens.Helper, as: TokensHelper + alias Ecto.Association.NotLoaded + alias Explorer.Chain + alias Explorer.Chain.Transaction + + def render("token_transfer.json", %{token_transfer: nil}) do + nil + end + + def render("token_transfer.json", %{ + token_transfer: token_transfer, + decoded_transaction_input: decoded_transaction_input, + conn: conn + }) do + prepare_token_transfer(token_transfer, conn, decoded_transaction_input) + end + + def render("token_transfers.json", %{ + token_transfers: token_transfers, + decoded_transactions_map: decoded_transactions_map, + next_page_params: next_page_params, + conn: conn + }) do + %{ + "items" => + Enum.map( + token_transfers, + &render("token_transfer.json", %{ + token_transfer: &1, + decoded_transaction_input: decoded_transactions_map[&1.transaction.hash], + conn: conn + }) + ), + "next_page_params" => next_page_params + } + end + + def prepare_token_transfer(token_transfer, _conn, decoded_input) do + %{ + "tx_hash" => token_transfer.transaction_hash, + "from" => Helper.address_with_info(nil, token_transfer.from_address, token_transfer.from_address_hash, false), + "to" => Helper.address_with_info(nil, token_transfer.to_address, token_transfer.to_address_hash, false), + "total" => prepare_token_transfer_total(token_transfer), + "token" => TokenView.render("token.json", %{token: token_transfer.token}), + "type" => Chain.get_token_transfer_type(token_transfer), + "timestamp" => + if(match?(%NotLoaded{}, token_transfer.block), + do: TransactionView.block_timestamp(token_transfer.transaction), + else: TransactionView.block_timestamp(token_transfer.block) + ), + "method" => Transaction.method_name(token_transfer.transaction, decoded_input, true), + "block_hash" => to_string(token_transfer.block_hash), + "block_number" => to_string(token_transfer.block_number), + "log_index" => to_string(token_transfer.log_index) + } + end + + # credo:disable-for-next-line /Complexity/ + def prepare_token_transfer_total(token_transfer) do + case TokensHelper.token_transfer_amount_for_api(token_transfer) do + {:ok, :erc721_instance} -> + %{"token_id" => token_transfer.token_ids && List.first(token_transfer.token_ids)} + + {:ok, :erc1155_erc404_instance, value, decimals} -> + %{ + "token_id" => token_transfer.token_ids && List.first(token_transfer.token_ids), + "value" => value, + "decimals" => decimals + } + + {:ok, :erc1155_erc404_instance, values, token_ids, decimals} -> + %{ + "token_id" => token_ids && List.first(token_ids), + "value" => values && List.first(values), + "decimals" => decimals + } + + {:ok, value, decimals} -> + %{"value" => value, "decimals" => decimals} + + _ -> + nil + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex index c950a69fa53c..cf37c5a686b3 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex @@ -1,11 +1,10 @@ defmodule BlockScoutWeb.API.V2.TransactionView do use BlockScoutWeb, :view - alias BlockScoutWeb.API.V2.{ApiView, Helper, TokenView} + alias BlockScoutWeb.API.V2.{ApiView, Helper, TokenTransferView, TokenView} alias BlockScoutWeb.{ABIEncodedValueView, TransactionView} alias BlockScoutWeb.Models.GetTransactionTags - alias BlockScoutWeb.Tokens.Helper, as: TokensHelper alias BlockScoutWeb.TransactionStateView alias Ecto.Association.NotLoaded alias Explorer.{Chain, Market} @@ -123,7 +122,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do "items" => token_transfers |> Enum.zip(decoded_transactions) - |> Enum.map(fn {tt, decoded_input} -> prepare_token_transfer(tt, conn, decoded_input) end), + |> Enum.map(fn {tt, decoded_input} -> TokenTransferView.prepare_token_transfer(tt, conn, decoded_input) end), "next_page_params" => next_page_params } end @@ -134,12 +133,12 @@ defmodule BlockScoutWeb.API.V2.TransactionView do token_transfers |> Enum.zip(decoded_transactions) - |> Enum.map(fn {tt, decoded_input} -> prepare_token_transfer(tt, conn, decoded_input) end) + |> Enum.map(fn {tt, decoded_input} -> TokenTransferView.prepare_token_transfer(tt, conn, decoded_input) end) end def render("token_transfer.json", %{token_transfer: token_transfer, conn: conn}) do {[decoded_transaction], _, _} = Transaction.decode_transactions([token_transfer.transaction], true, @api_true) - prepare_token_transfer(token_transfer, conn, decoded_transaction) + TokenTransferView.prepare_token_transfer(token_transfer, conn, decoded_transaction) end def render("transaction_actions.json", %{actions: actions}) do @@ -233,26 +232,6 @@ defmodule BlockScoutWeb.API.V2.TransactionView do Enum.reverse(result) end - def prepare_token_transfer(token_transfer, _conn, decoded_input) do - %{ - "tx_hash" => token_transfer.transaction_hash, - "from" => Helper.address_with_info(nil, token_transfer.from_address, token_transfer.from_address_hash, false), - "to" => Helper.address_with_info(nil, token_transfer.to_address, token_transfer.to_address_hash, false), - "total" => prepare_token_transfer_total(token_transfer), - "token" => TokenView.render("token.json", %{token: token_transfer.token}), - "type" => Chain.get_token_transfer_type(token_transfer), - "timestamp" => - if(match?(%NotLoaded{}, token_transfer.block), - do: block_timestamp(token_transfer.transaction), - else: block_timestamp(token_transfer.block) - ), - "method" => Transaction.method_name(token_transfer.transaction, decoded_input, true), - "block_hash" => to_string(token_transfer.block_hash), - "block_number" => to_string(token_transfer.block_number), - "log_index" => to_string(token_transfer.log_index) - } - end - def prepare_transaction_action(action) do %{ "protocol" => action.protocol, @@ -261,34 +240,6 @@ defmodule BlockScoutWeb.API.V2.TransactionView do } end - # credo:disable-for-next-line /Complexity/ - def prepare_token_transfer_total(token_transfer) do - case TokensHelper.token_transfer_amount_for_api(token_transfer) do - {:ok, :erc721_instance} -> - %{"token_id" => token_transfer.token_ids && List.first(token_transfer.token_ids)} - - {:ok, :erc1155_erc404_instance, value, decimals} -> - %{ - "token_id" => token_transfer.token_ids && List.first(token_transfer.token_ids), - "value" => value, - "decimals" => decimals - } - - {:ok, :erc1155_erc404_instance, values, token_ids, decimals} -> - %{ - "token_id" => token_ids && List.first(token_ids), - "value" => values && List.first(values), - "decimals" => decimals - } - - {:ok, value, decimals} -> - %{"value" => value, "decimals" => decimals} - - _ -> - nil - end - end - def prepare_internal_transaction(internal_transaction, block \\ nil) do %{ "error" => internal_transaction.error, @@ -701,10 +652,14 @@ defmodule BlockScoutWeb.API.V2.TransactionView do end end - defp block_timestamp(%Transaction{block_timestamp: block_ts}) when not is_nil(block_ts), do: block_ts - defp block_timestamp(%Transaction{block: %Block{} = block}), do: block.timestamp - defp block_timestamp(%Block{} = block), do: block.timestamp - defp block_timestamp(_), do: nil + @doc """ + Returns block's timestamp from Block/Transaction + """ + @spec block_timestamp(any()) :: :utc_datetime_usec | nil + def block_timestamp(%Transaction{block_timestamp: block_ts}) when not is_nil(block_ts), do: block_ts + def block_timestamp(%Transaction{block: %Block{} = block}), do: block.timestamp + def block_timestamp(%Block{} = block), do: block.timestamp + def block_timestamp(_), do: nil defp prepare_state_change(%StateChange{} = state_change) do coin_or_transfer = @@ -747,7 +702,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do change = if is_list(state_change.coin_or_token_transfers) and coin_or_transfer.token.type == "ERC-721" do for {direction, token_transfer} <- state_change.coin_or_token_transfers do - %{"total" => prepare_token_transfer_total(token_transfer), "direction" => direction} + %{"total" => TokenTransferView.prepare_token_transfer_total(token_transfer), "direction" => direction} end else state_change.balance_diff diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_transfer_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_transfer_controller_test.exs new file mode 100644 index 000000000000..a1a46b07ab3d --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_transfer_controller_test.exs @@ -0,0 +1,177 @@ +defmodule BlockScoutWeb.API.V2.TokenTransferControllerTest do + use BlockScoutWeb.ConnCase + + alias Explorer.Chain.{Address, TokenTransfer} + + describe "/token-transfers" do + test "empty list", %{conn: conn} do + request = get(conn, "/api/v2/token-transfers") + + assert response = json_response(request, 200) + assert response["items"] == [] + assert response["next_page_params"] == nil + end + + test "non empty list", %{conn: conn} do + tx = + :transaction + |> insert() + |> with_block() + + 1 |> insert_list(:token_transfer, transaction: tx) + + request = get(conn, "/api/v2/token-transfers") + + assert response = json_response(request, 200) + assert Enum.count(response["items"]) == 1 + assert response["next_page_params"] == nil + end + + test "filters by type", %{conn: conn} do + tx = + :transaction + |> insert() + |> with_block() + + token = insert(:token, type: "ERC-721") + + insert(:token_transfer, + transaction: tx, + token: token, + token_type: "ERC-721" + ) + + request = get(conn, "/api/v2/token-transfers?type=ERC-1155") + + assert response = json_response(request, 200) + assert Enum.count(response["items"]) == 0 + assert response["next_page_params"] == nil + end + + test "returns all transfers if filter is incorrect", %{conn: conn} do + tx = + :transaction + |> insert() + |> with_block() + + token = insert(:token, type: "ERC-100500") + + insert(:token_transfer, + transaction: tx, + token: token, + token_type: "ERC-721" + ) + + insert(:token_transfer, + transaction: tx, + token: token, + token_type: "ERC-20" + ) + + request = get(conn, "/api/v2/token-transfers?type=ERC-20") + + assert response = json_response(request, 200) + assert Enum.count(response["items"]) == 2 + assert response["next_page_params"] == nil + end + + test "token transfers with next_page_params", %{conn: conn} do + token_transfers = + for _i <- 0..50 do + tx = insert(:transaction) |> with_block() + + insert(:token_transfer, + transaction: tx, + block: tx.block, + block_number: tx.block_number + ) + end + + request = get(conn, "/api/v2/token-transfers") + assert response = json_response(request, 200) + + request_2nd_page = get(conn, "/api/v2/token-transfers", response["next_page_params"]) + assert response_2nd_page = json_response(request_2nd_page, 200) + + check_paginated_response(response, response_2nd_page, nil, token_transfers) + end + + test "flatten erc1155 batch token transfer", %{conn: conn} do + tx = insert(:transaction) |> with_block() + + insert(:token_transfer, + transaction: tx, + block: tx.block, + block_number: tx.block_number, + token_ids: [1, 2, 3], + amounts: [500, 600, 700], + token_type: "ERC-1155" + ) + + request = get(conn, "/api/v2/token-transfers") + assert response = json_response(request, 200) + assert Enum.count(response["items"]) == 3 + assert Enum.at(response["items"], 0)["total"] == %{"decimals" => "18", "value" => "700", "token_id" => "3"} + end + + test "paginates erc1155 batch token transfers", %{conn: conn} do + token_transfers = + for _i <- 0..50 do + tx = insert(:transaction) |> with_block() + + insert(:token_transfer, + transaction: tx, + block: tx.block, + block_number: tx.block_number, + token_ids: [1, 2], + amounts: [500, 600], + token_type: "ERC-1155" + ) + end + + request = get(conn, "/api/v2/token-transfers") + assert response = json_response(request, 200) + + request_2nd_page = get(conn, "/api/v2/token-transfers", response["next_page_params"]) + assert response_2nd_page = json_response(request_2nd_page, 200) + + request_3d_page = get(conn, "/api/v2/token-transfers", response_2nd_page["next_page_params"]) + assert response_3d_page = json_response(request_3d_page, 200) + + check_paginated_response(response, response_2nd_page, response_3d_page, token_transfers) + end + end + + defp compare_item(%TokenTransfer{} = token_transfer, json) do + assert Address.checksum(token_transfer.from_address_hash) == json["from"]["hash"] + assert Address.checksum(token_transfer.to_address_hash) == json["to"]["hash"] + assert to_string(token_transfer.transaction_hash) == json["tx_hash"] + assert token_transfer.transaction.block_timestamp == Timex.parse!(json["timestamp"], "{ISO:Extended:Z}") + assert json["method"] == nil + assert to_string(token_transfer.block_number) == json["block_number"] + assert to_string(token_transfer.log_index) == json["log_index"] + end + + defp check_paginated_response(first_page_resp, second_page_resp, third_page_resp, token_transfers) do + assert Enum.count(first_page_resp["items"]) == 50 + assert first_page_resp["next_page_params"] != nil + compare_item(Enum.at(token_transfers, 50), Enum.at(first_page_resp["items"], 0)) + + if is_nil(third_page_resp) do + compare_item(Enum.at(token_transfers, 1), Enum.at(first_page_resp["items"], 49)) + + assert Enum.count(second_page_resp["items"]) == 1 + assert second_page_resp["next_page_params"] == nil + compare_item(Enum.at(token_transfers, 0), Enum.at(second_page_resp["items"], 0)) + else + assert Enum.count(second_page_resp["items"]) == 50 + assert second_page_resp["next_page_params"] !== nil + + compare_item(Enum.at(token_transfers, 1), Enum.at(second_page_resp["items"], 49)) + + assert Enum.count(third_page_resp["items"]) == 2 + assert third_page_resp["next_page_params"] == nil + compare_item(Enum.at(token_transfers, 0), Enum.at(third_page_resp["items"], 0)) + end + end +end diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index 77cace2455c1..9ace1518d4b5 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -282,6 +282,49 @@ defmodule Explorer.Chain.TokenTransfer do end end + @spec fetch([paging_options | api?]) :: [] + def fetch(options) do + paging_options = Keyword.get(options, :paging_options, @default_paging_options) + token_type = Keyword.get(options, :token_type) + + case paging_options do + %PagingOptions{key: {0, 0}} -> + [] + + _ -> + preloads = + DenormalizationHelper.extend_transaction_preload([ + :transaction, + :token, + [from_address: [:names, :smart_contract, :proxy_implementations]], + [to_address: [:names, :smart_contract, :proxy_implementations]] + ]) + + only_consensus_transfers_query() + |> preload(^preloads) + |> order_by([tt], desc: tt.block_number, desc: tt.log_index) + |> maybe_filter_by_token_type(token_type) + |> page_token_transfer(paging_options) + |> limit(^paging_options.page_size) + |> Chain.select_repo(options).all() + end + end + + defp maybe_filter_by_token_type(query, token_types) do + if Enum.empty?(token_types) do + query + else + if DenormalizationHelper.tt_denormalization_finished?() do + query + |> where([tt], tt.token_type in ^token_types) + else + query + |> join(:inner, [tt], token in assoc(tt, :token), as: :token) + |> where([tt, block, token], token.type in ^token_types) + end + end + end + @spec count_token_transfers_from_token_hash(Hash.t()) :: non_neg_integer() def count_token_transfers_from_token_hash(token_address_hash) do query = From 3a8e9f4e667eab99f0329c0fb861d309a6cd66ff Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Fri, 4 Oct 2024 08:48:34 -0600 Subject: [PATCH 212/363] chore: ability to work with Blockscout code base within a VSCode devcontainer (#10838) Introduce dev container for streamlined Blockscout development - Add .devcontainer setup with pre-configured Elixir, Phoenix, and Node.js - Include PostgreSQL service in docker-compose.yml - Configure VS Code extensions and settings in devcontainer.json - Add bs script for common development tasks - Provide .blockscout_config.example for easy configuration - Enable GitHub Codespaces support for cloud-based development This change aims to reduce setup time, ensure consistency across environments, and allow developers to focus on core development tasks. --- .devcontainer/.blockscout_config.example | 67 +++++++ .devcontainer/Dockerfile | 50 +++++ .devcontainer/README.md | 199 +++++++++++++++++++ .devcontainer/bin/bs | 237 +++++++++++++++++++++++ .devcontainer/bin/chain-specific-checks | 17 ++ .devcontainer/bin/utils | 22 +++ .devcontainer/devcontainer.json | 47 +++++ .devcontainer/docker-compose.yml | 30 +++ .dockerignore | 1 + .gitignore | 1 + 10 files changed, 671 insertions(+) create mode 100644 .devcontainer/.blockscout_config.example create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/README.md create mode 100755 .devcontainer/bin/bs create mode 100644 .devcontainer/bin/chain-specific-checks create mode 100644 .devcontainer/bin/utils create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml diff --git a/.devcontainer/.blockscout_config.example b/.devcontainer/.blockscout_config.example new file mode 100644 index 000000000000..6730bb559ec5 --- /dev/null +++ b/.devcontainer/.blockscout_config.example @@ -0,0 +1,67 @@ +CHAIN_TYPE=ethereum + +ETHEREUM_JSONRPC_VARIANT=geth +ETHEREUM_JSONRPC_TRACE_URL="" + +API_RATE_LIMIT=100 +HEART_BEAT_TIMEOUT=30 +TXS_STATS_DAYS_TO_COMPILE_AT_INIT=2 +INDEXER_MEMORY_LIMIT=6 + +POOL_SIZE=50 +POOL_SIZE_API=50 +ACCOUNT_POOL_SIZE=10 + +INDEXER_DISABLE_EMPTY_BLOCKS_SANITIZER='true' +INDEXER_DISABLE_PENDING_TRANSACTIONS_FETCHER='true' +INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER='true' +INDEXER_DISABLE_BLOCK_REWARD_FETCHER='true' +INDEXER_DISABLE_ADDRESS_COIN_BALANCE_FETCHER='true' +INDEXER_DISABLE_CATALOGED_TOKEN_UPDATER_FETCHER='true' +ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES='true' +INDEXER_DISABLE_TOKEN_INSTANCE_RETRY_FETCHER='true' +INDEXER_DISABLE_TOKEN_INSTANCE_REALTIME_FETCHER='true' +INDEXER_DISABLE_TOKEN_INSTANCE_SANITIZE_FETCHER='true' +INDEXER_DISABLE_WITHDRAWALS_FETCHER='true' +INDEXER_DISABLE_TOKEN_INSTANCE_LEGACY_SANITIZE_FETCHER='true' + +INDEXER_CATCHUP_BLOCKS_BATCH_SIZE=5 +INDEXER_COIN_BALANCES_BATCH_SIZE=1 +TOKEN_ID_MIGRATION_BATCH_SIZE=1 +TOKEN_INSTANCE_OWNER_MIGRATION_BATCH_SIZE=1 +INDEXER_EMPTY_BLOCKS_SANITIZER_BATCH_SIZE=1 +INDEXER_BLOCK_REWARD_BATCH_SIZE=1 +INDEXER_RECEIPTS_BATCH_SIZE=10 +INDEXER_COIN_BALANCES_BATCH_SIZE=1 +INDEXER_TOKEN_BALANCES_BATCH_SIZE=1 + +INDEXER_CATCHUP_BLOCKS_CONCURRENCY=1 +TOKEN_INSTANCE_OWNER_MIGRATION_CONCURRENCY=1 +INDEXER_BLOCK_REWARD_CONCURRENCY=1 +INDEXER_RECEIPTS_CONCURRENCY=1 +INDEXER_COIN_BALANCES_CONCURRENCY=1 +INDEXER_TOKEN_CONCURRENCY=1 +INDEXER_TOKEN_BALANCES_CONCURRENCY=1 +INDEXER_TOKEN_INSTANCE_RETRY_CONCURRENCY=1 +INDEXER_TOKEN_INSTANCE_REALTIME_CONCURRENCY=1 +INDEXER_TOKEN_INSTANCE_SANITIZE_CONCURRENCY=1 +INDEXER_TOKEN_INSTANCE_LEGACY_SANITIZE_CONCURRENCY=1 +INDEXER_TOKEN_INSTANCE_RETRY_BATCH_SIZE=1 +INDEXER_TOKEN_INSTANCE_REALTIME_BATCH_SIZE=1 +INDEXER_TOKEN_INSTANCE_SANITIZE_BATCH_SIZE=1 +INDEXER_TOKEN_INSTANCE_LEGACY_SANITIZE_BATCH_SIZE=1 + +INDEXER_TOKEN_BALANCES_FETCHER_INIT_QUERY_LIMIT=2 +INDEXER_COIN_BALANCES_FETCHER_INIT_QUERY_LIMIT=2 + +DISABLE_EXCHANGE_RATES='true' +SOURCIFY_INTEGRATION_ENABLED='false' +EXCHANGE_RATES_COINGECKO_PLATFORM_ID='' +DISABLE_TOKEN_EXCHANGE_RATE='true' + +API_V2_ENABLED=true + +DISABLE_CATCHUP_INDEXER='false' +INDEXER_CATCHUP_BLOCKS_BATCH_SIZE=10 +INDEXER_CATCHUP_BLOCKS_CONCURRENCY=10 +ETHEREUM_JSONRPC_HTTP_URL="https://ethereum-sepolia-rpc.publicnode.com" diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000000..a030c1694001 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,50 @@ +# Since this is a copy of https://github.com/blockscout/devcontainer-elixir/blob/main/Dockerfile +# So after successful testing this file, the original one must be updated as well. +ARG VARIANT="1.17.3-erlang-27.1-debian-bullseye-20240926" +FROM hexpm/elixir:${VARIANT} + +# ARGs declared before FROM are not persisted beyond the FROM instruction. +# They must be redeclared here to be available in the rest of the Dockerfile. +ARG PHOENIX_VERSION="1.7.10" +ARG NODE_VERSION="18" + +# This Dockerfile adds a non-root user with sudo access. Update the “remoteUser” property in +# devcontainer.json to use it. More info: https://aka.ms/vscode-remote/containers/non-root-user. +ARG USERNAME=vscode +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +# Options for common package install script +ARG INSTALL_ZSH="true" +ARG UPGRADE_PACKAGES="true" +ARG COMMON_SCRIPT_SOURCE="https://raw.githubusercontent.com/microsoft/vscode-dev-containers/main/script-library/common-debian.sh" + +# Options for setup nodejs +ARG NODE_SCRIPT_SOURCE="https://raw.githubusercontent.com/microsoft/vscode-dev-containers/main/script-library/node-debian.sh" +ENV NVM_DIR=/usr/local/share/nvm +ENV NVM_SYMLINK_CURRENT=true +ENV PATH=${NVM_DIR}/current/bin:${PATH} + +# Install needed packages and setup non-root user. Use a separate RUN statement to add your own dependencies. +RUN apt-get update \ + && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends curl ca-certificates 2>&1 \ + && curl -sSL ${COMMON_SCRIPT_SOURCE} -o /tmp/common-setup.sh \ + && /bin/bash /tmp/common-setup.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" \ + # + # Install Node.js for use with web applications + && curl -sSL ${NODE_SCRIPT_SOURCE} -o /tmp/node-setup.sh \ + && /bin/bash /tmp/node-setup.sh "${NVM_DIR}" "${NODE_VERSION}" "${USERNAME}" \ + && npm install -g cspell@latest \ + # + # Install dependencies + && apt-get install -y build-essential inotify-tools \ + # + # Clean up + && apt-get autoremove -y \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* /tmp/common-setup.sh /tmp/node-setup.sh + +RUN su ${USERNAME} -c "mix local.hex --force \ + && mix local.rebar --force \ + && mix archive.install --force hex phx_new ${PHOENIX_VERSION}" diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 000000000000..c32c5add62fd --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,199 @@ +# Blockscout Backend Development with VSCode Devcontainers and GitHub Codespaces + +## Table of Contents +1. [Motivation](#motivation) +2. [Setting Up VSCode Devcontainer Locally](#setting-up-vscode-devcontainer-locally) +3. [Using GitHub Codespaces in the Browser](#using-github-codespaces-in-the-browser) +4. [Configuring Postgres DB Access](#configuring-postgres-db-access) +5. [Developing Blockscout Backend](#developing-blockscout-backend) +6. [Upgrading Elixir Version](#upgrading-elixir-version) +7. [Contributing](#contributing) + +## Motivation + +Setting up a local development environment for Blockscout can be time-consuming and error-prone. This devcontainer setup streamlines the process by providing a pre-configured environment with all necessary dependencies. It ensures consistency across development environments, reduces setup time, and allows developers to focus on coding rather than configuration. + +Key benefits include: +- Pre-configured environment with Elixir, Phoenix, and Node.js +- Integrated PostgreSQL database +- Essential VS Code extensions pre-installed +- Simplified database management +- Consistent development environment across team members + +## Setting Up VSCode Devcontainer Locally + +1. Clone the Blockscout repository: + ``` + git clone https://github.com/blockscout/blockscout.git + cd blockscout + ``` + +2. Open the project in VS Code: + ``` + code . + ``` + +3. Before re-opening in the container, you may find it useful to configure SSH authorization. To do this: + + a. Ensure you have SSH access to GitHub configured on your local machine. + + b. Open `.devcontainer/devcontainer.json`. + + c. Uncomment the `mounts` section: + ```json + "mounts": [ + "source=${localEnv:HOME}/.ssh/known_hosts,target=/home/vscode/.ssh/known_hosts,type=bind,consistency=cached", + "source=${localEnv:HOME}/.ssh/config,target=/home/vscode/.ssh/config,type=bind,consistency=cached", + "source=${localEnv:HOME}/.ssh/id_rsa,target=/home/vscode/.ssh/id_rsa,type=bind,consistency=cached" + ], + ``` + + d. Adjust the paths if your SSH keys are stored in a different location. + +4. When prompted, click "Reopen in Container". If not prompted, press `F1`, type "Remote-Containers: Reopen in Container", and press Enter. + +5. VS Code will build the devcontainer. This process includes: + - Pulling the base Docker image + - Installing specified VS Code extensions + - Setting up the PostgreSQL database + - Installing project dependencies + + This may take several minutes the first time. + +6. Once the devcontainer is built, you'll be working inside the containerized environment. + +7. If you modified the `devcontainer.json` file in step 3, you may want to execute `git update-index --assume-unchanged .devcontainer/devcontainer.json` in a terminal within your devcontainer to prevent the changes to `devcontainer.json` from appearing in `git status` and VS Code's Source Control. + +### Additional Setup for Cursor.ai Users + +If you're using Cursor.ai instead of VSCode, you may need to perform some additional setup steps. Please note that these changes will not persist after reloading the devcontainer, so you may need to repeat these steps each time you start a new session. + +1. **Git Configuration**: You may encounter issues when trying to perform Git operations from the terminal or the "Source Control" tab. To resolve this, set up your Git configuration inside the devcontainer: + + a. Open a terminal in your devcontainer. + b. Set your Git username: + ``` + git config --global user.name "Your Name" + ``` + c. Set your Git email: + ``` + git config --global user.email "your.email@example.com" + ``` + + Replace "Your Name" and "your.email@example.com" with your actual name and email associated with your GitHub account. + +2. **ElixirLS: Elixir support and debugger** (JakeBecker.elixir-ls): This extension may not be automatically installed in Cursor.ai, even though it's specified in the devcontainer configuration. To install it manually: + + a. Open the Extensions tab. + b. Search for "JakeBecker.elixir-ls". + c. Look for the extension "ElixirLS: Elixir support and debugger" by JakeBecker and click "Install". + +Remember, you may need to repeat these steps each time you start a new Cursor.ai session with the devcontainer. + +### Signing in to GitHub for Pull Request Extension + +1. In the devcontainer, click on the GitHub icon in the Primary sidebar. +2. Click on "Sign in to GitHub" and follow the prompts to authenticate. + +## Using GitHub Codespaces in the Browser + +To open the project in GitHub Codespaces: + +1. Navigate to the Blockscout repository on GitHub. +2. Switch to the branch you want to work on. +3. Click the "Code" button. +4. Instead of clicking "Create codespace on [branch]" (which would use the default machine type that may not be sufficient for this Elixir-based project), click on the three dots (...) next to it. +5. Select "New with options". +6. Choose the "4-core/16GB RAM" machine type for optimal performance. +7. Click "Create codespace". + +This will create a new Codespace with the specified resources, ensuring adequate performance for the Elixir-based project. + +Note: After the container opens, you may see an error about the inability to use "GitHub Copilot Chat". This Copilot functionality will not be accessible in the Codespace environment. + +## Configuring Postgres DB Access + +To configure access to the PostgreSQL database using the VS Code extension: + +1. Click on the PostgreSQL icon in the Primary sidebar. +2. Click "+" (Add Connection) in the PostgreSQL explorer. +3. Use the following details: + - Host: `db` + - User: `postgres` + - Password: `postgres` + - Port: `5432` + - Use an ssl connection: "Standard connection" + - Database: `blockscout` + - The display name: "" + +These credentials are derived from the `DATABASE_URL` in the `bs` script. + +## Developing Blockscout Backend + +### Configuration + +Before running the Blockscout server, you need to set up the configuration: + +1. Copy the `.devcontainer/.blockscout_config.example` file to `.devcontainer/.blockscout_config`. +2. Adjust the settings in `.devcontainer/.blockscout_config` as needed for your development environment. + +For a comprehensive list of environment variables that can be set in this configuration file, refer to the [Blockscout documentation](https://docs.blockscout.com/setup/env-variables). + +### Using the `bs` Script + +The `bs` script in `.devcontainer/bin/` helps orchestrate common development tasks. Here are some key commands: + +- Initialize the project: `bs --init` +- Initialize or re-initialize the database: `bs --db-init` (This will remove all data and tables from the DB and re-create the tables) +- Run the server: `bs` +- Run the server without syncing: `bs --no-sync` +- Recompile the project: `bs --recompile` (Use this when new dependencies arrive after a merge or when switching to another `CHAIN_TYPE`) +- Run various checks: `bs --spellcheck`, `bs --dialyzer`, `bs --credo`, `bs --format` + +For a full list of options, run `bs --help`. + +### Interacting with the Blockscout API + +For local devcontainer setups (not applicable to GitHub Codespaces), you can use API testing tools like Postman or Insomnia on your host machine to interact with the Blockscout API running in the container: + +1. Ensure the Blockscout server is running in the devcontainer. +2. In the API testing tool on your host machine, use `http://127.0.0.1:4000` as the base URL. +3. Example endpoint: `GET http://127.0.0.1:4000/api/v2/blocks` + +This allows testing API endpoints directly from your host machine while the server runs in the container. + +### Troubleshooting + +If you face issues with dependency compilation or dialyzer after container creation: + +1. Check for untracked files: `git ls-files --others` +2. Remove compilation artifacts or generated files if present. +3. For persistent issues, consider cleaning all untracked files (use with caution): + ``` + git clean -fdX + bs --recompile + ``` + +This ensures a clean compilation environment within the container. + +## Upgrading Elixir Version + +To upgrade the Elixir version: + +1. Open `.devcontainer/Dockerfile`. +2. Update the `VARIANT` argument with the desired Elixir version. +3. Rebuild the devcontainer. + +Note: Ensure that the version you choose is compatible with the project dependencies. + +After testing the new Elixir version, propagate the corresponding changes in the Dockerfile to the repo https://github.com/blockscout/devcontainer-elixir. Once a new release tag is published there and a new docker image `ghcr.io/blockscout/devcontainer-elixir` appears in the GitHub registry, modify the `docker-compose.yml` file in the `.devcontainer` directory to reflect the proper docker image tag. + +## Contributing + +When contributing changes that require additional checks for specific blockchain types: + +1. Open `.devcontainer/bin/chain-specific-checks`. +2. Add your checks under the appropriate `CHAIN_TYPE` case. +3. Ensure your checks exit with a non-zero code if unsuccessful. + +Remember to document any new checks or configuration options in this README. \ No newline at end of file diff --git a/.devcontainer/bin/bs b/.devcontainer/bin/bs new file mode 100755 index 000000000000..da9790bb4758 --- /dev/null +++ b/.devcontainer/bin/bs @@ -0,0 +1,237 @@ +#!/bin/bash + +# This script helps to orchestrate typical tasks when developing Blockscout + +source $(dirname $0)/utils + +# cd $(dirname $0)/../../ + +# Source and exaport environment variables related to the backend configuration +BLOCKSCOUT_CONFIG_FILE=".devcontainer/.blockscout_config" +if [ -f "./${BLOCKSCOUT_CONFIG_FILE}" ]; then + set -a # Automatically export all variables + source ./${BLOCKSCOUT_CONFIG_FILE} + set +a # Disable automatic export +else + echo "Warning: ${BLOCKSCOUT_CONFIG_FILE} file not found. Skipping configuration loading." +fi + +if [ "${DATABASE_URL}" == "" ]; then + export DATABASE_URL="postgresql://postgres:postgres@db:5432/blockscout" +fi + +# Initialize variables +INIT=false +NO_SYNC=false +DB_INIT=false +RECOMPILE=false +SPELLCHECK=false +DIALYZER=false +CREDO=false +FORMAT=false +DOCS=false +HELP=false + +# Parse command line arguments +for arg in "$@" +do + case $arg in + --help) + HELP=true + shift # Remove --help from processing + ;; + --init) + INIT=true + shift # Remove --init from processing + ;; + --no-sync) + NO_SYNC=true + shift # Remove --no-sync from processing + ;; + --db-init) + DB_INIT=true + shift # Remove --db-init from processing + ;; + --recompile) + RECOMPILE=true + shift # Remove --recompile from processing + ;; + --spellcheck) + SPELLCHECK=true + shift # Remove --spellcheck from processing + ;; + --dialyzer) + DIALYZER=true + shift # Remove --dialyzer from processing + ;; + --credo) + CREDO=true + shift # Remove --credo from processing + ;; + --format) + FORMAT=true + shift # Remove --format from processing + ;; + --docs) + DOCS=true + shift # Remove --docs from processing + ;; + esac +done + +# Define the help function +show_help() { + echo "Usage: bs [OPTION]" + echo "Orchestrate typical tasks when developing Blockscout backend server" + echo + echo "Options:" + echo " --help Show this help message and exit" + echo " --init Initialize the project directory" + echo " --format Run code formatter" + echo " --spellcheck Run spellcheck" + echo " --dialyzer Run dialyzer" + echo " --credo Run credo" + echo " --docs Generate documentation" + echo " --recompile Re-fetch dependencies and recompile" + echo " --db-init (Re)initialize the database" + echo " --no-sync Run the server with disabled indexer, so only the API is available" + echo + echo "If no option is provided, the script will run the backend server." +} + +# If --help argument is passed, show help and exit +if [ "$HELP" = true ]; then + show_help + exit 0 +fi + +# Define the project directory initialization subroutine +initialize_project() { + if [ ! -d "apps/block_scout_web/priv/cert" ]; then + mix local.rebar --force + mix deps.compile + mix compile + + # cd apps/block_scout_web/assets + # npm install && node_modules/webpack/bin/webpack.js --mode production + # cd - + # cd apps/explorer + # npm install + # cd - + + cd apps/block_scout_web + mix phx.gen.cert blockscout blockscout.local + cd - + else + echo "Looks like the project directory is already initialized" + fi +} + +# Define the initialization subroutine +initialize_db() { + echo "Initializing database. Step 1 of 2: Dropping database" + mix ecto.drop > /dev/null 2>&1 + echo "Initializing database. Step 2 of 2: Creating database" + mix do ecto.create, ecto.migrate | grep Runn +} + +# Define the recompile subroutine +recompile() { + mix deps.clean block_scout_web + mix deps.clean explorer + mix deps.clean indexer + mix deps.get + mix deps.compile --force +} + +# Define the spellcheck subroutine +spellcheck() { + cspell --config cspell.json "**/*.ex*" "**/*.eex" "**/*.js" --gitignore | less +} + +# Define the dialyzer subroutine +dialyzer() { + mix dialyzer +} + +# Define the credo subroutine +credo() { + mix credo +} + +# Define the format subroutine +format() { + mix format +} + +# Define the generate_docs subroutine +generate_docs() { + mix docs +} + +# If --init argument is passed, run the project dir initialization subroutine and exit +if [ "$INIT" = true ]; then + initialize_project + exit 0 +fi + +# If --db-init argument is passed, run the database initialization subroutine and exit +if [ "$DB_INIT" = true ]; then + initialize_db + exit 0 +fi + +# If --recompile argument is passed, run the recompile subroutine and exit +if [ "$RECOMPILE" = true ]; then + recompile + exit 0 +fi + +# If --spellcheck argument is passed, run the spellcheck subroutine and exit +if [ "$SPELLCHECK" = true ]; then + spellcheck + exit 0 +fi + +# If --dialyzer argument is passed, run the dialyzer subroutine and exit +if [ "$DIALYZER" = true ]; then + dialyzer + exit 0 +fi + +# If --credo argument is passed, run the credo subroutine and exit +if [ "$CREDO" = true ]; then + credo + exit 0 +fi + +# If --format argument is passed, run the format subroutine and exit +if [ "$FORMAT" = true ]; then + format + exit 0 +fi + +# If --doc argument is passed, run the format subroutine and exit +if [ "$DOCS" = true ]; then + generate_docs + exit 0 +fi + +if [ "${ETHEREUM_JSONRPC_HTTP_URL}" != "" ]; then + check_server_availability ${ETHEREUM_JSONRPC_HTTP_URL} + check_server_accessibility ${ETHEREUM_JSONRPC_HTTP_URL} +fi + +if [ "${CHAIN_TYPE}" != "" -o "${CHAIN_TYPE}" != "ethereum" -o "${CHAIN_TYPE}" != "default" ]; then + source $(dirname $0)/chain-specific-checks +fi + +if [ ! -d "apps/block_scout_web/priv/cert" ]; then + echo "Project directory is not initialized" + echo "Run 'bs --init' to initialize the project directory" + exit 1 +fi + +export DISABLE_INDEXER=${NO_SYNC} + +mix phx.server diff --git a/.devcontainer/bin/chain-specific-checks b/.devcontainer/bin/chain-specific-checks new file mode 100644 index 000000000000..c6129f26b09a --- /dev/null +++ b/.devcontainer/bin/chain-specific-checks @@ -0,0 +1,17 @@ +# The script is sourced from the main script, so the unsuccessful check must exit +# with non-zero code to terminate the main script. + +source $(dirname $0)/utils + +# Run the appropriate checks based on CHAIN_TYPE +case "${CHAIN_TYPE}" in + "arbitrum") + echo "Arbitrum sepcific checks" + # if the check is not successful, exit with code 1 + check_server_availability ${INDEXER_ARBITRUM_L1_RPC} + check_server_accessibility ${INDEXER_ARBITRUM_L1_RPC} + ;; + *) + echo "No special checks for CHAIN_TYPE: $CHAIN_TYPE" + ;; +esac diff --git a/.devcontainer/bin/utils b/.devcontainer/bin/utils new file mode 100644 index 000000000000..bd86034b8e5e --- /dev/null +++ b/.devcontainer/bin/utils @@ -0,0 +1,22 @@ +# Function to check server availability +check_server_availability() { + local url=$1 + + curl --connect-timeout 3 --silent ${url} 1>/dev/null + if [ $? -ne 0 ]; then + echo "VPN must be enabled to connect to ${url}" + exit 1 + fi +} + +# Function to check server accessibility with a POST request +check_server_accessibility() { + local url=$1 + local payload='[{"id":0,"params":["latest",false],"method":"eth_getBlockByNumber","jsonrpc":"2.0"}]' + + http_code=$(curl -s -o /dev/null -w "%{http_code}" -X POST ${url} -H "Content-Type: application/json" -d "${payload}") + if [ "$http_code" -ne 200 ]; then + echo "VPN must be enabled to access ${url} (HTTP status code: ${http_code})" + exit 1 + fi +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000000..f89237d1ae0f --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,47 @@ +{ + "name": "Blockscout Elixir", + "dockerComposeFile": "docker-compose.yml", + "service": "elixir", + "workspaceFolder": "/workspace", + "postCreateCommand": { + "safe-directory": "git config --global --add safe.directory ${containerWorkspaceFolder}", + "deps": "mix deps.get" + }, + "remoteEnv": { + "PATH": "${containerEnv:PATH}:${containerWorkspaceFolder}/.devcontainer/bin" + }, + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "JakeBecker.elixir-ls", + "ckolkman.vscode-postgres", + "GitHub.copilot", + "GitHub.copilot-chat", + "GitHub.vscode-pull-request-github" + ] + } + }, + "features": { + "ghcr.io/stuartleeks/dev-container-features/shell-history:0": {} + }, + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // This can be used to network with other containers or with the host. + "forwardPorts": [ + 4000, + 4001, + 5432 + ], + // Uncomment and adjust the private key path to the one you use to authenticate on GitHub + // if you want to have ability to push to GitHub from the container. + // "mounts": [ + // "source=${localEnv:HOME}/.ssh/known_hosts,target=/home/vscode/.ssh/known_hosts,type=bind,consistency=cached", + // "source=${localEnv:HOME}/.ssh/config,target=/home/vscode/.ssh/config,type=bind,consistency=cached", + // // Make sure that the private key can be used to authenticate on GitHub + // "source=${localEnv:HOME}/.ssh/id_rsa,target=/home/vscode/.ssh/id_rsa,type=bind,consistency=cached" + // ], + // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode" +} \ No newline at end of file diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 000000000000..280ceeafd3ef --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,30 @@ +services: + elixir: + image: ghcr.io/blockscout/devcontainer-elixir:1.17.3-erlang-27.1 + + # Uncomment next lines to use test Dockerfile with new Elixir version + # build: + # context: . + # dockerfile: Dockerfile + + volumes: + - ..:/workspace:cached + # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function. + network_mode: service:db + + # Overrides default command so things don't shut down after the process ends. + command: sleep infinity + + db: + image: postgres:latest + command: postgres -c 'max_connections=250' + restart: unless-stopped + volumes: + - postgres-data:/var/lib/postgresql/data + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: app + +volumes: + postgres-data: diff --git a/.dockerignore b/.dockerignore index 8ff38c4786c6..86f0f1d690d0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -10,3 +10,4 @@ test erl_crash.dump logs apps/*/test +.devcontainer \ No newline at end of file diff --git a/.gitignore b/.gitignore index bfd06c0a3aac..db2acc80dd2e 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,4 @@ dump.rdb *.env.example *.env.local *.env.staging +.devcontainer/.blockscout_config \ No newline at end of file From 1866353ebead39c398cc8b3cc47f2fac6f4cfe84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:03:08 +0300 Subject: [PATCH 213/363] chore(deps): bump comeonin from 5.4.0 to 5.5.0 (#10888) Bumps [comeonin](https://github.com/riverrun/comeonin) from 5.4.0 to 5.5.0. - [Changelog](https://github.com/riverrun/comeonin/blob/master/CHANGELOG.md) - [Commits](https://github.com/riverrun/comeonin/compare/v5.4.0...v5.5.0) --- updated-dependencies: - dependency-name: comeonin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index b486297fc463..ffd8626aa837 100644 --- a/mix.lock +++ b/mix.lock @@ -22,7 +22,7 @@ "cloak_ecto": {:hex, :cloak_ecto, "1.3.0", "0de127c857d7452ba3c3367f53fb814b0410ff9c680a8d20fbe8b9a3c57a1118", [:mix], [{:cloak, "~> 1.1.1", [hex: :cloak, repo: "hexpm", optional: false]}, {:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm", "314beb0c123b8a800418ca1d51065b27ba3b15f085977e65c0f7b2adab2de1cc"}, "coerce": {:hex, :coerce, "1.0.1", "211c27386315dc2894ac11bc1f413a0e38505d808153367bd5c6e75a4003d096", [:mix], [], "hexpm", "b44a691700f7a1a15b4b7e2ff1fa30bebd669929ac8aa43cffe9e2f8bf051cf1"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, - "comeonin": {:hex, :comeonin, "5.4.0", "246a56ca3f41d404380fc6465650ddaa532c7f98be4bda1b4656b3a37cc13abe", [:mix], [], "hexpm", "796393a9e50d01999d56b7b8420ab0481a7538d0caf80919da493b4a6e51faf1"}, + "comeonin": {:hex, :comeonin, "5.5.0", "364d00df52545c44a139bad919d7eacb55abf39e86565878e17cebb787977368", [:mix], [], "hexpm", "6287fc3ba0aad34883cbe3f7949fc1d1e738e5ccdce77165bc99490aa69f47fb"}, "con_cache": {:hex, :con_cache, "1.1.1", "9f47a68dfef5ac3bbff8ce2c499869dbc5ba889dadde6ac4aff8eb78ddaf6d82", [:mix], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1def4d1bec296564c75b5bbc60a19f2b5649d81bfa345a2febcc6ae380e8ae15"}, "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, "cors_plug": {:hex, :cors_plug, "3.0.3", "7c3ac52b39624bc616db2e937c282f3f623f25f8d550068b6710e58d04a0e330", [:mix], [{:plug, "~> 1.13", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3f2d759e8c272ed3835fab2ef11b46bddab8c1ab9528167bd463b6452edf830d"}, From 93a0657430db1db4349834f4d56f1f0731b638ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:03:23 +0300 Subject: [PATCH 214/363] chore(deps): bump ecto_sql from 3.12.0 to 3.12.1 (#10887) Bumps [ecto_sql](https://github.com/elixir-ecto/ecto_sql) from 3.12.0 to 3.12.1. - [Changelog](https://github.com/elixir-ecto/ecto_sql/blob/master/CHANGELOG.md) - [Commits](https://github.com/elixir-ecto/ecto_sql/compare/v3.12.0...v3.12.1) --- updated-dependencies: - dependency-name: ecto_sql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.lock b/mix.lock index ffd8626aa837..c28fe98eb485 100644 --- a/mix.lock +++ b/mix.lock @@ -39,8 +39,8 @@ "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, "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.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, - "ecto": {:hex, :ecto, "3.12.3", "1a9111560731f6c3606924c81c870a68a34c819f6d4f03822f370ea31a582208", [:mix], [{:decimal, "~> 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", "9efd91506ae722f95e48dc49e70d0cb632ede3b7a23896252a60a14ac6d59165"}, - "ecto_sql": {:hex, :ecto_sql, "3.12.0", "73cea17edfa54bde76ee8561b30d29ea08f630959685006d9c6e7d1e59113b7d", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 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", "dc9e4d206f274f3947e96142a8fdc5f69a2a6a9abb4649ef5c882323b6d512f0"}, + "ecto": {:hex, :ecto, "3.12.4", "267c94d9f2969e6acc4dd5e3e3af5b05cdae89a4d549925f3008b2b7eb0b93c3", [:mix], [{:decimal, "~> 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", "ef04e4101688a67d061e1b10d7bc1fbf00d1d13c17eef08b71d070ff9188f747"}, + "ecto_sql": {:hex, :ecto_sql, "3.12.1", "c0d0d60e85d9ff4631f12bafa454bc392ce8b9ec83531a412c12a0d415a3a4d0", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 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", "aff5b958a899762c5f09028c847569f7dfb9cc9d63bdb8133bff8a5546de6bf5"}, "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_abi": {:hex, :ex_abi, "0.8.1", "451fa960ddc4dfbb350e13509f3dd64ca586b8484a77aad9f7d778161b5eab79", [:mix], [{:ex_keccak, "~> 0.7.5", [hex: :ex_keccak, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "abcf53d556c2948e5c1241340afd4a72cdf93ab6daef16fc200c16ca1183cdca"}, From 6a22f3afc3c85b0e6e7c2adb3a5aad1dd06a2a8c Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Wed, 9 Oct 2024 15:46:06 +0300 Subject: [PATCH 215/363] fix: do not include unrelated token transfers in `tokenTransferTxs` (#10889) * fix: do not include unrelated token transfers in `tokenTransferTxs` * fix: increase `API_GRAPHQL_MAX_COMPLEXITY` for celo instances * chore: add spec and doc --- .../publish-docker-image-for-celo.yml | 2 +- .../graphql/celo/resolvers/token_transfer.ex | 9 ++++---- .../celo/resolvers/token_transfer_tx.ex | 7 ------ apps/explorer/lib/explorer/graphql/celo.ex | 23 ++++++++++++++++++- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/.github/workflows/publish-docker-image-for-celo.yml b/.github/workflows/publish-docker-image-for-celo.yml index 6843e949f22a..5aa28a8903d3 100644 --- a/.github/workflows/publish-docker-image-for-celo.yml +++ b/.github/workflows/publish-docker-image-for-celo.yml @@ -36,7 +36,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - API_GRAPHQL_MAX_COMPLEXITY=6650 + API_GRAPHQL_MAX_COMPLEXITY=10400 CACHE_EXCHANGE_RATES_PERIOD= API_V1_READ_METHODS_DISABLED=false DISABLE_WEBAPP=false diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer.ex b/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer.ex index 31e4a252c3f5..bf21bc8b5c5d 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer.ex @@ -7,11 +7,10 @@ defmodule BlockScoutWeb.GraphQL.Celo.Resolvers.TokenTransfer do alias Explorer.GraphQL.Celo, as: GraphQL alias Explorer.Repo - def get_by(_, args, _) do - connection_args = Map.take(args, [:after, :before, :first, :last]) - - GraphQL.token_tx_transfers_query() - |> Connection.from_query(&Repo.all/1, connection_args, options(args)) + def get_by(%{transaction_hash: hash}, args, _) do + hash + |> GraphQL.token_tx_transfers_query_by_txhash() + |> Connection.from_query(&Repo.all/1, args, options(args)) end defp options(%{before: _}), do: [] diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer_tx.ex b/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer_tx.ex index 557b30dde0a1..f038c3b92c2a 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer_tx.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer_tx.ex @@ -19,13 +19,6 @@ defmodule BlockScoutWeb.GraphQL.Celo.Resolvers.TokenTransferTx do |> Connection.from_query(&Repo.all/1, connection_args, options(args)) end - def get_by(_, args, _) do - connection_args = Map.take(args, [:after, :before, :first, :last]) - - GraphQL.token_tx_transfers_query() - |> Connection.from_query(&Repo.all/1, connection_args, options(args)) - end - defp options(%{before: _}), do: [] defp options(%{count: count}), do: [count: count] diff --git a/apps/explorer/lib/explorer/graphql/celo.ex b/apps/explorer/lib/explorer/graphql/celo.ex index 3b92d12082a3..4705274ca0fe 100644 --- a/apps/explorer/lib/explorer/graphql/celo.ex +++ b/apps/explorer/lib/explorer/graphql/celo.ex @@ -76,7 +76,8 @@ defmodule Explorer.GraphQL.Celo do ) query - |> order_by([transaction: t], + |> order_by( + [transaction: t], desc: t.block_number, desc: t.hash, asc: t.nonce, @@ -85,6 +86,26 @@ defmodule Explorer.GraphQL.Celo do ) end + @doc """ + Constructs a query to fetch token transfers within a given transaction. + + ## Parameters + - tx_hash: the hash of the transaction + + ## Returns + - Ecto query + """ + @spec token_tx_transfers_query_by_txhash(Hash.Full.t()) :: Ecto.Query.t() + def token_tx_transfers_query_by_txhash(tx_hash) do + query = token_tx_transfers_query() + + from( + t in subquery(query), + where: t.transaction_hash == ^tx_hash, + order_by: [t.log_index] + ) + end + @doc """ Constructs a query for token transfers filtered by a specific address. """ From f9b352189fa3f509188577e5ea1f80073ef5eb30 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 10 Oct 2024 10:53:24 +0300 Subject: [PATCH 216/363] chore: Add shrink int txs docker image build for Celo chain type (#10894) --- .../publish-docker-image-for-celo.yml | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/.github/workflows/publish-docker-image-for-celo.yml b/.github/workflows/publish-docker-image-for-celo.yml index 5aa28a8903d3..901560cb9bce 100644 --- a/.github/workflows/publish-docker-image-for-celo.yml +++ b/.github/workflows/publish-docker-image-for-celo.yml @@ -47,3 +47,73 @@ jobs: BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + + - name: Build and push Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true From f597adfea03deb4ca6465945d7db5a26efbdd413 Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Thu, 10 Oct 2024 16:22:00 +0300 Subject: [PATCH 217/363] fix: add missing build arg to celo workflow (#10895) --- .github/workflows/publish-docker-image-for-celo.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/publish-docker-image-for-celo.yml b/.github/workflows/publish-docker-image-for-celo.yml index 901560cb9bce..18ddcbc4f064 100644 --- a/.github/workflows/publish-docker-image-for-celo.yml +++ b/.github/workflows/publish-docker-image-for-celo.yml @@ -12,6 +12,7 @@ jobs: env: RELEASE_VERSION: 6.8.1 DOCKER_CHAIN_NAME: celo + API_GRAPHQL_MAX_COMPLEXITY: 10400 steps: - uses: actions/checkout@v4 - name: Setup repo @@ -60,6 +61,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | + API_GRAPHQL_MAX_COMPLEXITY=${{ env.API_GRAPHQL_MAX_COMPLEXITY }} CACHE_EXCHANGE_RATES_PERIOD= API_V1_READ_METHODS_DISABLED=false DISABLE_WEBAPP=false @@ -84,6 +86,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | + API_GRAPHQL_MAX_COMPLEXITY=${{ env.API_GRAPHQL_MAX_COMPLEXITY }} CACHE_EXCHANGE_RATES_PERIOD= DISABLE_WEBAPP=true DISABLE_API=true @@ -107,6 +110,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | + API_GRAPHQL_MAX_COMPLEXITY=${{ env.API_GRAPHQL_MAX_COMPLEXITY }} CACHE_EXCHANGE_RATES_PERIOD= DISABLE_WEBAPP=true DISABLE_INDEXER=true From 84247986b600e001c0220ddfeac1bcb25057cc72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 11:14:52 +0300 Subject: [PATCH 218/363] chore(deps): bump bcrypt_elixir from 3.1.0 to 3.2.0 (#10886) Bumps [bcrypt_elixir](https://github.com/riverrun/bcrypt_elixir) from 3.1.0 to 3.2.0. - [Changelog](https://github.com/riverrun/bcrypt_elixir/blob/master/CHANGELOG.md) - [Commits](https://github.com/riverrun/bcrypt_elixir/compare/v3.1.0...v3.2.0) --- updated-dependencies: - dependency-name: bcrypt_elixir dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.lock b/mix.lock index c28fe98eb485..312670caf86b 100644 --- a/mix.lock +++ b/mix.lock @@ -5,7 +5,7 @@ "absinthe_relay": {:hex, :absinthe_relay, "1.5.2", "cfb8aed70f4e4c7718d3f1c212332d2ea728f17c7fc0f68f1e461f0f5f0c4b9a", [:mix], [{:absinthe, "~> 1.5.0 or ~> 1.6.0 or ~> 1.7.0", [hex: :absinthe, repo: "hexpm", optional: false]}, {:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "0587ee913afa31512e1457a5064ee88427f8fe7bcfbeeecd41c71d9cff0b62b6"}, "accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm", "11b18c220bcc2eab63b5470c038ef10eb6783bcb1fcdb11aa4137defa5ac1bb8"}, "bamboo": {:hex, :bamboo, "2.3.1", "85029339f01c3dd59071cfd2b7b18e826aa7fc172cc324d75603bb99d95b6544", [:mix], [{:hackney, ">= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.4 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "895b2993ed195b2b0fa79c0d5a1d36aa529e817b6df257e4a10745459048d505"}, - "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"}, + "bcrypt_elixir": {:hex, :bcrypt_elixir, "3.2.0", "feab711974beba4cb348147170346fe097eea2e840db4e012a145e180ed4ab75", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "563e92a6c77d667b19c5f4ba17ab6d440a085696bdf4c68b9b0f5b30bc5422b8"}, "benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"}, "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"}, "blake2": {:hex, :blake2, "1.0.4", "8263c69a191142922bc2510f1ffc0de0ae96e8c3bd5e2ad3fac7e87aed94c8b1", [:mix], [], "hexpm", "e9f4120d163ba14d86304195e50745fa18483e6ad2be94c864ae449bbdd6a189"}, @@ -14,7 +14,7 @@ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "bureaucrat": {:hex, :bureaucrat, "0.2.10", "b0de157dad540e40007b663b683f716ced21f85ff0591093aadb209ad0d967e1", [: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 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "bc7e5162b911c29c8ebefee87a2c16fbf13821a58f448a8fd024eb6c17fae15c"}, "bypass": {:hex, :bypass, "2.1.0", "909782781bf8e20ee86a9cabde36b259d44af8b9f38756173e8f5e2e1fabb9b1", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "d9b5df8fa5b7a6efa08384e9bbecfe4ce61c77d28a4282f79e02f1ef78d96b80"}, - "castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"}, + "castore": {:hex, :castore, "1.0.9", "5cc77474afadf02c7c017823f460a17daa7908e991b0cc917febc90e466a375c", [:mix], [], "hexpm", "5ea956504f1ba6f2b4eb707061d8e17870de2bee95fb59d512872c2ef06925e7"}, "cbor": {:hex, :cbor, "1.0.1", "39511158e8ea5a57c1fcb9639aaa7efde67129678fee49ebbda780f6f24959b0", [:mix], [], "hexpm", "5431acbe7a7908f17f6a9cd43311002836a34a8ab01876918d8cfb709cd8b6a2"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, "cldr_utils": {:hex, :cldr_utils, "2.28.2", "f500667164a9043369071e4f9dcef31f88b8589b2e2c07a1eb9f9fa53cb1dce9", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "c506eb1a170ba7cdca59b304ba02a56795ed119856662f6b1a420af80ec42551"}, @@ -41,7 +41,7 @@ "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "ecto": {:hex, :ecto, "3.12.4", "267c94d9f2969e6acc4dd5e3e3af5b05cdae89a4d549925f3008b2b7eb0b93c3", [:mix], [{:decimal, "~> 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", "ef04e4101688a67d061e1b10d7bc1fbf00d1d13c17eef08b71d070ff9188f747"}, "ecto_sql": {:hex, :ecto_sql, "3.12.1", "c0d0d60e85d9ff4631f12bafa454bc392ce8b9ec83531a412c12a0d415a3a4d0", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 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", "aff5b958a899762c5f09028c847569f7dfb9cc9d63bdb8133bff8a5546de6bf5"}, - "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, + "elixir_make": {:hex, :elixir_make, "0.8.4", "4960a03ce79081dee8fe119d80ad372c4e7badb84c493cc75983f9d3bc8bde0f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "6e7f1d619b5f61dfabd0a20aa268e575572b542ac31723293a4c1a567d5ef040"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_abi": {:hex, :ex_abi, "0.8.1", "451fa960ddc4dfbb350e13509f3dd64ca586b8484a77aad9f7d778161b5eab79", [:mix], [{:ex_keccak, "~> 0.7.5", [hex: :ex_keccak, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "abcf53d556c2948e5c1241340afd4a72cdf93ab6daef16fc200c16ca1183cdca"}, "ex_cldr": {:hex, :ex_cldr, "2.40.1", "c1fcb0cd9d2a70d28f4540a99f32127e7f1813e0db109d65ab29dea5337ae266", [:mix], [{:cldr_utils, "~> 2.28", [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", "509810702e8e81991851d9426ffe6b34b48b7b9baa12922e7b3fb8f6368606f3"}, From 7573eb3ae645a0aed645b9fef631a9326fafd97f Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Mon, 14 Oct 2024 17:07:53 +0300 Subject: [PATCH 219/363] fix: add missing block timestamp in election rewards for address response (#10907) --- .../lib/block_scout_web/views/api/v2/celo_view.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex index 99241c6d4efd..b013d67009f2 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex @@ -221,11 +221,12 @@ defmodule BlockScoutWeb.API.V2.CeloView do } end - defp prepare_election_reward(%ElectionReward{token: %Token{}} = reward) do + defp prepare_election_reward(%ElectionReward{token: %Token{}, block: %Block{}} = reward) do %{ amount: reward.amount, block_number: reward.block.number, block_hash: reward.block_hash, + block_timestamp: reward.block.timestamp, epoch_number: reward.block.number |> CeloHelper.block_number_to_epoch_number(), account: Helper.address_with_info( From e702e935d5f554d078a0b426046c175c4869b567 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 14 Oct 2024 17:42:35 +0300 Subject: [PATCH 220/363] chore: remove unused fetch_and_lock_by_hashes/1 public function --- .../lib/explorer/chain/pending_block_operation.ex | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/pending_block_operation.ex b/apps/explorer/lib/explorer/chain/pending_block_operation.ex index 0852e317b8d3..9a1b5b48941c 100644 --- a/apps/explorer/lib/explorer/chain/pending_block_operation.ex +++ b/apps/explorer/lib/explorer/chain/pending_block_operation.ex @@ -35,19 +35,6 @@ defmodule Explorer.Chain.PendingBlockOperation do |> unique_constraint(:block_hash, name: :pending_block_operations_pkey) end - @doc """ - Returns all pending block operations with the `block_hash` in the given list, - using "FOR UPDATE" to grab ShareLocks in order (see docs: sharelocks.md) - """ - def fetch_and_lock_by_hashes(hashes) when is_list(hashes) do - from( - pending_ops in __MODULE__, - where: pending_ops.block_hash in ^hashes, - order_by: [asc: pending_ops.block_hash], - lock: "FOR UPDATE" - ) - end - def block_hashes do from( pending_ops in __MODULE__, From 6042b40b077b33d19eed3eee136a08d9ff5a57cb Mon Sep 17 00:00:00 2001 From: "siegi.eth" <12938502+sieg-i@users.noreply.github.com> Date: Mon, 14 Oct 2024 17:57:36 +0200 Subject: [PATCH 221/363] Fix env. variables link in README.md (#10898) Co-authored-by: Siegi Skalla --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3216456d710d..8e2059a1ef23 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ See the [project documentation](https://docs.blockscout.com/) for instructions: - [Kubernetes deployment](https://docs.blockscout.com/for-developers/deployment/kubernetes-deployment) - [Manual deployment (backend + old UI)](https://docs.blockscout.com/for-developers/deployment/manual-old-ui) - [Ansible deployment](https://docs.blockscout.com/for-developers/ansible-deployment) -- [ENV variables](https://docs.blockscout.com/for-developers/information-and-settings/env-variables) +- [ENV variables](https://docs.blockscout.com/setup/env-variables) - [Configuration options](https://docs.blockscout.com/for-developers/configuration-options) ## Acknowledgements From 7d763c0cbc395fb605bc9bdefa602209af120fba Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Wed, 16 Oct 2024 12:32:33 +0400 Subject: [PATCH 222/363] chore: Reverse internal transactions fetching order (#10912) * chore: Reverse internal transactions fetching order * Add env var for internal transactions fetching order --- apps/explorer/lib/explorer/chain.ex | 4 +++- config/runtime.exs | 4 +++- docker-compose/envs/common-blockscout.env | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 216d42c70075..346b2dd4fa9d 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -1899,12 +1899,14 @@ defmodule Explorer.Chain do when accumulator: term() def stream_blocks_with_unfetched_internal_transactions(initial, reducer, limited? \\ false) when is_function(reducer, 2) do + direction = Application.get_env(:indexer, :internal_transactions_fetch_order) + query = from( po in PendingBlockOperation, where: not is_nil(po.block_number), select: po.block_number, - order_by: [desc: po.block_number] + order_by: [{^direction, po.block_number}] ) query diff --git a/config/runtime.exs b/config/runtime.exs index 3bf15bdd5fc5..e0be45126204 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -681,7 +681,9 @@ config :indexer, 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), - graceful_shutdown_period: ConfigHelper.parse_time_env_var("INDEXER_GRACEFUL_SHUTDOWN_PERIOD", "5m") + graceful_shutdown_period: ConfigHelper.parse_time_env_var("INDEXER_GRACEFUL_SHUTDOWN_PERIOD", "5m"), + internal_transactions_fetch_order: + ConfigHelper.parse_catalog_value("INDEXER_INTERNAL_TRANSACTIONS_FETCH_ORDER", ["asc", "desc"], true, "asc") config :indexer, :ipfs, gateway_url: System.get_env("IPFS_GATEWAY_URL", "https://ipfs.io/ipfs"), diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 7c82e9e21c32..dec8df0744af 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -279,6 +279,7 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_TOKEN_BALANCES_FETCHER_INIT_QUERY_LIMIT= # INDEXER_COIN_BALANCES_FETCHER_INIT_QUERY_LIMIT= # INDEXER_GRACEFUL_SHUTDOWN_PERIOD= +# INDEXER_INTERNAL_TRANSACTIONS_FETCH_ORDER= # WITHDRAWALS_FIRST_BLOCK= # INDEXER_OPTIMISM_L1_RPC= # INDEXER_OPTIMISM_L1_SYSTEM_CONFIG_CONTRACT= From f71a4cc4c649a7436332fab7a496ea3d9ad5e500 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 16 Oct 2024 13:35:22 +0300 Subject: [PATCH 223/363] chore: Set user agent to metadata requests (#10834) * chore: Set user agent to metadata requests * Add backend version to User-agent --- apps/explorer/lib/explorer/token/metadata_retriever.ex | 9 +++++---- .../test/explorer/token/metadata_retriever_test.exs | 2 +- bin/version_bump.sh | 3 +++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/explorer/lib/explorer/token/metadata_retriever.ex b/apps/explorer/lib/explorer/token/metadata_retriever.ex index 30d0c9968343..19a94a411ddc 100644 --- a/apps/explorer/lib/explorer/token/metadata_retriever.ex +++ b/apps/explorer/lib/explorer/token/metadata_retriever.ex @@ -14,6 +14,7 @@ defmodule Explorer.Token.MetadataRetriever do @no_uri_error "no uri" @vm_execution_error "VM execution error" @invalid_base64_data "invalid data:application/json;base64" + @default_headers [{"User-Agent", "blockscout-6.8.1"}] # https://eips.ethereum.org/EIPS/eip-1155#metadata @erc1155_token_id_placeholder "{id}" @@ -475,12 +476,12 @@ defmodule Explorer.Token.MetadataRetriever do gateway_url_param_value = ipfs_params[:gateway_url_param_value] if gateway_url_param_key && gateway_url_param_value do - [{gateway_url_param_key, gateway_url_param_value}] + [{gateway_url_param_key, gateway_url_param_value} | @default_headers] else - [] + @default_headers end else - [] + @default_headers end end @@ -670,7 +671,7 @@ defmodule Explorer.Token.MetadataRetriever do end defp fetch_metadata_from_uri_request(uri, hex_token_id, ipfs?) do - headers = if ipfs?, do: ipfs_headers(), else: [] + headers = if ipfs?, do: ipfs_headers(), else: @default_headers case Application.get_env(:explorer, :http_adapter).get(uri, headers, recv_timeout: 30_000, diff --git a/apps/explorer/test/explorer/token/metadata_retriever_test.exs b/apps/explorer/test/explorer/token/metadata_retriever_test.exs index 00d3ac5107a9..b45c3b2d4314 100644 --- a/apps/explorer/test/explorer/token/metadata_retriever_test.exs +++ b/apps/explorer/test/explorer/token/metadata_retriever_test.exs @@ -763,7 +763,7 @@ defmodule Explorer.Token.MetadataRetrieverTest do Explorer.Mox.HTTPoison |> expect(:get, fn "https://ipfs.io/ipfs/QmT1Yz43R1PLn2RVovAnEM5dHQEvpTcnwgX8zftvY1FcjP", - [{"x-apikey", "mykey"}], + [{"x-apikey", "mykey"}, {"User-Agent", _}], _options -> {:ok, %HTTPoison.Response{status_code: 200, body: Jason.encode!(result)}} end) diff --git a/bin/version_bump.sh b/bin/version_bump.sh index 0359e289243f..bf79d90035f1 100755 --- a/bin/version_bump.sh +++ b/bin/version_bump.sh @@ -12,6 +12,7 @@ CONFIG_FILE="$(pwd)/rel/config.exs" DOCKER_COMPOSE_FILE="$(pwd)/docker-compose/docker-compose.yml" MAKE_FILE="$(pwd)/docker/Makefile" WORKFLOW_FILES=($(find "$(pwd)/.github/workflows" -type f \( -name "pre-release-*" -o -name "release-*" -o -name "publish-docker-image-*" \))) +METADATA_RETRIEVER_FILE="$(pwd)/apps/explorer/lib/explorer/token/metadata_retriever.ex" # Function to bump version bump_version() { @@ -69,6 +70,8 @@ bump_version() { sed -i '' "s/RELEASE_VERSION: $current_version/RELEASE_VERSION: $new_version/" "$WORKFLOW_FILE" done + sed -i '' "s/\"blockscout-$current_version\"/\"blockscout-$new_version\"/" "$METADATA_RETRIEVER_FILE" + echo "Version bumped from $current_version to $new_version" } From a44488c517d316e0a785fee53639ec0d4cc96352 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:15:16 +0300 Subject: [PATCH 224/363] feat: Support snake_case in metadata service (#10722) --- .../views/api/v2/proxy/metadata_view.ex | 4 ++-- .../lib/explorer/microservice_interfaces/metadata.ex | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/proxy/metadata_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/proxy/metadata_view.ex index 50b8cf2e0399..301ae84af566 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/proxy/metadata_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/proxy/metadata_view.ex @@ -3,8 +3,8 @@ defmodule BlockScoutWeb.API.V2.Proxy.MetadataView do alias BlockScoutWeb.API.V2.AddressView - def render("addresses.json", %{result: {:ok, %{"addresses" => addresses} = body}}) do - Map.put(body, "addresses", Enum.map(addresses, &AddressView.prepare_address/1)) + def render("addresses.json", %{result: {:ok, %{"items" => addresses} = body}}) do + Map.put(body, "items", Enum.map(addresses, &AddressView.prepare_address/1)) end def render("addresses.json", %{result: :error}) do diff --git a/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex b/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex index ad9e692cbe2a..60c1476b1715 100644 --- a/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex +++ b/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex @@ -22,8 +22,8 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do with :ok <- Microservice.check_enabled(__MODULE__) do body = %{ addresses: Enum.join(addresses, ","), - tagsLimit: @tags_per_address_limit, - chainId: Application.get_env(:block_scout_web, :chain_id) + tags_limit: @tags_per_address_limit, + chain_id: Application.get_env(:block_scout_web, :chain_id) } http_get_request(addresses_metadata_url(), body) @@ -38,8 +38,8 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do with :ok <- Microservice.check_enabled(__MODULE__) do params = params - |> Map.put("pageSize", @page_size) - |> Map.put("chainId", Application.get_env(:block_scout_web, :chain_id)) + |> Map.put("page_size", @page_size) + |> Map.put("chain_id", Application.get_env(:block_scout_web, :chain_id)) http_get_request_for_proxy_method(addresses_url(), params, &prepare_addresses_response/1) end @@ -136,11 +136,11 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do Map.put(tag, "meta", Jason.decode!(meta)) end - defp prepare_addresses_response({:ok, %{"addresses" => addresses} = response}) do + defp prepare_addresses_response({:ok, %{"items" => addresses} = response}) do {:ok, Map.put( response, - "addresses", + "items", addresses |> Chain.hashes_to_addresses( necessity_by_association: %{names: :optional, smart_contract: :optional, proxy_implementations: :optional} From 02792f7132ca366e679642e7c98c1dfd86f3e36a Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 17 Oct 2024 17:36:10 +0300 Subject: [PATCH 225/363] feat: Open access to re-fetch metadata button for token instances without metadata initially fetched (#10878) * feat: Open access to re-fetch metadata button for token instances without metadata initially fetched * Adjust existing test * Refactor tests * Add corresponding test to BlockScoutWeb.API.V2.TokenControllerTest module --- .../api/v2/token_controller_test.exs | 63 +++++++++++++++++++ .../token_instance_metadata_refetch.ex | 4 +- .../token_instance_metadata_refetch_test.exs | 32 ++++++---- 3 files changed, 83 insertions(+), 16 deletions(-) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs index bf1a85281f1b..daefa03d40d6 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs @@ -1550,6 +1550,69 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do assert %{"message" => "Not found"} = json_response(request, 404) end + + test "fetch token instance metadata for existing token instance with no metadata", %{conn: conn} do + BlockScoutWeb.TestCaptchaHelper + |> expect(:recaptcha_passed?, fn _captcha_response -> true end) + + token = insert(:token, type: "ERC-721") + token_id = 1 + + insert(:token_instance, + token_id: token_id, + token_contract_address_hash: token.contract_address_hash, + metadata: nil + ) + + metadata = %{"name" => "Super Token"} + url = "http://metadata.endpoint.com" + token_contract_address_hash_string = to_string(token.contract_address_hash) + + TestHelper.fetch_token_uri_mock(url, token_contract_address_hash_string) + + Application.put_env(:explorer, :http_adapter, Explorer.Mox.HTTPoison) + + Explorer.Mox.HTTPoison + |> expect(:get, fn ^url, _headers, _options -> + {:ok, %HTTPoison.Response{status_code: 200, body: Jason.encode!(metadata)}} + end) + + topic = "token_instances:#{token_contract_address_hash_string}" + + {:ok, _reply, _socket} = + BlockScoutWeb.UserSocketV2 + |> socket("no_id", %{}) + |> subscribe_and_join(topic) + + request = + patch(conn, "/api/v2/tokens/#{token.contract_address.hash}/instances/#{token_id}/refetch-metadata", %{ + "recaptcha_response" => "123" + }) + + assert %{"message" => "OK"} = json_response(request, 200) + + :timer.sleep(100) + + assert_receive( + {:chain_event, :fetched_token_instance_metadata, :on_demand, + [^token_contract_address_hash_string, ^token_id, ^metadata]} + ) + + assert_receive %Phoenix.Socket.Message{ + payload: %{token_id: ^token_id, fetched_metadata: ^metadata}, + event: "fetched_token_instance_metadata", + topic: ^topic + }, + :timer.seconds(1) + + token_instance_from_db = + Repo.get_by(Instance, token_id: token_id, token_contract_address_hash: token.contract_address_hash) + + assert(token_instance_from_db) + assert token_instance_from_db.metadata == metadata + + Application.put_env(:explorer, :http_adapter, HTTPoison) + end end def compare_item(%Address{} = address, json) do diff --git a/apps/indexer/lib/indexer/fetcher/on_demand/token_instance_metadata_refetch.ex b/apps/indexer/lib/indexer/fetcher/on_demand/token_instance_metadata_refetch.ex index 652b4b3a743f..02792dd625c6 100644 --- a/apps/indexer/lib/indexer/fetcher/on_demand/token_instance_metadata_refetch.ex +++ b/apps/indexer/lib/indexer/fetcher/on_demand/token_instance_metadata_refetch.ex @@ -20,9 +20,7 @@ defmodule Indexer.Fetcher.OnDemand.TokenInstanceMetadataRefetch do @spec trigger_refetch(TokenInstance.t()) :: :ok def trigger_refetch(token_instance) do - unless is_nil(token_instance.metadata) do - GenServer.cast(__MODULE__, {:refetch, token_instance}) - end + GenServer.cast(__MODULE__, {:refetch, token_instance}) end defp fetch_metadata(token_instance, state) do diff --git a/apps/indexer/test/indexer/fetcher/on_demand/token_instance_metadata_refetch_test.exs b/apps/indexer/test/indexer/fetcher/on_demand/token_instance_metadata_refetch_test.exs index d1203de5e99f..79d6f527b8bc 100644 --- a/apps/indexer/test/indexer/fetcher/on_demand/token_instance_metadata_refetch_test.exs +++ b/apps/indexer/test/indexer/fetcher/on_demand/token_instance_metadata_refetch_test.exs @@ -33,6 +33,12 @@ defmodule Indexer.Fetcher.OnDemand.TokenInstanceMetadataRefetchTest do setup do Subscriber.to(:fetched_token_instance_metadata, :on_demand) + Application.put_env(:explorer, :http_adapter, Explorer.Mox.HTTPoison) + + on_exit(fn -> + Application.put_env(:explorer, :http_adapter, HTTPoison) + end) + :ok end @@ -54,8 +60,6 @@ defmodule Indexer.Fetcher.OnDemand.TokenInstanceMetadataRefetchTest do TestHelper.fetch_token_uri_mock(url, token_contract_address_hash_string) - Application.put_env(:explorer, :http_adapter, Explorer.Mox.HTTPoison) - Explorer.Mox.HTTPoison |> expect(:get, fn ^url, _headers, _options -> {:ok, %HTTPoison.Response{status_code: 200, body: Jason.encode!(metadata)}} @@ -83,11 +87,9 @@ defmodule Indexer.Fetcher.OnDemand.TokenInstanceMetadataRefetchTest do {:chain_event, :fetched_token_instance_metadata, :on_demand, [^token_contract_address_hash_string, ^token_id, ^metadata]} ) - - Application.put_env(:explorer, :http_adapter, HTTPoison) end - test "don't run the update on the token instance with no metadata fetched initially" do + test "run the update on the token instance with no metadata fetched initially" do token = insert(:token, name: "Super Token", type: "ERC-721") token_id = 1 @@ -100,9 +102,17 @@ defmodule Indexer.Fetcher.OnDemand.TokenInstanceMetadataRefetchTest do |> Repo.preload(:token) metadata = %{"name" => "Super Token"} + url = "http://metadata.endpoint.com" token_contract_address_hash_string = to_string(token.contract_address_hash) - assert TokenInstanceMetadataRefetchOnDemand.trigger_refetch(token_instance) == nil + TestHelper.fetch_token_uri_mock(url, token_contract_address_hash_string) + + Explorer.Mox.HTTPoison + |> expect(:get, fn ^url, _headers, _options -> + {:ok, %HTTPoison.Response{status_code: 200, body: Jason.encode!(metadata)}} + end) + + assert TokenInstanceMetadataRefetchOnDemand.trigger_refetch(token_instance) == :ok :timer.sleep(100) @@ -110,7 +120,7 @@ defmodule Indexer.Fetcher.OnDemand.TokenInstanceMetadataRefetchTest do Repo.get_by(TokenInstance, token_id: token_id, token_contract_address_hash: token.contract_address_hash) assert(token_instance_from_db) - assert is_nil(token_instance_from_db.metadata) + assert token_instance_from_db.metadata == metadata assert is_nil( Repo.get_by(TokenInstanceMetadataRefetchAttempt, @@ -119,9 +129,9 @@ defmodule Indexer.Fetcher.OnDemand.TokenInstanceMetadataRefetchTest do ) ) - refute_receive( + assert_receive( {:chain_event, :fetched_token_instance_metadata, :on_demand, - [^token_contract_address_hash_string, ^token_id, %{metadata: ^metadata}]} + [^token_contract_address_hash_string, ^token_id, ^metadata]} ) end @@ -143,8 +153,6 @@ defmodule Indexer.Fetcher.OnDemand.TokenInstanceMetadataRefetchTest do TestHelper.fetch_token_uri_mock(url, token_contract_address_hash_string) - Application.put_env(:explorer, :http_adapter, Explorer.Mox.HTTPoison) - Explorer.Mox.HTTPoison |> expect(:get, fn ^url, _headers, _options -> {:ok, %HTTPoison.Response{status_code: 200, body: nil}} @@ -174,8 +182,6 @@ defmodule Indexer.Fetcher.OnDemand.TokenInstanceMetadataRefetchTest do {:chain_event, :fetched_token_instance_metadata, :on_demand, [^token_contract_address_hash_string, ^token_id, %{metadata: ^metadata}]} ) - - Application.put_env(:explorer, :http_adapter, HTTPoison) end end end From 8635b5ca86b8362c75396e195c5fe923df26d69b Mon Sep 17 00:00:00 2001 From: Alexander Filippov Date: Thu, 17 Oct 2024 18:08:53 +0300 Subject: [PATCH 226/363] feat: EIP-7702 support (#10870) * feat: return `authorizationList` for EIP-7702 transactions in `/transactions/:tx_hash` response (#10776) * feat: support EIP-7702 transactions * fix: handle invalid signatures * fix: save authority * Update apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex Co-authored-by: Kirill Fedoseev * Update apps/explorer/lib/explorer/chain/signed_authorization.ex Co-authored-by: Kirill Fedoseev * Update apps/indexer/lib/indexer/block/fetcher.ex Co-authored-by: Kirill Fedoseev * fix: remove set_code_transaction from @allowed_type_labels * Update apps/explorer/lib/explorer/chain/import/runner/signed_authorizations.ex Co-authored-by: Kirill Fedoseev * fix: move signed_authorization to a separate module * add todo --------- Co-authored-by: Kirill Fedoseev * feat: support EIP-7702 in `/address/:address` endpoint (#10799) * feat: support EIP-7702 transactions * fix: handle invalid signatures * fix: save authority * Update apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex Co-authored-by: Kirill Fedoseev * Update apps/explorer/lib/explorer/chain/signed_authorization.ex Co-authored-by: Kirill Fedoseev * Update apps/indexer/lib/indexer/block/fetcher.ex Co-authored-by: Kirill Fedoseev * fix: remove set_code_transaction from @allowed_type_labels * feat: add EIP-7702 support to /address/:address endpoint * fix: refactor fetch? * fix: move get_implementation_address_hash_string_eip7702 * fix: remove EIP-7702 flag from response, modify transactions filter to handle EOA with code correctly * fix: minor refactoring * fix: remove unused alias * fix: review comments * Update apps/explorer/priv/repo/migrations/20240904161254_create_signed_authorizations.exs Co-authored-by: Kirill Fedoseev --------- Co-authored-by: Kirill Fedoseev * fix: remove unused code * fix: refactor code * Update apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/signed_authorization.ex Co-authored-by: Alexander Kolotov * chore: documentation improvement * fix typo * fix: move spec and doc * fix: authorization_list spec * fix: wrap address and authority with checksum * fix: invalid typespec Co-authored-by: Alexander Kolotov * Apply suggestions from code review Co-authored-by: Alexander Kolotov * fix typo * Update apps/explorer/lib/explorer/chain/smart_contract/proxy/eip_7702.ex Co-authored-by: Alexander Kolotov --------- Co-authored-by: Kirill Fedoseev Co-authored-by: Alexander Kolotov --- .../controllers/api/v2/address_controller.ex | 3 +- .../api/v2/transaction_controller.ex | 4 +- .../views/api/v2/transaction_view.ex | 65 ++++++++++- apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex | 58 ++++++++- .../lib/ethereum_jsonrpc/fetched_code.ex | 54 ++++++++- .../lib/ethereum_jsonrpc/fetched_codes.ex | 38 +++++- .../ethereum_jsonrpc/signed_authorization.ex | 58 +++++++++ .../lib/ethereum_jsonrpc/transaction.ex | 31 ++++- apps/explorer/lib/explorer/chain/address.ex | 59 +++++++++- .../lib/explorer/chain/address/counters.ex | 49 +++++++- .../import/runner/signed_authorizations.ex | 110 ++++++++++++++++++ .../chain/import/stage/block_referencing.ex | 3 +- .../explorer/chain/signed_authorization.ex | 80 +++++++++++++ .../explorer/chain/smart_contract/proxy.ex | 30 ++++- .../chain/smart_contract/proxy/eip_7702.ex | 73 ++++++++++++ .../proxy/models/implementation.ex | 1 + .../lib/explorer/chain/transaction.ex | 11 +- apps/explorer/lib/explorer/counters/helper.ex | 10 ++ .../address_contract_code_fetch_attempt.ex | 27 ++++- ...904161254_create_signed_authorizations.exs | 25 ++++ .../20240918104231_new_proxy_type_eip7702.exs | 7 ++ apps/indexer/lib/indexer/block/fetcher.ex | 4 +- .../fetcher/on_demand/contract_code.ex | 53 ++++++++- .../transform/signed_authorizations.ex | 75 ++++++++++++ 24 files changed, 890 insertions(+), 38 deletions(-) create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/signed_authorization.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/signed_authorizations.ex create mode 100644 apps/explorer/lib/explorer/chain/signed_authorization.ex create mode 100644 apps/explorer/lib/explorer/chain/smart_contract/proxy/eip_7702.ex create mode 100644 apps/explorer/priv/repo/migrations/20240904161254_create_signed_authorizations.exs create mode 100644 apps/explorer/priv/repo/migrations/20240918104231_new_proxy_type_eip7702.exs create mode 100644 apps/indexer/lib/indexer/transform/signed_authorizations.ex diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index 447d830313c4..012ecc6843f1 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -77,7 +77,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do :names => :optional, :scam_badge => :optional, :token => :optional, - :proxy_implementations => :optional + :proxy_implementations => :optional, + :signed_authorization => :optional }, api?: true ] diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex index ce5e5bbed9c6..c828b27f57a8 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex @@ -117,7 +117,9 @@ defmodule BlockScoutWeb.API.V2.TransactionController do @spec transaction(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} def transaction(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do necessity_by_association_with_actions = - Map.put(@transaction_necessity_by_association, :transaction_actions, :optional) + @transaction_necessity_by_association + |> Map.put(:transaction_actions, :optional) + |> Map.put(:signed_authorizations, :optional) necessity_by_association = case Application.get_env(:explorer, :chain_type) do diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex index cf37c5a686b3..2c921347f131 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex @@ -8,7 +8,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do alias BlockScoutWeb.TransactionStateView alias Ecto.Association.NotLoaded alias Explorer.{Chain, Market} - alias Explorer.Chain.{Address, Block, Log, Token, Transaction, Wei} + alias Explorer.Chain.{Address, Block, Log, SignedAuthorization, Token, Transaction, Wei} alias Explorer.Chain.Block.Reward alias Explorer.Chain.Transaction.StateChange alias Explorer.Counters.AverageBlockTime @@ -209,6 +209,12 @@ defmodule BlockScoutWeb.API.V2.TransactionView do } end + def render("authorization_list.json", %{signed_authorizations: signed_authorizations}) do + signed_authorizations + |> Enum.sort_by(& &1.index, :asc) + |> Enum.map(&prepare_signed_authorization/1) + end + @doc """ Decodes list of logs """ @@ -287,6 +293,28 @@ defmodule BlockScoutWeb.API.V2.TransactionView do } end + @doc """ + Extracts the necessary fields from the signed authorization for the API response. + + ## Parameters + - `signed_authorization`: A `SignedAuthorization.t()` struct containing the signed authorization data. + + ## Returns + - A map with the necessary fields for the API response. + """ + @spec prepare_signed_authorization(SignedAuthorization.t()) :: map() + def prepare_signed_authorization(signed_authorization) do + %{ + "address" => Address.checksum(signed_authorization.address), + "chain_id" => signed_authorization.chain_id, + "nonce" => signed_authorization.nonce, + "r" => signed_authorization.r, + "s" => signed_authorization.s, + "v" => signed_authorization.v, + "authority" => Address.checksum(signed_authorization.authority) + } + end + defp get_tx_hash(%Transaction{} = tx), do: to_string(tx.hash) defp get_tx_hash(hash), do: to_string(hash) @@ -401,7 +429,8 @@ defmodule BlockScoutWeb.API.V2.TransactionView do "method" => Transaction.method_name(transaction, decoded_input), "tx_types" => tx_types(transaction), "tx_tag" => GetTransactionTags.get_transaction_tags(transaction.hash, current_user(single_tx? && conn)), - "has_error_in_internal_txs" => transaction.has_error_in_internal_txs + "has_error_in_internal_txs" => transaction.has_error_in_internal_txs, + "authorization_list" => authorization_list(transaction.signed_authorizations) } result @@ -433,6 +462,23 @@ defmodule BlockScoutWeb.API.V2.TransactionView do render("transaction_actions.json", %{actions: actions}) end + @doc """ + Renders the authorization list for a transaction. + + ## Parameters + - `signed_authorizations`: A list of `SignedAuthorization.t()` structs. + + ## Returns + - A list of maps with the necessary fields for the API response. + """ + @spec authorization_list(nil | NotLoaded.t() | [SignedAuthorization.t()]) :: [map()] + def authorization_list(nil), do: [] + def authorization_list(%NotLoaded{}), do: [] + + def authorization_list(signed_authorizations) do + render("authorization_list.json", %{signed_authorizations: signed_authorizations}) + end + defp burnt_fees(transaction, max_fee_per_gas, base_fee_per_gas) do if !is_nil(max_fee_per_gas) and !is_nil(transaction.gas_used) and !is_nil(base_fee_per_gas) do if Decimal.compare(max_fee_per_gas.value, 0) == :eq do @@ -559,7 +605,20 @@ defmodule BlockScoutWeb.API.V2.TransactionView do | :token_creation | :token_transfer | :blob_transaction - def tx_types(tx, types \\ [], stage \\ :blob_transaction) + | :set_code_transaction + def tx_types(tx, types \\ [], stage \\ :set_code_transaction) + + def tx_types(%Transaction{type: type} = tx, types, :set_code_transaction) do + # EIP-7702 set code transaction type + types = + if type == 4 do + [:set_code_transaction | types] + else + types + end + + tx_types(tx, types, :blob_transaction) + end def tx_types(%Transaction{type: type} = tx, types, :blob_transaction) do # EIP-2718 blob transaction type diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex index 3d5cc09968f2..5c4d4a396e26 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex @@ -238,7 +238,26 @@ defmodule EthereumJSONRPC do end @doc """ - Fetches code for each given `address` at the `block_number`. + Fetches contract code for multiple addresses at specified block numbers. + + This function takes a list of parameters, each containing an address and a + block number, and retrieves the contract code for each address at the + specified block. + + ## Parameters + - `params_list`: A list of maps, each containing: + - `:block_quantity`: The block number (as a quantity string) at which to fetch the code. + - `:address`: The address of the contract to fetch the code for. + - `json_rpc_named_arguments`: A keyword list of JSON-RPC configuration options. + + ## Returns + - `{:ok, fetched_codes}`, where `fetched_codes` is a `FetchedCodes.t()` struct containing: + - `params_list`: A list of successfully fetched code parameters, each containing: + - `address`: The contract address. + - `block_number`: The block number at which the code was fetched. + - `code`: The fetched contract code in hexadecimal format. + - `errors`: A list of errors encountered during the fetch operation. + - `{:error, reason}`: An error occurred during the fetch operation. """ @spec fetch_codes( [%{required(:block_quantity) => quantity, required(:address) => address()}], @@ -423,7 +442,21 @@ defmodule EthereumJSONRPC do end @doc """ - Assigns an id to each set of params in `params_list` for batch request-response correlation + Assigns a unique integer ID to each set of parameters in the given list. + + This function is used to prepare parameters for batch request-response + correlation in JSON-RPC calls. + + ## Parameters + - `params_list`: A list of parameter sets, where each set can be of any type. + + ## Returns + A map where the keys are integer IDs (starting from 0) and the values are + the corresponding parameter sets from the input list. + + ## Example + iex> id_to_params([%{block: 1}, %{block: 2}]) + %{0 => %{block: 1}, 1 => %{block: 2}} """ @spec id_to_params([params]) :: %{id => params} when id: non_neg_integer(), params: any() def id_to_params(params_list) do @@ -433,8 +466,27 @@ defmodule EthereumJSONRPC do end @doc """ - Assigns not matched ids between requests and responses to responses with incorrect ids + Sanitizes responses by assigning unmatched IDs to responses with missing IDs. + + This function processes a list of responses and a map of expected IDs to + parameters. It handles cases where responses have missing (nil) IDs by + assigning them unmatched IDs from the id_to_params map. + + ## Parameters + - `responses`: A list of response maps from a batch JSON-RPC call. + - `id_to_params`: A map of request IDs to their corresponding parameters. + + ## Returns + A list of sanitized response maps where each response has a valid ID. + + ## Example + iex> responses = [%{id: 1, result: "ok"}, %{id: nil, result: "error"}] + iex> id_to_params = %{1 => %{}, 2 => %{}, 3 => %{}} + iex> EthereumJSONRPC.sanitize_responses(responses, id_to_params) + [%{id: 1, result: "ok"}, %{id: 2, result: "error"}] """ + @spec sanitize_responses(Transport.batch_response(), %{id => params}) :: Transport.batch_response() + when id: EthereumJSONRPC.request_id(), params: any() def sanitize_responses(responses, id_to_params) do responses |> Enum.reduce( diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_code.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_code.ex index 58d52509b447..2b7211fe4d44 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_code.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_code.ex @@ -9,9 +9,37 @@ defmodule EthereumJSONRPC.FetchedCode do @type error :: %{code: integer(), message: String.t(), data: %{block_quantity: String.t(), address: String.t()}} @doc """ - Converts `response` to code params or annotated error. - """ + Converts a JSON-RPC response of `eth_getCode` to code params or an annotated error. + + This function handles two types of responses: + 1. Successful responses with fetched code. + 2. Error responses. + + ## Parameters + - `response`: A map containing either a successful result or an error. + - `id_to_params`: A map of request IDs to their corresponding parameters. + ## Returns + - `{:ok, params()}` for successful responses, where `params()` is a map + containing the address, block number, and fetched code. + - `{:error, error()}` for error responses, where `error()` is a map + containing the error code, message, and additional data. + + ## Examples + iex> # Successful response: + iex> response = %{id: 1, result: "0x123"} + iex> id_to_params = %{1 => %{block_quantity: "0x1", address: "0xabc"}} + iex> FetchedCode.from_response(response, id_to_params) + {:ok, %{address: "0xabc", block_number: 1, code: "0x123"}} + iex> # Error response: + iex> response = %{id: 1, error: %{code: 100, message: "Error"}} + iex> id_to_params = %{1 => %{block_quantity: "0x1", address: "0xabc"}} + iex> FetchedCode.from_response(response, id_to_params) + {:error, %{code: 100, message: "Error", data: %{block_quantity: "0x1", address: "0xabc"}}} + """ + @spec from_response(%{id: EthereumJSONRPC.request_id(), result: String.t()}, %{ + non_neg_integer() => %{block_quantity: String.t(), address: String.t()} + }) :: {:ok, params()} def from_response(%{id: id, result: fetched_code}, id_to_params) when is_map(id_to_params) do %{block_quantity: block_quantity, address: address} = Map.fetch!(id_to_params, id) @@ -23,9 +51,9 @@ defmodule EthereumJSONRPC.FetchedCode do }} end - @spec from_response(%{id: id, result: String.t()}, %{id => %{block_quantity: block_quantity, address: address}}) :: - {:ok, params()} - when id: non_neg_integer(), block_quantity: String.t(), address: String.t() + @spec from_response(%{id: EthereumJSONRPC.request_id(), error: %{code: integer(), message: String.t()}}, %{ + non_neg_integer() => %{block_quantity: String.t(), address: String.t()} + }) :: {:error, error()} def from_response(%{id: id, error: %{code: code, message: message} = error}, id_to_params) when is_integer(code) and is_binary(message) and is_map(id_to_params) do %{block_quantity: block_quantity, address: address} = Map.fetch!(id_to_params, id) @@ -35,6 +63,22 @@ defmodule EthereumJSONRPC.FetchedCode do {:error, annotated_error} end + @doc """ + Creates a standardized JSON-RPC request structure to fetch contract code using `eth_getCode`. + + ## Parameters + - `id`: The request identifier. + - `block_quantity`: The block number or tag (e.g., "latest") for which to + fetch the code. + - `address`: The address of the contract whose code is to be fetched. + + ## Returns + A map representing a JSON-RPC request with the following structure: + - `jsonrpc`: The JSON-RPC version (always "2.0"). + - `id`: The request identifier passed in. + - `method`: The RPC method name (always "eth_getCode"). + - `params`: A list containing the contract address and block identifier. + """ @spec request(%{id: id, block_quantity: block_quantity, address: address}) :: %{ jsonrpc: String.t(), id: id, diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_codes.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_codes.ex index 257037990931..a15ce4f8ce02 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_codes.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_codes.ex @@ -15,7 +15,22 @@ defmodule EthereumJSONRPC.FetchedCodes do @type t :: %__MODULE__{params_list: [FetchedCode.params()], errors: [FetchedCode.error()]} @doc """ - `eth_getCode` requests for `id_to_params`. + Generates a list of `eth_getCode` JSON-RPC requests for the given parameters. + + This function takes a map of request IDs to parameter maps and transforms them + into a list of JSON-RPC request structures for fetching contract code. + + ## Parameters + - `id_to_params`: A map where keys are request IDs and values are maps + containing `block_quantity` and `address` for each request. + + ## Returns + A list of maps, each representing a JSON-RPC request with the following + structure: + - `jsonrpc`: The JSON-RPC version (always "2.0"). + - `id`: The request identifier. + - `method`: The RPC method name (always "eth_getCode"). + - `params`: A list containing the contract address and block identifier. """ @spec requests(%{id => %{block_quantity: block_quantity, address: address}}) :: [ %{jsonrpc: String.t(), id: id, method: String.t(), params: [address | block_quantity]} @@ -30,8 +45,27 @@ defmodule EthereumJSONRPC.FetchedCodes do end @doc """ - Converts `responses` to `t/0`. + Processes responses from `eth_getCode` JSON-RPC calls and converts them into a structured format. + + This function takes a list of responses from `eth_getCode` calls and a map of + request IDs to their corresponding parameters. It sanitizes the responses, + processes each one, and accumulates the results into a `FetchedCodes` struct. + + ## Parameters + - `responses`: A list of response maps from `eth_getCode` calls. Each map + contains an `:id` key and either a `:result` or `:error` key. + - `id_to_params`: A map where keys are request IDs and values are maps + containing `block_quantity` and `address` for each request. + + ## Returns + A `FetchedCodes` struct containing: + - `params_list`: A list of successfully fetched code parameters. + - `errors`: A list of errors encountered during the process. """ + @spec from_responses( + [%{:id => EthereumJSONRPC.request_id(), optional(:error) => map(), optional(:result) => String.t()}], + %{non_neg_integer() => %{block_quantity: String.t(), address: String.t()}} + ) :: t() def from_responses(responses, id_to_params) do responses |> EthereumJSONRPC.sanitize_responses(id_to_params) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/signed_authorization.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/signed_authorization.ex new file mode 100644 index 000000000000..943183a3d81b --- /dev/null +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/signed_authorization.ex @@ -0,0 +1,58 @@ +defmodule EthereumJSONRPC.SignedAuthorization do + @moduledoc """ + The format of authorization tuples returned for + set code transactions [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702). + """ + + import EthereumJSONRPC, only: [quantity_to_integer: 1] + + @typedoc """ + * `"chainId"` - specifies the chain for which the authorization was created `t:EthereumJSONRPC.quantity/0`. + * `"address"` - `t:EthereumJSONRPC.address/0` of the delegate contract. + * `"nonce"` - signature nonce `t:EthereumJSONRPC.quantity/0`. + * `"v"` - v component of the signature `t:EthereumJSONRPC.quantity/0`. + * `"r"` - r component of the signature `t:EthereumJSONRPC.quantity/0`. + * `"s"` - s component of the signature `t:EthereumJSONRPC.quantity/0`. + """ + @type t :: %{ + String.t() => EthereumJSONRPC.address() | EthereumJSONRPC.quantity() + } + + @typedoc """ + * `"chain_id"` - specifies the chain for which the authorization was created. + * `"address"` - address of the delegate contract. + * `"nonce"` - signature nonce. + * `"v"` - v component of the signature. + * `"r"` - r component of the signature. + * `"s"` - s component of the signature. + """ + @type params :: %{ + chain_id: non_neg_integer(), + address: EthereumJSONRPC.address(), + nonce: non_neg_integer(), + r: non_neg_integer(), + s: non_neg_integer(), + v: non_neg_integer() + } + + @doc """ + Converts a signed authorization map into its corresponding parameters map format. + + ## Parameters + - `raw`: Map with signed authorization data. + + ## Returns + - Parameters map in the `params()` format. + """ + @spec to_params(t()) :: params() + def to_params(raw) do + %{ + chain_id: quantity_to_integer(raw["chainId"]), + address: raw["address"], + nonce: quantity_to_integer(raw["nonce"]), + r: quantity_to_integer(raw["r"]), + s: quantity_to_integer(raw["s"]), + v: quantity_to_integer(raw["v"]) + } + end +end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex index 0287f699b03a..c61e74f4c612 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex @@ -16,6 +16,7 @@ defmodule EthereumJSONRPC.Transaction do ] alias EthereumJSONRPC + alias EthereumJSONRPC.SignedAuthorization case Application.compile_env(:explorer, :chain_type) do :ethereum -> @@ -74,8 +75,16 @@ defmodule EthereumJSONRPC.Transaction do @chain_type_fields quote(do: []) end + # todo: Check if it's possible to simplify by avoiding t -> elixir -> params conversions + # and directly convert t -> params. @type elixir :: %{ - String.t() => EthereumJSONRPC.address() | EthereumJSONRPC.hash() | String.t() | non_neg_integer() | nil + String.t() => + EthereumJSONRPC.address() + | EthereumJSONRPC.hash() + | String.t() + | non_neg_integer() + | [SignedAuthorization.params()] + | nil } @typedoc """ @@ -105,6 +114,7 @@ defmodule EthereumJSONRPC.Transaction do * `"maxPriorityFeePerGas"` - `t:EthereumJSONRPC.quantity/0` of wei to denote max priority fee per unit of gas used. Introduced in [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) * `"maxFeePerGas"` - `t:EthereumJSONRPC.quantity/0` of wei to denote max fee per unit of gas used. Introduced in [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) * `"type"` - `t:EthereumJSONRPC.quantity/0` denotes transaction type. Introduced in [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) + * `"authorizationList"` - `t:list/0` of `t:EthereumJSONRPC.SignedAuthorization.t/0` authorization tuples. Introduced in [EIP-7702](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7702.md) #{case Application.compile_env(:explorer, :chain_type) do :ethereum -> """ * `"maxFeePerBlobGas"` - `t:EthereumJSONRPC.quantity/0` of wei to denote max fee per unit of blob gas used. Introduced in [EIP-4844](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md) @@ -128,7 +138,12 @@ defmodule EthereumJSONRPC.Transaction do """ @type t :: %{ String.t() => - EthereumJSONRPC.address() | EthereumJSONRPC.hash() | EthereumJSONRPC.quantity() | String.t() | nil + EthereumJSONRPC.address() + | EthereumJSONRPC.hash() + | EthereumJSONRPC.quantity() + | String.t() + | [SignedAuthorization.t()] + | nil } @type params :: %{ @@ -150,7 +165,8 @@ defmodule EthereumJSONRPC.Transaction do transaction_index: non_neg_integer(), max_priority_fee_per_gas: non_neg_integer(), max_fee_per_gas: non_neg_integer(), - type: non_neg_integer() + type: non_neg_integer(), + authorization_list: [SignedAuthorization.params()] } @doc """ @@ -322,7 +338,8 @@ defmodule EthereumJSONRPC.Transaction do {"block_timestamp", :block_timestamp}, {"r", :r}, {"s", :s}, - {"v", :v} + {"v", :v}, + {"authorizationList", :authorization_list} ]) end @@ -368,7 +385,8 @@ defmodule EthereumJSONRPC.Transaction do {"block_timestamp", :block_timestamp}, {"r", :r}, {"s", :s}, - {"v", :v} + {"v", :v}, + {"authorizationList", :authorization_list} ]) end @@ -700,6 +718,9 @@ defmodule EthereumJSONRPC.Transaction do end end + defp entry_to_elixir({"authorizationList" = key, value}), + do: {key, value |> Enum.map(&SignedAuthorization.to_params/1)} + # Celo-specific fields if Application.compile_env(:explorer, :chain_type) == :celo do defp entry_to_elixir({key, value}) diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index 700cbb126c24..a8f7b8641787 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -13,6 +13,7 @@ defmodule Explorer.Chain.Address.Schema do DecompiledSmartContract, Hash, InternalTransaction, + SignedAuthorization, SmartContract, Token, Transaction, @@ -107,6 +108,10 @@ defmodule Explorer.Chain.Address.Schema do has_many(:decompiled_smart_contracts, DecompiledSmartContract, foreign_key: :address_hash, references: :hash) has_many(:withdrawals, Withdrawal, foreign_key: :address_hash, references: :hash) + # In practice, this is a one-to-many relationship, but we only need to check if any signed authorization + # exists for a given address. This done this way to avoid loading all signed authorizations for an address. + has_one(:signed_authorization, SignedAuthorization, foreign_key: :authority, references: :hash) + timestamps() unquote_splicing(@chain_type_fields) @@ -130,8 +135,9 @@ defmodule Explorer.Chain.Address do alias Explorer.Helper, as: ExplorerHelper alias Explorer.Chain.Cache.{Accounts, NetVersion} alias Explorer.Chain.SmartContract.Proxy + alias Explorer.Chain.SmartContract.Proxy.EIP7702 alias Explorer.Chain.SmartContract.Proxy.Models.Implementation - alias Explorer.Chain.{Address, Hash} + alias Explorer.Chain.{Address, Data, Hash} alias Explorer.{Chain, PagingOptions, Repo} @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce decompiled verified gas_used transactions_count token_transfers_count)a @@ -422,7 +428,18 @@ defmodule Explorer.Chain.Address do end @doc """ - Checks if given address is smart-contract + Determines if the given address is a smart contract. + + This function checks the contract code of an address to determine if it's a + smart contract. + + ## Parameters + - `address`: The address to check. Can be an `Address` struct or any other value. + + ## Returns + - `true` if the address is a smart contract + - `false` if the address is not a smart contract + - `nil` if the contract code hasn't been loaded """ @spec smart_contract?(any()) :: boolean() | nil def smart_contract?(%__MODULE__{contract_code: nil}), do: false @@ -430,6 +447,31 @@ defmodule Explorer.Chain.Address do def smart_contract?(%NotLoaded{}), do: nil def smart_contract?(_), do: false + @doc """ + Checks if the given address is an Externally Owned Account (EOA) with code, + as defined in EIP-7702. + + This function determines whether an address represents an EOA that has + associated code, which is a special case introduced by EIP-7702. It checks + the contract code of the address for the presence of a delegate address + according to the EIP-7702 specification. + + ## Parameters + - `address`: The address to check. Can be an `Address` struct or any other value. + + ## Returns + - `true` if the address is an EOA with code (EIP-7702 compliant) + - `false` if the address is not an EOA with code + - `nil` if the contract code hasn't been loaded + """ + @spec eoa_with_code?(any()) :: boolean() | nil + def eoa_with_code?(%__MODULE__{contract_code: %Data{bytes: code}}) do + EIP7702.get_delegate_address(code) != nil + end + + def eoa_with_code?(%NotLoaded{}), do: nil + def eoa_with_code?(_), do: false + defp get_addresses(options) do accounts_with_n = fetch_top_addresses(options) @@ -529,7 +571,18 @@ defmodule Explorer.Chain.Address do end @doc """ - Sets contract_code for the given Explorer.Chain.Address + Sets the contract code for the given address. + + This function updates the contract code and the `updated_at` timestamp for an + address in the database. + + ## Parameters + - `address_hash`: The hash of the address to update. + - `contract_code`: The new contract code to set. + + ## Returns + A tuple `{count, nil}`, where `count` is the number of rows updated + (typically 1 if the address exists, 0 otherwise). """ @spec set_contract_code(Hash.Address.t(), binary()) :: {non_neg_integer(), nil} def set_contract_code(address_hash, contract_code) when not is_nil(address_hash) and is_binary(contract_code) do diff --git a/apps/explorer/lib/explorer/chain/address/counters.ex b/apps/explorer/lib/explorer/chain/address/counters.ex index 9ac501b43480..3fdb09acb1d1 100644 --- a/apps/explorer/lib/explorer/chain/address/counters.ex +++ b/apps/explorer/lib/explorer/chain/address/counters.ex @@ -181,6 +181,20 @@ defmodule Explorer.Chain.Address.Counters do Repo.aggregate(to_address_query, :count, :hash, timeout: :infinity) end + @doc """ + Calculates the total gas used by incoming transactions to a given address. + + This function queries the database for all transactions where the + `to_address_hash` matches the provided `address_hash`, and sums up the + `gas_used` for these transactions. + + ## Parameters + - `address_hash`: The address hash to query for incoming transactions. + + ## Returns + - The total gas used by incoming transactions, or `nil` if no transactions + are found or if the sum is null. + """ @spec address_to_incoming_transaction_gas_usage(Hash.Address.t()) :: Decimal.t() | nil def address_to_incoming_transaction_gas_usage(address_hash) do to_address_query = @@ -192,6 +206,19 @@ defmodule Explorer.Chain.Address.Counters do Repo.aggregate(to_address_query, :sum, :gas_used, timeout: :infinity) end + @doc """ + Calculates the total gas used by outgoing transactions from a given address. + + This function queries the database for all transactions where the + `from_address_hash` matches the provided `address_hash`, and sums up the + `gas_used` for these transactions. + + ## Parameters + - `address_hash`: the address to query. + + ## Returns + - The total gas used, or `nil` if no transactions are found or if the sum is null. + """ @spec address_to_outcoming_transaction_gas_usage(Hash.Address.t()) :: Decimal.t() | nil def address_to_outcoming_transaction_gas_usage(address_hash) do to_address_query = @@ -226,9 +253,29 @@ defmodule Explorer.Chain.Address.Counters do ) end + @doc """ + Calculates the total gas usage for a given address. + + This function determines the appropriate gas usage calculation based on the + address type: + + - For smart contracts (excluding EOAs with code), it first checks the gas + usage of incoming transactions. If there are no incoming transactions or + their gas usage is zero, it falls back to the gas usage of outgoing + transactions. + - For regular addresses and EOAs with code, it calculates the gas usage of + outgoing transactions. + + ## Parameters + - `address`: The address to calculate gas usage for. + + ## Returns + - The total gas usage for the address. + - `nil` if no relevant transactions are found or if the sum is null. + """ @spec address_to_gas_usage_count(Address.t()) :: Decimal.t() | nil def address_to_gas_usage_count(address) do - if Address.smart_contract?(address) do + if Address.smart_contract?(address) and not Address.eoa_with_code?(address) do incoming_transaction_gas_usage = address_to_incoming_transaction_gas_usage(address.hash) cond do diff --git a/apps/explorer/lib/explorer/chain/import/runner/signed_authorizations.ex b/apps/explorer/lib/explorer/chain/import/runner/signed_authorizations.ex new file mode 100644 index 000000000000..147f5c821dc7 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/signed_authorizations.ex @@ -0,0 +1,110 @@ +defmodule Explorer.Chain.Import.Runner.SignedAuthorizations do + @moduledoc """ + Bulk imports `t:Explorer.Chain.SignedAuthorization.t/0`. + """ + + require Ecto.Query + + import Ecto.Query, only: [from: 2] + + alias Ecto.{Changeset, Multi, Repo} + alias Explorer.Chain.{Import, SignedAuthorization} + alias Explorer.Prometheus.Instrumenter + + @behaviour Import.Runner + + # milliseconds + @timeout 60_000 + + @type imported :: [SignedAuthorization.t()] + + @impl Import.Runner + def ecto_schema_module, do: SignedAuthorization + + @impl Import.Runner + def option_key, do: :signed_authorizations + + @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, :signed_authorizations, fn repo, _ -> + Instrumenter.block_import_stage_runner( + fn -> insert(repo, changes_list, insert_options) end, + :block_referencing, + :signed_authorizations, + :signed_authorizations + ) + 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, [SignedAuthorization.t()]} + | {:error, [Changeset.t()]} + defp 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) + conflict_target = [:transaction_hash, :index] + + {:ok, _} = + Import.insert_changes_list( + repo, + changes_list, + for: SignedAuthorization, + on_conflict: on_conflict, + conflict_target: conflict_target, + returning: true, + timeout: timeout, + timestamps: timestamps + ) + end + + defp default_on_conflict do + from( + authorization in SignedAuthorization, + update: [ + set: [ + chain_id: fragment("EXCLUDED.chain_id"), + address: fragment("EXCLUDED.address"), + nonce: fragment("EXCLUDED.nonce"), + r: fragment("EXCLUDED.r"), + s: fragment("EXCLUDED.s"), + v: fragment("EXCLUDED.v"), + authority: fragment("EXCLUDED.authority"), + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", authorization.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", authorization.updated_at) + ] + ], + where: + fragment( + "(EXCLUDED.chain_id, EXCLUDED.address, EXCLUDED.nonce, EXCLUDED.r, EXCLUDED.s, EXCLUDED.v, EXCLUDED.authority) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?)", + authorization.chain_id, + authorization.address, + authorization.nonce, + authorization.r, + authorization.s, + authorization.v, + authorization.authority + ) + ) + end +end diff --git a/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex b/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex index df3b0c264d79..621a712168b4 100644 --- a/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex +++ b/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex @@ -14,7 +14,8 @@ defmodule Explorer.Chain.Import.Stage.BlockReferencing do Runner.Tokens, Runner.TokenInstances, Runner.TransactionActions, - Runner.Withdrawals + Runner.Withdrawals, + Runner.SignedAuthorizations ] @extra_runners_by_chain_type %{ diff --git a/apps/explorer/lib/explorer/chain/signed_authorization.ex b/apps/explorer/lib/explorer/chain/signed_authorization.ex new file mode 100644 index 000000000000..f43d847eea91 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/signed_authorization.ex @@ -0,0 +1,80 @@ +defmodule Explorer.Chain.SignedAuthorization do + @moduledoc "Models a transaction extension with authorization tuples from eip7702 set code transactions." + + use Explorer.Schema + + alias Explorer.Chain.{Hash, Transaction} + + @optional_attrs ~w(authority)a + @required_attrs ~w(transaction_hash index chain_id address nonce r s v)a + + @typedoc """ + Descriptor of the signed authorization tuple from EIP-7702 set code transactions: + * `transaction_hash` - the hash of the associated transaction. + * `index` - the index of this authorization in the authorization list. + * `chain_id` - the ID of the chain for which the authorization was created. + * `address` - the address of the delegate contract. + * `nonce` - the signature nonce. + * `v` - the 'v' component of the signature. + * `r` - the 'r' component of the signature. + * `s` - the 's' component of the signature. + * `authority` - the signer of the authorization. + """ + @type to_import :: %__MODULE__{ + transaction_hash: binary(), + index: non_neg_integer(), + chain_id: non_neg_integer(), + address: binary(), + nonce: non_neg_integer(), + r: non_neg_integer(), + s: non_neg_integer(), + v: non_neg_integer(), + authority: binary() | nil + } + + @typedoc """ + * `transaction_hash` - the hash of the associated transaction. + * `index` - the index of this authorization in the authorization list. + * `chain_id` - the ID of the chain for which the authorization was created. + * `address` - the address of the delegate contract. + * `nonce` - the signature nonce. + * `v` - the 'v' component of the signature. + * `r` - the 'r' component of the signature. + * `s` - the 's' component of the signature. + * `authority` - the signer of the authorization. + * `inserted_at` - timestamp indicating when the signed authorization was created. + * `updated_at` - timestamp indicating when the signed authorization was last updated. + * `transaction` - an instance of `Explorer.Chain.Transaction` referenced by `transaction_hash`. + """ + @primary_key false + typed_schema "signed_authorizations" do + field(:index, :integer, primary_key: true, null: false) + field(:chain_id, :integer, null: false) + field(:address, Hash.Address, null: false) + field(:nonce, :integer, null: false) + field(:r, :decimal, null: false) + field(:s, :decimal, null: false) + field(:v, :integer, null: false) + field(:authority, Hash.Address, null: true) + + belongs_to(:transaction, Transaction, + foreign_key: :transaction_hash, + primary_key: true, + references: :hash, + type: Hash.Full + ) + + timestamps() + end + + @doc """ + Validates that the `attrs` are valid. + """ + @spec changeset(Ecto.Schema.t(), map()) :: Ecto.Schema.t() + def changeset(%__MODULE__{} = struct, attrs \\ %{}) do + struct + |> cast(attrs, @required_attrs ++ @optional_attrs) + |> validate_required(@required_attrs) + |> foreign_key_constraint(:transaction_hash) + end +end diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex index 50bafa094425..7e1e26d86942 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex @@ -15,6 +15,7 @@ defmodule Explorer.Chain.SmartContract.Proxy do EIP1822, EIP1967, EIP2535, + EIP7702, EIP930, MasterCopy } @@ -116,7 +117,20 @@ defmodule Explorer.Chain.SmartContract.Proxy do end @doc """ - Decodes address output into 20 bytes address hash + Decodes and formats an address output from a smart contract ABI. + + This function handles various input formats and edge cases when decoding + address outputs from smart contract function calls or events. + + ## Parameters + - `address`: The address output to decode. Can be `nil`, `"0x"`, a binary string, or `:error`. + + ## Returns + - `nil` if the input is `nil`. + - The burn address hash string if the input is `"0x"`. + - A formatted address string if the input is a valid binary string. + - `:error` if the input is `:error`. + - `nil` for any other input type. """ @spec abi_decode_address_output(any()) :: nil | :error | binary() def abi_decode_address_output(nil), do: nil @@ -250,6 +264,20 @@ defmodule Explorer.Chain.SmartContract.Proxy do proxy_abi, go_to_fallback? ], + :get_implementation_address_hash_string_eip7702 + ) + end + + @doc """ + Returns EIP-7702 implementation address or tries next proxy pattern + """ + @spec get_implementation_address_hash_string_eip7702(Hash.Address.t(), any(), bool()) :: + %{implementation_address_hash_strings: [String.t()] | :error | nil, proxy_type: atom() | :unknown} + def get_implementation_address_hash_string_eip7702(proxy_address_hash, proxy_abi, go_to_fallback?) do + get_implementation_address_hash_string_by_module( + EIP7702, + :eip7702, + [proxy_address_hash, proxy_abi, go_to_fallback?], :get_implementation_address_hash_string_eip1967 ) end diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy/eip_7702.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy/eip_7702.ex new file mode 100644 index 000000000000..eb349460fe41 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy/eip_7702.ex @@ -0,0 +1,73 @@ +defmodule Explorer.Chain.SmartContract.Proxy.EIP7702 do + @moduledoc """ + Module for fetching EOA delegate from https://eips.ethereum.org/EIPS/eip-7702 + """ + + alias Explorer.Chain + alias Explorer.Chain.{Address, Hash} + alias Explorer.Chain.SmartContract.Proxy + + @doc """ + Retrieves the delegate address hash string for an EIP-7702 compatible EOA. + + This function fetches the contract code for the given address and extracts + the delegate address according to the EIP-7702 specification. + + ## Parameters + - `address_hash`: The address of the contract to check. + - `options`: Optional keyword list of options (default: `[]`). + + ## Returns + - The delegate address in the hex string format if found and successfully decoded. + - `nil` if the address doesn't exist, has no contract code, or the delegate address + couldn't be extracted or decoded. + """ + @spec get_implementation_address_hash_string(Hash.Address.t(), Keyword.t()) :: String.t() | nil + @spec get_implementation_address_hash_string(Hash.Address.t()) :: String.t() | nil + def get_implementation_address_hash_string(address_hash, options \\ []) do + case Chain.select_repo(options).get(Address, address_hash) do + nil -> + nil + + target_address -> + contract_code = target_address.contract_code + + case contract_code do + %Chain.Data{bytes: contract_code_bytes} -> + contract_code_bytes |> get_delegate_address() |> Proxy.abi_decode_address_output() + + _ -> + nil + end + end + end + + @doc """ + Extracts the EIP-7702 delegate address from the bytecode. + + This function analyzes the given bytecode to identify and extract the delegate + address according to the EIP-7702 specification. + + ## Parameters + - `contract_code_bytes`: The binary representation of the contract bytecode. + + ## Returns + - A string representation of the delegate address prefixed with "0x" if found. + - `nil` if the delegate address is not present in the bytecode. + + ## Examples + iex> get_delegate_address(<<239, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20>>) + "0x0102030405060708090a0b0c0d0e0f10111213" + + iex> get_delegate_address(<<1, 2, 3>>) + nil + """ + @spec get_delegate_address(binary()) :: String.t() | nil + def get_delegate_address(contract_code_bytes) do + case contract_code_bytes do + # 0xef0100 <> address + <<239, 1, 0>> <> <> -> "0x" <> Base.encode16(address, case: :lower) + _ -> nil + end + end +end diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex index 728e0710e518..fd5387e35a23 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex @@ -49,6 +49,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do :comptroller, :eip2535, :clone_with_immutable_arguments, + :eip7702, :unknown ], null: true diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index b6f2f734546f..af9014f52819 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -16,6 +16,7 @@ defmodule Explorer.Chain.Transaction.Schema do Hash, InternalTransaction, Log, + SignedAuthorization, TokenTransfer, TransactionAction, Wei @@ -270,6 +271,11 @@ defmodule Explorer.Chain.Transaction.Schema do type: Hash.Address ) + has_many(:signed_authorizations, SignedAuthorization, + foreign_key: :transaction_hash, + references: :hash + ) + unquote_splicing(@chain_type_fields) end end @@ -1958,12 +1964,13 @@ defmodule Explorer.Chain.Transaction do @doc """ Dynamically adds to/from for `transactions` query based on whether the target address EOA or smart-contract - todo: pay attention to [EIP-5003](https://eips.ethereum.org/EIPS/eip-5003): if it will be included, this logic should be rolled back. + EOAs with code (EIP-7702) are treated as regular EOAs. """ @spec where_transactions_to_from(Hash.Address.t()) :: any() def where_transactions_to_from(address_hash) do with {:ok, address} <- Chain.hash_to_address(address_hash), - true <- Address.smart_contract?(address) do + true <- Address.smart_contract?(address), + false <- Address.eoa_with_code?(address) do dynamic([transaction], transaction.to_address_hash == ^address_hash) else _ -> diff --git a/apps/explorer/lib/explorer/counters/helper.ex b/apps/explorer/lib/explorer/counters/helper.ex index 6a461df32178..2ec17a78bc46 100644 --- a/apps/explorer/lib/explorer/counters/helper.ex +++ b/apps/explorer/lib/explorer/counters/helper.ex @@ -10,6 +10,16 @@ defmodule Explorer.Counters.Helper do read_concurrency: true ] + @doc """ + Returns the current time in milliseconds since the Unix epoch. + + This function retrieves the current UTC time and converts it to Unix + timestamp in milliseconds. + + ## Returns + - The number of milliseconds since the Unix epoch. + """ + @spec current_time() :: non_neg_integer() def current_time do utc_now = DateTime.utc_now() diff --git a/apps/explorer/lib/explorer/utility/address_contract_code_fetch_attempt.ex b/apps/explorer/lib/explorer/utility/address_contract_code_fetch_attempt.ex index 951a401a08a1..167584ab3584 100644 --- a/apps/explorer/lib/explorer/utility/address_contract_code_fetch_attempt.ex +++ b/apps/explorer/lib/explorer/utility/address_contract_code_fetch_attempt.ex @@ -23,7 +23,15 @@ defmodule Explorer.Utility.AddressContractCodeFetchAttempt do end @doc """ - Gets retries number and updated_at for the Explorer.Chain.Address + Retrieves the number of retries and the last update timestamp for a given address. + + ## Parameters + - `address_hash`: The address to query. + + ## Returns + - `{retries_number, updated_at}`: A tuple containing the number of retries and + the last update timestamp. + - `nil`: If no record is found for the given address. """ @spec get_retries_number(Hash.Address.t()) :: {non_neg_integer(), DateTime.t()} | nil def get_retries_number(address_hash) do @@ -37,7 +45,15 @@ defmodule Explorer.Utility.AddressContractCodeFetchAttempt do end @doc """ - Deletes row from address_contract_code_fetch_attempts table for the given address + Deletes the entry from the `address_contract_code_fetch_attempts` table that corresponds to the provided address hash. + + ## Parameters + - `address_hash`: The `t:Explorer.Chain.Hash.Address.t/0` of the address + whose fetch attempt record should be deleted. + + ## Returns + A tuple `{count, nil}`, where `count` is the number of records deleted + (typically 1 if the record existed, 0 otherwise). """ @spec delete(Hash.Address.t()) :: {non_neg_integer(), nil} def delete(address_hash) do @@ -47,14 +63,13 @@ defmodule Explorer.Utility.AddressContractCodeFetchAttempt do end @doc """ - Inserts the number of retries for fetching contract code for a given address. + Inserts the number of retries for fetching contract code for a given address. - ## Parameters + ## Parameters - `address_hash` - The hash of the address for which the retries number is to be inserted. - ## Returns + ## Returns The result of the insertion operation. - """ @spec insert_retries_number(Hash.Address.t()) :: {non_neg_integer(), nil | [term()]} def insert_retries_number(address_hash) do diff --git a/apps/explorer/priv/repo/migrations/20240904161254_create_signed_authorizations.exs b/apps/explorer/priv/repo/migrations/20240904161254_create_signed_authorizations.exs new file mode 100644 index 000000000000..e37cd4297f50 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20240904161254_create_signed_authorizations.exs @@ -0,0 +1,25 @@ +defmodule Explorer.Repo.Migrations.CreateSignedAuthorizations do + use Ecto.Migration + + def change do + create table(:signed_authorizations, primary_key: false) do + add(:transaction_hash, references(:transactions, column: :hash, on_delete: :delete_all, type: :bytea), + null: false, + primary_key: true + ) + + add(:index, :integer, null: false, primary_key: true) + add(:chain_id, :bigint, null: false) + add(:address, :bytea, null: false) + add(:nonce, :integer, null: false) + add(:v, :integer, null: false) + add(:r, :numeric, precision: 100, null: false) + add(:s, :numeric, precision: 100, null: false) + add(:authority, :bytea, null: true) + + timestamps(null: false, type: :utc_datetime_usec) + end + + create(index(:signed_authorizations, [:authority, :nonce])) + end +end diff --git a/apps/explorer/priv/repo/migrations/20240918104231_new_proxy_type_eip7702.exs b/apps/explorer/priv/repo/migrations/20240918104231_new_proxy_type_eip7702.exs new file mode 100644 index 000000000000..85c1ef80c85b --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20240918104231_new_proxy_type_eip7702.exs @@ -0,0 +1,7 @@ +defmodule Explorer.Repo.Migrations.NewProxyTypeEip7702 do + use Ecto.Migration + + def change do + execute("ALTER TYPE proxy_type ADD VALUE 'eip7702' BEFORE 'unknown'") + end +end diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index c86d5443f12c..7ab0f66905eb 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -44,6 +44,7 @@ defmodule Indexer.Block.Fetcher do Addresses, AddressTokenBalances, MintTransfers, + SignedAuthorizations, TokenInstances, TokenTransfers, TransactionActions @@ -236,7 +237,8 @@ defmodule Indexer.Block.Fetcher do tokens: %{params: tokens}, transactions: %{params: transactions_with_receipts}, withdrawals: %{params: withdrawals_params}, - token_instances: %{params: token_instances} + token_instances: %{params: token_instances}, + signed_authorizations: %{params: SignedAuthorizations.parse(transactions_with_receipts)} }, chain_type_import_options = %{ transactions_with_receipts: transactions_with_receipts, diff --git a/apps/indexer/lib/indexer/fetcher/on_demand/contract_code.ex b/apps/indexer/lib/indexer/fetcher/on_demand/contract_code.ex index 3b4823005af6..12385f2f4674 100644 --- a/apps/indexer/lib/indexer/fetcher/on_demand/contract_code.ex +++ b/apps/indexer/lib/indexer/fetcher/on_demand/contract_code.ex @@ -24,8 +24,23 @@ defmodule Indexer.Fetcher.OnDemand.ContractCode do end end + # Attempts to fetch the contract code for a given address. + # + # This function checks if the contract code needs to be fetched and if enough time + # has passed since the last attempt. If conditions are met, it triggers the fetch + # and broadcast process. + # + # ## Parameters + # address: The address of the contract. + # state: The current state of the fetcher, containing JSON-RPC configuration. + # + # ## Returns + # `:ok` in all cases. + @spec fetch_contract_code(Address.t(), %{ + json_rpc_named_arguments: EthereumJSONRPC.json_rpc_named_arguments() + }) :: :ok defp fetch_contract_code(address, state) do - with {:empty_nonce, true} <- {:empty_nonce, is_nil(address.nonce)}, + with {:need_to_fetch, true} <- {:need_to_fetch, fetch?(address)}, {:retries_number, {retries_number, updated_at}} <- {:retries_number, AddressContractCodeFetchAttempt.get_retries_number(address.hash)}, updated_at_ms = DateTime.to_unix(updated_at, :millisecond), @@ -35,7 +50,7 @@ defmodule Indexer.Fetcher.OnDemand.ContractCode do threshold(retries_number)} do fetch_and_broadcast_bytecode(address.hash, state) else - {:empty_nonce, false} -> + {:need_to_fetch, false} -> :ok {:retries_number, nil} -> @@ -47,7 +62,35 @@ defmodule Indexer.Fetcher.OnDemand.ContractCode do end end - defp fetch_and_broadcast_bytecode(address_hash, state) do + # Determines if contract code should be fetched for an address + @spec fetch?(Address.t()) :: boolean() + defp fetch?(address) when is_nil(address.nonce), do: true + # if the address has a signed authorization, it might have a bytecode + # according to EIP-7702 + defp fetch?(%{signed_authorization: %{authority: _}}), do: true + defp fetch?(_), do: false + + # Fetches and broadcasts the bytecode for a given address. + # + # This function attempts to retrieve the contract bytecode for the specified address + # using the Ethereum JSON-RPC API. If successful, it updates the database as described below + # and broadcasts the result: + # 1. Updates the `addresses` table with the contract code if fetched successfully. + # 2. Modifies the `address_contract_code_fetch_attempts` table: + # - Deletes the entry if the code is successfully set. + # - Increments the retry count if the fetch fails or returns empty code. + # 3. Broadcasts a message with the fetched bytecode if successful. + # + # ## Parameters + # address_hash: The `t:Explorer.Chain.Hash.Address.t/0` of the contract. + # state: The current state of the fetcher, containing JSON-RPC configuration. + # + # ## Returns + # `:ok` (the function always returns `:ok`, actual results are handled via side effects) + @spec fetch_and_broadcast_bytecode(Explorer.Chain.Hash.Address.t(), %{ + json_rpc_named_arguments: EthereumJSONRPC.json_rpc_named_arguments() + }) :: :ok + defp fetch_and_broadcast_bytecode(address_hash, %{json_rpc_named_arguments: _} = state) do with {:fetched_code, {:ok, %EthereumJSONRPC.FetchedCodes{params_list: fetched_codes}}} <- {:fetched_code, fetch_codes( @@ -90,10 +133,14 @@ defmodule Indexer.Fetcher.OnDemand.ContractCode do {:noreply, state} end + # An initial threshold to fetch smart-contract bytecode on-demand + @spec update_threshold_ms() :: non_neg_integer() defp update_threshold_ms do Application.get_env(:indexer, __MODULE__)[:threshold] end + # Calculates the delay for the next fetch attempt based on the number of retries + @spec threshold(non_neg_integer()) :: non_neg_integer() defp threshold(retries_number) do delay_in_ms = trunc(update_threshold_ms() * :math.pow(2, retries_number)) diff --git a/apps/indexer/lib/indexer/transform/signed_authorizations.ex b/apps/indexer/lib/indexer/transform/signed_authorizations.ex new file mode 100644 index 000000000000..b7e5f8e50980 --- /dev/null +++ b/apps/indexer/lib/indexer/transform/signed_authorizations.ex @@ -0,0 +1,75 @@ +defmodule Indexer.Transform.SignedAuthorizations do + @moduledoc """ + Helper functions for extracting signed authorizations from EIP-7702 transactions. + """ + + alias Explorer.Chain.{Hash, SignedAuthorization} + + # The magic number used in EIP-7702 to prefix the message to be signed. + @eip7702_magic 0x5 + + @doc """ + Extracts signed authorizations from a list of transactions with receipts. + + This function parses the authorization tuples from EIP-7702 set code transactions, + recovers the authority address from the signature, and prepares the data for database import. + + ## Parameters + - `transactions_with_receipts`: A list of transactions with receipts. + + ## Returns + A list of signed authorizations ready for database import. + """ + @spec parse([ + %{optional(:authorization_list) => [EthereumJSONRPC.SignedAuthorization.params()], optional(any()) => any()} + ]) :: [SignedAuthorization.to_import()] + def parse(transactions_with_receipts) do + transactions_with_receipts + |> Enum.filter(&Map.has_key?(&1, :authorization_list)) + |> Enum.flat_map( + &(&1.authorization_list + |> Enum.with_index() + |> Enum.map(fn {authorization, index} -> + authorization + |> Map.merge(%{ + transaction_hash: &1.hash, + index: index, + authority: recover_authority(authorization) + }) + end)) + ) + end + + # This function recovers the signer address from the signed authorization data using this formula: + # authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s] + @spec recover_authority(EthereumJSONRPC.SignedAuthorization.params()) :: String.t() | nil + defp recover_authority(signed_authorization) do + {:ok, %{bytes: address}} = Hash.Address.cast(signed_authorization.address) + + signed_message = + ExKeccak.hash_256( + <<@eip7702_magic>> <> ExRLP.encode([signed_authorization.chain_id, address, signed_authorization.nonce]) + ) + + authority = + ec_recover(signed_message, signed_authorization.r, signed_authorization.s, signed_authorization.v) + + authority + end + + # This function uses elliptic curve recovery to get the address from the signed message and the signature. + @spec ec_recover(binary(), non_neg_integer(), non_neg_integer(), non_neg_integer()) :: EthereumJSONRPC.address() | nil + defp ec_recover(signed_message, r, s, v) do + r_bytes = <> + s_bytes = <> + + with {:ok, <<_compression::bytes-size(1), public_key::binary>>} <- + ExSecp256k1.recover(signed_message, r_bytes, s_bytes, v), + <<_::bytes-size(12), hash::binary>> <- ExKeccak.hash_256(public_key) do + address = Base.encode16(hash, case: :lower) + "0x" <> address + else + _ -> nil + end + end +end From 92e7ce50f5764c3eaa3f6c87c222d7cb41d1d7a2 Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Fri, 18 Oct 2024 11:57:37 +0300 Subject: [PATCH 227/363] fix: Handle delegatecall in state changes (#10906) --- .../models/transaction_state_helper.ex | 4 ++- .../api/v2/transaction_controller_test.exs | 36 ++++++++++++++++--- .../chain/transaction/state_change.ex | 3 ++ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex index 7774d28759ed..ea100611244d 100644 --- a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex @@ -8,7 +8,7 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do alias Explorer.Chain.Transaction.StateChange alias Explorer.{Chain, PagingOptions, Repo} - alias Explorer.Chain.{BlockNumberHelper, Transaction, Wei} + alias Explorer.Chain.{BlockNumberHelper, InternalTransaction, Transaction, Wei} alias Explorer.Chain.Cache.StateChanges alias Indexer.Fetcher.OnDemand.CoinBalance, as: CoinBalanceOnDemand alias Indexer.Fetcher.OnDemand.TokenBalance, as: TokenBalanceOnDemand @@ -114,6 +114,8 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do ) end + defp internal_transaction_to_coin_balances(%InternalTransaction{call_type: :delegatecall}, _, _, acc), do: acc + defp internal_transaction_to_coin_balances(internal_transaction, previous_block_number, options, acc) do if internal_transaction.value |> Wei.to(:wei) |> Decimal.positive?() do acc diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs index 5b771b7ce662..8d783b4d2dc0 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs @@ -1039,7 +1039,11 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do internal_transaction_from = insert(:address) internal_transaction_to = insert(:address) + internal_transaction_from_delegatecall = insert(:address) + internal_transaction_to_delegatecall = insert(:address) + insert(:internal_transaction, + call_type: :call, transaction: transaction, index: 0, block_number: transaction.block_number, @@ -1053,7 +1057,9 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do to_address: internal_transaction_to ) + # must be ignored, hence we expect only 5 state changes insert(:internal_transaction, + call_type: :delegatecall, transaction: transaction, index: 1, block_number: transaction.block_number, @@ -1061,6 +1067,21 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do block_hash: transaction.block_hash, block_index: 1, value: %Wei{value: Decimal.new(7)}, + from_address_hash: internal_transaction_from_delegatecall.hash, + from_address: internal_transaction_from_delegatecall, + to_address_hash: internal_transaction_to_delegatecall.hash, + to_address: internal_transaction_to_delegatecall + ) + + insert(:internal_transaction, + call_type: :call, + transaction: transaction, + index: 2, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 2, + value: %Wei{value: Decimal.new(7)}, from_address_hash: internal_transaction_from.hash, from_address: internal_transaction_from, to_address_hash: internal_transaction_to.hash, @@ -1070,31 +1091,36 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do insert(:address_coin_balance, address: transaction.from_address, address_hash: transaction.from_address_hash, - block_number: block_before.number + block_number: block_before.number, + value: %Wei{value: Decimal.new(1000)} ) insert(:address_coin_balance, address: transaction.to_address, address_hash: transaction.to_address_hash, - block_number: block_before.number + block_number: block_before.number, + value: %Wei{value: Decimal.new(1000)} ) insert(:address_coin_balance, address: transaction.block.miner, address_hash: transaction.block.miner_hash, - block_number: block_before.number + block_number: block_before.number, + value: %Wei{value: Decimal.new(1000)} ) insert(:address_coin_balance, address: internal_transaction_from, address_hash: internal_transaction_from.hash, - block_number: block_before.number + block_number: block_before.number, + value: %Wei{value: Decimal.new(1000)} ) insert(:address_coin_balance, address: internal_transaction_to, address_hash: internal_transaction_to.hash, - block_number: block_before.number + block_number: block_before.number, + value: %Wei{value: Decimal.new(1000)} ) request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/state-changes") diff --git a/apps/explorer/lib/explorer/chain/transaction/state_change.ex b/apps/explorer/lib/explorer/chain/transaction/state_change.ex index 9bdf9222b6d1..d9b8bd93d579 100644 --- a/apps/explorer/lib/explorer/chain/transaction/state_change.ex +++ b/apps/explorer/lib/explorer/chain/transaction/state_change.ex @@ -56,6 +56,9 @@ defmodule Explorer.Chain.Transaction.StateChange do end end + defp update_coin_balances_from_internal_tx(%InternalTransaction{call_type: :delegatecall}, coin_balances), + do: coin_balances + defp update_coin_balances_from_internal_tx(%InternalTransaction{index: 0}, coin_balances), do: coin_balances defp update_coin_balances_from_internal_tx(internal_tx, coin_balances) do From a23e03e8723f1fafed0f653d09b7d90dafd40e29 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Fri, 18 Oct 2024 13:56:45 +0400 Subject: [PATCH 228/363] fix: Delete incorrect coin balances on reorg (#10879) --- .../lib/explorer/chain/import/runner.ex | 2 +- .../explorer/chain/import/runner/addresses.ex | 4 +- .../explorer/chain/import/runner/blocks.ex | 79 ++++++++++++++++++- .../chain/import/runner/blocks_test.exs | 28 +++++++ 4 files changed, 109 insertions(+), 4 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/import/runner.ex b/apps/explorer/lib/explorer/chain/import/runner.ex index 0a543d6b7c80..97d36a2aff3a 100644 --- a/apps/explorer/lib/explorer/chain/import/runner.ex +++ b/apps/explorer/lib/explorer/chain/import/runner.ex @@ -22,7 +22,7 @@ defmodule Explorer.Chain.Import.Runner do @type changes_list :: [changes] @type changeset_function_name :: atom - @type on_conflict :: :nothing | :replace_all | Ecto.Query.t() + @type on_conflict :: :nothing | :replace_all | {:replace, [atom()]} | Ecto.Query.t() @typedoc """ Runner-specific options under `c:option_key/0` in all options passed to `c:run/3`. diff --git a/apps/explorer/lib/explorer/chain/import/runner/addresses.ex b/apps/explorer/lib/explorer/chain/import/runner/addresses.ex index fe6cea4f7d1e..c74b73c166c0 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/addresses.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/addresses.ex @@ -168,8 +168,8 @@ defmodule Explorer.Chain.Import.Runner.Addresses do required(:timeout) => timeout, required(:timestamps) => Import.timestamps() }) :: {:ok, [Address.t()]} - defp insert(repo, ordered_changes_list, %{timeout: timeout, timestamps: timestamps} = options) - when is_list(ordered_changes_list) do + def insert(repo, ordered_changes_list, %{timeout: timeout, timestamps: timestamps} = options) + when is_list(ordered_changes_list) do on_conflict = Map.get_lazy(options, :on_conflict, &default_on_conflict/0) Import.insert_changes_list( diff --git a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex index 0eab751f7436..e25b41386927 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex @@ -31,7 +31,7 @@ defmodule Explorer.Chain.Import.Runner.Blocks do alias Explorer.Chain.Block.Reward alias Explorer.Chain.Import.Runner alias Explorer.Chain.Import.Runner.Address.CurrentTokenBalances - alias Explorer.Chain.Import.Runner.{TokenInstances, Tokens} + alias Explorer.Chain.Import.Runner.{Addresses, TokenInstances, Tokens} alias Explorer.Prometheus.Instrumenter alias Explorer.Utility.MissingRangesManipulator @@ -160,6 +160,23 @@ defmodule Explorer.Chain.Import.Runner.Blocks do :derive_transaction_forks ) end) + |> Multi.run(:delete_address_coin_balances, fn repo, %{lose_consensus: non_consensus_blocks} -> + Instrumenter.block_import_stage_runner( + fn -> delete_address_coin_balances(repo, non_consensus_blocks, insert_options) end, + :address_referencing, + :blocks, + :delete_address_coin_balances + ) + end) + |> Multi.run(:derive_address_fetched_coin_balances, fn repo, + %{delete_address_coin_balances: delete_address_coin_balances} -> + Instrumenter.block_import_stage_runner( + fn -> derive_address_fetched_coin_balances(repo, delete_address_coin_balances, insert_options) end, + :address_referencing, + :blocks, + :derive_address_fetched_coin_balances + ) + end) |> Multi.run(:delete_address_token_balances, fn repo, %{lose_consensus: non_consensus_blocks} -> Instrumenter.block_import_stage_runner( fn -> delete_address_token_balances(repo, non_consensus_blocks, insert_options) end, @@ -464,6 +481,66 @@ defmodule Explorer.Chain.Import.Runner.Blocks do ) end + defp delete_address_coin_balances(_repo, [], _options), do: {:ok, []} + + defp delete_address_coin_balances(repo, non_consensus_blocks, %{timeout: timeout}) do + non_consensus_block_numbers = Enum.map(non_consensus_blocks, fn {number, _hash} -> number end) + + ordered_query = + from(cb in Address.CoinBalance, + where: cb.block_number in ^non_consensus_block_numbers, + select: map(cb, [:address_hash, :block_number]), + # Enforce TokenBalance ShareLocks order (see docs: sharelocks.md) + order_by: [cb.address_hash, cb.block_number], + lock: "FOR UPDATE" + ) + + query = + from(cb in Address.CoinBalance, + select: cb.address_hash, + inner_join: ordered_address_coin_balance in subquery(ordered_query), + on: + ordered_address_coin_balance.address_hash == cb.address_hash and + ordered_address_coin_balance.block_number == cb.block_number + ) + + try do + {_count, deleted_coin_balances_address_hashes} = repo.delete_all(query, timeout: timeout) + + {:ok, deleted_coin_balances_address_hashes} + rescue + postgrex_error in Postgrex.Error -> + {:error, %{exception: postgrex_error, block_numbers: non_consensus_block_numbers}} + end + end + + defp derive_address_fetched_coin_balances(_repo, [], _options), do: {:ok, []} + + defp derive_address_fetched_coin_balances(repo, deleted_balances_address_hashes, options) do + last_balances_query = + from(cb in Address.CoinBalance, + where: cb.address_hash in ^deleted_balances_address_hashes, + where: not is_nil(cb.value), + distinct: cb.address_hash, + order_by: [asc: cb.address_hash, desc: cb.block_number], + select: %{ + hash: cb.address_hash, + fetched_coin_balance: cb.value, + fetched_coin_balance_block_number: cb.block_number + } + ) + + addresses_params = + last_balances_query + |> repo.all() + |> Enum.sort_by(& &1.hash) + + addresses_options = + Map.put(options, :on_conflict, {:replace, [:fetched_coin_balance, :fetched_coin_balance_block_number]}) + + Addresses.insert(repo, addresses_params, addresses_options) + end + defp delete_address_token_balances(_, [], _), do: {:ok, []} defp delete_address_token_balances(repo, non_consensus_blocks, %{timeout: timeout}) do 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 ee68a91eeef8..86353db3e423 100644 --- a/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs +++ b/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs @@ -203,6 +203,34 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do end) end + test "coin balances are deleted and new balances are derived if some blocks lost consensus", + %{consensus_block: %{number: block_number} = block, options: options} do + %{hash: address_hash} = address = insert(:address) + + prev_block_number = block_number - 1 + + insert(:address_coin_balance, address: address, block_number: block_number) + %{value: prev_value} = insert(:address_coin_balance, address: address, block_number: prev_block_number) + + assert count(Address.CoinBalance) == 2 + + insert(:block, number: block_number, consensus: true) + + assert {:ok, + %{ + delete_address_coin_balances: [^address_hash], + derive_address_fetched_coin_balances: [ + %{ + hash: ^address_hash, + fetched_coin_balance: ^prev_value, + fetched_coin_balance_block_number: ^prev_block_number + } + ] + }} = run_block_consensus_change(block, true, options) + + assert %{value: ^prev_value, block_number: ^prev_block_number} = Repo.one(Address.CoinBalance) + end + test "delete_address_current_token_balances deletes rows with matching block number when consensus is true", %{consensus_block: %{number: block_number} = block, options: options} do %Address.CurrentTokenBalance{address_hash: address_hash, token_contract_address_hash: token_contract_address_hash} = From 898e3e3832ee2ebb3aa563d6e688ef7d12608242 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 18 Oct 2024 14:39:58 +0300 Subject: [PATCH 229/363] fix: Filter out nil implementations from combine_proxy_implementation_addresses_map function result (#10943) --- apps/explorer/lib/explorer/chain/transaction.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index af9014f52819..f80a199eed00 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -2058,6 +2058,7 @@ defmodule Explorer.Chain.Transaction do |> Enum.map(fn implementation_address_hash -> Map.get(implementation_addresses_with_smart_contracts, implementation_address_hash) end) + |> Enum.filter(&(!is_nil(&1))) proxy_implementation_addresses_map |> Map.put(proxy_implementations.proxy_address_hash, implementation_addresses_with_smart_contract_preload) From f3e279d8534d3debd462f62bca7831627ff205bc Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:10:44 +0400 Subject: [PATCH 230/363] chore: Set indexer memory limit based on system info as a fallback (#10697) --- apps/indexer/config/config.exs | 5 +++ apps/indexer/lib/indexer/application.ex | 8 +---- apps/indexer/lib/indexer/memory/monitor.ex | 40 ++++++++++++++++------ apps/indexer/mix.exs | 2 +- config/config_helper.exs | 6 ++-- config/runtime.exs | 1 + cspell.json | 2 ++ docker-compose/envs/common-blockscout.env | 1 + 8 files changed, 42 insertions(+), 23 deletions(-) diff --git a/apps/indexer/config/config.exs b/apps/indexer/config/config.exs index 5ce9ab28a24e..808c3671e34f 100644 --- a/apps/indexer/config/config.exs +++ b/apps/indexer/config/config.exs @@ -20,6 +20,11 @@ config :logger, :indexer, block_number step count error_count shrunk import_id transaction_id)a, metadata_filter: [application: :indexer] +config :os_mon, + start_cpu_sup: false, + start_disksup: false, + start_memsup: true + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{config_env()}.exs" diff --git a/apps/indexer/lib/indexer/application.ex b/apps/indexer/lib/indexer/application.ex index 14ee265fbe8b..5851e73adb4d 100644 --- a/apps/indexer/lib/indexer/application.ex +++ b/apps/indexer/lib/indexer/application.ex @@ -15,12 +15,6 @@ defmodule Indexer.Application do @impl Application def start(_type, _args) do - memory_monitor_options = - case Application.get_env(:indexer, :memory_limit) do - nil -> %{} - integer when is_integer(integer) -> %{limit: integer} - end - memory_monitor_name = Memory.Monitor json_rpc_named_arguments = Application.fetch_env!(:indexer, :json_rpc_named_arguments) @@ -46,7 +40,7 @@ defmodule Indexer.Application do base_children = [ :hackney_pool.child_spec(:token_instance_fetcher, max_connections: pool_size), - {Memory.Monitor, [memory_monitor_options, [name: memory_monitor_name]]}, + {Memory.Monitor, [%{}, [name: memory_monitor_name]]}, {CoinBalanceOnDemand.Supervisor, [json_rpc_named_arguments]}, {ContractCodeOnDemand.Supervisor, [json_rpc_named_arguments]}, {TokenInstanceMetadataRefetchOnDemand.Supervisor, [json_rpc_named_arguments]}, diff --git a/apps/indexer/lib/indexer/memory/monitor.ex b/apps/indexer/lib/indexer/memory/monitor.ex index c907cda6b59a..c4032f7632a2 100644 --- a/apps/indexer/lib/indexer/memory/monitor.ex +++ b/apps/indexer/lib/indexer/memory/monitor.ex @@ -7,9 +7,9 @@ defmodule Indexer.Memory.Monitor do `c:Indexer.Memory.Shrinkable.shrink/0`. """ - require Bitwise require Logger + import Bitwise import Indexer.Logger, only: [process: 1] alias Indexer.Memory.Shrinkable @@ -47,7 +47,7 @@ defmodule Indexer.Memory.Monitor do @impl GenServer def init(options) when is_map(options) do - state = struct!(__MODULE__, options) + state = struct!(__MODULE__, Map.put_new(options, :limit, define_memory_limit())) {:ok, timer_reference} = :timer.send_interval(state.timer_interval, :check) {:ok, %__MODULE__{state | timer_reference: timer_reference}} @@ -66,14 +66,14 @@ defmodule Indexer.Memory.Monitor do end @impl GenServer - def handle_info(:check, state) do + def handle_info(:check, %{limit: limit} = state) do total = :erlang.memory(:total) set_metrics(state) shrunk_state = - if memory_limit() < total do - log_memory(%{limit: memory_limit(), total: total}) + if limit < total do + log_memory(%{limit: limit, total: total}) shrink_or_log(state) %{state | shrunk?: true} else @@ -81,8 +81,8 @@ defmodule Indexer.Memory.Monitor do end final_state = - if state.shrunk? and total <= memory_limit() * @expandable_memory_coefficient do - log_expandable_memory(%{limit: memory_limit(), total: total}) + if state.shrunk? and total <= limit * @expandable_memory_coefficient do + log_expandable_memory(%{limit: limit, total: total}) expand(state) %{state | shrunk?: false} else @@ -94,6 +94,28 @@ defmodule Indexer.Memory.Monitor do {:noreply, final_state} end + defp define_memory_limit do + case Application.get_env(:indexer, :memory_limit) do + integer when is_integer(integer) -> integer + _not_set -> memory_limit_from_system() + end + end + + defp memory_limit_from_system do + default_limit = 1 <<< 30 + + percentage = + case Application.get_env(:explorer, :mode) do + :indexer -> 100 + :all -> Application.get_env(:indexer, :system_memory_percentage) + end + + case :memsup.get_system_memory_data()[:total_memory] do + nil -> default_limit + total_memory -> floor(total_memory * percentage / 100) + end + end + defp flush(message) do receive do ^message -> flush(message) @@ -250,8 +272,4 @@ defmodule Indexer.Memory.Monitor do |> Enum.map(fn pid -> {pid, memory(pid)} end) |> Enum.sort_by(&elem(&1, 1), &>=/2) end - - defp memory_limit do - Application.get_env(:indexer, :memory_limit) - end end diff --git a/apps/indexer/mix.exs b/apps/indexer/mix.exs index acfebe6c6a45..3e2456724dc7 100644 --- a/apps/indexer/mix.exs +++ b/apps/indexer/mix.exs @@ -31,7 +31,7 @@ defmodule Indexer.MixProject do # Run "mix help compile.app" to learn about applications. def application do [ - extra_applications: [:logger], + extra_applications: [:logger, :os_mon], mod: {Indexer.Application, []} ] end diff --git a/config/config_helper.exs b/config/config_helper.exs index 6def45a00aaa..882ef3bfc7ae 100644 --- a/config/config_helper.exs +++ b/config/config_helper.exs @@ -160,16 +160,14 @@ defmodule ConfigHelper do @spec indexer_memory_limit() :: integer() def indexer_memory_limit do - indexer_memory_limit_default = 1 - "INDEXER_MEMORY_LIMIT" - |> safe_get_env(to_string(indexer_memory_limit_default)) + |> safe_get_env(nil) |> String.downcase() |> Integer.parse() |> case do {integer, g} when g in ["g", "gb", ""] -> integer <<< 30 {integer, m} when m in ["m", "mb"] -> integer <<< 20 - _ -> indexer_memory_limit_default <<< 30 + _ -> nil end end diff --git a/config/runtime.exs b/config/runtime.exs index e0be45126204..caa9b9438e0b 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -673,6 +673,7 @@ config :indexer, trace_last_block: trace_last_block, fetch_rewards_way: System.get_env("FETCH_REWARDS_WAY", "trace_block"), memory_limit: ConfigHelper.indexer_memory_limit(), + system_memory_percentage: ConfigHelper.parse_integer_env_var("INDEXER_SYSTEM_MEMORY_PERCENTAGE", 60), receipts_batch_size: ConfigHelper.parse_integer_env_var("INDEXER_RECEIPTS_BATCH_SIZE", 250), receipts_concurrency: ConfigHelper.parse_integer_env_var("INDEXER_RECEIPTS_CONCURRENCY", 10), hide_indexing_progress_alert: ConfigHelper.parse_bool_env_var("INDEXER_HIDE_INDEXING_PROGRESS_ALERT"), diff --git a/cspell.json b/cspell.json index 3e2c5a3b11fc..fe3444f1df18 100644 --- a/cspell.json +++ b/cspell.json @@ -161,6 +161,7 @@ "describedby", "differenceby", "discordapp", + "disksup", "dropzone", "dxgd", "dyntsrohg", @@ -312,6 +313,7 @@ "mdef", "MDWW", "meer", + "memsup", "Mendonça", "Menlo", "mergeable", diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index dec8df0744af..4c4bdafd8561 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -280,6 +280,7 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_COIN_BALANCES_FETCHER_INIT_QUERY_LIMIT= # INDEXER_GRACEFUL_SHUTDOWN_PERIOD= # INDEXER_INTERNAL_TRANSACTIONS_FETCH_ORDER= +# INDEXER_SYSTEM_MEMORY_PERCENTAGE= # WITHDRAWALS_FIRST_BLOCK= # INDEXER_OPTIMISM_L1_RPC= # INDEXER_OPTIMISM_L1_SYSTEM_CONFIG_CONTRACT= From 1b2232a88cf1a2cf0c925c36b715ff88609f7fd2 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 21 Oct 2024 17:49:19 +0300 Subject: [PATCH 231/363] chore: Remove deprecated single implementation property of the smart-contract from the API response (#10715) --- .../lib/block_scout_web/views/api/v2/smart_contract_view.ex | 4 ---- apps/explorer/lib/explorer/chain/address.ex | 3 --- 2 files changed, 7 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex index 5f559decee79..d9f49cb2c8ea 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex @@ -162,7 +162,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do conn ) do bytecode_twin = SmartContract.get_address_verified_bytecode_twin_contract(address.hash, @api_true) - minimal_proxy_address_hash = address.implementation bytecode_twin_contract = bytecode_twin.verified_contract smart_contract_verified = AddressView.smart_contract_verified?(address) fully_verified = SmartContract.verified_with_full_match?(address.hash, @api_true) @@ -203,9 +202,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do "has_methods_write" => write_methods?, "has_methods_read_proxy" => is_proxy, "has_methods_write_proxy" => is_proxy && write_methods?, - # todo: remove this property once frontend is bound to "implementations" - "minimal_proxy_address_hash" => - minimal_proxy_address_hash && Address.checksum(minimal_proxy_address_hash.address_hash), "proxy_type" => proxy_type, "implementations" => implementations, "sourcify_repo_url" => diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index a8f7b8641787..5c68c28b0b97 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -82,9 +82,6 @@ defmodule Explorer.Chain.Address.Schema do field(:ens_domain_name, :string, virtual: true) field(:metadata, :any, virtual: true) - # todo: remove virtual field for a single implementation when frontend is bound to "implementations" object value in API - field(:implementation, :any, virtual: true) - has_one(:smart_contract, SmartContract, references: :hash) has_one(:token, Token, foreign_key: :contract_address_hash, references: :hash) has_one(:proxy_implementations, Implementation, foreign_key: :proxy_address_hash, references: :hash) From f8b4cb794a480759f9fb1116f985c952e1aee385 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:54:47 +0300 Subject: [PATCH 232/363] feat: Add missing filecoin robust addresses (#10935) * feat: Add missing filecoin robust addresses * Narrow down spec * Refactoring --- .../controllers/api/v2/address_controller.ex | 23 ++- .../views/api/v2/address_view.ex | 36 +++-- .../views/api/v2/filecoin_view.ex | 136 +++++++++++++----- .../views/api/v2/search_view.ex | 20 ++- .../views/api/v2/smart_contract_view.ex | 20 ++- .../views/api/v2/token_view.ex | 14 ++ apps/explorer/lib/explorer/chain/address.ex | 28 +++- 7 files changed, 221 insertions(+), 56 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index 012ecc6843f1..08a829248419 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -83,12 +83,23 @@ defmodule BlockScoutWeb.API.V2.AddressController do api?: true ] - @contract_address_preloads [ - :smart_contract, - :contracts_creation_internal_transaction, - :contracts_creation_transaction, - :proxy_implementations - ] + case Application.compile_env(:explorer, :chain_type) do + :filecoin -> + @contract_address_preloads [ + :smart_contract, + [contracts_creation_internal_transaction: :from_address], + [contracts_creation_transaction: :from_address], + :proxy_implementations + ] + + _ -> + @contract_address_preloads [ + :smart_contract, + :contracts_creation_internal_transaction, + :contracts_creation_transaction, + :proxy_implementations + ] + end @nft_necessity_by_association [ necessity_by_association: %{ diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex index b39fe2f26f5c..b3010165fffa 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex @@ -91,7 +91,8 @@ defmodule BlockScoutWeb.API.V2.AddressView do balance = address.fetched_coin_balance && address.fetched_coin_balance.value exchange_rate = Market.get_coin_exchange_rate().usd_value - creator_hash = AddressView.from_address_hash(address) + creation_transaction = Address.creation_transaction(address) + creator_hash = creation_transaction && creation_transaction.from_address_hash creation_tx = creator_hash && AddressView.transaction_hash(address) token = address.token && TokenView.render("token.json", %{token: address.token}) @@ -112,14 +113,18 @@ defmodule BlockScoutWeb.API.V2.AddressView do "has_beacon_chain_withdrawals" => Counters.check_if_withdrawals_at_address(address.hash, @api_true) }) - if Enum.empty?(implementations) do - extended_info - else - Map.merge(extended_info, %{ - "proxy_type" => proxy_type, - "implementations" => implementations - }) - end + result = + if Enum.empty?(implementations) do + extended_info + else + Map.merge(extended_info, %{ + "proxy_type" => proxy_type, + "implementations" => implementations + }) + end + + result + |> chain_type_fields(%{address: creation_transaction && creation_transaction.from_address, field_prefix: "creator"}) end @spec prepare_token_balance(Chain.Address.TokenBalance.t(), boolean()) :: map() @@ -238,4 +243,17 @@ defmodule BlockScoutWeb.API.V2.AddressView do token: token }) end + + case Application.compile_env(:explorer, :chain_type) do + :filecoin -> + defp chain_type_fields(result, params) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + BlockScoutWeb.API.V2.FilecoinView.put_filecoin_robust_address(result, params) + end + + _ -> + defp chain_type_fields(result, _params) do + result + end + end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex index 191fbf6f6918..398bc56f67e9 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex @@ -1,35 +1,105 @@ -defmodule BlockScoutWeb.API.V2.FilecoinView do - @moduledoc """ - View functions for rendering Filecoin-related data in JSON format. - """ - - alias Explorer.Chain.Address - - @doc """ - Extends the json output with a sub-map containing information related to - Filecoin native addressing. - """ - @spec extend_address_json_response(map(), Address.t()) :: map() - def extend_address_json_response(result, %Address{} = address) do - filecoin_id = Map.get(address, :filecoin_id) - filecoin_robust = Map.get(address, :filecoin_robust) - filecoin_actor_type = Map.get(address, :filecoin_actor_type) - - is_fetched = - Enum.all?( - [ - filecoin_id, - filecoin_robust, - filecoin_actor_type - ], - &(not is_nil(&1)) - ) - - Map.put(result, :filecoin, %{ - is_fetched: is_fetched, - id: filecoin_id, - robust: filecoin_robust, - actor_type: filecoin_actor_type - }) +if Application.compile_env(:explorer, :chain_type) == :filecoin do + defmodule BlockScoutWeb.API.V2.FilecoinView do + @moduledoc """ + View functions for rendering Filecoin-related data in JSON format. + """ + + alias Explorer.Chain + alias Explorer.Chain.Address + + @api_true [api?: true] + + @doc """ + Extends the json output with a sub-map containing information related to + Filecoin native addressing. + """ + @spec extend_address_json_response(map(), Address.t()) :: map() + def extend_address_json_response( + result, + %Address{filecoin_id: filecoin_id, filecoin_robust: filecoin_robust, filecoin_actor_type: filecoin_actor_type} + ) do + Map.put(result, :filecoin, %{ + id: filecoin_id, + robust: filecoin_robust, + actor_type: filecoin_actor_type + }) + end + + @spec preload_and_put_filecoin_robust_address(map(), %{ + address_hash: String.t() | nil, + field_prefix: String.t() | nil + }) :: + map() + def preload_and_put_filecoin_robust_address(result, %{address_hash: address_hash} = params) do + address = address_hash && Address.get(address_hash, @api_true) + + put_filecoin_robust_address(result, Map.put(params, :address, address)) + end + + @doc """ + Adds a Filecoin robust address to the given result. + + ## Parameters + + - result: The initial result to which the Filecoin robust address will be added. + - opts: A map containing the following keys: + - `:address` - A struct containing the `filecoin_robust` address. + - `:field_prefix` - A prefix to be used for the field name in the result. + + ## Returns + + The updated result with the Filecoin robust address added. + """ + @spec put_filecoin_robust_address(map(), %{ + required(:address) => Address.t(), + required(:field_prefix) => String.t() | nil, + optional(any) => any + }) :: map() + def put_filecoin_robust_address(result, %{ + address: %Address{filecoin_robust: filecoin_robust}, + field_prefix: field_prefix + }) do + put_filecoin_robust_address_internal(result, filecoin_robust, field_prefix) + end + + def put_filecoin_robust_address(result, %{field_prefix: field_prefix}) do + put_filecoin_robust_address_internal(result, nil, field_prefix) + end + + defp put_filecoin_robust_address_internal(result, filecoin_robust, field_prefix) do + field_name = (field_prefix && "#{field_prefix}_filecoin_robust_address") || "filecoin_robust_address" + Map.put(result, field_name, filecoin_robust) + end + + @doc """ + Preloads and inserts Filecoin robust addresses into the search results. + + ## Parameters + + - search_results: The search results that need to be enriched with Filecoin robust addresses. + + ## Returns + + - The search results with preloaded Filecoin robust addresses. + """ + @spec preload_and_put_filecoin_robust_address_to_search_results(list()) :: list() + def preload_and_put_filecoin_robust_address_to_search_results(search_results) do + addresses_map = + search_results + |> Enum.map(& &1["address"]) + |> Enum.reject(&is_nil/1) + |> Chain.hashes_to_addresses(@api_true) + |> Enum.group_by(&to_string(&1.hash)) + + search_results + |> Enum.map(fn + %{"address" => address_hash} = result when not is_nil(address_hash) -> + address = addresses_map[String.downcase(address_hash)] |> List.first() + put_filecoin_robust_address(result, %{address: address, field_prefix: nil}) + + other -> + other + end) + end end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex index 026f4ba68041..24db1555252a 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 @@ -6,11 +6,14 @@ defmodule BlockScoutWeb.API.V2.SearchView do alias Explorer.Chain.{Address, Beacon.Blob, Block, Hash, Transaction, UserOperation} def render("search_results.json", %{search_results: search_results, next_page_params: next_page_params}) do - %{"items" => Enum.map(search_results, &prepare_search_result/1), "next_page_params" => next_page_params} + %{ + "items" => search_results |> Enum.map(&prepare_search_result/1) |> chain_type_fields(), + "next_page_params" => next_page_params + } end def render("search_results.json", %{search_results: search_results}) do - Enum.map(search_results, &prepare_search_result/1) + search_results |> Enum.map(&prepare_search_result/1) |> chain_type_fields() end def render("search_results.json", %{result: {:ok, result}}) do @@ -159,4 +162,17 @@ defmodule BlockScoutWeb.API.V2.SearchView do defp redirect_search_results(%Blob{} = item) do %{"type" => "blob", "parameter" => to_string(item.hash)} end + + case Application.compile_env(:explorer, :chain_type) do + :filecoin -> + defp chain_type_fields(result) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + BlockScoutWeb.API.V2.FilecoinView.preload_and_put_filecoin_robust_address_to_search_results(result) + end + + _ -> + defp chain_type_fields(result) do + result + end + end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex index d9f49cb2c8ea..80c98107e272 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex @@ -184,10 +184,10 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do target_contract = if smart_contract_verified, do: smart_contract, else: bytecode_twin_contract + verified_twin_address_hash = bytecode_twin_contract && Address.checksum(bytecode_twin_contract.address_hash) + %{ - "verified_twin_address_hash" => - bytecode_twin_contract && - Address.checksum(bytecode_twin_contract.address_hash), + "verified_twin_address_hash" => verified_twin_address_hash, "is_verified" => smart_contract_verified, "is_changed_bytecode" => smart_contract_verified && smart_contract.is_changed_bytecode, "is_partially_verified" => smart_contract.partially_verified && smart_contract_verified, @@ -237,6 +237,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do } |> Map.merge(bytecode_info(address)) |> add_zksync_info(target_contract) + |> chain_type_fields(%{address_hash: verified_twin_address_hash, field_prefix: "verified_twin"}) end def prepare_smart_contract(address, implementations, proxy_type, conn) do @@ -447,4 +448,17 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do def render_json(value, _type) do to_string(value) end + + case Application.compile_env(:explorer, :chain_type) do + :filecoin -> + defp chain_type_fields(result, params) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + BlockScoutWeb.API.V2.FilecoinView.preload_and_put_filecoin_robust_address(result, params) + end + + _ -> + defp chain_type_fields(result, _address) do + result + end + end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex index f1ed2d99537c..935a06ff8df3 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex @@ -42,6 +42,7 @@ defmodule BlockScoutWeb.API.V2.TokenView do "circulating_market_cap" => token.circulating_market_cap } |> maybe_append_bridged_info(token) + |> chain_type_fields(%{address: token.contract_address, field_prefix: nil}) end def render("token_balances.json", %{ @@ -139,4 +140,17 @@ defmodule BlockScoutWeb.API.V2.TokenView do map end end + + case Application.compile_env(:explorer, :chain_type) do + :filecoin -> + defp chain_type_fields(result, params) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + BlockScoutWeb.API.V2.FilecoinView.put_filecoin_robust_address(result, params) + end + + _ -> + defp chain_type_fields(result, _params) do + result + end + end end diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index 5c68c28b0b97..72baeb7babb7 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -134,7 +134,7 @@ defmodule Explorer.Chain.Address do alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.SmartContract.Proxy.EIP7702 alias Explorer.Chain.SmartContract.Proxy.Models.Implementation - alias Explorer.Chain.{Address, Data, Hash} + alias Explorer.Chain.{Address, Data, Hash, InternalTransaction, Transaction} alias Explorer.{Chain, PagingOptions, Repo} @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce decompiled verified gas_used transactions_count token_transfers_count)a @@ -225,8 +225,8 @@ defmodule Explorer.Chain.Address do |> unique_constraint(:hash) end - @spec get(Hash.Address.t(), [Chain.necessity_by_association_option() | Chain.api?()]) :: t() | nil - def get(hash, options) do + @spec get(Hash.Address.t() | binary(), [Chain.necessity_by_association_option() | Chain.api?()]) :: t() | nil + def get(hash, options \\ []) do necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) query = from(address in Address, where: address.hash == ^hash) @@ -611,4 +611,26 @@ defmodule Explorer.Chain.Address do {[], nil} end end + + @doc """ + Retrieves the creation transaction for a given address. + + ## Parameters + - `address`: The address for which to find the creation transaction. + + ## Returns + - `nil` if no creation transaction is found. + - `%InternalTransaction{}` if the creation transaction is an internal transaction. + - `%Transaction{}` if the creation transaction is a regular transaction. + """ + @spec creation_transaction(any()) :: nil | InternalTransaction.t() | Transaction.t() + def creation_transaction(%__MODULE__{contracts_creation_internal_transaction: %InternalTransaction{}} = address) do + address.contracts_creation_internal_transaction + end + + def creation_transaction(%__MODULE__{contracts_creation_transaction: %Transaction{}} = address) do + address.contracts_creation_transaction + end + + def creation_transaction(_address), do: nil end From c408fc482d9251b108f5f3417e2028c1ada33591 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Mon, 21 Oct 2024 19:14:49 +0400 Subject: [PATCH 233/363] feat: Allow to provide DB schema other than public (#10946) --- .../lib/explorer/repo/config_helper.ex | 38 ++++++++++++++++++- ...20231207201701_add_watchlist_id_column.exs | 2 +- ...transactions_add_to_address_hash_index.exs | 6 +-- .../20220919105140_add_method_id_index.exs | 2 +- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/apps/explorer/lib/explorer/repo/config_helper.ex b/apps/explorer/lib/explorer/repo/config_helper.ex index 68b18e005a39..bfed70d369c7 100644 --- a/apps/explorer/lib/explorer/repo/config_helper.ex +++ b/apps/explorer/lib/explorer/repo/config_helper.ex @@ -49,7 +49,7 @@ defmodule Explorer.Repo.ConfigHelper do Application.put_env(:explorer, module, merged) - {:ok, Keyword.put(opts, :url, db_url)} + {:ok, opts |> Keyword.put(:url, remove_search_path(db_url)) |> Keyword.merge(Keyword.take(merged, [:search_path]))} end def ssl_enabled?, do: String.equivalent?(System.get_env("ECTO_USE_SSL") || "true", "true") @@ -58,10 +58,44 @@ defmodule Explorer.Repo.ConfigHelper do # sobelow_skip ["DOS.StringToAtom"] def extract_parameters(database_url) do - ~r/\w*:\/\/(?[a-zA-Z0-9_-]*):(?[a-zA-Z0-9-*#!%^&$_.]*)?@(?(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])):(?\d+)\/(?[a-zA-Z0-9_-]*)/ + ~r/\w*:\/\/(?[a-zA-Z0-9_-]*):(?[a-zA-Z0-9-*#!%^&$_.]*)?@(?(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])):(?\d+)\/(?[a-zA-Z0-9_\-]*)(\?.*search_path=(?[a-zA-Z0-9_\-,]+))?/ |> Regex.named_captures(database_url) |> Keyword.new(fn {k, v} -> {String.to_atom(k), v} end) |> Keyword.put(:url, database_url) + |> adjust_search_path() + end + + defp adjust_search_path(params) do + case params[:search_path] do + empty when empty in [nil, ""] -> Keyword.delete(params, :search_path) + [_search_path] -> params + search_path -> Keyword.put(params, :search_path, [search_path]) + end + end + + # Workaround for Ecto.Repo init. + # It takes parameters from the url in priority over provided options (as strings) + # while Postgrex expects search_path to be a list + # which means that it will always crash if there is a search_path parameter in DB url. + # That's why we need to remove this parameter from DB url before passing it to Ecto. + defp remove_search_path(nil), do: nil + + defp remove_search_path(db_url) do + case URI.parse(db_url) do + %{query: nil} -> + db_url + + %{query: query} = uri -> + query_without_search_path = + query + |> URI.decode_query() + |> Map.delete("search_path") + |> URI.encode_query() + + uri + |> Map.put(:query, query_without_search_path) + |> URI.to_string() + end end defp get_env_vars(vars, env_function) do diff --git a/apps/explorer/priv/account/migrations/20231207201701_add_watchlist_id_column.exs b/apps/explorer/priv/account/migrations/20231207201701_add_watchlist_id_column.exs index 346c9ec05ee8..176b65cd5a84 100644 --- a/apps/explorer/priv/account/migrations/20231207201701_add_watchlist_id_column.exs +++ b/apps/explorer/priv/account/migrations/20231207201701_add_watchlist_id_column.exs @@ -3,7 +3,7 @@ defmodule Explorer.Repo.Account.Migrations.AddWatchlistIdColumn do def change do execute(""" - ALTER TABLE public.account_watchlist_notifications + ALTER TABLE account_watchlist_notifications DROP CONSTRAINT account_watchlist_notifications_watchlist_address_id_fkey; """) diff --git a/apps/explorer/priv/repo/migrations/20191203112646_internal_transactions_add_to_address_hash_index.exs b/apps/explorer/priv/repo/migrations/20191203112646_internal_transactions_add_to_address_hash_index.exs index f0ab8787602e..bf9bc5ce1065 100644 --- a/apps/explorer/priv/repo/migrations/20191203112646_internal_transactions_add_to_address_hash_index.exs +++ b/apps/explorer/priv/repo/migrations/20191203112646_internal_transactions_add_to_address_hash_index.exs @@ -3,15 +3,15 @@ defmodule Explorer.Repo.Migrations.InternalTransactionsAddToAddressHashIndex do def change do execute( - "CREATE INDEX IF NOT EXISTS internal_transactions_from_address_hash_partial_index on public.internal_transactions(from_address_hash, block_number DESC, transaction_index DESC, index DESC) WHERE (((type = 'call') AND (index > 0)) OR (type != 'call'));" + "CREATE INDEX IF NOT EXISTS internal_transactions_from_address_hash_partial_index on internal_transactions(from_address_hash, block_number DESC, transaction_index DESC, index DESC) WHERE (((type = 'call') AND (index > 0)) OR (type != 'call'));" ) execute( - "CREATE INDEX IF NOT EXISTS internal_transactions_to_address_hash_partial_index on public.internal_transactions(to_address_hash, block_number DESC, transaction_index DESC, index DESC) WHERE (((type = 'call') AND (index > 0)) OR (type != 'call'));" + "CREATE INDEX IF NOT EXISTS internal_transactions_to_address_hash_partial_index on internal_transactions(to_address_hash, block_number DESC, transaction_index DESC, index DESC) WHERE (((type = 'call') AND (index > 0)) OR (type != 'call'));" ) execute( - "CREATE INDEX IF NOT EXISTS internal_transactions_created_contract_address_hash_partial_index on public.internal_transactions(created_contract_address_hash, block_number DESC, transaction_index DESC, index DESC) WHERE (((type = 'call') AND (index > 0)) OR (type != 'call'));" + "CREATE INDEX IF NOT EXISTS internal_transactions_created_contract_address_hash_partial_index on internal_transactions(created_contract_address_hash, block_number DESC, transaction_index DESC, index DESC) WHERE (((type = 'call') AND (index > 0)) OR (type != 'call'));" ) drop_if_exists( diff --git a/apps/explorer/priv/repo/migrations/20220919105140_add_method_id_index.exs b/apps/explorer/priv/repo/migrations/20220919105140_add_method_id_index.exs index 95ad38588c15..daa96722b5b1 100644 --- a/apps/explorer/priv/repo/migrations/20220919105140_add_method_id_index.exs +++ b/apps/explorer/priv/repo/migrations/20220919105140_add_method_id_index.exs @@ -5,7 +5,7 @@ defmodule Explorer.Repo.Migrations.AddMethodIdIndex do def up do execute(""" - CREATE INDEX CONCURRENTLY IF NOT EXISTS method_id ON public.transactions USING btree (substring(input for 4)); + CREATE INDEX CONCURRENTLY IF NOT EXISTS method_id ON transactions USING btree (substring(input for 4)); """) end From 6d8aa194c3eea56c23493149691546a7ae88da41 Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Mon, 21 Oct 2024 19:16:48 +0400 Subject: [PATCH 234/363] perf: refactor tx data decoding with fewer DB queries (#10842) * perf: refactor tx data decoding with fewer DB queries * fix: tests * chore: more refactor * chore: fix merge conflicts --- .../api/v2/advanced_filter_controller.ex | 17 +- .../api/v2/token_transfer_controller.ex | 2 +- .../controllers/api/v2/utils_controller.ex | 2 +- .../transaction_interpretation.ex | 4 +- .../views/api/v2/suave_view.ex | 2 +- .../views/api/v2/transaction_view.ex | 16 +- .../block_scout_web/views/transaction_view.ex | 3 +- ...saction_token_transfer_controller_test.exs | 2 + apps/explorer/lib/explorer/chain.ex | 6 +- .../lib/explorer/chain/contract_method.ex | 9 +- .../address_transaction_csv_exporter.ex | 1 - apps/explorer/lib/explorer/chain/log.ex | 2 +- .../explorer/chain/smart_contract/proxy.ex | 49 +-- .../proxy/models/implementation.ex | 8 +- .../lib/explorer/chain/transaction.ex | 313 ++++++++---------- .../proxy/models/implementation_test.exs | 7 + .../chain/smart_contract/proxy_test.exs | 46 ++- .../test/explorer/chain/transaction_test.exs | 16 +- 18 files changed, 224 insertions(+), 281 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex index 35975e90ec2d..d0696c4aff6f 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex @@ -57,7 +57,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do {advanced_filters, next_page} = split_list_by_page(advanced_filters_plus_one) - {decoded_transactions, _abi_acc, methods_acc} = + decoded_transactions = advanced_filters |> Enum.map(fn af -> %Transaction{to_address: af.to_address, input: af.input, hash: af.hash} end) |> Transaction.decode_transactions(true, @api_true) @@ -69,7 +69,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do advanced_filters: advanced_filters, decoded_transactions: decoded_transactions, search_params: %{ - method_ids: method_id_to_name_from_params(full_options[:methods] || [], methods_acc), + method_ids: method_id_to_name_from_params(full_options[:methods] || [], decoded_transactions), tokens: contract_address_hash_to_token_from_params(full_options[:token_contract_address_hashes]) }, next_page_params: next_page_params @@ -142,22 +142,19 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do render(conn, :methods, methods: @methods) end - defp method_id_to_name_from_params(prepared_method_ids, methods_acc) do + defp method_id_to_name_from_params(prepared_method_ids, decoded_transactions) do {decoded_method_ids, method_ids_to_find} = Enum.reduce(prepared_method_ids, {%{}, []}, fn method_id, {decoded, to_decode} -> {:ok, method_id_hash} = Data.cast(method_id) + trimmed_method_id = method_id_hash.bytes |> Base.encode16(case: :lower) case {Map.get(@methods_id_to_name_map, method_id), - methods_acc - |> Map.get(method_id_hash.bytes, []) - |> Enum.find( - &match?(%ContractMethod{abi: %{"type" => "function", "name" => name}} when is_binary(name), &1) - )} do + decoded_transactions |> Enum.find(&match?({:ok, ^trimmed_method_id, _, _}, &1))} do {name, _} when is_binary(name) -> {Map.put(decoded, method_id, name), to_decode} - {_, %ContractMethod{abi: %{"type" => "function", "name" => name}}} when is_binary(name) -> - {Map.put(decoded, method_id, name), to_decode} + {_, {:ok, _, function_signature, _}} when is_binary(function_signature) -> + {Map.put(decoded, method_id, function_signature |> String.split("(") |> Enum.at(0)), to_decode} {nil, nil} -> {decoded, [method_id_hash.bytes | to_decode]} diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_transfer_controller.ex index 05234b6ec791..f353bcc8bcf6 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_transfer_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_transfer_controller.ex @@ -58,7 +58,7 @@ defmodule BlockScoutWeb.API.V2.TokenTransferController do end) |> Enum.uniq() - {decoded_transactions, _, _} = Transaction.decode_transactions(transactions, true, @api_true) + decoded_transactions = Transaction.decode_transactions(transactions, true, @api_true) decoded_transactions_map = transactions diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/utils_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/utils_controller.ex index 1826c67598c1..233066aaff43 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/utils_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/utils_controller.ex @@ -25,7 +25,7 @@ defmodule BlockScoutWeb.API.V2.UtilsController do updated_smart_contract end - {decoded_input, _abi_acc, _methods_acc} = + decoded_input = Transaction.decoded_input_data( %Transaction{ input: data, diff --git a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex index 11eb2458146d..c2c1c8aa7baf 100644 --- a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex +++ b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex @@ -140,7 +140,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do end) skip_sig_provider? = false - {decoded_input, _abi_acc, _methods_acc} = Transaction.decoded_input_data(transaction, skip_sig_provider?, @api_true) + decoded_input = Transaction.decoded_input_data(transaction, skip_sig_provider?, @api_true) decoded_input_data = decoded_input |> Transaction.format_decoded_input() |> TransactionView.decoded_input() @@ -383,7 +383,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do skip_sig_provider? = false - {decoded_input, _abi_acc, _methods_acc} = Transaction.decoded_input_data(mock_tx, skip_sig_provider?, @api_true) + decoded_input = Transaction.decoded_input_data(mock_tx, skip_sig_provider?, @api_true) {mock_tx, decoded_input, decoded_input |> Transaction.format_decoded_input() |> TransactionView.decoded_input()} end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/suave_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/suave_view.ex index 5d334ee06c84..42b3612532ac 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/suave_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/suave_view.ex @@ -27,7 +27,7 @@ defmodule BlockScoutWeb.API.V2.SuaveView do wrapped_max_fee_per_gas = Map.get(transaction, :wrapped_max_fee_per_gas) wrapped_value = Map.get(transaction, :wrapped_value) - {[wrapped_decoded_input], _, _} = + [wrapped_decoded_input] = Transaction.decode_transactions( [ %Transaction{ diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex index 2c921347f131..9b2b3a18dd51 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex @@ -29,7 +29,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do watchlist_names: watchlist_names }) do block_height = Chain.block_height(@api_true) - {decoded_transactions, _, _} = Transaction.decode_transactions(transactions, true, @api_true) + decoded_transactions = Transaction.decode_transactions(transactions, true, @api_true) %{ "items" => @@ -49,7 +49,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do watchlist_names: watchlist_names }) do block_height = Chain.block_height(@api_true) - {decoded_transactions, _, _} = Transaction.decode_transactions(transactions, true, @api_true) + decoded_transactions = Transaction.decode_transactions(transactions, true, @api_true) transactions |> chain_type_transformations() @@ -61,7 +61,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do def render("transactions.json", %{transactions: transactions, next_page_params: next_page_params, conn: conn}) do block_height = Chain.block_height(@api_true) - {decoded_transactions, _, _} = Transaction.decode_transactions(transactions, true, @api_true) + decoded_transactions = Transaction.decode_transactions(transactions, true, @api_true) %{ "items" => @@ -81,7 +81,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do def render("transactions.json", %{transactions: transactions, conn: conn}) do block_height = Chain.block_height(@api_true) - {decoded_transactions, _, _} = Transaction.decode_transactions(transactions, true, @api_true) + decoded_transactions = Transaction.decode_transactions(transactions, true, @api_true) transactions |> chain_type_transformations() @@ -91,7 +91,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do def render("transaction.json", %{transaction: transaction, conn: conn}) do block_height = Chain.block_height(@api_true) - {[decoded_input], _, _} = Transaction.decode_transactions([transaction], false, @api_true) + [decoded_input] = Transaction.decode_transactions([transaction], false, @api_true) transaction |> chain_type_transformations() @@ -115,7 +115,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do end def render("token_transfers.json", %{token_transfers: token_transfers, next_page_params: next_page_params, conn: conn}) do - {decoded_transactions, _, _} = + decoded_transactions = Transaction.decode_transactions(Enum.map(token_transfers, fn tt -> tt.transaction end), true, @api_true) %{ @@ -128,7 +128,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do end def render("token_transfers.json", %{token_transfers: token_transfers, conn: conn}) do - {decoded_transactions, _, _} = + decoded_transactions = Transaction.decode_transactions(Enum.map(token_transfers, fn tt -> tt.transaction end), true, @api_true) token_transfers @@ -137,7 +137,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do end def render("token_transfer.json", %{token_transfer: token_transfer, conn: conn}) do - {[decoded_transaction], _, _} = Transaction.decode_transactions([token_transfer.transaction], true, @api_true) + [decoded_transaction] = Transaction.decode_transactions([token_transfer.transaction], true, @api_true) TokenTransferView.prepare_token_transfer(token_transfer, conn, decoded_transaction) end diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex index 07e6f7396775..81b1b06ad148 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex @@ -395,8 +395,7 @@ defmodule BlockScoutWeb.TransactionView do end def decoded_input_data(transaction) do - {result, _, _} = Transaction.decoded_input_data(transaction, []) - result + Transaction.decoded_input_data(transaction, []) end def decoded_revert_reason(revert_reason, transaction, options) do diff --git a/apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs index 96fcbe6889c8..de82d6c15571 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs @@ -159,6 +159,8 @@ defmodule BlockScoutWeb.TransactionTokenTransferControllerTest do end test "preloads to_address smart contract verified", %{conn: conn} do + TestHelper.get_eip1967_implementation_zero_addresses() + transaction = insert(:transaction_to_verified_contract) conn = get(conn, transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, transaction.hash)) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 346b2dd4fa9d..6472fe13b0fb 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -1095,7 +1095,11 @@ defmodule Explorer.Chain do """ @spec hashes_to_addresses([Hash.Address.t()], [necessity_by_association_option | api?]) :: [Address.t()] - def hashes_to_addresses(hashes, options \\ []) when is_list(hashes) do + def hashes_to_addresses(hashes, options \\ []) + + def hashes_to_addresses([], _), do: [] + + def hashes_to_addresses(hashes, options) when is_list(hashes) do necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) query = diff --git a/apps/explorer/lib/explorer/chain/contract_method.ex b/apps/explorer/lib/explorer/chain/contract_method.ex index 8d1c3815716f..021e62d4f7d9 100644 --- a/apps/explorer/lib/explorer/chain/contract_method.ex +++ b/apps/explorer/lib/explorer/chain/contract_method.ex @@ -109,14 +109,19 @@ defmodule Explorer.Chain.ContractMethod do @doc """ Finds contract methods by selector id """ - @spec find_contract_methods(binary(), [Chain.api?()]) :: [__MODULE__.t()] + @spec find_contract_methods([binary()], [Chain.api?()]) :: [__MODULE__.t()] + def find_contract_methods(method_ids, options) + + def find_contract_methods([], _), do: [] + def find_contract_methods(method_ids, options) do query = from( contract_method in __MODULE__, distinct: contract_method.identifier, where: contract_method.abi["type"] == "function", - where: contract_method.identifier in ^method_ids + where: contract_method.identifier in ^method_ids, + order_by: [asc: contract_method.identifier, asc: contract_method.inserted_at] ) Chain.select_repo(options).all(query) diff --git a/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex b/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex index 88496d4cd3c0..f9676e18688d 100644 --- a/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex +++ b/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex @@ -19,7 +19,6 @@ defmodule Explorer.Chain.CSVExport.AddressTransactionCsvExporter do transactions |> Transaction.decode_transactions(true, api?: true) - |> elem(0) |> Enum.zip(transactions) |> to_csv_format(address_hash, exchange_rate) |> Helper.dump_to_stream() diff --git a/apps/explorer/lib/explorer/chain/log.ex b/apps/explorer/lib/explorer/chain/log.ex index 8fdb03cb8a84..7ef35888444b 100644 --- a/apps/explorer/lib/explorer/chain/log.ex +++ b/apps/explorer/lib/explorer/chain/log.ex @@ -224,7 +224,7 @@ defmodule Explorer.Chain.Log do else case Chain.find_contract_address(address_hash, address_options, false) do {:ok, %{smart_contract: smart_contract}} -> - full_abi = Proxy.combine_proxy_implementation_abi(smart_contract, %{}, true, options) + full_abi = Proxy.combine_proxy_implementation_abi(smart_contract, options) {full_abi, Map.put(acc, address_hash, full_abi)} _ -> diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex index 7e1e26d86942..2412874ccbbc 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex @@ -498,54 +498,15 @@ defmodule Explorer.Chain.SmartContract.Proxy do @doc """ Returns combined ABI from proxy and implementation smart-contracts """ - @spec combine_proxy_implementation_abi(any(), map(), boolean(), any()) :: SmartContract.abi() + @spec combine_proxy_implementation_abi(any(), any()) :: SmartContract.abi() def combine_proxy_implementation_abi( smart_contract, - proxy_implementation_addresses_map \\ %{}, - fetch_proxy?, options \\ [] - ) - - def combine_proxy_implementation_abi( - %SmartContract{abi: abi} = smart_contract, - proxy_implementation_addresses_map, - fetch_proxy?, - options - ) - when not is_nil(abi) do - implementation_abi = - get_implementation_abi(smart_contract, options, proxy_implementation_addresses_map, fetch_proxy?) - - if Enum.empty?(implementation_abi), do: abi, else: implementation_abi ++ abi - end - - def combine_proxy_implementation_abi(smart_contract, proxy_implementation_addresses_map, fetch_proxy?, options) do - get_implementation_abi(smart_contract, options, proxy_implementation_addresses_map, fetch_proxy?) - end - - defp get_implementation_abi(smart_contract, options, proxy_implementation_addresses_map, fetch_proxy?) do - if fetch_proxy? do - Proxy.get_implementation_abi_from_proxy(smart_contract, options) - else - implementations = - proxy_implementation_addresses_map - |> Map.get(smart_contract.address_hash) - - parse_abi_from_proxy_implementations(implementations) - end - end - - defp parse_abi_from_proxy_implementations(nil), do: [] + ) do + proxy_abi = (smart_contract && smart_contract.abi) || [] + implementation_abi = Proxy.get_implementation_abi_from_proxy(smart_contract, options) - defp parse_abi_from_proxy_implementations(implementations) do - implementations - |> Enum.reduce([], fn implementation, acc -> - if implementation.smart_contract && implementation.smart_contract.abi do - acc ++ implementation.smart_contract.abi - else - acc - end - end) + proxy_abi ++ implementation_abi end defp find_input_by_name(inputs, name) do diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex index fd5387e35a23..a60fb55887f5 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex @@ -94,8 +94,12 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do @doc """ Returns all implementations for the given smart-contract address hashes """ - @spec get_proxy_implementations_for_multiple_proxies([Hash.Address.t()], Keyword.t()) :: __MODULE__.t() | nil - def get_proxy_implementations_for_multiple_proxies(proxy_address_hashes, options \\ []) do + @spec get_proxy_implementations_for_multiple_proxies([Hash.Address.t()], Keyword.t()) :: [__MODULE__.t()] + def get_proxy_implementations_for_multiple_proxies(proxy_address_hashes, options \\ []) + + def get_proxy_implementations_for_multiple_proxies([], _), do: [] + + def get_proxy_implementations_for_multiple_proxies(proxy_address_hashes, options) do proxy_address_hashes |> get_proxy_implementations_by_multiple_hashes_query() |> select_repo(options).all() diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index f80a199eed00..75883500c023 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -303,7 +303,6 @@ defmodule Explorer.Chain.Transaction do Data, DenormalizationHelper, Hash, - SmartContract, SmartContract.Proxy, TokenTransfer, Transaction, @@ -737,17 +736,14 @@ defmodule Explorer.Chain.Transaction do {:ok, identifier, text, mapping} _ -> - {result, _, _} = - decoded_input_data( - %Transaction{ - to_address: smart_contract, - hash: hash, - input: %Data{bytes: binary_revert_reason} - }, - options - ) - - result + decoded_input_data( + %Transaction{ + to_address: smart_contract, + hash: hash, + input: %Data{bytes: binary_revert_reason} + }, + options + ) end _ -> @@ -760,23 +756,19 @@ defmodule Explorer.Chain.Transaction do NotLoaded.t() | Transaction.t(), boolean(), [Chain.api?()], - full_abi_acc, - methods_acc, - proxy_implementation_addresses_map - ) :: - {error_type | success_type, full_abi_acc, methods_acc} - when full_abi_acc: map(), - methods_acc: map(), - proxy_implementation_addresses_map: map(), + methods_map, + proxy_implementation_abi_map + ) :: error_type | success_type + when methods_map: map(), + proxy_implementation_abi_map: map(), error_type: {:error, any()} | {:error, :contract_not_verified | :contract_verified, list()}, success_type: {:ok | binary(), any()} | {:ok, binary(), binary(), list()} def decoded_input_data( tx, skip_sig_provider? \\ false, options, - full_abi_acc \\ %{}, - methods_acc \\ %{}, - proxy_implementation_addresses_map \\ %{} + methods_map \\ %{}, + proxy_implementation_abi_map \\ %{} ) # skip decoding if there is no to_address @@ -784,27 +776,25 @@ defmodule Explorer.Chain.Transaction do %__MODULE__{to_address: nil}, _, _, - full_abi_acc, - methods_acc, - _proxy_implementation_addresses_map + _, + _ ), - do: {{:error, :no_to_address}, full_abi_acc, methods_acc} + do: {:error, :no_to_address} # skip decoding if transaction is not loaded - def decoded_input_data(%NotLoaded{}, _, _, full_abi_acc, methods_acc, _proxy_implementation_addresses_map), - do: {{:error, :not_loaded}, full_abi_acc, methods_acc} + def decoded_input_data(%NotLoaded{}, _, _, _, _), + do: {:error, :not_loaded} # skip decoding if input is empty def decoded_input_data( %__MODULE__{input: %{bytes: bytes}}, _, _, - full_abi_acc, - methods_acc, - _proxy_implementation_addresses_map + _, + _ ) when bytes in [nil, <<>>] do - {{:error, :no_input_data}, full_abi_acc, methods_acc} + {:error, :no_input_data} end # skip decoding if to_address is not a contract unless DECODE_NOT_A_CONTRACT_CALLS is set @@ -813,11 +803,10 @@ defmodule Explorer.Chain.Transaction do %__MODULE__{to_address: %{contract_code: nil}}, _, _, - full_abi_acc, - methods_acc, - _proxy_implementation_addresses_map + _, + _ ), - do: {{:error, :not_a_contract_call}, full_abi_acc, methods_acc} + do: {:error, :not_a_contract_call} end # if to_address's smart_contract is nil reduce to the case when to_address is not loaded @@ -829,9 +818,8 @@ defmodule Explorer.Chain.Transaction do }, skip_sig_provider?, options, - full_abi_acc, - methods_acc, - proxy_implementation_addresses_map + methods_map, + proxy_implementation_abi_map ) do decoded_input_data( %__MODULE__{ @@ -841,9 +829,8 @@ defmodule Explorer.Chain.Transaction do }, skip_sig_provider?, options, - full_abi_acc, - methods_acc, - proxy_implementation_addresses_map + methods_map, + proxy_implementation_abi_map ) end @@ -856,9 +843,8 @@ defmodule Explorer.Chain.Transaction do }, skip_sig_provider?, options, - full_abi_acc, - methods_acc, - proxy_implementation_addresses_map + methods_map, + proxy_implementation_abi_map ) do decoded_input_data( %__MODULE__{ @@ -868,9 +854,8 @@ defmodule Explorer.Chain.Transaction do }, skip_sig_provider?, options, - full_abi_acc, - methods_acc, - proxy_implementation_addresses_map + methods_map, + proxy_implementation_abi_map ) end @@ -883,33 +868,26 @@ defmodule Explorer.Chain.Transaction do }, skip_sig_provider?, options, - full_abi_acc, - methods_acc, - proxy_implementation_addresses_map + methods_map, + _proxy_implementation_abi_map ) do - {methods, methods_acc} = - method_id - |> check_methods_cache(methods_acc, options) + methods = check_methods_cache(method_id, methods_map, options) candidates = methods |> Enum.flat_map(fn candidate -> case do_decoded_input_data( data, - %SmartContract{abi: [candidate.abi], address_hash: nil}, - hash, - options, - %{}, - proxy_implementation_addresses_map + [candidate.abi], + hash ) do - {{:ok, _, _, _} = decoded, _} -> [decoded] + {:ok, _, _, _} = decoded -> [decoded] _ -> [] end end) - {{:error, :contract_not_verified, - if(candidates == [], do: decode_function_call_via_sig_provider(input, hash, skip_sig_provider?), else: candidates)}, - full_abi_acc, methods_acc} + {:error, :contract_not_verified, + if(candidates == [], do: decode_function_call_via_sig_provider(input, hash, skip_sig_provider?), else: candidates)} end # if to_address is not loaded and input is not a method call return error @@ -917,11 +895,10 @@ defmodule Explorer.Chain.Transaction do %__MODULE__{to_address: %NotLoaded{}}, _, _, - full_abi_acc, - methods_acc, - _proxy_implementation_addresses_map + _, + _ ) do - {{:error, :contract_not_verified, []}, full_abi_acc, methods_acc} + {:error, :contract_not_verified, []} end def decoded_input_data( @@ -932,20 +909,14 @@ defmodule Explorer.Chain.Transaction do }, skip_sig_provider?, options, - full_abi_acc, - methods_acc, - proxy_implementation_addresses_map + methods_map, + proxy_implementation_abi_map ) do - case do_decoded_input_data( - data, - smart_contract, - hash, - options, - full_abi_acc, - proxy_implementation_addresses_map - ) do + full_abi = check_full_abi_cache(smart_contract, proxy_implementation_abi_map, options) + + case do_decoded_input_data(data, full_abi, hash) do # In some cases transactions use methods of some unpredictable contracts, so we can try to look up for method in a whole DB - {{:error, error}, full_abi_acc} when error in [:could_not_decode, :no_matching_function] -> + {:error, error} when error in [:could_not_decode, :no_matching_function] -> case decoded_input_data( %__MODULE__{ to_address: %NotLoaded{}, @@ -954,22 +925,21 @@ defmodule Explorer.Chain.Transaction do }, skip_sig_provider?, options, - full_abi_acc, - methods_acc, - proxy_implementation_addresses_map + methods_map, + proxy_implementation_abi_map ) do - {{:error, :contract_not_verified, []}, full_abi_acc, methods_acc} -> - {decode_function_call_via_sig_provider_wrapper(input, hash, skip_sig_provider?), full_abi_acc, methods_acc} + {:error, :contract_not_verified, []} -> + decode_function_call_via_sig_provider_wrapper(input, hash, skip_sig_provider?) - {{:error, :contract_not_verified, candidates}, full_abi_acc, methods_acc} -> - {{:error, :contract_verified, candidates}, full_abi_acc, methods_acc} + {:error, :contract_not_verified, candidates} -> + {:error, :contract_verified, candidates} - {_, full_abi_acc, methods_acc} -> - {{:error, :could_not_decode}, full_abi_acc, methods_acc} + _ -> + {:error, :could_not_decode} end - {output, full_abi_acc} -> - {output, full_abi_acc, methods_acc} + output -> + output end end @@ -983,24 +953,13 @@ defmodule Explorer.Chain.Transaction do end end - defp do_decoded_input_data( - data, - smart_contract, - hash, - options, - full_abi_acc, - proxy_implementation_addresses_map \\ %{} - ) do - {full_abi, full_abi_acc} = - check_full_abi_cache(smart_contract, full_abi_acc, options, proxy_implementation_addresses_map) - - {with( - {:ok, {selector, values}} <- find_and_decode(full_abi, data, hash), - {:ok, mapping} <- selector_mapping(selector, values, hash), - identifier <- Base.encode16(selector.method_id, case: :lower), - text <- function_call(selector.function, mapping), - do: {:ok, identifier, text, mapping} - ), full_abi_acc} + defp do_decoded_input_data(data, full_abi, hash) do + with {:ok, {selector, values}} <- find_and_decode(full_abi, data, hash), + {:ok, mapping} <- selector_mapping(selector, values, hash), + identifier <- Base.encode16(selector.method_id, case: :lower), + text <- function_call(selector.function, mapping) do + {:ok, identifier, text, mapping} + end end defp decode_function_call_via_sig_provider(%{bytes: data} = input, hash, skip_sig_provider?) do @@ -1010,8 +969,7 @@ defmodule Explorer.Chain.Transaction do true <- is_list(result), false <- Enum.empty?(result), abi <- [result |> List.first() |> Map.put("outputs", []) |> Map.put("type", "function")], - {{:ok, _, _, _} = candidate, _} <- - do_decoded_input_data(data, %SmartContract{abi: abi, address_hash: nil}, hash, [], %{}) do + {:ok, _, _, _} = candidate <- do_decoded_input_data(data, abi, hash) do [candidate] else _ -> @@ -1019,39 +977,22 @@ defmodule Explorer.Chain.Transaction do end end - defp check_methods_cache(method_id, methods_acc, options) do - if Map.has_key?(methods_acc, method_id) do - {methods_acc[method_id], methods_acc} - else - candidates_query = ContractMethod.find_contract_method_query(method_id, 1) - - result = - candidates_query - |> Chain.select_repo(options).all() - - {result, Map.put(methods_acc, method_id, result)} - end + defp check_methods_cache(method_id, methods_map, options) do + Map.get_lazy(methods_map, method_id, fn -> + method_id + |> ContractMethod.find_contract_method_query(1) + |> Chain.select_repo(options).all() + end) end defp check_full_abi_cache( - %{address_hash: address_hash} = smart_contract, - full_abi_acc, - options, - proxy_implementation_addresses_map + smart_contract, + proxy_implementation_abi_map, + options ) do - if !is_nil(address_hash) && Map.has_key?(full_abi_acc, address_hash) do - {full_abi_acc[address_hash], full_abi_acc} - else - full_abi = - Proxy.combine_proxy_implementation_abi( - smart_contract, - proxy_implementation_addresses_map, - false, - options - ) - - {full_abi, Map.put(full_abi_acc, address_hash, full_abi)} - end + Map.get_lazy(proxy_implementation_abi_map, smart_contract, fn -> + Proxy.combine_proxy_implementation_abi(smart_contract, options) + end) end def get_method_name( @@ -1071,10 +1012,10 @@ defmodule Explorer.Chain.Transaction do true, [] ) do - {{:error, :contract_not_verified, [{:ok, _method_id, decoded_func, _}]}, _, _} -> + {:error, :contract_not_verified, [{:ok, _method_id, decoded_func, _}]} -> parse_method_name(decoded_func) - {{:error, :contract_not_verified, []}, _, _} -> + {:error, :contract_not_verified, []} -> "0x" <> Base.encode16(method_id, case: :lower) _ -> @@ -2000,35 +1941,59 @@ defmodule Explorer.Chain.Transaction do end @doc """ - Receives as input list of transactions and returns tuple {decoded_input_data, abi_acc, methods_acc} + Receives as input list of transactions and returns decoded_input_data Where - `decoded_input_data` is list of results: either `{:ok, _identifier, _text, _mapping}` or `nil` - - `abi_acc` is list of all smart contracts ABIs fetched during decoding - - `methods_acc` is list of all smart contracts methods fetched from `contract_methods` table during decoding """ - @spec decode_transactions([Transaction.t()], boolean(), Keyword.t()) :: {[any()], map(), map()} + @spec decode_transactions([Transaction.t()], boolean(), Keyword.t()) :: [nil | {:ok, String.t(), String.t(), map()}] def decode_transactions(transactions, skip_sig_provider?, opts) do - proxy_implementation_addresses_map = combine_proxy_implementation_addresses_map(transactions) - - {results, abi_acc, methods_acc} = - Enum.reduce(transactions, {[], %{}, %{}}, fn transaction, {results, abi_acc, methods_acc} -> - {result, abi_acc, methods_acc} = - decoded_input_data( - transaction, - skip_sig_provider?, - opts, - abi_acc, - methods_acc, - proxy_implementation_addresses_map - ) + proxy_implementation_abi_map = combine_proxy_implementation_abi_map(transactions) - {[format_decoded_input(result) | results], abi_acc, methods_acc} + # first we assemble an empty methods map, so that decoded_input_data will skip ContractMethod.t() lookup and decoding + empty_methods_map = + transactions + |> Enum.flat_map(fn + %{input: <>} -> [method_id] + _ -> [] end) + |> Enum.into(%{}, &{&1, []}) - {Enum.reverse(results), abi_acc, methods_acc} + # try to decode transaction using full abi data from proxy_implementation_abi_map + decoded_transactions = + transactions + |> Enum.map(fn transaction -> + transaction + |> decoded_input_data(skip_sig_provider?, opts, empty_methods_map, proxy_implementation_abi_map) + |> format_decoded_input() + end) + |> Enum.zip(transactions) + + # assemble a new methods map from methods in non-decoded transactions + methods_map = + decoded_transactions + |> Enum.flat_map(fn + {nil, %{input: <>}} -> [method_id] + _ -> [] + end) + |> Enum.uniq() + |> ContractMethod.find_contract_methods(opts) + |> Enum.into(%{}, &{&1.identifier, [&1]}) + + # decode remaining transaction using methods map + decoded_transactions + |> Enum.map(fn + {nil, transaction} -> + transaction + |> Map.put(:to_address, %NotLoaded{}) + |> decoded_input_data(skip_sig_provider?, opts, methods_map, proxy_implementation_abi_map) + |> format_decoded_input() + + {decoded, _} -> + decoded + end) end - defp combine_proxy_implementation_addresses_map(transactions) do + defp combine_proxy_implementation_abi_map(transactions) do # parse unique address hashes of smart-contracts from to_address and created_contract_address properties of the transactions list unique_to_address_hashes = transactions @@ -2043,32 +2008,34 @@ defmodule Explorer.Chain.Transaction do multiple_proxy_implementations = Implementation.get_proxy_implementations_for_multiple_proxies(unique_to_address_hashes) - # query from the DB address objects with smart_contract preload for all found above implementation addresses - implementation_addresses_with_smart_contracts = + # query from the DB address objects with smart_contract preload for all found above proxy and implementation addresses + addresses_with_smart_contracts = multiple_proxy_implementations |> Enum.flat_map(fn proxy_implementations -> proxy_implementations.address_hashes end) + |> Enum.concat(unique_to_address_hashes) |> Chain.hashes_to_addresses(necessity_by_association: %{smart_contract: :optional}) |> Enum.into(%{}, &{&1.hash, &1}) - # combine map %{proxy_address_hash => the list of implementations as Address.t() object with preloaded SmartContract.t()} + # combine map %{proxy_address_hash => combined proxy abi} multiple_proxy_implementations - |> Enum.reduce(%{}, fn proxy_implementations, proxy_implementation_addresses_map -> - implementation_addresses_with_smart_contract_preload = - proxy_implementations.address_hashes - |> Enum.map(fn implementation_address_hash -> - Map.get(implementation_addresses_with_smart_contracts, implementation_address_hash) + |> Enum.into(%{}, fn proxy_implementations -> + full_abi = + [proxy_implementations.proxy_address_hash | proxy_implementations.address_hashes] + |> Enum.map(&Map.get(addresses_with_smart_contracts, &1)) + |> Enum.flat_map(fn + %{smart_contract: %{abi: abi}} when is_list(abi) -> abi + _ -> [] end) |> Enum.filter(&(!is_nil(&1))) - proxy_implementation_addresses_map - |> Map.put(proxy_implementations.proxy_address_hash, implementation_addresses_with_smart_contract_preload) + {proxy_implementations.proxy_address_hash, full_abi} end) end @doc """ Receives as input result of decoded_input_data/5, returns either nil or decoded input in format: {:ok, _identifier, _text, _mapping} """ - @spec format_decoded_input(any()) :: nil | tuple() + @spec format_decoded_input(any()) :: nil | {:ok, String.t(), String.t(), map()} def format_decoded_input({:error, _, []}), do: nil def format_decoded_input({:error, _, candidates}), do: Enum.at(candidates, 0) def format_decoded_input({:ok, _identifier, _text, _mapping} = decoded), do: decoded diff --git a/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs b/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs index 7893139d5b04..506ba65a1fc4 100644 --- a/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs +++ b/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs @@ -102,6 +102,13 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do assert implementation_1.updated_at == implementation_2.updated_at && contract_1.updated_at == contract_2.updated_at + + proxy = + :explorer + |> Application.get_env(:proxy) + |> Keyword.replace(:fallback_cached_implementation_data_ttl, :timer.seconds(20)) + + Application.put_env(:explorer, :proxy, proxy) end test "get_implementation/1 for twins contract" do diff --git a/apps/explorer/test/explorer/chain/smart_contract/proxy_test.exs b/apps/explorer/test/explorer/chain/smart_contract/proxy_test.exs index 1e7ea53887c0..b38977f85be4 100644 --- a/apps/explorer/test/explorer/chain/smart_contract/proxy_test.exs +++ b/apps/explorer/test/explorer/chain/smart_contract/proxy_test.exs @@ -131,36 +131,36 @@ defmodule Explorer.Chain.SmartContract.ProxyTest do } ] - test "combine_proxy_implementation_abi/4 returns empty [] abi if proxy abi is null" do + test "combine_proxy_implementation_abi/2 returns empty [] abi if proxy abi is null" do proxy_contract_address = insert(:contract_address) - assert Proxy.combine_proxy_implementation_abi( - %SmartContract{address_hash: proxy_contract_address.hash, abi: nil}, - %{}, - false - ) == + assert Proxy.combine_proxy_implementation_abi(%SmartContract{address_hash: proxy_contract_address.hash, abi: nil}) == [] end - test "combine_proxy_implementation_abi/4 returns [] abi for unverified proxy" do + test "combine_proxy_implementation_abi/2 returns [] abi for unverified proxy" do + TestHelper.get_eip1967_implementation_zero_addresses() + proxy_contract_address = insert(:contract_address) smart_contract = insert(:smart_contract, address_hash: proxy_contract_address.hash, abi: [], contract_code_md5: "123") - assert Proxy.combine_proxy_implementation_abi(smart_contract, %{}, false) == [] + assert Proxy.combine_proxy_implementation_abi(smart_contract) == [] end - test "combine_proxy_implementation_abi/4 returns proxy abi if implementation is not verified" do + test "combine_proxy_implementation_abi/2 returns proxy abi if implementation is not verified" do + TestHelper.get_eip1967_implementation_zero_addresses() + proxy_contract_address = insert(:contract_address) smart_contract = insert(:smart_contract, address_hash: proxy_contract_address.hash, abi: @proxy_abi, contract_code_md5: "123") - assert Proxy.combine_proxy_implementation_abi(smart_contract, %{}, false) == @proxy_abi + assert Proxy.combine_proxy_implementation_abi(smart_contract) == @proxy_abi end - test "combine_proxy_implementation_abi/4 returns proxy + implementation abi if implementation is verified" do + test "combine_proxy_implementation_abi/2 returns proxy + implementation abi if implementation is verified" do proxy_contract_address = insert(:contract_address) proxy_smart_contract = @@ -176,9 +176,6 @@ defmodule Explorer.Chain.SmartContract.ProxyTest do name: "impl" ) - implementation_contract_address_with_smart_contract_preload = - implementation_contract_address |> Repo.preload(:smart_contract) - insert(:proxy_implementation, proxy_address_hash: proxy_contract_address.hash, proxy_type: "eip1167", @@ -186,19 +183,7 @@ defmodule Explorer.Chain.SmartContract.ProxyTest do names: [implementation_smart_contract.name] ) - _implementation_contract_address_hash_string = - Base.encode16(implementation_contract_address.hash.bytes, case: :lower) - - proxy_implementation_addresses_map = - %{} - |> Map.put(proxy_contract_address.hash, [implementation_contract_address_with_smart_contract_preload]) - - combined_abi = - Proxy.combine_proxy_implementation_abi( - proxy_smart_contract, - proxy_implementation_addresses_map, - false - ) + combined_abi = Proxy.combine_proxy_implementation_abi(proxy_smart_contract) assert Enum.any?(@proxy_abi, fn el -> el == Enum.at(@implementation_abi, 0) end) == false assert Enum.any?(@proxy_abi, fn el -> el == Enum.at(@implementation_abi, 1) end) == false @@ -482,6 +467,13 @@ defmodule Explorer.Chain.SmartContract.ProxyTest do assert Proxy.proxy_contract?(smart_contract) verify!(EthereumJSONRPC.Mox) + + proxy = + :explorer + |> Application.get_env(:proxy) + |> Keyword.replace(:fallback_cached_implementation_data_ttl, :timer.seconds(20)) + + Application.put_env(:explorer, :proxy, proxy) end defp eip_1967_beacon_proxy_mock_requests( diff --git a/apps/explorer/test/explorer/chain/transaction_test.exs b/apps/explorer/test/explorer/chain/transaction_test.exs index febe9e703f9a..69a456402e7c 100644 --- a/apps/explorer/test/explorer/chain/transaction_test.exs +++ b/apps/explorer/test/explorer/chain/transaction_test.exs @@ -252,7 +252,7 @@ defmodule Explorer.Chain.TransactionTest do test "that a transaction that is not a contract call returns a commensurate error" do transaction = insert(:transaction) - assert {{:error, :not_a_contract_call}, _, _} = Transaction.decoded_input_data(transaction, []) + assert {:error, :not_a_contract_call} = Transaction.decoded_input_data(transaction, []) end test "that a contract call transaction that has no verified contract returns a commensurate error" do @@ -261,20 +261,24 @@ defmodule Explorer.Chain.TransactionTest do |> insert(to_address: insert(:contract_address), input: "0x1234567891") |> Repo.preload(to_address: :smart_contract) - assert {{:error, :contract_not_verified, []}, _, _} = Transaction.decoded_input_data(transaction, []) + assert {:error, :contract_not_verified, []} = Transaction.decoded_input_data(transaction, []) end test "that a contract call transaction that has a verified contract returns the decoded input data" do + TestHelper.get_eip1967_implementation_zero_addresses() + transaction = :transaction_to_verified_contract |> insert() |> Repo.preload(to_address: :smart_contract) - assert {{:ok, "60fe47b1", "set(uint256 x)", [{"x", "uint256", 50}]}, _, _} = + assert {:ok, "60fe47b1", "set(uint256 x)", [{"x", "uint256", 50}]} = Transaction.decoded_input_data(transaction, []) end test "that a contract call will look up a match in contract_methods table" do + TestHelper.get_eip1967_implementation_zero_addresses() + :transaction_to_verified_contract |> insert() |> Repo.preload(to_address: :smart_contract) @@ -291,11 +295,13 @@ defmodule Explorer.Chain.TransactionTest do |> insert(to_address: contract.address, input: "0x" <> input_data) |> Repo.preload(to_address: :smart_contract) - assert {{:ok, "60fe47b1", "set(uint256 x)", [{"x", "uint256", 10}]}, _, _} = + assert {:ok, "60fe47b1", "set(uint256 x)", [{"x", "uint256", 10}]} = Transaction.decoded_input_data(transaction, []) end test "arguments name in function call replaced with argN if it's empty string" do + TestHelper.get_eip1967_implementation_zero_addresses() + contract = insert(:smart_contract, contract_code_md5: "123", @@ -323,7 +329,7 @@ defmodule Explorer.Chain.TransactionTest do |> insert(to_address: contract.address, input: "0x" <> input_data) |> Repo.preload(to_address: :smart_contract) - assert {{:ok, "60fe47b1", "set(uint256 arg0)", [{"arg0", "uint256", 10}]}, _, _} = + assert {:ok, "60fe47b1", "set(uint256 arg0)", [{"arg0", "uint256", 10}]} = Transaction.decoded_input_data(transaction, []) end end From b030792cf17a3fbfd5f0159d4dbfdfbda75ea416 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 22 Oct 2024 10:44:20 +0300 Subject: [PATCH 235/363] refactor: Fixate naming convention for "transaction" and "block number" entities (#10913) * refactor: tx_hash -> transaction_hash, txn -> transaction, "block" in API v2 response to "block_number" * More functions/vars to rename * Update apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/query_fields.ex Co-authored-by: Fedor Ivanov * Rename files * Return l1_tx_origin / l2_tx_gas_limit in optimism view for the compatibility with fe at the time of transaition to new props * Rename shorthand in template * Repair merge --------- Co-authored-by: Fedor Ivanov --- CONTRIBUTING.md | 24 + .../lib/block_scout_web/chain.ex | 26 +- .../account/api/v2/tags_controller.ex | 4 +- .../account/api/v2/user_controller.ex | 6 +- .../account/tag_transaction_controller.ex | 4 +- .../controllers/address_controller.ex | 4 +- .../api/rpc/transaction_controller.ex | 10 +- .../controllers/api/v2/address_controller.ex | 4 +- .../api/v2/advanced_filter_controller.ex | 33 +- .../controllers/api/v2/arbitrum_controller.ex | 11 +- .../controllers/api/v2/block_controller.ex | 4 +- .../controllers/api/v2/fallback_controller.ex | 6 +- .../controllers/api/v2/import_controller.ex | 4 +- .../controllers/api/v2/optimism_controller.ex | 24 +- .../proxy/account_abstraction_controller.ex | 11 +- .../api/v2/proxy/noves_fi_controller.ex | 6 +- .../controllers/api/v2/stats_controller.ex | 2 +- .../api/v2/transaction_controller.ex | 31 +- .../controllers/chain_controller.ex | 6 +- .../controllers/transaction_controller.ex | 4 +- ...saction_internal_transaction_controller.ex | 2 +- .../controllers/transaction_log_controller.ex | 2 +- .../transaction_raw_trace_controller.ex | 2 +- .../transaction_state_controller.ex | 2 +- .../transaction_token_transfer_controller.ex | 2 +- .../lib/block_scout_web/etherscan.ex | 2 +- .../graphql/celo/resolvers/token_transfer.ex | 2 +- ...er_tx.ex => token_transfer_transaction.ex} | 4 +- .../graphql/celo/schema/query_fields.ex | 6 +- .../graphql/celo/schema/types.ex | 6 +- .../block_scout_web/graphql/schema/types.ex | 2 +- .../transaction_interpretation.ex | 21 +- .../models/get_transaction_tags.ex | 8 +- .../models/transaction_state_helper.ex | 11 +- .../lib/block_scout_web/notifier.ex | 6 +- .../lib/block_scout_web/paging_helper.ex | 6 +- .../lib/block_scout_web/routers/api_router.ex | 13 +- .../account/tag_transaction/form.html.eex | 6 +- .../account/tag_transaction/index.html.eex | 6 +- .../account/tag_transaction/row.html.eex | 8 +- .../templates/address/_tile.html.eex | 2 +- .../templates/robots/sitemap.xml.eex | 6 +- .../templates/search/_tile.html.eex | 4 +- .../templates/transaction/_tile.html.eex | 8 +- .../templates/transaction/overview.html.eex | 8 +- .../views/account/api/v2/tags_view.ex | 6 +- .../views/account/api/v2/user_view.ex | 6 +- .../views/api/rpc/contract_view.ex | 6 +- .../views/api/rpc/transaction_view.ex | 10 +- .../views/api/v2/address_view.ex | 16 +- .../views/api/v2/arbitrum_view.ex | 97 +-- .../views/api/v2/block_view.ex | 6 +- .../views/api/v2/ethereum_view.ex | 4 +- .../block_scout_web/views/api/v2/mud_view.ex | 2 + .../views/api/v2/optimism_view.ex | 40 +- .../views/api/v2/polygon_edge_view.ex | 6 +- .../views/api/v2/polygon_zkevm_view.ex | 28 +- .../views/api/v2/search_view.ex | 8 +- .../views/api/v2/smart_contract_view.ex | 2 + .../views/api/v2/stability_view.ex | 6 +- .../views/api/v2/suave_view.ex | 12 +- .../views/api/v2/token_transfer_view.ex | 6 +- .../views/api/v2/transaction_view.ex | 189 +++-- .../views/api/v2/zksync_view.ex | 48 +- .../lib/block_scout_web/views/tab_helper.ex | 2 +- .../views/transaction_state_view.ex | 4 +- .../channels/websocket_v2_test.exs | 36 +- .../account/api/v2/user_controller_test.exs | 60 +- .../api/rpc/address_controller_test.exs | 8 +- .../api/rpc/contract_controller_test.exs | 14 +- .../api/v2/address_controller_test.exs | 693 ++++++++++-------- .../v2/advanced_filter_controller_test.exs | 288 ++++---- .../api/v2/block_controller_test.exs | 52 +- .../api/v2/main_page_controller_test.exs | 24 +- .../api/v2/search_controller_test.exs | 20 +- .../api/v2/smart_contract_controller_test.exs | 16 +- .../api/v2/token_controller_test.exs | 98 +-- .../api/v2/token_transfer_controller_test.exs | 44 +- .../api/v2/transaction_controller_test.exs | 450 ++++++------ .../transaction_controller_test.exs | 2 +- .../verified_contracts_controller_test.exs | 2 +- .../features/viewing_transactions_test.exs | 4 +- .../lib/ethereum_jsonrpc/filecoin.ex | 22 +- .../lib/ethereum_jsonrpc/geth.ex | 20 +- .../lib/ethereum_jsonrpc/geth/tracer.ex | 8 +- .../lib/ethereum_jsonrpc/transaction.ex | 14 +- .../ethereum_jsonrpc/geth/tracer_test.exs | 6 +- .../test/ethereum_jsonrpc/geth_test.exs | 10 +- apps/explorer/lib/encrypt.ex | 4 +- .../lib/explorer/account/notifier/email.ex | 2 +- .../lib/explorer/account/notifier/notify.ex | 2 +- .../lib/explorer/account/notifier/summary.ex | 14 +- .../lib/explorer/account/tag_transaction.ex | 36 +- .../account/watchlist_notification.ex | 17 +- apps/explorer/lib/explorer/chain.ex | 246 ++++--- .../lib/explorer/chain/address/counters.ex | 130 ++-- .../chain/address/metadata_preloader.ex | 22 +- .../lib/explorer/chain/advanced_filter.ex | 79 +- .../chain/arbitrum/batch_transaction.ex | 14 +- .../chain/arbitrum/da_multi_purpose_record.ex | 15 +- .../lib/explorer/chain/arbitrum/reader.ex | 59 +- apps/explorer/lib/explorer/chain/block.ex | 4 +- .../lib/explorer/chain/bridged_token.ex | 42 +- .../chain/cache/addresses_tabs_counters.ex | 31 +- .../lib/explorer/chain/cache/state_changes.ex | 4 +- .../cache/transaction_action_tokens_data.ex | 2 +- .../cache/transaction_action_uniswap_pools.ex | 2 +- .../address_transaction_csv_exporter.ex | 4 +- ...ook_up_smart_contract_sources_on_demand.ex | 4 +- .../runner/arbitrum/batch_transactions.ex | 4 +- .../runner/arbitrum/lifecycle_transactions.ex | 14 +- .../import/runner/internal_transactions.ex | 51 +- ...{txn_batches.ex => transaction_batches.ex} | 26 +- .../polygon_zkevm/lifecycle_transactions.ex | 8 +- .../chain/import/runner/transactions.ex | 6 +- .../runner/zksync/batch_transactions.ex | 4 +- .../runner/zksync/lifecycle_transactions.ex | 8 +- .../runner/zksync/transaction_batches.ex | 10 +- .../chain/import/stage/block_referencing.ex | 2 +- apps/explorer/lib/explorer/chain/log.ex | 4 +- .../lib/explorer/chain/metrics/queries.ex | 126 ++-- .../lib/explorer/chain/optimism/deposit.ex | 6 +- .../explorer/chain/optimism/frame_sequence.ex | 18 +- .../{txn_batch.ex => transaction_batch.ex} | 12 +- .../lib/explorer/chain/optimism/withdrawal.ex | 6 +- .../polygon_zkevm/lifecycle_transaction.ex | 4 +- .../explorer/chain/polygon_zkevm/reader.ex | 4 +- apps/explorer/lib/explorer/chain/search.ex | 38 +- .../lib/explorer/chain/smart_contract.ex | 38 +- .../lib/explorer/chain/transaction.ex | 53 +- .../chain/transaction/history/historian.ex | 2 +- .../chain/transaction/state_change.ex | 94 +-- .../lib/explorer/chain/transaction_action.ex | 4 +- .../chain/zksync/batch_transaction.ex | 10 +- .../lib/explorer/chain/zksync/reader.ex | 6 +- .../chain/zksync/transaction_batch.ex | 10 +- .../counters/block_burnt_fee_counter.ex | 2 +- .../counters/block_priority_fee_counter.ex | 2 +- .../counters/transactions_24h_stats.ex | 24 +- apps/explorer/lib/explorer/etherscan.ex | 6 +- apps/explorer/lib/explorer/graphql/celo.ex | 58 +- .../restore_omitted_weth_transfers.ex | 2 +- apps/explorer/lib/explorer/paging_options.ex | 6 +- .../lib/explorer/smart_contract/helper.ex | 33 +- .../smart_contract/sig_provider_interface.ex | 4 +- .../smart_contract/solidity/verifier.ex | 50 +- .../explorer/smart_contract/vyper/verifier.ex | 8 +- .../third_party_integrations/noves_fi.ex | 16 +- .../20241015091450_rename_tx_hash_field.exs | 9 + ...15093220_rename_tx_hash_field_arbitrum.exs | 7 + ...40121_rename_tx_related_field_optimism.exs | 7 + ...dd_transaction_hash_inserted_at_index.exs} | 2 +- ...nding_internal_transactions_operation.exs} | 2 +- ...> 20200421102450_pending_transactions.exs} | 2 +- ..._in_internal_txs_field_to_transaction.exs} | 0 ...221126103223_add_transactions_indexes.exs} | 2 +- ...93914_allow_nil_transaction_gas_price.exs} | 2 +- ...20241015140214_rename_tx_related_field.exs | 7 + ...1015093336_rename_tx_hash_field_zksync.exs | 7 + .../explorer/account/notifier/email_test.exs | 9 +- .../explorer/account/notifier/notify_test.exs | 34 +- .../account/notifier/summary_test.exs | 106 +-- .../chain/cache/gas_price_oracle_test.exs | 2 +- ...dress_token_transfer_csv_exporter_test.exs | 6 +- .../chain/import/runner/blocks_test.exs | 4 +- .../runner/internal_transactions_test.exs | 32 +- .../test/explorer/chain/import_test.exs | 8 +- apps/explorer/test/explorer/chain_test.exs | 24 +- .../smart_contract/solidity/verifier_test.exs | 2 +- apps/explorer/test/support/factory.ex | 2 +- ...n_importer_transaction_without_block.json} | 0 apps/indexer/lib/indexer/block/fetcher.ex | 14 +- .../lib/indexer/block/realtime/fetcher.ex | 4 +- .../indexer/fetcher/arbitrum/da/celestia.ex | 19 +- .../arbitrum/messages_to_l2_matcher.ex | 104 +-- .../lib/indexer/fetcher/arbitrum/messaging.ex | 22 +- .../arbitrum/tracking_batches_statuses.ex | 16 +- .../lib/indexer/fetcher/arbitrum/utils/db.ex | 52 +- .../indexer/fetcher/arbitrum/utils/helper.ex | 40 +- .../lib/indexer/fetcher/arbitrum/utils/rpc.ex | 22 +- .../workers/historical_messages_on_l2.ex | 30 +- .../arbitrum/workers/l1_finalization.ex | 22 +- .../fetcher/arbitrum/workers/new_batches.ex | 235 +++--- .../arbitrum/workers/new_confirmations.ex | 195 ++--- .../arbitrum/workers/new_l1_executions.ex | 44 +- .../arbitrum/workers/new_messages_to_l2.ex | 33 +- .../indexer/fetcher/internal_transaction.ex | 2 +- .../indexer/fetcher/on_demand/first_trace.ex | 2 +- apps/indexer/lib/indexer/fetcher/optimism.ex | 7 +- .../lib/indexer/fetcher/optimism/deposit.ex | 14 +- .../{txn_batch.ex => transaction_batch.ex} | 112 +-- .../indexer/fetcher/optimism/withdrawal.ex | 8 +- .../fetcher/optimism/withdrawal_event.ex | 26 +- .../lib/indexer/fetcher/polygon_edge.ex | 14 +- .../fetcher/polygon_zkevm/bridge_l1.ex | 8 +- .../fetcher/polygon_zkevm/bridge_l2.ex | 8 +- .../polygon_zkevm/transaction_batch.ex | 71 +- .../fetcher/rollup_l1_reorg_monitor.ex | 2 +- .../lib/indexer/fetcher/shibarium/l1.ex | 8 +- .../lib/indexer/fetcher/shibarium/l2.ex | 12 +- .../lib/indexer/fetcher/transaction_action.ex | 12 +- .../fetcher/zksync/discovery/batches_data.ex | 88 +-- .../fetcher/zksync/discovery/workers.ex | 23 +- .../zksync/status_tracking/committed.ex | 20 +- .../fetcher/zksync/status_tracking/common.ex | 95 +-- .../zksync/status_tracking/executed.ex | 20 +- .../fetcher/zksync/status_tracking/proven.ex | 19 +- .../lib/indexer/fetcher/zksync/utils/db.ex | 60 +- .../lib/indexer/fetcher/zksync/utils/rpc.ex | 38 +- apps/indexer/lib/indexer/helper.ex | 6 +- .../indexer/pending_transactions_sanitizer.ex | 40 +- apps/indexer/lib/indexer/supervisor.ex | 2 +- .../lib/indexer/transform/addresses.ex | 22 +- .../celo/transaction_token_transfers.ex | 55 +- .../lib/indexer/transform/shibarium/bridge.ex | 10 +- .../indexer/transform/transaction_actions.ex | 39 +- apps/indexer/mix.exs | 2 +- .../fetcher/internal_transaction_test.exs | 10 +- .../fetcher/token_instance/helper_test.exs | 2 +- .../token_instance/sanitize_erc721_test.exs | 8 +- config/runtime.exs | 10 +- 221 files changed, 3388 insertions(+), 2891 deletions(-) rename apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/{token_transfer_tx.ex => token_transfer_transaction.ex} (80%) rename apps/explorer/lib/explorer/chain/import/runner/optimism/{txn_batches.ex => transaction_batches.ex} (78%) rename apps/explorer/lib/explorer/chain/optimism/{txn_batch.ex => transaction_batch.ex} (95%) create mode 100644 apps/explorer/priv/account/migrations/20241015091450_rename_tx_hash_field.exs create mode 100644 apps/explorer/priv/arbitrum/migrations/20241015093220_rename_tx_hash_field_arbitrum.exs create mode 100644 apps/explorer/priv/optimism/migrations/20241015140121_rename_tx_related_field_optimism.exs rename apps/explorer/priv/repo/migrations/{20190613065856_add_tx_hash_inserted_at_index.exs => 20190613065856_add_transaction_hash_inserted_at_index.exs} (59%) rename apps/explorer/priv/repo/migrations/{20191018140054_add_pending_internal_txs_operation.exs => 20191018140054_add_pending_internal_transactions_operation.exs} (96%) rename apps/explorer/priv/repo/migrations/{20200421102450_pending_txs.exs => 20200421102450_pending_transactions.exs} (89%) rename apps/explorer/priv/repo/migrations/{20211217201759_add_has_error_in_iternal_txs_field_to_transaction.exs => 20211217201759_add_has_error_in_internal_txs_field_to_transaction.exs} (100%) rename apps/explorer/priv/repo/migrations/{20221126103223_add_txs_indexes.exs => 20221126103223_add_transactions_indexes.exs} (96%) rename apps/explorer/priv/repo/migrations/{20230417093914_allow_nil_tx_gas_price.exs => 20230417093914_allow_nil_transaction_gas_price.exs} (82%) create mode 100644 apps/explorer/priv/repo/migrations/20241015140214_rename_tx_related_field.exs create mode 100644 apps/explorer/priv/zk_sync/migrations/20241015093336_rename_tx_hash_field_zksync.exs rename apps/explorer/test/support/fixture/vcr_cassettes/{transaction_importer_txn_without_block.json => transaction_importer_transaction_without_block.json} (100%) rename apps/indexer/lib/indexer/fetcher/optimism/{txn_batch.ex => transaction_batch.ex} (92%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 694f0e404236..321123881c5d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -59,3 +59,27 @@ There is a [PULL_REQUEST_TEMPLATE.md](PULL_REQUEST_TEMPLATE.md) for this reposit * What was changed for incompatible changes See [#255](https://github.com/blockscout/blockscout/pull/255) as an example PR that uses GitHub keywords and a Changelog to explain multiple changes. + +## Basic Naming Convention + +When contributing to the codebase, please adhere to the following naming conventions to ensure clarity and consistency: + +- Use full names for entities. Avoid abbreviations or shorthand. + - Instead of "tx" or "txn", use "transaction". + - Instead of "txs", use "transactions". + - Instead of "tx_hash" or "txn_hash", use "transaction_hash". + - Instead of "block_num", use "block_number". +- Ensure that variable names are descriptive and convey the purpose or content clearly. +- Consistent naming helps in maintaining readability and understanding of the code, especially for new contributors. + +By following these conventions, we can maintain a clean and understandable codebase. + + +### API V2 Naming Convention + +When contributing to the API v2, please adhere to the following naming conventions for response fields to ensure clarity and consistency: + +- The block number should be returned as a number in the `block_number` property. +- The transaction hash should be returned as a hex string in the `transaction_hash` property. +- All fields that contain the "index" suffix should be returned as numbers. + diff --git a/apps/block_scout_web/lib/block_scout_web/chain.ex b/apps/block_scout_web/lib/block_scout_web/chain.ex index 87bd279ae872..a02b75482915 100644 --- a/apps/block_scout_web/lib/block_scout_web/chain.ex +++ b/apps/block_scout_web/lib/block_scout_web/chain.ex @@ -181,21 +181,21 @@ defmodule BlockScoutWeb.Chain do def paging_options(%{ "address_hash" => address_hash_string, - "tx_hash" => tx_hash_string, + "transaction_hash" => transaction_hash_string, "block_hash" => block_hash_string, "holder_count" => holder_count_string, "name" => name_string, "inserted_at" => inserted_at_string, "item_type" => item_type_string }) - when is_binary(address_hash_string) and is_binary(tx_hash_string) and is_binary(block_hash_string) and + when is_binary(address_hash_string) and is_binary(transaction_hash_string) and is_binary(block_hash_string) and is_binary(holder_count_string) and is_binary(name_string) and is_binary(inserted_at_string) and is_binary(item_type_string) do [ paging_options: %{ @default_paging_options | key: - {address_hash_string, tx_hash_string, block_hash_string, holder_count_string, name_string, + {address_hash_string, transaction_hash_string, block_hash_string, holder_count_string, name_string, inserted_at_string, item_type_string} } ] @@ -376,7 +376,7 @@ defmodule BlockScoutWeb.Chain do when is_binary(inserted_at_string) and is_binary(hash_string) do with {:ok, inserted_at, _} <- DateTime.from_iso8601(inserted_at_string), {:ok, hash} <- string_to_transaction_hash(hash_string) do - [paging_options: %{@default_paging_options | key: {inserted_at, hash}, is_pending_tx: true}] + [paging_options: %{@default_paging_options | key: {inserted_at, hash}, is_pending_transaction: true}] else _ -> [paging_options: @default_paging_options] @@ -406,7 +406,7 @@ defmodule BlockScoutWeb.Chain do end def paging_options(%{"smart_contract_id" => id_str} = params) do - transactions_count = parse_integer(params["tx_count"]) + transactions_count = parse_integer(params["transaction_count"]) coin_balance = parse_integer(params["coin_balance"]) id = parse_integer(id_str) @@ -425,10 +425,10 @@ defmodule BlockScoutWeb.Chain do end end - def paging_options(%{"l1_block_number" => block_number, "tx_hash" => tx_hash}) do + def paging_options(%{"l1_block_number" => block_number, "transaction_hash" => transaction_hash}) do with {block_number, ""} <- Integer.parse(block_number), - {:ok, tx_hash} <- string_to_transaction_hash(tx_hash) do - [paging_options: %{@default_paging_options | key: {block_number, tx_hash}}] + {:ok, transaction_hash} <- string_to_transaction_hash(transaction_hash) do + [paging_options: %{@default_paging_options | key: {block_number, transaction_hash}}] else _ -> [paging_options: @default_paging_options] @@ -707,8 +707,8 @@ defmodule BlockScoutWeb.Chain do %{"smart_contract_id" => smart_contract.id} end - defp paging_params(%OptimismDeposit{l1_block_number: l1_block_number, l2_transaction_hash: l2_tx_hash}) do - %{"l1_block_number" => l1_block_number, "tx_hash" => l2_tx_hash} + defp paging_params(%OptimismDeposit{l1_block_number: l1_block_number, l2_transaction_hash: l2_transaction_hash}) do + %{"l1_block_number" => l1_block_number, "transaction_hash" => l2_transaction_hash} end defp paging_params(%OptimismOutputRoot{l2_output_index: index}) do @@ -718,7 +718,7 @@ defmodule BlockScoutWeb.Chain do defp paging_params(%SmartContract{} = smart_contract) do %{ "smart_contract_id" => smart_contract.id, - "tx_count" => smart_contract.address.transactions_count, + "transaction_count" => smart_contract.address.transactions_count, "coin_balance" => smart_contract.address.fetched_coin_balance && Wei.to(smart_contract.address.fetched_coin_balance, :wei) } @@ -744,7 +744,7 @@ defmodule BlockScoutWeb.Chain do # clause for search results pagination defp paging_params(%{ address_hash: address_hash, - tx_hash: tx_hash, + transaction_hash: transaction_hash, block_hash: block_hash, holder_count: holder_count, name: name, @@ -755,7 +755,7 @@ defmodule BlockScoutWeb.Chain do %{ "address_hash" => address_hash, - "tx_hash" => tx_hash, + "transaction_hash" => transaction_hash, "block_hash" => block_hash, "holder_count" => holder_count, "name" => name, diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/tags_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/tags_controller.ex index 13c37f48b551..b76004ea97ee 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/tags_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/tags_controller.ex @@ -54,7 +54,7 @@ defmodule BlockScoutWeb.Account.Api.V2.TagsController do personal_tags = if is_nil(current_user(conn)) do - %{personal_tags: [], watchlist_names: [], personal_tx_tag: nil} + %{personal_tags: [], watchlist_names: [], personal_transaction_tag: nil} else uid = current_user(conn).id @@ -68,7 +68,7 @@ defmodule BlockScoutWeb.Account.Api.V2.TagsController do }) else _ -> - %{personal_tags: [], watchlist_names: [], personal_tx_tag: nil} + %{personal_tags: [], watchlist_names: [], personal_transaction_tag: nil} end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/user_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/user_controller.ex index 89e60d607f61..461d4afcb442 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/user_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/user_controller.ex @@ -298,13 +298,13 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do end end - def create_tag_transaction(conn, %{"transaction_hash" => tx_hash, "name" => name}) do + def create_tag_transaction(conn, %{"transaction_hash" => transaction_hash, "name" => name}) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, {:ok, transaction_tag} <- TagTransaction.create(%{ name: name, - tx_hash: tx_hash, + transaction_hash: transaction_hash, identity_id: identity.id }) do conn @@ -321,7 +321,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do reject_nil_map_values(%{ id: tag_id, name: attrs["name"], - tx_hash: attrs["transaction_hash"], + transaction_hash: attrs["transaction_hash"], identity_id: identity.id }) ) do diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/tag_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/tag_transaction_controller.ex index cba3dea142ac..4a4c8fca3773 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/account/tag_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/tag_transaction_controller.ex @@ -8,7 +8,7 @@ defmodule BlockScoutWeb.Account.TagTransactionController do def index(conn, _params) do current_user = authenticate!(conn) - render(conn, "index.html", tx_tags: TagTransaction.get_tags_transaction_by_identity_id(current_user.id)) + render(conn, "index.html", transaction_tags: TagTransaction.get_tags_transaction_by_identity_id(current_user.id)) end def new(conn, _params) do @@ -22,7 +22,7 @@ defmodule BlockScoutWeb.Account.TagTransactionController do case TagTransaction.create(%{ name: tag_address["name"], - tx_hash: tag_address["tx_hash"], + transaction_hash: tag_address["transaction_hash"], identity_id: current_user.id }) do {:ok, _} -> diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex index 96556cc387ee..ac50e41af632 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex @@ -58,7 +58,7 @@ defmodule BlockScoutWeb.AddressController do items = addresses_page |> Enum.with_index(1) - |> Enum.map(fn {{address, tx_count}, index} -> + |> Enum.map(fn {{address, transaction_count}, index} -> View.render_to_string( AddressView, "_tile.html", @@ -66,7 +66,7 @@ defmodule BlockScoutWeb.AddressController do index: items_count + index, exchange_rate: exchange_rate, total_supply: total_supply, - tx_count: tx_count + transaction_count: transaction_count ) end) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex index fbea15ca76f1..0d932e6c0741 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex @@ -9,7 +9,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionController do @api_true [api?: true] def gettxinfo(conn, params) do - with {:txhash_param, {:ok, txhash_param}} <- fetch_txhash(params), + with {:txhash_param, {:ok, txhash_param}} <- fetch_transaction_hash(params), {:format, {:ok, transaction_hash}} <- to_transaction_hash(txhash_param), {:transaction, {:ok, %Transaction{revert_reason: revert_reason, error: error} = transaction}} <- transaction_from_hash(transaction_hash), @@ -19,7 +19,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionController do transaction_updated = if (error == "Reverted" || error == "execution reverted") && !revert_reason do - %Transaction{transaction | revert_reason: Chain.fetch_tx_revert_reason(transaction)} + %Transaction{transaction | revert_reason: Chain.fetch_transaction_revert_reason(transaction)} else transaction end @@ -43,7 +43,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionController do end def gettxreceiptstatus(conn, params) do - with {:txhash_param, {:ok, txhash_param}} <- fetch_txhash(params), + with {:txhash_param, {:ok, txhash_param}} <- fetch_transaction_hash(params), {:format, {:ok, transaction_hash}} <- to_transaction_hash(txhash_param) do status = to_transaction_status(transaction_hash) render(conn, :gettxreceiptstatus, %{status: status}) @@ -57,7 +57,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionController do end def getstatus(conn, params) do - with {:txhash_param, {:ok, txhash_param}} <- fetch_txhash(params), + with {:txhash_param, {:ok, txhash_param}} <- fetch_transaction_hash(params), {:format, {:ok, transaction_hash}} <- to_transaction_hash(txhash_param) do error = to_transaction_error(transaction_hash) render(conn, :getstatus, %{error: error}) @@ -70,7 +70,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionController do end end - defp fetch_txhash(params) do + defp fetch_transaction_hash(params) do {:txhash_param, Map.fetch(params, "txhash")} end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index 08a829248419..97ea24054eac 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -506,12 +506,12 @@ defmodule BlockScoutWeb.API.V2.AddressController do with {:ok, address_hash, _address} <- validate_address(address_hash_string, params) do counter_name_to_json_field_name = %{ validations: :validations_count, - txs: :transactions_count, + transactions: :transactions_count, token_transfers: :token_transfers_count, token_balances: :token_balances_count, logs: :logs_count, withdrawals: :withdrawals_count, - internal_txs: :internal_txs_count, + internal_transactions: :internal_transactions_count, celo_election_rewards: :celo_election_rewards_count } diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex index d0696c4aff6f..14223ccabb2f 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex @@ -191,7 +191,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do defp extract_filters(params) do [ - tx_types: prepare_tx_types(params["tx_types"]), + transaction_types: prepare_transaction_types(params["transaction_types"]), methods: params["methods"] |> prepare_methods(), age: prepare_age(params["age_from"], params["age_to"]), from_address_hashes: @@ -221,16 +221,16 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do ] end - @allowed_tx_types ~w(COIN_TRANSFER ERC-20 ERC-404 ERC-721 ERC-1155) + @allowed_transaction_types ~w(COIN_TRANSFER ERC-20 ERC-404 ERC-721 ERC-1155) - defp prepare_tx_types(tx_types) when is_binary(tx_types) do - tx_types + defp prepare_transaction_types(transaction_types) when is_binary(transaction_types) do + transaction_types |> String.upcase() |> String.split(",") - |> Enum.filter(&(&1 in @allowed_tx_types)) + |> Enum.filter(&(&1 in @allowed_transaction_types)) end - defp prepare_tx_types(_), do: nil + defp prepare_transaction_types(_), do: nil defp prepare_methods(methods) when is_binary(methods) do methods @@ -312,14 +312,15 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do defp paging_options(%{ "block_number" => block_number_string, - "transaction_index" => tx_index_string, - "internal_transaction_index" => internal_tx_index_string, + "transaction_index" => transaction_index_string, + "internal_transaction_index" => internal_transaction_index_string, "token_transfer_index" => token_transfer_index_string, "token_transfer_batch_index" => token_transfer_batch_index_string }) do with {block_number, ""} <- block_number_string && Integer.parse(block_number_string), - {tx_index, ""} <- tx_index_string && Integer.parse(tx_index_string), - {:ok, internal_tx_index} <- parse_nullable_integer_paging_parameter(internal_tx_index_string), + {transaction_index, ""} <- transaction_index_string && Integer.parse(transaction_index_string), + {:ok, internal_transaction_index} <- + parse_nullable_integer_paging_parameter(internal_transaction_index_string), {:ok, token_transfer_index} <- parse_nullable_integer_paging_parameter(token_transfer_index_string), {:ok, token_transfer_batch_index} <- parse_nullable_integer_paging_parameter(token_transfer_batch_index_string) do [ @@ -327,8 +328,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do default_paging_options() | key: %{ block_number: block_number, - transaction_index: tx_index, - internal_transaction_index: internal_tx_index, + transaction_index: transaction_index, + internal_transaction_index: internal_transaction_index, token_transfer_index: token_transfer_index, token_transfer_batch_index: token_transfer_batch_index } @@ -354,15 +355,15 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do defp paging_params(%AdvancedFilter{ block_number: block_number, - transaction_index: tx_index, - internal_transaction_index: internal_tx_index, + transaction_index: transaction_index, + internal_transaction_index: internal_transaction_index, token_transfer_index: token_transfer_index, token_transfer_batch_index: token_transfer_batch_index }) do %{ block_number: block_number, - transaction_index: tx_index, - internal_transaction_index: internal_tx_index, + transaction_index: transaction_index, + internal_transaction_index: internal_transaction_index, token_transfer_index: token_transfer_index, token_transfer_batch_index: token_transfer_batch_index } diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/arbitrum_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/arbitrum_controller.ex index e6faabdc8f1c..2c2a538ac7d7 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/arbitrum_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/arbitrum_controller.ex @@ -81,7 +81,7 @@ defmodule BlockScoutWeb.API.V2.ArbitrumController do @doc """ Function to handle GET requests to `/api/v2/arbitrum/batches/da/:data_hash` or - `/api/v2/arbitrum/batches/da/:tx_commitment/:height` endpoints. + `/api/v2/arbitrum/batches/da/:transaction_commitment/:height` endpoints. """ @spec batch_by_data_availability_info(Plug.Conn.t(), map()) :: Plug.Conn.t() def batch_by_data_availability_info(conn, %{"data_hash" => data_hash} = _params) do @@ -95,10 +95,13 @@ defmodule BlockScoutWeb.API.V2.ArbitrumController do end end - def batch_by_data_availability_info(conn, %{"tx_commitment" => tx_commitment, "height" => height} = _params) do + def batch_by_data_availability_info( + conn, + %{"transaction_commitment" => transaction_commitment, "height" => height} = _params + ) do # In case of Celestia, `data_key` is the hash of the height and the commitment hash - with {:ok, :hash, tx_commitment_hash} <- parse_block_hash_or_number_param(tx_commitment), - key <- calculate_celestia_data_key(height, tx_commitment_hash) do + with {:ok, :hash, transaction_commitment_hash} <- parse_block_hash_or_number_param(transaction_commitment), + key <- calculate_celestia_data_key(height, transaction_commitment_hash) do case Reader.get_da_record_by_data_key(key, api?: true) do {:ok, {batch_number, _}} -> batch(conn, %{"batch_number" => batch_number}) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex index f440de46f83f..56ce57306d26 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex @@ -35,7 +35,7 @@ defmodule BlockScoutWeb.API.V2.BlockController do alias Explorer.Chain.Celo.EpochReward, as: CeloEpochReward alias Explorer.Chain.Celo.Reader, as: CeloReader alias Explorer.Chain.InternalTransaction - alias Explorer.Chain.Optimism.TxnBatch, as: OptimismTxnBatch + alias Explorer.Chain.Optimism.TransactionBatch, as: OptimismTransactionBatch case Application.compile_env(:explorer, :chain_type) do :ethereum -> @@ -212,7 +212,7 @@ defmodule BlockScoutWeb.API.V2.BlockController do {blocks, next_page} = batch_number - |> OptimismTxnBatch.batch_blocks(full_options) + |> OptimismTransactionBatch.batch_blocks(full_options) |> split_list_by_page() next_page_params = next_page |> next_page_params(blocks, delete_parameters_from_next_page_params(params)) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex index f6c90431e3fa..df67fa9627c1 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex @@ -30,7 +30,7 @@ defmodule BlockScoutWeb.API.V2.FallbackController do @vyper_smart_contract_is_not_supported "Vyper smart-contracts are not supported by SolidityScan" @unverified_smart_contract "Smart-contract is unverified" @empty_response "Empty response" - @tx_interpreter_service_disabled "Transaction Interpretation Service is disabled" + @transaction_interpreter_service_disabled "Transaction Interpretation Service is disabled" @disabled "API endpoint is disabled" @service_disabled "Service is disabled" @@ -296,11 +296,11 @@ defmodule BlockScoutWeb.API.V2.FallbackController do |> render(:message, %{message: @empty_response}) end - def call(conn, {:tx_interpreter_enabled, false}) do + def call(conn, {:transaction_interpreter_enabled, false}) do conn |> put_status(:forbidden) |> put_view(ApiView) - |> render(:message, %{message: @tx_interpreter_service_disabled}) + |> render(:message, %{message: @transaction_interpreter_service_disabled}) end def call(conn, {:disabled, _}) do diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/import_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/import_controller.ex index e41abe047b57..d31c46560a33 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/import_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/import_controller.ex @@ -70,11 +70,11 @@ defmodule BlockScoutWeb.API.V2.ImportController do {:not_found, {:ok, address}} <- {:not_found, Chain.hash_to_address(address_hash, @api_true, false)}, {:already_verified, smart_contract} when is_nil(smart_contract) <- {:already_verified, SmartContract.address_hash_to_smart_contract(address_hash, @api_true)} do - creation_tx_input = contract_creation_input(address.hash) + creation_transaction_input = contract_creation_input(address.hash) with {:ok, %{"sourceType" => type} = source} <- %{} - |> prepare_bytecode_for_microservice(creation_tx_input, Data.to_string(address.contract_code)) + |> prepare_bytecode_for_microservice(creation_transaction_input, Data.to_string(address.contract_code)) |> EthBytecodeDBInterface.search_contract_in_eth_bytecode_internal_db( address_hash_string, params_to_contract_search_options(params) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/optimism_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/optimism_controller.ex index ecd5d35a4553..b74df7c614f3 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/optimism_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/optimism_controller.ex @@ -22,7 +22,7 @@ defmodule BlockScoutWeb.API.V2.OptimismController do FrameSequence, FrameSequenceBlob, OutputRoot, - TxnBatch, + TransactionBatch, Withdrawal } @@ -32,22 +32,22 @@ defmodule BlockScoutWeb.API.V2.OptimismController do Function to handle GET requests to `/api/v2/optimism/txn-batches` and `/api/v2/optimism/txn-batches/:l2_block_range_start/:l2_block_range_end` endpoints. """ - @spec txn_batches(Plug.Conn.t(), map()) :: Plug.Conn.t() - def txn_batches(conn, params) do + @spec transaction_batches(Plug.Conn.t(), map()) :: Plug.Conn.t() + def transaction_batches(conn, params) do {batches, next_page} = params |> paging_options() |> Keyword.put(:api?, true) |> Keyword.put(:l2_block_range_start, Map.get(params, "l2_block_range_start")) |> Keyword.put(:l2_block_range_end, Map.get(params, "l2_block_range_end")) - |> TxnBatch.list() + |> TransactionBatch.list() |> split_list_by_page() next_page_params = next_page_params(next_page, batches, delete_parameters_from_next_page_params(params)) conn |> put_status(200) - |> render(:optimism_txn_batches, %{ + |> render(:optimism_transaction_batches, %{ batches: batches, next_page_params: next_page_params }) @@ -56,9 +56,9 @@ defmodule BlockScoutWeb.API.V2.OptimismController do @doc """ Function to handle GET requests to `/api/v2/optimism/txn-batches/count` endpoint. """ - @spec txn_batches_count(Plug.Conn.t(), map()) :: Plug.Conn.t() - def txn_batches_count(conn, _params) do - items_count(conn, TxnBatch) + @spec transaction_batches_count(Plug.Conn.t(), map()) :: Plug.Conn.t() + def transaction_batches_count(conn, _params) do + items_count(conn, TransactionBatch) end @doc """ @@ -80,14 +80,14 @@ defmodule BlockScoutWeb.API.V2.OptimismController do batches |> Enum.map(fn fs -> Task.async(fn -> - l2_block_number_from = TxnBatch.edge_l2_block_number(fs.id, :min) - l2_block_number_to = TxnBatch.edge_l2_block_number(fs.id, :max) - tx_count = Transaction.tx_count_for_block_range(l2_block_number_from..l2_block_number_to) + l2_block_number_from = TransactionBatch.edge_l2_block_number(fs.id, :min) + l2_block_number_to = TransactionBatch.edge_l2_block_number(fs.id, :max) + transaction_count = Transaction.transaction_count_for_block_range(l2_block_number_from..l2_block_number_to) {batch_data_container, _} = FrameSequenceBlob.list(fs.id, api?: true) fs |> Map.put(:l2_block_range, l2_block_number_from..l2_block_number_to) - |> Map.put(:tx_count, tx_count) + |> Map.put(:transaction_count, transaction_count) |> Map.put(:batch_data_container, batch_data_container) end) end) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex index 1b0bacf4a578..9571069769bc 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex @@ -24,7 +24,7 @@ defmodule BlockScoutWeb.API.V2.Proxy.AccountAbstractionController do Function to handle GET requests to `/api/v2/proxy/account-abstraction/operations/:user_operation_hash_param/summary` endpoint. """ @spec summary(Plug.Conn.t(), map()) :: - {:error | :format | :tx_interpreter_enabled | non_neg_integer(), any()} | Plug.Conn.t() + {:error | :format | :transaction_interpreter_enabled | non_neg_integer(), any()} | Plug.Conn.t() def summary(conn, %{"operation_hash_param" => operation_hash_string, "just_request_body" => "true"}) do with {:format, {:ok, _operation_hash}} <- {:format, Chain.string_to_transaction_hash(operation_hash_string)}, {200, %{"hash" => _} = user_op} <- AccountAbstraction.get_user_ops_by_hash(operation_hash_string) do @@ -35,12 +35,13 @@ defmodule BlockScoutWeb.API.V2.Proxy.AccountAbstractionController do def summary(conn, %{"operation_hash_param" => operation_hash_string}) do with {:format, {:ok, _operation_hash}} <- {:format, Chain.string_to_transaction_hash(operation_hash_string)}, - {:tx_interpreter_enabled, true} <- {:tx_interpreter_enabled, TransactionInterpretationService.enabled?()}, + {:transaction_interpreter_enabled, true} <- + {:transaction_interpreter_enabled, TransactionInterpretationService.enabled?()}, {200, %{"hash" => _} = user_op} <- AccountAbstraction.get_user_ops_by_hash(operation_hash_string) do {response, code} = case TransactionInterpretationService.interpret_user_operation(user_op) do {:ok, response} -> {response, 200} - {:error, %Jason.DecodeError{}} -> {%{error: "Error while tx interpreter response decoding"}, 500} + {:error, %Jason.DecodeError{}} -> {%{error: "Error while transaction interpreter response decoding"}, 500} {{:error, error}, code} -> {%{error: error}, code} end @@ -233,10 +234,10 @@ defmodule BlockScoutWeb.API.V2.Proxy.AccountAbstractionController do defp try_to_decode_call_data(%{"call_data" => _call_data} = user_op) do user_op_hash = user_op["hash"] - {_mock_tx, _decoded_call_data, decoded_call_data_json} = + {_mock_transaction, _decoded_call_data, decoded_call_data_json} = TransactionInterpretationService.decode_user_op_calldata(user_op_hash, user_op["call_data"]) - {_mock_tx, _decoded_execute_call_data, decoded_execute_call_data_json} = + {_mock_transaction, _decoded_execute_call_data, decoded_execute_call_data_json} = TransactionInterpretationService.decode_user_op_calldata(user_op_hash, user_op["execute_call_data"]) user_op diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/noves_fi_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/noves_fi_controller.ex index c14d2bc60040..09f7523777e8 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/noves_fi_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/noves_fi_controller.ex @@ -16,7 +16,7 @@ defmodule BlockScoutWeb.API.V2.Proxy.NovesFiController do necessity_by_association: %{}, api?: true ), - url = NovesFi.tx_url(transaction_hash_string), + url = NovesFi.transaction_url(transaction_hash_string), {response, status} <- NovesFi.api_request(url, conn), {:is_empty_response, false} <- {:is_empty_response, is_nil(response)} do conn @@ -31,7 +31,7 @@ defmodule BlockScoutWeb.API.V2.Proxy.NovesFiController do @spec address_transactions(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} def address_transactions(conn, %{"address_hash_param" => address_hash_string} = params) do with {:ok, _address_hash, _address} <- AddressController.validate_address(address_hash_string, params), - url = NovesFi.address_txs_url(address_hash_string), + url = NovesFi.address_transactions_url(address_hash_string), {response, status} <- NovesFi.api_request(url, conn), {:is_empty_response, false} <- {:is_empty_response, is_nil(response)} do conn @@ -45,7 +45,7 @@ defmodule BlockScoutWeb.API.V2.Proxy.NovesFiController do """ @spec describe_transactions(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} def describe_transactions(conn, _) do - url = NovesFi.describe_txs_url() + url = NovesFi.describe_transactions_url() with {response, status} <- NovesFi.api_request(url, conn, :post_transactions), {:is_empty_response, false} <- {:is_empty_response, is_nil(response)} do diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex index 804974f88ebb..b429832bd513 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex @@ -109,7 +109,7 @@ defmodule BlockScoutWeb.API.V2.StatsController do transaction_history_data = date_range |> Enum.map(fn row -> - %{date: row.date, tx_count: row.number_of_transactions} + %{date: row.date, transaction_count: row.number_of_transactions} end) json(conn, %{ diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex index c828b27f57a8..41b7f7fa52ab 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex @@ -42,7 +42,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do alias Explorer.Chain.Arbitrum.Reader, as: ArbitrumReader alias Explorer.Chain.Beacon.Reader, as: BeaconReader alias Explorer.Chain.{Hash, InternalTransaction, Transaction} - alias Explorer.Chain.Optimism.TxnBatch, as: OptimismTxnBatch + alias Explorer.Chain.Optimism.TransactionBatch, as: OptimismTransactionBatch alias Explorer.Chain.PolygonZkevm.Reader, as: PolygonZkevmReader alias Explorer.Chain.ZkSync.Reader, as: ZkSyncReader alias Explorer.Counters.{FreshPendingTransactionsCounter, Transactions24hStats} @@ -95,7 +95,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional } - @token_transfers_in_tx_necessity_by_association %{ + @token_transfers_in_transaction_necessity_by_association %{ [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, token: :required @@ -160,7 +160,12 @@ defmodule BlockScoutWeb.API.V2.TransactionController do api?: true ), preloaded <- - Chain.preload_token_transfers(transaction, @token_transfers_in_tx_necessity_by_association, @api_true, false) do + Chain.preload_token_transfers( + transaction, + @token_transfers_in_transaction_necessity_by_association, + @api_true, + false + ) do conn |> put_status(200) |> render(:transaction, %{ @@ -208,7 +213,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do transactions = batch_number |> PolygonZkevmReader.batch_transactions(api?: true) - |> Enum.map(fn tx -> tx.hash end) + |> Enum.map(fn transaction -> transaction.hash end) |> Chain.hashes_to_transactions(api?: true, necessity_by_association: @transaction_necessity_by_association) conn @@ -245,8 +250,8 @@ defmodule BlockScoutWeb.API.V2.TransactionController do def optimism_batch(conn, %{"batch_number" => batch_number_string} = params) do {batch_number, ""} = Integer.parse(batch_number_string) - l2_block_number_from = OptimismTxnBatch.edge_l2_block_number(batch_number, :min) - l2_block_number_to = OptimismTxnBatch.edge_l2_block_number(batch_number, :max) + l2_block_number_from = OptimismTransactionBatch.edge_l2_block_number(batch_number, :min) + l2_block_number_to = OptimismTransactionBatch.edge_l2_block_number(batch_number, :max) transactions_plus_one = if is_nil(l2_block_number_from) or is_nil(l2_block_number_to) do @@ -307,7 +312,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do transactions_plus_one = batch_number |> batch_transactions_fun.(@api_true) - |> Enum.map(fn tx -> tx.tx_hash end) + |> Enum.map(fn transaction -> transaction.transaction_hash end) |> Chain.hashes_to_transactions(full_options) {transactions, next_page} = split_list_by_page(transactions_plus_one) @@ -462,7 +467,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do conn |> put_status(200) |> render(:logs, %{ - tx_hash: transaction_hash, + transaction_hash: transaction_hash, logs: logs |> maybe_preload_ens() |> maybe_preload_metadata(), next_page_params: next_page_params }) @@ -520,7 +525,8 @@ defmodule BlockScoutWeb.API.V2.TransactionController do end def summary(conn, %{"transaction_hash_param" => transaction_hash_string, "just_request_body" => "true"} = params) do - with {:tx_interpreter_enabled, true} <- {:tx_interpreter_enabled, TransactionInterpretationService.enabled?()}, + with {:transaction_interpreter_enabled, true} <- + {:transaction_interpreter_enabled, TransactionInterpretationService.enabled?()}, {:ok, transaction, _transaction_hash} <- validate_transaction(transaction_hash_string, params, necessity_by_association: %{ @@ -541,10 +547,11 @@ defmodule BlockScoutWeb.API.V2.TransactionController do {:format, :error} | {:not_found, {:error, :not_found}} | {:restricted_access, true} - | {:tx_interpreter_enabled, boolean} + | {:transaction_interpreter_enabled, boolean} | Plug.Conn.t() def summary(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do - with {:tx_interpreter_enabled, true} <- {:tx_interpreter_enabled, TransactionInterpretationService.enabled?()}, + with {:transaction_interpreter_enabled, true} <- + {:transaction_interpreter_enabled, TransactionInterpretationService.enabled?()}, {:ok, transaction, _transaction_hash} <- validate_transaction(transaction_hash_string, params, necessity_by_association: %{ @@ -556,7 +563,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do {response, code} = case TransactionInterpretationService.interpret(transaction) do {:ok, response} -> {response, 200} - {:error, %Jason.DecodeError{}} -> {%{error: "Error while tx interpreter response decoding"}, 500} + {:error, %Jason.DecodeError{}} -> {%{error: "Error while transaction interpreter response decoding"}, 500} {{:error, error}, code} -> {%{error: error}, code} end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex index 41bd04b4a536..a867058d4b5a 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex @@ -97,13 +97,13 @@ defmodule BlockScoutWeb.ChainController do encoded_results = results |> Enum.map(fn item -> - tx_hash_bytes = Map.get(item, :tx_hash) + transaction_hash_bytes = Map.get(item, :transaction_hash) block_hash_bytes = Map.get(item, :block_hash) item = - if tx_hash_bytes do + if transaction_hash_bytes do item - |> Map.replace(:tx_hash, "0x" <> Base.encode16(tx_hash_bytes, case: :lower)) + |> Map.replace(:transaction_hash, "0x" <> Base.encode16(transaction_hash_bytes, case: :lower)) else item end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex index 166566e35536..3f26d9af4cde 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex @@ -167,7 +167,7 @@ defmodule BlockScoutWeb.TransactionController do transaction: transaction, from_tags: get_address_tags(transaction.from_address_hash, current_user(conn)), to_tags: get_address_tags(transaction.to_address_hash, current_user(conn)), - tx_tags: + transaction_tags: get_transaction_with_addresses_tags( transaction, current_user(conn) @@ -196,7 +196,7 @@ defmodule BlockScoutWeb.TransactionController do transaction: transaction, from_tags: get_address_tags(transaction.from_address_hash, current_user(conn)), to_tags: get_address_tags(transaction.to_address_hash, current_user(conn)), - tx_tags: + transaction_tags: get_transaction_with_addresses_tags( transaction, current_user(conn) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex index f3f9cc4910c9..7af84eb1ea46 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex @@ -110,7 +110,7 @@ defmodule BlockScoutWeb.TransactionInternalTransactionController do transaction: transaction, from_tags: get_address_tags(transaction.from_address_hash, current_user(conn)), to_tags: get_address_tags(transaction.to_address_hash, current_user(conn)), - tx_tags: + transaction_tags: get_transaction_with_addresses_tags( transaction, current_user(conn) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex index 53ba82ed3571..fe0c93ad5134 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex @@ -101,7 +101,7 @@ defmodule BlockScoutWeb.TransactionLogController do exchange_rate: Market.get_coin_exchange_rate(), from_tags: get_address_tags(transaction.from_address_hash, current_user(conn)), to_tags: get_address_tags(transaction.to_address_hash, current_user(conn)), - tx_tags: + transaction_tags: get_transaction_with_addresses_tags( transaction, current_user(conn) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex index 748281903a38..6beedaf261c9 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex @@ -60,7 +60,7 @@ defmodule BlockScoutWeb.TransactionRawTraceController do transaction: transaction, from_tags: get_address_tags(transaction.from_address_hash, current_user(conn)), to_tags: get_address_tags(transaction.to_address_hash, current_user(conn)), - tx_tags: + transaction_tags: get_transaction_with_addresses_tags( transaction, current_user(conn) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex index b071a2e09992..15cc258da8fa 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex @@ -104,7 +104,7 @@ defmodule BlockScoutWeb.TransactionStateController do transaction: transaction, from_tags: get_address_tags(transaction.from_address_hash, current_user(conn)), to_tags: get_address_tags(transaction.to_address_hash, current_user(conn)), - tx_tags: + transaction_tags: get_transaction_with_addresses_tags( transaction, current_user(conn) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex index 437b8515c3aa..0db5840c9827 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex @@ -113,7 +113,7 @@ defmodule BlockScoutWeb.TransactionTokenTransferController do transaction: transaction, from_tags: get_address_tags(transaction.from_address_hash, current_user(conn)), to_tags: get_address_tags(transaction.to_address_hash, current_user(conn)), - tx_tags: + transaction_tags: get_transaction_with_addresses_tags( transaction, current_user(conn) diff --git a/apps/block_scout_web/lib/block_scout_web/etherscan.ex b/apps/block_scout_web/lib/block_scout_web/etherscan.ex index b6be61809eb5..472bf82780d7 100644 --- a/apps/block_scout_web/lib/block_scout_web/etherscan.ex +++ b/apps/block_scout_web/lib/block_scout_web/etherscan.ex @@ -1149,7 +1149,7 @@ defmodule BlockScoutWeb.Etherscan do confirmations: @confirmation_type, success: %{ type: "boolean", - definition: "Flag for success during tx execution", + definition: "Flag for success during transaction execution", example: ~s(true) }, from: @address_hash_type, diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer.ex b/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer.ex index bf21bc8b5c5d..0eb2a5a32375 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer.ex @@ -9,7 +9,7 @@ defmodule BlockScoutWeb.GraphQL.Celo.Resolvers.TokenTransfer do def get_by(%{transaction_hash: hash}, args, _) do hash - |> GraphQL.token_tx_transfers_query_by_txhash() + |> GraphQL.token_transaction_transfers_query_by_transaction_hash() |> Connection.from_query(&Repo.all/1, args, options(args)) end diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer_tx.ex b/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer_transaction.ex similarity index 80% rename from apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer_tx.ex rename to apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer_transaction.ex index f038c3b92c2a..4b39ec0c4bd2 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer_tx.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/celo/resolvers/token_transfer_transaction.ex @@ -1,4 +1,4 @@ -defmodule BlockScoutWeb.GraphQL.Celo.Resolvers.TokenTransferTx do +defmodule BlockScoutWeb.GraphQL.Celo.Resolvers.TokenTransferTransaction do @moduledoc false alias Absinthe.Relay.Connection @@ -15,7 +15,7 @@ defmodule BlockScoutWeb.GraphQL.Celo.Resolvers.TokenTransferTx do end address_hash - |> GraphQL.token_tx_transfers_query_for_address(offset, limit) + |> GraphQL.token_transaction_transfers_query_for_address(offset, limit) |> Connection.from_query(&Repo.all/1, connection_args, options(args)) end diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/query_fields.ex b/apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/query_fields.ex index f24fa7b6354d..8e2d277accc7 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/query_fields.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/query_fields.ex @@ -3,7 +3,7 @@ defmodule BlockScoutWeb.GraphQL.Celo.QueryFields do Query fields for the CELO schema. """ - alias BlockScoutWeb.GraphQL.Celo.Resolvers.TokenTransferTx + alias BlockScoutWeb.GraphQL.Celo.Resolvers.TokenTransferTransaction use Absinthe.Schema.Notation use Absinthe.Relay.Schema, :modern @@ -11,11 +11,11 @@ defmodule BlockScoutWeb.GraphQL.Celo.QueryFields do defmacro generate do quote do @desc "Gets token transfer transactions." - connection field(:token_transfer_txs, node_type: :transfer_tx) do + connection field(:token_transfer_txs, node_type: :transfer_transaction) do arg(:address_hash, :address_hash) arg(:count, :integer) - resolve(&TokenTransferTx.get_by/3) + resolve(&TokenTransferTransaction.get_by/3) complexity(fn %{first: first}, child_complexity -> first * child_complexity diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/types.ex b/apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/types.ex index 6d65f185f0b9..61419d03e998 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/types.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/celo/schema/types.ex @@ -35,7 +35,7 @@ defmodule BlockScoutWeb.GraphQL.Celo.Schema.Types do @desc """ Represents a CELO token transfer between addresses. """ - node object(:transfer_tx, id_fetcher: &transfer_tx_id_fetcher/2) do + node object(:transfer_transaction, id_fetcher: &transfer_transaction_id_fetcher/2) do field(:gateway_fee_recipient, :address_hash) field(:gateway_fee, :address_hash) field(:fee_currency, :address_hash) @@ -62,10 +62,10 @@ defmodule BlockScoutWeb.GraphQL.Celo.Schema.Types do end end - connection(node_type: :transfer_tx) + connection(node_type: :transfer_transaction) connection(node_type: :celo_transfer) - defp transfer_tx_id_fetcher( + defp transfer_transaction_id_fetcher( %{transaction_hash: transaction_hash, address_hash: address_hash}, _ ) do diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex b/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex index 6cfde913b422..f9fdee7ba7e1 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex @@ -42,7 +42,7 @@ defmodule BlockScoutWeb.GraphQL.Schema.Transaction do field(:max_priority_fee_per_gas, :wei) field(:max_fee_per_gas, :wei) field(:type, :integer) - field(:has_error_in_internal_txs, :boolean) + field(:has_error_in_internal_transactions, :boolean) field :block, :block do resolve(&Block.get_by/3) diff --git a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex index c2c1c8aa7baf..f2daacb0c442 100644 --- a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex +++ b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex @@ -57,7 +57,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do end @doc """ - Build the request body as for the tx interpreter POST request. + Build the request body as for the transaction interpreter POST request. """ @spec get_request_body(Transaction.t()) :: map() def get_request_body(transaction) do @@ -65,7 +65,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do end @doc """ - Build the request body as for the tx interpreter POST request. + Build the request body as for the transaction interpreter POST request. """ @spec get_user_op_request_body(map()) :: map() def get_user_op_request_body(user_op) do @@ -160,7 +160,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do value: transaction_with_meta.value, method: Transaction.method_name(transaction_with_meta, Transaction.format_decoded_input(decoded_input)), status: transaction_with_meta.status, - tx_types: TransactionView.tx_types(transaction_with_meta), + transaction_types: TransactionView.transaction_types(transaction_with_meta), raw_input: transaction_with_meta.input, decoded_input: decoded_input_data, token_transfers: prepare_token_transfers(token_transfers_with_meta, decoded_input), @@ -333,7 +333,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do user_op_from = user_op["sender"] user_op_to = user_op["execute_target"] || user_op["sender"] - {mock_tx, decoded_input, decoded_input_json} = decode_user_op_calldata(user_op_hash, user_op_call_data) + {mock_transaction, decoded_input, decoded_input_json} = decode_user_op_calldata(user_op_hash, user_op_call_data) {prepared_logs, prepared_token_transfers} = user_op_to_logs_and_token_transfers(user_op, decoded_input) @@ -352,10 +352,10 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do hash: user_op_hash, type: 0, value: "0", - method: Transaction.method_name(mock_tx, Transaction.format_decoded_input(decoded_input), true), + method: Transaction.method_name(mock_transaction, Transaction.format_decoded_input(decoded_input), true), status: user_op["status"], actions: [], - tx_types: [], + transaction_types: [], raw_input: user_op_call_data, decoded_input: decoded_input_json, token_transfers: prepared_token_transfers @@ -365,7 +365,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do end @doc """ - Decodes user_op["call_data"] and return {mock_tx, decoded_input, decoded_input_json} + Decodes user_op["call_data"] and return {mock_transaction, decoded_input, decoded_input_json} """ @spec decode_user_op_calldata(binary(), binary() | nil) :: {Transaction.t(), tuple(), map()} | {nil, nil, nil} def decode_user_op_calldata(_user_op_hash, nil), do: {nil, nil, nil} @@ -375,7 +375,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do {:ok, op_hash} = Chain.string_to_transaction_hash(user_op_hash) - mock_tx = %Transaction{ + mock_transaction = %Transaction{ to_address: %NotLoaded{}, input: input, hash: op_hash @@ -383,8 +383,9 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do skip_sig_provider? = false - decoded_input = Transaction.decoded_input_data(mock_tx, skip_sig_provider?, @api_true) + decoded_input = Transaction.decoded_input_data(mock_transaction, skip_sig_provider?, @api_true) - {mock_tx, decoded_input, decoded_input |> Transaction.format_decoded_input() |> TransactionView.decoded_input()} + {mock_transaction, decoded_input, + decoded_input |> Transaction.format_decoded_input() |> TransactionView.decoded_input()} end end diff --git a/apps/block_scout_web/lib/block_scout_web/models/get_transaction_tags.ex b/apps/block_scout_web/lib/block_scout_web/models/get_transaction_tags.ex index 2f3ef3f029bb..f7ca350515a2 100644 --- a/apps/block_scout_web/lib/block_scout_web/models/get_transaction_tags.ex +++ b/apps/block_scout_web/lib/block_scout_web/models/get_transaction_tags.ex @@ -13,9 +13,9 @@ defmodule BlockScoutWeb.Models.GetTransactionTags do %Transaction{} = transaction, %{id: identity_id, watchlist_id: watchlist_id} ) do - tx_tag = get_transaction_tags(transaction.hash, %{id: identity_id}) + transaction_tag = get_transaction_tags(transaction.hash, %{id: identity_id}) addresses_tags = get_addresses_tags_for_transaction(transaction, %{id: identity_id, watchlist_id: watchlist_id}) - Map.put(addresses_tags, :personal_tx_tag, tx_tag) + Map.put(addresses_tags, :personal_transaction_tag, transaction_tag) end def get_transaction_with_addresses_tags(%Transaction{} = transaction, _), @@ -23,11 +23,11 @@ defmodule BlockScoutWeb.Models.GetTransactionTags do common_tags: get_tags_on_address(transaction.to_address_hash), personal_tags: [], watchlist_names: [], - personal_tx_tag: nil + personal_transaction_tag: nil } def get_transaction_tags(transaction_hash, %{id: identity_id}) do - Repo.account_repo().get_by(TagTransaction, tx_hash_hash: transaction_hash, identity_id: identity_id) + Repo.account_repo().get_by(TagTransaction, transaction_hash_hash: transaction_hash, identity_id: identity_id) end def get_transaction_tags(_, _), do: nil diff --git a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex index ea100611244d..7959c4b02ed9 100644 --- a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex @@ -55,7 +55,7 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do end defp do_state_changes(%Transaction{} = transaction, options) do - block_txs = + block_transactions = transaction.block_hash |> Chain.block_to_transactions( paging_options: %PagingOptions{key: nil, page_size: nil}, @@ -65,7 +65,7 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do |> Repo.preload([:token_transfers, :internal_transactions]) transaction = - block_txs + block_transactions |> Enum.find(&(&1.hash == transaction.hash)) |> Repo.preload( token_transfers: [ @@ -85,14 +85,15 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do coin_balances_before_block = transaction_to_coin_balances(transaction, previous_block_number, options) - coin_balances_before_tx = StateChange.coin_balances_before(transaction, block_txs, coin_balances_before_block) + coin_balances_before_transaction = + StateChange.coin_balances_before(transaction, block_transactions, coin_balances_before_block) - native_coin_entries = StateChange.native_coin_entries(transaction, coin_balances_before_tx) + native_coin_entries = StateChange.native_coin_entries(transaction, coin_balances_before_transaction) token_balances_before = transaction.token_transfers |> Enum.reduce(%{}, &token_transfers_to_balances_reducer(&1, &2, previous_block_number, options)) - |> StateChange.token_balances_before(transaction, block_txs) + |> StateChange.token_balances_before(transaction, block_transactions) tokens_entries = StateChange.token_entries(transaction.token_transfers, token_balances_before) diff --git a/apps/block_scout_web/lib/block_scout_web/notifier.ex b/apps/block_scout_web/lib/block_scout_web/notifier.ex index 089bc27de3fb..d630a31167dd 100644 --- a/apps/block_scout_web/lib/block_scout_web/notifier.ex +++ b/apps/block_scout_web/lib/block_scout_web/notifier.ex @@ -163,7 +163,7 @@ defmodule BlockScoutWeb.Notifier do Endpoint.broadcast("transactions:#{transaction_hash}", "raw_trace", %{raw_trace_origin: transaction_hash}) end - # internal txs broadcast disabled on the indexer level, therefore it out of scope of the refactoring within https://github.com/blockscout/blockscout/pull/7474 + # internal transactions broadcast disabled on the indexer level, therefore it out of scope of the refactoring within https://github.com/blockscout/blockscout/pull/7474 def handle_event({:chain_event, :internal_transactions, :realtime, internal_transactions}) do internal_transactions |> Stream.map( @@ -215,9 +215,9 @@ defmodule BlockScoutWeb.Notifier do transactions |> Repo.preload(preloads) |> broadcast_transactions_websocket_v2() - |> Enum.map(fn tx -> + |> Enum.map(fn transaction -> # Disable parsing of token transfers from websocket for transaction tab because we display token transfers at a separate tab - Map.put(tx, :token_transfers, []) + Map.put(transaction, :token_transfers, []) end) |> Enum.each(&broadcast_transaction/1) end diff --git a/apps/block_scout_web/lib/block_scout_web/paging_helper.ex b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex index 15bdf142ef8d..11b9552bf273 100644 --- a/apps/block_scout_web/lib/block_scout_web/paging_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex @@ -52,7 +52,7 @@ defmodule BlockScoutWeb.PagingHelper do def paging_options(%{"inserted_at" => inserted_at_string, "hash" => hash_string}, [:pending | _]) do with {:ok, inserted_at, _} <- DateTime.from_iso8601(inserted_at_string), {:ok, hash} <- string_to_transaction_hash(hash_string) do - [paging_options: %{@default_paging_options | key: {inserted_at, hash}, is_pending_tx: true}] + [paging_options: %{@default_paging_options | key: {inserted_at, hash}, is_pending_transaction: true}] else _ -> [paging_options: @default_paging_options] @@ -274,8 +274,8 @@ defmodule BlockScoutWeb.PagingHelper do defp do_smart_contracts_sorting("balance", "asc"), do: [{:asc_nulls_first, :fetched_coin_balance, :address}] defp do_smart_contracts_sorting("balance", "desc"), do: [{:desc_nulls_last, :fetched_coin_balance, :address}] - defp do_smart_contracts_sorting("txs_count", "asc"), do: [{:asc_nulls_first, :transactions_count, :address}] - defp do_smart_contracts_sorting("txs_count", "desc"), do: [{:desc_nulls_last, :transactions_count, :address}] + defp do_smart_contracts_sorting("transactions_count", "asc"), do: [{:asc_nulls_first, :transactions_count, :address}] + defp do_smart_contracts_sorting("transactions_count", "desc"), do: [{:desc_nulls_last, :transactions_count, :address}] defp do_smart_contracts_sorting(_, _), do: [] @spec address_transactions_sorting(%{required(String.t()) => String.t()}) :: [ diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index 2b0e32754beb..126a7e5cc8f9 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -247,9 +247,9 @@ defmodule BlockScoutWeb.Routers.ApiRouter do scope "/optimism" do if Application.compile_env(:explorer, :chain_type) == :optimism do - get("/txn-batches", V2.OptimismController, :txn_batches) - get("/txn-batches/count", V2.OptimismController, :txn_batches_count) - get("/txn-batches/:l2_block_range_start/:l2_block_range_end", V2.OptimismController, :txn_batches) + get("/txn-batches", V2.OptimismController, :transaction_batches) + get("/txn-batches/count", V2.OptimismController, :transaction_batches_count) + get("/txn-batches/:l2_block_range_start/:l2_block_range_end", V2.OptimismController, :transaction_batches) get("/batches", V2.OptimismController, :batches) get("/batches/count", V2.OptimismController, :batches_count) get("/batches/da/celestia/:height/:commitment", V2.OptimismController, :batch_by_celestia_blob) @@ -388,7 +388,12 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/batches/count", V2.ArbitrumController, :batches_count) get("/batches/:batch_number", V2.ArbitrumController, :batch) get("/batches/da/anytrust/:data_hash", V2.ArbitrumController, :batch_by_data_availability_info) - get("/batches/da/celestia/:height/:tx_commitment", V2.ArbitrumController, :batch_by_data_availability_info) + + get( + "/batches/da/celestia/:height/:transaction_commitment", + V2.ArbitrumController, + :batch_by_data_availability_info + ) end end diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/form.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/form.html.eex index 1426bdd90aab..6f6600f68066 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/form.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/form.html.eex @@ -8,9 +8,9 @@ - <%= Enum.map(@tx_tags, fn at -> + <%= Enum.map(@transaction_tags, fn at -> render("row.html", tx_tag: at, conn: @conn) end) %> @@ -33,7 +33,7 @@ <% end %> - <%= if Enum.count(@tx_tags) < TagTransaction.get_max_tags_count() do %> + <%= if Enum.count(@transaction_tags) < TagTransaction.get_max_tags_count() do %> <%= gettext "Add transaction tag" %> <% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/row.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/row.html.eex index 55151ae0ac34..c2c5c0ff2804 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/row.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/row.html.eex @@ -1,14 +1,14 @@ -<%= if @tx_tag.tx_hash do %> +<%= if @tx_tag.transaction_hash do %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/robots/sitemap.xml.eex b/apps/block_scout_web/lib/block_scout_web/templates/robots/sitemap.xml.eex index 55d8b1a6115a..0e67d48ab1c8 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/robots/sitemap.xml.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/robots/sitemap.xml.eex @@ -19,10 +19,10 @@ <% end %> - <% txs = Chain.recent_transactions(params, [:validated]) %> - <%= for tx <- txs do %> + <% transactions = Chain.recent_transactions(params, [:validated]) %> + <%= for transaction <- transactions do %> - <%= host %>/tx/<%= to_string(tx.hash) %> + <%= host %>/tx/<%= to_string(transaction.hash) %> <%= date %> <% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/search/_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/search/_tile.html.eex index 85ccdffd56b0..981d1dd803ff 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/search/_tile.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/search/_tile.html.eex @@ -1,4 +1,4 @@ --<%= if @result.block_hash, do: Base.encode16(@result.block_hash, case: :lower), else: "" %>"> +-<%= if @result.block_hash, do: Base.encode16(@result.block_hash, case: :lower), else: "" %>"> <%= render BlockScoutWeb.SearchView, "_empty_td.html" %> <%= case @result.type do %> @@ -79,7 +79,7 @@ <% "transaction" -> %> <%= render BlockScoutWeb.TransactionView, "_link.html", - transaction_hash: "0x" <> Base.encode16(@result.tx_hash, case: :lower) %> + transaction_hash: "0x" <> Base.encode16(@result.transaction_hash, case: :lower) %> <% "user_operation" -> %> <%= "0x" <> Base.encode16(@result.user_operation_hash, case: :lower) %> <% "blob" -> %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex index d7fa7e29c32b..5e34875f477e 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex @@ -1,5 +1,5 @@ <% status = transaction_status(@transaction) %> -<% error_in_internal_tx = @transaction.has_error_in_internal_txs %> +<% error_in_internal_transaction = @transaction.has_error_in_internal_transactions %> <% current_user = AuthController.current_user(@conn) %> <% tx_tags = BlockScoutWeb.Models.GetTransactionTags.get_transaction_with_addresses_tags(@transaction, current_user) %>
@@ -7,7 +7,7 @@
- <%= if error_in_internal_tx do %> + <%= if error_in_internal_transaction do %> <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", text: gettext("Error in internal transactions"), additional_classes: ["color-inherit"] %> <% end %> @@ -33,8 +33,8 @@ <%= if method_name do %> <%= render BlockScoutWeb.FormView, "_tag.html", text: method_name, additional_classes: ["method", "ml-1"] %> <% end %> - <%= if tx_tags.personal_tx_tag && tx_tags.personal_tx_tag.name !== :error do %> - <%= render BlockScoutWeb.FormView, "_tag.html", text: tx_tags.personal_tx_tag.name, additional_classes: [tag_name_to_label(tx_tags.personal_tx_tag.name), "ml-1"] %> + <%= if tx_tags.personal_transaction_tag && tx_tags.personal_transaction_tag.name !== :error do %> + <%= render BlockScoutWeb.FormView, "_tag.html", text: tx_tags.personal_transaction_tag.name, additional_classes: [tag_name_to_label(tx_tags.personal_transaction_tag.name), "ml-1"] %> <% end %> <%= render BlockScoutWeb.AddressView, "_labels.html", tags: tx_tags %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex index b529f940a2c5..bb1b41181dc6 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex @@ -48,11 +48,11 @@

<%= gettext "Transaction Details" %> - <% personal_tx_tag = if assigns[:tx_tags], do: @tx_tags.personal_tx_tag, else: nil %> - <%= if personal_tx_tag && personal_tx_tag.name !== :error do %> - <%= render BlockScoutWeb.FormView, "_tag.html", text: personal_tx_tag.name, additional_classes: [tag_name_to_label(personal_tx_tag.name), "ml-1"] %> + <% personal_transaction_tag = if assigns[:tx_tags], do: @transaction_tags.personal_transaction_tag, else: nil %> + <%= if personal_transaction_tag && personal_transaction_tag.name !== :error do %> + <%= render BlockScoutWeb.FormView, "_tag.html", text: personal_transaction_tag.name, additional_classes: [tag_name_to_label(personal_transaction_tag.name), "ml-1"] %> <% end %> - <%= render BlockScoutWeb.AddressView, "_labels.html", tags: @tx_tags %> + <%= render BlockScoutWeb.AddressView, "_labels.html", tags: @transaction_tags %>

<%= if status == :pending do %> diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/tags_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/tags_view.ex index 80670000d2e6..e520fe0bddda 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/tags_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/tags_view.ex @@ -7,12 +7,14 @@ defmodule BlockScoutWeb.Account.Api.V2.TagsView do tags_map: %{ personal_tags: personal_tags, watchlist_names: watchlist_names, - personal_tx_tag: personal_tx_tag, + personal_transaction_tag: personal_transaction_tag, common_tags: common_tags } }) do %{ - personal_tx_tag: prepare_transaction_tag(personal_tx_tag), + personal_transaction_tag: prepare_transaction_tag(personal_transaction_tag), + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `personal_transaction_tag` property + personal_tx_tag: prepare_transaction_tag(personal_transaction_tag), personal_tags: personal_tags, watchlist_names: watchlist_names, common_tags: common_tags diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex index bf6f869a224c..6e7211322dca 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex @@ -161,7 +161,11 @@ defmodule BlockScoutWeb.Account.Api.V2.UserView do def prepare_transaction_tag(nil), do: nil def prepare_transaction_tag(transaction_tag) do - %{"id" => transaction_tag.id, "transaction_hash" => transaction_tag.tx_hash, "name" => transaction_tag.name} + %{ + "id" => transaction_tag.id, + "transaction_hash" => transaction_tag.transaction_hash, + "name" => transaction_tag.name + } end def prepare_public_tags_request(public_tags_request) do diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex index 93a341379289..8b8828b916b5 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex @@ -267,13 +267,13 @@ defmodule BlockScoutWeb.API.RPC.ContractView do defp address_to_response(address) do creator_hash = AddressView.from_address_hash(address) - creation_tx = creator_hash && AddressView.transaction_hash(address) + creation_transaction = creator_hash && AddressView.transaction_hash(address) - creation_tx && + creation_transaction && %{ "contractAddress" => to_string(address.hash), "contractCreator" => to_string(creator_hash), - "txHash" => to_string(creation_tx) + "txHash" => to_string(creation_transaction) } end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex index 4a18643aa354..74c8ba5a32e1 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex @@ -15,7 +15,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionView do end def render("gettxreceiptstatus.json", %{status: status}) do - prepared_status = prepare_tx_receipt_status(status) + prepared_status = prepare_transaction_receipt_status(status) RPCView.render("show.json", data: %{"status" => prepared_status}) end @@ -27,13 +27,13 @@ defmodule BlockScoutWeb.API.RPC.TransactionView do RPCView.render("error.json", assigns) end - defp prepare_tx_receipt_status(""), do: "" + defp prepare_transaction_receipt_status(""), do: "" - defp prepare_tx_receipt_status(nil), do: "" + defp prepare_transaction_receipt_status(nil), do: "" - defp prepare_tx_receipt_status(:ok), do: "1" + defp prepare_transaction_receipt_status(:ok), do: "1" - defp prepare_tx_receipt_status(_), do: "0" + defp prepare_transaction_receipt_status(_), do: "0" defp prepare_error("") do %{ diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex index b3010165fffa..9b19c5103e39 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex @@ -73,11 +73,15 @@ defmodule BlockScoutWeb.API.V2.AddressView do @spec prepare_address( {atom() | %{:fetched_coin_balance => any(), :hash => any(), optional(any()) => any()}, any()} | Explorer.Chain.Address.t() - ) :: %{optional(:coin_balance) => any(), optional(:tx_count) => binary(), optional(<<_::32, _::_*8>>) => any()} - def prepare_address({address, tx_count}) do + ) :: %{ + optional(:coin_balance) => any(), + optional(:transaction_count) => binary(), + optional(<<_::32, _::_*8>>) => any() + } + def prepare_address({address, transaction_count}) do nil |> Helper.address_with_info(address, address.hash, true) - |> Map.put(:tx_count, to_string(tx_count)) + |> Map.put(:transaction_count, to_string(transaction_count)) |> Map.put(:coin_balance, if(address.fetched_coin_balance, do: address.fetched_coin_balance.value)) end @@ -93,13 +97,15 @@ defmodule BlockScoutWeb.API.V2.AddressView do creation_transaction = Address.creation_transaction(address) creator_hash = creation_transaction && creation_transaction.from_address_hash - creation_tx = creator_hash && AddressView.transaction_hash(address) + creation_transaction_hash = creator_hash && AddressView.transaction_hash(address) token = address.token && TokenView.render("token.json", %{token: address.token}) extended_info = Map.merge(base_info, %{ "creator_address_hash" => creator_hash && Address.checksum(creator_hash), - "creation_tx_hash" => creation_tx, + "creation_transaction_hash" => creation_transaction_hash, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `creation_transaction_hash` property + "creation_tx_hash" => creation_transaction_hash, "token" => token, "coin_balance" => balance, "exchange_rate" => exchange_rate, diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex index e35adcb29e13..bdce021b179f 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/arbitrum_view.ex @@ -70,7 +70,7 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do "before_acc" => batch.before_acc, "after_acc" => batch.after_acc } - |> add_l1_tx_info(batch) + |> add_l1_transaction_info(batch) |> add_da_info(batch) end @@ -170,7 +170,7 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do "blocks_count" => batch.end_block - batch.start_block + 1, "batch_data_container" => batch.batch_container } - |> add_l1_tx_info(batch) + |> add_l1_transaction_info(batch) end @doc """ @@ -245,7 +245,7 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do }) :: map() defp extend_with_settlement_info(out_json, arbitrum_entity) do out_json - |> add_l1_txs_info_and_status(%{ + |> add_l1_transactions_info_and_status(%{ batch_number: get_batch_number(arbitrum_entity), commitment_transaction: arbitrum_entity.arbitrum_commitment_transaction, confirmation_transaction: arbitrum_entity.arbitrum_confirmation_transaction @@ -285,25 +285,25 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do end # Augments an output JSON with commit transaction details and its status. - @spec add_l1_tx_info(map(), %{ + @spec add_l1_transaction_info(map(), %{ :commitment_transaction => LifecycleTransaction.t() | LifecycleTransaction.to_import(), optional(any()) => any() }) :: map() - defp add_l1_tx_info(out_json, %L1Batch{} = batch) do - l1_tx = %{commitment_transaction: handle_associated_l1_txs_properly(batch.commitment_transaction)} + defp add_l1_transaction_info(out_json, %L1Batch{} = batch) do + l1_transaction = %{commitment_transaction: handle_associated_l1_transactions_properly(batch.commitment_transaction)} out_json |> Map.merge(%{ "commitment_transaction" => %{ - "hash" => APIV2Helper.get_2map_data(l1_tx, :commitment_transaction, :hash), - "block_number" => APIV2Helper.get_2map_data(l1_tx, :commitment_transaction, :block), - "timestamp" => APIV2Helper.get_2map_data(l1_tx, :commitment_transaction, :ts), - "status" => APIV2Helper.get_2map_data(l1_tx, :commitment_transaction, :status) + "hash" => APIV2Helper.get_2map_data(l1_transaction, :commitment_transaction, :hash), + "block_number" => APIV2Helper.get_2map_data(l1_transaction, :commitment_transaction, :block), + "timestamp" => APIV2Helper.get_2map_data(l1_transaction, :commitment_transaction, :ts), + "status" => APIV2Helper.get_2map_data(l1_transaction, :commitment_transaction, :status) } }) end - defp add_l1_tx_info(out_json, %{ + defp add_l1_transaction_info(out_json, %{ commitment_transaction: %{ hash: hash, block_number: block_number, @@ -419,34 +419,36 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do out |> Map.merge(%{ "height" => Map.get(da_info, "height"), - "tx_commitment" => Map.get(da_info, "tx_commitment") + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_count` property + "tx_commitment" => Map.get(da_info, "transaction_commitment"), + "transaction_commitment" => Map.get(da_info, "transaction_commitment") }) end # Augments an output JSON with commit and confirm transaction details and their statuses. - @spec add_l1_txs_info_and_status(map(), %{ + @spec add_l1_transactions_info_and_status(map(), %{ :commitment_transaction => any(), :confirmation_transaction => any(), optional(:batch_number) => any() }) :: map() - defp add_l1_txs_info_and_status(out_json, arbitrum_item) + defp add_l1_transactions_info_and_status(out_json, arbitrum_item) when is_map(arbitrum_item) and is_map_key(arbitrum_item, :commitment_transaction) and is_map_key(arbitrum_item, :confirmation_transaction) do - l1_txs = get_associated_l1_txs(arbitrum_item) + l1_transactions = get_associated_l1_transactions(arbitrum_item) out_json |> Map.merge(%{ "status" => block_or_transaction_status(arbitrum_item), "commitment_transaction" => %{ - "hash" => APIV2Helper.get_2map_data(l1_txs, :commitment_transaction, :hash), - "timestamp" => APIV2Helper.get_2map_data(l1_txs, :commitment_transaction, :ts), - "status" => APIV2Helper.get_2map_data(l1_txs, :commitment_transaction, :status) + "hash" => APIV2Helper.get_2map_data(l1_transactions, :commitment_transaction, :hash), + "timestamp" => APIV2Helper.get_2map_data(l1_transactions, :commitment_transaction, :ts), + "status" => APIV2Helper.get_2map_data(l1_transactions, :commitment_transaction, :status) }, "confirmation_transaction" => %{ - "hash" => APIV2Helper.get_2map_data(l1_txs, :confirmation_transaction, :hash), - "timestamp" => APIV2Helper.get_2map_data(l1_txs, :confirmation_transaction, :ts), - "status" => APIV2Helper.get_2map_data(l1_txs, :confirmation_transaction, :status) + "hash" => APIV2Helper.get_2map_data(l1_transactions, :confirmation_transaction, :hash), + "timestamp" => APIV2Helper.get_2map_data(l1_transactions, :confirmation_transaction, :ts), + "status" => APIV2Helper.get_2map_data(l1_transactions, :confirmation_transaction, :status) } }) end @@ -459,7 +461,7 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do # # ## Returns # A map containing nesting maps describing corresponding L1 transactions - @spec get_associated_l1_txs(%{ + @spec get_associated_l1_transactions(%{ :commitment_transaction => any(), :confirmation_transaction => any(), optional(any()) => any() @@ -481,15 +483,15 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do :status => nil | :finalized | :unfinalized } } - defp get_associated_l1_txs(arbitrum_item) do + defp get_associated_l1_transactions(arbitrum_item) do [:commitment_transaction, :confirmation_transaction] - |> Enum.reduce(%{}, fn key, l1_txs -> - Map.put(l1_txs, key, handle_associated_l1_txs_properly(Map.get(arbitrum_item, key))) + |> Enum.reduce(%{}, fn key, l1_transactions -> + Map.put(l1_transactions, key, handle_associated_l1_transactions_properly(Map.get(arbitrum_item, key))) end) end # Returns details of an associated L1 transaction or nil if not loaded or not available. - @spec handle_associated_l1_txs_properly(LifecycleTransaction | Ecto.Association.NotLoaded.t() | nil) :: + @spec handle_associated_l1_transactions_properly(LifecycleTransaction | Ecto.Association.NotLoaded.t() | nil) :: nil | %{ :hash => nil | binary(), @@ -497,8 +499,8 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do :ts => nil | DateTime.t(), :status => nil | :finalized | :unfinalized } - defp handle_associated_l1_txs_properly(associated_l1_tx) do - case associated_l1_tx do + defp handle_associated_l1_transactions_properly(associated_l1_transaction) do + case associated_l1_transaction do nil -> nil %Ecto.Association.NotLoaded{} -> nil value -> %{hash: value.hash, block: value.block_number, ts: value.timestamp, status: value.status} @@ -531,7 +533,7 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do # direction of the message, its status and the associated L1 transaction. # # ## Parameters - # - `arbitrum_tx`: An Arbitrum transaction. + # - `arbitrum_transaction`: An Arbitrum transaction. # # ## Returns # - A map extended with fields indicating the direction of the message, its status @@ -542,15 +544,15 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do :arbitrum_message_from_l2 => any(), optional(any()) => any() }) :: map() - defp extend_if_message(arbitrum_json, %Transaction{} = arbitrum_tx) do + defp extend_if_message(arbitrum_json, %Transaction{} = arbitrum_transaction) do {message_type, message_data} = - case {APIV2Helper.specified?(Map.get(arbitrum_tx, :arbitrum_message_to_l2)), - APIV2Helper.specified?(Map.get(arbitrum_tx, :arbitrum_message_from_l2))} do + case {APIV2Helper.specified?(Map.get(arbitrum_transaction, :arbitrum_message_to_l2)), + APIV2Helper.specified?(Map.get(arbitrum_transaction, :arbitrum_message_from_l2))} do {true, false} -> - {"incoming", l1_tx_and_status_for_message(arbitrum_tx, :incoming)} + {"incoming", l1_transaction_and_status_for_message(arbitrum_transaction, :incoming)} {false, true} -> - {"outcoming", l1_tx_and_status_for_message(arbitrum_tx, :outcoming)} + {"outcoming", l1_transaction_and_status_for_message(arbitrum_transaction, :outcoming)} _ -> {nil, %{}} @@ -562,7 +564,7 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do end # Determines the associated L1 transaction and its status for the given message direction. - @spec l1_tx_and_status_for_message( + @spec l1_transaction_and_status_for_message( %{ :__struct__ => Transaction, :arbitrum_message_to_l2 => any(), @@ -571,20 +573,21 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do }, :incoming | :outcoming ) :: map() - defp l1_tx_and_status_for_message(arbitrum_tx, message_direction) do - {l1_tx, status} = + defp l1_transaction_and_status_for_message(arbitrum_transaction, message_direction) do + {l1_transaction, status} = case message_direction do :incoming -> - l1_tx = APIV2Helper.get_2map_data(arbitrum_tx, :arbitrum_message_to_l2, :originating_transaction_hash) + l1_transaction = + APIV2Helper.get_2map_data(arbitrum_transaction, :arbitrum_message_to_l2, :originating_transaction_hash) - if is_nil(l1_tx) do + if is_nil(l1_transaction) do {nil, "Syncing with base layer"} else - {l1_tx, "Relayed"} + {l1_transaction, "Relayed"} end :outcoming -> - case APIV2Helper.get_2map_data(arbitrum_tx, :arbitrum_message_from_l2, :status) do + case APIV2Helper.get_2map_data(arbitrum_transaction, :arbitrum_message_from_l2, :status) do :initiated -> {nil, "Settlement pending"} @@ -595,12 +598,12 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do {nil, "Ready for relay"} :relayed -> - {APIV2Helper.get_2map_data(arbitrum_tx, :arbitrum_message_from_l2, :completion_transaction_hash), + {APIV2Helper.get_2map_data(arbitrum_transaction, :arbitrum_message_from_l2, :completion_transaction_hash), "Relayed"} end end - %{"associated_l1_transaction" => l1_tx, "message_status" => status} + %{"associated_l1_transaction" => l1_transaction, "message_status" => status} end # Extends the output JSON with information from Arbitrum-specific fields of the transaction. @@ -611,14 +614,14 @@ defmodule BlockScoutWeb.API.V2.ArbitrumView do :gas_price => Wei.t(), optional(any()) => any() }) :: map() - defp extend_with_transaction_info(out_json, %Transaction{} = arbitrum_tx) do + defp extend_with_transaction_info(out_json, %Transaction{} = arbitrum_transaction) do # Map.get is only needed for the case when the module is compiled with # chain_type different from "arbitrum", `|| 0` is used to avoid nil values # for the transaction prior to the migration to Arbitrum specific BS build. - gas_used_for_l1 = Map.get(arbitrum_tx, :gas_used_for_l1, 0) || 0 + gas_used_for_l1 = Map.get(arbitrum_transaction, :gas_used_for_l1, 0) || 0 - gas_used = Map.get(arbitrum_tx, :gas_used, 0) || 0 - gas_price = Map.get(arbitrum_tx, :gas_price, 0) || 0 + gas_used = Map.get(arbitrum_transaction, :gas_used, 0) || 0 + gas_price = Map.get(arbitrum_transaction, :gas_price, 0) || 0 gas_used_for_l2 = gas_used diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex index 75a22551b884..6f9b2ccc48eb 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex @@ -36,6 +36,8 @@ defmodule BlockScoutWeb.API.V2.BlockView do %{ "height" => block.number, "timestamp" => block.timestamp, + "transaction_count" => count_transactions(block), + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_count` property "tx_count" => count_transactions(block), "miner" => Helper.address_with_info(nil, block.miner, block.miner_hash, false), "size" => block.size, @@ -57,6 +59,8 @@ defmodule BlockScoutWeb.API.V2.BlockView do "gas_used_percentage" => Block.gas_used_percentage(block), "burnt_fees_percentage" => burnt_fees_percentage(burnt_fees, transaction_fees), "type" => block |> BlockView.block_type() |> String.downcase(), + "transaction_fees" => transaction_fees, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_fees` property "tx_fees" => transaction_fees, "withdrawals_count" => count_withdrawals(block) } @@ -93,7 +97,7 @@ defmodule BlockScoutWeb.API.V2.BlockView do def burnt_fees_percentage(_, _), do: nil - def count_transactions(%Block{transactions: txs}) when is_list(txs), do: Enum.count(txs) + def count_transactions(%Block{transactions: transactions}) when is_list(transactions), do: Enum.count(transactions) def count_transactions(_), do: nil def count_withdrawals(%Block{withdrawals: withdrawals}) when is_list(withdrawals), do: Enum.count(withdrawals) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/ethereum_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/ethereum_view.ex index 6f7af666458a..46322420b6d5 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/ethereum_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/ethereum_view.ex @@ -1,9 +1,9 @@ defmodule BlockScoutWeb.API.V2.EthereumView do alias Explorer.Chain.{Block, Transaction} - defp count_blob_transactions(%Block{transactions: txs}) when is_list(txs), + defp count_blob_transactions(%Block{transactions: transactions}) when is_list(transactions), # EIP-2718 blob transaction type - do: Enum.count(txs, &(&1.type == 3)) + do: Enum.count(transactions, &(&1.type == 3)) defp count_blob_transactions(_), do: nil diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/mud_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/mud_view.ex index c628d7994b63..488425cb4b11 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/mud_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/mud_view.ex @@ -83,7 +83,9 @@ defmodule BlockScoutWeb.API.V2.MudView do defp prepare_world_for_list(%Address{} = address) do %{ "address" => Helper.address_with_info(address, address.hash), + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_count` property "tx_count" => address.transactions_count, + "transaction_count" => address.transactions_count, "coin_balance" => if(address.fetched_coin_balance, do: address.fetched_coin_balance.value) } end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex index 2c64837ee141..62e43585aaae 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex @@ -13,7 +13,7 @@ defmodule BlockScoutWeb.API.V2.OptimismView do Function to render GET requests to `/api/v2/optimism/txn-batches` endpoint. """ @spec render(binary(), map()) :: map() | list() | non_neg_integer() - def render("optimism_txn_batches.json", %{ + def render("optimism_transaction_batches.json", %{ batches: batches, next_page_params: next_page_params }) do @@ -21,7 +21,7 @@ defmodule BlockScoutWeb.API.V2.OptimismView do batches |> Enum.map(fn batch -> Task.async(fn -> - tx_count = + transaction_count = Repo.replica().aggregate( from( t in Transaction, @@ -35,7 +35,11 @@ defmodule BlockScoutWeb.API.V2.OptimismView do %{ "l2_block_number" => batch.l2_block_number, - "tx_count" => tx_count, + "transaction_count" => transaction_count, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_count` property + "tx_count" => transaction_count, + "l1_transaction_hashes" => batch.frame_sequence.l1_transaction_hashes, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l1_transaction_hashes` property "l1_tx_hashes" => batch.frame_sequence.l1_transaction_hashes, "l1_timestamp" => batch.frame_sequence.l1_timestamp } @@ -67,7 +71,11 @@ defmodule BlockScoutWeb.API.V2.OptimismView do "l1_timestamp" => batch.l1_timestamp, "l2_block_start" => from, "l2_block_end" => to, - "tx_count" => batch.tx_count, + "transaction_count" => batch.transaction_count, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_count` property + "tx_count" => batch.transaction_count, + "l1_transaction_hashes" => batch.l1_transaction_hashes, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l1_transaction_hashes` property "l1_tx_hashes" => batch.l1_transaction_hashes, "batch_data_container" => batch.batch_data_container } @@ -100,6 +108,8 @@ defmodule BlockScoutWeb.API.V2.OptimismView do %{ "l2_output_index" => r.l2_output_index, "l2_block_number" => r.l2_block_number, + "l1_transaction_hash" => r.l1_transaction_hash, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l1_transaction_hash` property "l1_tx_hash" => r.l1_transaction_hash, "l1_timestamp" => r.l1_timestamp, "l1_block_number" => r.l1_block_number, @@ -155,11 +165,19 @@ defmodule BlockScoutWeb.API.V2.OptimismView do Enum.map(deposits, fn deposit -> %{ "l1_block_number" => deposit.l1_block_number, + "l2_transaction_hash" => deposit.l2_transaction_hash, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l2_transaction_hash` property "l2_tx_hash" => deposit.l2_transaction_hash, "l1_block_timestamp" => deposit.l1_block_timestamp, + "l1_transaction_hash" => deposit.l1_transaction_hash, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l1_transaction_hash` property "l1_tx_hash" => deposit.l1_transaction_hash, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l1_transaction_origin` property "l1_tx_origin" => deposit.l1_transaction_origin, - "l2_tx_gas_limit" => deposit.l2_transaction.gas + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l2_transaction_gas_limit` property + "l2_tx_gas_limit" => deposit.l2_transaction.gas, + "l1_transaction_origin" => deposit.l1_transaction_origin, + "l2_transaction_gas_limit" => deposit.l2_transaction.gas } end), next_page_params: next_page_params @@ -174,8 +192,12 @@ defmodule BlockScoutWeb.API.V2.OptimismView do %{ "l1_block_number" => deposit.l1_block_number, "l1_block_timestamp" => deposit.l1_block_timestamp, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l1_transaction_hash` property "l1_tx_hash" => deposit.l1_transaction_hash, - "l2_tx_hash" => deposit.l2_transaction_hash + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l2_transaction_hash` property + "l2_tx_hash" => deposit.l2_transaction_hash, + "l1_transaction_hash" => deposit.l1_transaction_hash, + "l2_transaction_hash" => deposit.l2_transaction_hash } end) end @@ -228,9 +250,13 @@ defmodule BlockScoutWeb.API.V2.OptimismView do "msg_nonce" => msg_nonce, "msg_nonce_version" => msg_nonce_version, "from" => Helper.address_with_info(conn, from_address, from_address_hash, w.from), + "l2_transaction_hash" => w.l2_transaction_hash, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l2_transaction_hash` property "l2_tx_hash" => w.l2_transaction_hash, "l2_timestamp" => w.l2_timestamp, "status" => status, + "l1_transaction_hash" => w.l1_transaction_hash, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l1_transaction_hash` property "l1_tx_hash" => w.l1_transaction_hash, "challenge_period_end" => challenge_period_end } @@ -275,6 +301,8 @@ defmodule BlockScoutWeb.API.V2.OptimismView do %{ "internal_id" => frame_sequence.id, "l1_timestamp" => frame_sequence.l1_timestamp, + "l1_transaction_hashes" => frame_sequence.l1_transaction_hashes, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l1_transaction_hashes` property "l1_tx_hashes" => frame_sequence.l1_transaction_hashes, "batch_data_container" => batch_data_container } diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_edge_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_edge_view.ex index 497ef214c7be..51b2515997f3 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_edge_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_edge_view.ex @@ -52,10 +52,10 @@ defmodule BlockScoutWeb.API.V2.PolygonEdgeView do count end - def extend_transaction_json_response(out_json, tx_hash, connection) do + def extend_transaction_json_response(out_json, transaction_hash, connection) do out_json - |> Map.put("polygon_edge_deposit", polygon_edge_deposit(tx_hash, connection)) - |> Map.put("polygon_edge_withdrawal", polygon_edge_withdrawal(tx_hash, connection)) + |> Map.put("polygon_edge_deposit", polygon_edge_deposit(transaction_hash, connection)) + |> Map.put("polygon_edge_withdrawal", polygon_edge_withdrawal(transaction_hash, connection)) end defp polygon_edge_deposit(transaction_hash, conn) do diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_zkevm_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_zkevm_view.ex index ccf6e33573b5..0960ca7943ae 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_zkevm_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_zkevm_view.ex @@ -9,19 +9,19 @@ defmodule BlockScoutWeb.API.V2.PolygonZkevmView do """ @spec render(binary(), map()) :: map() | non_neg_integer() def render("zkevm_batch.json", %{batch: batch}) do - sequence_tx_hash = + sequence_transaction_hash = if Map.has_key?(batch, :sequence_transaction) and not is_nil(batch.sequence_transaction) do batch.sequence_transaction.hash end - verify_tx_hash = + verify_transaction_hash = if Map.has_key?(batch, :verify_transaction) and not is_nil(batch.verify_transaction) do batch.verify_transaction.hash end l2_transactions = if Map.has_key?(batch, :l2_transactions) do - Enum.map(batch.l2_transactions, fn tx -> tx.hash end) + Enum.map(batch.l2_transactions, fn transaction -> transaction.hash end) end %{ @@ -31,8 +31,12 @@ defmodule BlockScoutWeb.API.V2.PolygonZkevmView do "transactions" => l2_transactions, "global_exit_root" => batch.global_exit_root, "acc_input_hash" => batch.acc_input_hash, - "sequence_tx_hash" => sequence_tx_hash, - "verify_tx_hash" => verify_tx_hash, + "sequence_transaction_hash" => sequence_transaction_hash, + "verify_transaction_hash" => verify_transaction_hash, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `sequence_transaction_hash` property + "sequence_tx_hash" => sequence_transaction_hash, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `verify_transaction_hash` property + "verify_tx_hash" => verify_transaction_hash, "state_root" => batch.state_root } end @@ -141,12 +145,12 @@ defmodule BlockScoutWeb.API.V2.PolygonZkevmView do defp render_zkevm_batches(batches) do Enum.map(batches, fn batch -> - sequence_tx_hash = + sequence_transaction_hash = if not is_nil(batch.sequence_transaction) do batch.sequence_transaction.hash end - verify_tx_hash = + verify_transaction_hash = if not is_nil(batch.verify_transaction) do batch.verify_transaction.hash end @@ -155,9 +159,15 @@ defmodule BlockScoutWeb.API.V2.PolygonZkevmView do "number" => batch.number, "status" => batch_status(batch), "timestamp" => batch.timestamp, + "transaction_count" => batch.l2_transactions_count, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_count` property "tx_count" => batch.l2_transactions_count, - "sequence_tx_hash" => sequence_tx_hash, - "verify_tx_hash" => verify_tx_hash + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `sequence_transaction_hash` property + "sequence_tx_hash" => sequence_transaction_hash, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `verify_transaction_hash` property + "verify_tx_hash" => verify_transaction_hash, + "sequence_transaction_hash" => sequence_transaction_hash, + "verify_transaction_hash" => verify_transaction_hash } end) end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex index 24db1555252a..1134914e6248 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 @@ -95,12 +95,14 @@ defmodule BlockScoutWeb.API.V2.SearchView do end def prepare_search_result(%{type: "transaction"} = search_result) do - tx_hash = hash_to_string(search_result.tx_hash) + transaction_hash = hash_to_string(search_result.transaction_hash) %{ "type" => search_result.type, - "tx_hash" => tx_hash, - "url" => transaction_path(Endpoint, :show, tx_hash), + "transaction_hash" => transaction_hash, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_hash` property + "tx_hash" => transaction_hash, + "url" => transaction_path(Endpoint, :show, transaction_hash), "timestamp" => search_result.timestamp, "priority" => search_result.priority } diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex index 80c98107e272..f685fe2833fd 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex @@ -358,6 +358,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do ), "compiler_version" => smart_contract.compiler_version, "optimization_enabled" => smart_contract.optimization, + "transaction_count" => smart_contract.address.transactions_count, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_count` property "tx_count" => smart_contract.address.transactions_count, "language" => smart_contract_language(smart_contract), "verified_at" => smart_contract.inserted_at, diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/stability_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/stability_view.ex index 2faa972485b7..3bd0c2a6f1e7 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/stability_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/stability_view.ex @@ -90,7 +90,11 @@ defmodule BlockScoutWeb.API.V2.StabilityView do defp do_extend_with_stability_fees_info(transactions) when is_list(transactions) do {transactions, _tokens_acc} = Enum.map_reduce(transactions, %{}, fn transaction, tokens_acc -> - case Log.fetch_log_by_tx_hash_and_first_topic(transaction.hash, @transaction_fee_event_signature, @api_true) do + case Log.fetch_log_by_transaction_hash_and_first_topic( + transaction.hash, + @transaction_fee_event_signature, + @api_true + ) do fee_log when not is_nil(fee_log) -> {:ok, _selector, mapping} = Log.find_and_decode(@transaction_fee_event_abi, fee_log, transaction.hash) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/suave_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/suave_view.ex index 42b3612532ac..732f37116a92 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/suave_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/suave_view.ex @@ -9,7 +9,13 @@ defmodule BlockScoutWeb.API.V2.SuaveView do @suave_bid_event "0x83481d5b04dea534715acad673a8177a46fc93882760f36bdc16ccac439d504e" - def extend_transaction_json_response(%Transaction{} = transaction, out_json, single_tx?, conn, watchlist_names) do + def extend_transaction_json_response( + %Transaction{} = transaction, + out_json, + single_transaction?, + conn, + watchlist_names + ) do if is_nil(Map.get(transaction, :execution_node_hash)) do out_json else @@ -48,7 +54,7 @@ defmodule BlockScoutWeb.API.V2.SuaveView do conn, execution_node, execution_node_hash, - single_tx?, + single_transaction?, watchlist_names ) ) @@ -60,7 +66,7 @@ defmodule BlockScoutWeb.API.V2.SuaveView do conn, wrapped_to_address, wrapped_to_address_hash, - single_tx?, + single_transaction?, watchlist_names ), "gas_limit" => wrapped_gas, diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_transfer_view.ex index a202137aba5e..33c52bd9bd84 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_transfer_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_transfer_view.ex @@ -41,6 +41,8 @@ defmodule BlockScoutWeb.API.V2.TokenTransferView do def prepare_token_transfer(token_transfer, _conn, decoded_input) do %{ + "transaction_hash" => token_transfer.transaction_hash, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_hash` property "tx_hash" => token_transfer.transaction_hash, "from" => Helper.address_with_info(nil, token_transfer.from_address, token_transfer.from_address_hash, false), "to" => Helper.address_with_info(nil, token_transfer.to_address, token_transfer.to_address_hash, false), @@ -54,8 +56,8 @@ defmodule BlockScoutWeb.API.V2.TokenTransferView do ), "method" => Transaction.method_name(token_transfer.transaction, decoded_input, true), "block_hash" => to_string(token_transfer.block_hash), - "block_number" => to_string(token_transfer.block_number), - "log_index" => to_string(token_transfer.log_index) + "block_number" => token_transfer.block_number, + "log_index" => token_transfer.log_index } end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex index 9b2b3a18dd51..7defad2807f5 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex @@ -36,8 +36,8 @@ defmodule BlockScoutWeb.API.V2.TransactionView do transactions |> chain_type_transformations() |> Enum.zip(decoded_transactions) - |> Enum.map(fn {tx, decoded_input} -> - prepare_transaction(tx, conn, false, block_height, watchlist_names, decoded_input) + |> Enum.map(fn {transaction, decoded_input} -> + prepare_transaction(transaction, conn, false, block_height, watchlist_names, decoded_input) end), "next_page_params" => next_page_params } @@ -54,8 +54,8 @@ defmodule BlockScoutWeb.API.V2.TransactionView do transactions |> chain_type_transformations() |> Enum.zip(decoded_transactions) - |> Enum.map(fn {tx, decoded_input} -> - prepare_transaction(tx, conn, false, block_height, watchlist_names, decoded_input) + |> Enum.map(fn {transaction, decoded_input} -> + prepare_transaction(transaction, conn, false, block_height, watchlist_names, decoded_input) end) end @@ -68,7 +68,9 @@ defmodule BlockScoutWeb.API.V2.TransactionView do transactions |> chain_type_transformations() |> Enum.zip(decoded_transactions) - |> Enum.map(fn {tx, decoded_input} -> prepare_transaction(tx, conn, false, block_height, decoded_input) end), + |> Enum.map(fn {transaction, decoded_input} -> + prepare_transaction(transaction, conn, false, block_height, decoded_input) + end), "next_page_params" => next_page_params } end @@ -86,7 +88,9 @@ defmodule BlockScoutWeb.API.V2.TransactionView do transactions |> chain_type_transformations() |> Enum.zip(decoded_transactions) - |> Enum.map(fn {tx, decoded_input} -> prepare_transaction(tx, conn, false, block_height, decoded_input) end) + |> Enum.map(fn {transaction, decoded_input} -> + prepare_transaction(transaction, conn, false, block_height, decoded_input) + end) end def render("transaction.json", %{transaction: transaction, conn: conn}) do @@ -166,12 +170,14 @@ defmodule BlockScoutWeb.API.V2.TransactionView do } end - def render("logs.json", %{logs: logs, next_page_params: next_page_params, tx_hash: tx_hash}) do + def render("logs.json", %{logs: logs, next_page_params: next_page_params, transaction_hash: transaction_hash}) do decoded_logs = decode_logs(logs, false) %{ "items" => - logs |> Enum.zip(decoded_logs) |> Enum.map(fn {log, decoded_log} -> prepare_log(log, tx_hash, decoded_log) end), + logs + |> Enum.zip(decoded_logs) + |> Enum.map(fn {log, decoded_log} -> prepare_log(log, transaction_hash, decoded_log) end), "next_page_params" => next_page_params } end @@ -264,6 +270,8 @@ defmodule BlockScoutWeb.API.V2.TransactionView do false ), "value" => internal_transaction.value, + "block_number" => internal_transaction.block_number, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `block_number` property "block" => internal_transaction.block_number, "timestamp" => (block && block.timestamp) || internal_transaction.block.timestamp, "index" => internal_transaction.index, @@ -276,7 +284,9 @@ defmodule BlockScoutWeb.API.V2.TransactionView do decoded = process_decoded_log(decoded_log) %{ - "tx_hash" => get_tx_hash(transaction_or_hash), + "transaction_hash" => get_transaction_hash(transaction_or_hash), + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_hash` property + "tx_hash" => get_transaction_hash(transaction_or_hash), "address" => Helper.address_with_info(nil, log.address, log.address_hash, tags_for_address_needed?), "topics" => [ log.first_topic, @@ -315,11 +325,11 @@ defmodule BlockScoutWeb.API.V2.TransactionView do } end - defp get_tx_hash(%Transaction{} = tx), do: to_string(tx.hash) - defp get_tx_hash(hash), do: to_string(hash) + defp get_transaction_hash(%Transaction{} = transaction), do: to_string(transaction.hash) + defp get_transaction_hash(hash), do: to_string(hash) - defp smart_contract_info(%Transaction{} = tx), - do: Helper.address_with_info(nil, tx.to_address, tx.to_address_hash, false) + defp smart_contract_info(%Transaction{} = transaction), + do: Helper.address_with_info(nil, transaction.to_address, transaction.to_address_hash, false) defp smart_contract_info(_), do: nil @@ -333,12 +343,12 @@ defmodule BlockScoutWeb.API.V2.TransactionView do end end - defp prepare_transaction(tx, conn, single_tx?, block_height, watchlist_names \\ nil, decoded_input) + defp prepare_transaction(transaction, conn, single_transaction?, block_height, watchlist_names \\ nil, decoded_input) defp prepare_transaction( {%Reward{} = emission_reward, %Reward{} = validator_reward}, conn, - single_tx?, + single_transaction?, _block_height, _watchlist_names, _decoded_input @@ -347,19 +357,31 @@ defmodule BlockScoutWeb.API.V2.TransactionView do "emission_reward" => emission_reward.reward, "block_hash" => validator_reward.block_hash, "from" => - Helper.address_with_info(single_tx? && conn, emission_reward.address, emission_reward.address_hash, single_tx?), + Helper.address_with_info( + single_transaction? && conn, + emission_reward.address, + emission_reward.address_hash, + single_transaction? + ), "to" => Helper.address_with_info( - single_tx? && conn, + single_transaction? && conn, validator_reward.address, validator_reward.address_hash, - single_tx? + single_transaction? ), "types" => [:reward] } end - defp prepare_transaction(%Transaction{} = transaction, conn, single_tx?, block_height, watchlist_names, decoded_input) do + defp prepare_transaction( + %Transaction{} = transaction, + conn, + single_transaction?, + block_height, + watchlist_names, + decoded_input + ) do base_fee_per_gas = transaction.block && transaction.block.base_fee_per_gas max_priority_fee_per_gas = transaction.max_priority_fee_per_gas max_fee_per_gas = transaction.max_fee_per_gas @@ -378,30 +400,32 @@ defmodule BlockScoutWeb.API.V2.TransactionView do "hash" => transaction.hash, "result" => status, "status" => transaction.status, + "block_number" => transaction.block_number, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `block_number` property "block" => transaction.block_number, "timestamp" => block_timestamp(transaction), "from" => Helper.address_with_info( - single_tx? && conn, + single_transaction? && conn, transaction.from_address, transaction.from_address_hash, - single_tx?, + single_transaction?, watchlist_names ), "to" => Helper.address_with_info( - single_tx? && conn, + single_transaction? && conn, transaction.to_address, transaction.to_address_hash, - single_tx?, + single_transaction?, watchlist_names ), "created_contract" => Helper.address_with_info( - single_tx? && conn, + single_transaction? && conn, transaction.created_contract_address, transaction.created_contract_address_hash, - single_tx?, + single_transaction?, watchlist_names ), "confirmations" => transaction.block |> Chain.confirmations(block_height: block_height) |> format_confirmations(), @@ -422,19 +446,26 @@ defmodule BlockScoutWeb.API.V2.TransactionView do "revert_reason" => revert_reason, "raw_input" => transaction.input, "decoded_input" => decoded_input_data, - "token_transfers" => token_transfers(transaction.token_transfers, conn, single_tx?), - "token_transfers_overflow" => token_transfers_overflow(transaction.token_transfers, single_tx?), + "token_transfers" => token_transfers(transaction.token_transfers, conn, single_transaction?), + "token_transfers_overflow" => token_transfers_overflow(transaction.token_transfers, single_transaction?), "actions" => transaction_actions(transaction.transaction_actions), "exchange_rate" => Market.get_coin_exchange_rate().usd_value, "method" => Transaction.method_name(transaction, decoded_input), - "tx_types" => tx_types(transaction), - "tx_tag" => GetTransactionTags.get_transaction_tags(transaction.hash, current_user(single_tx? && conn)), - "has_error_in_internal_txs" => transaction.has_error_in_internal_txs, + "transaction_types" => transaction_types(transaction), + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_types` property + "tx_types" => transaction_types(transaction), + "transaction_tag" => + GetTransactionTags.get_transaction_tags(transaction.hash, current_user(single_transaction? && conn)), + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_tag` property + "tx_tag" => GetTransactionTags.get_transaction_tags(transaction.hash, current_user(single_transaction? && conn)), + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `has_error_in_internal_transactions` property + "has_error_in_internal_txs" => transaction.has_error_in_internal_transactions, + "has_error_in_internal_transactions" => transaction.has_error_in_internal_transactions, "authorization_list" => authorization_list(transaction.signed_authorizations) } result - |> chain_type_fields(transaction, single_tx?, conn, watchlist_names) + |> chain_type_fields(transaction, single_transaction?, conn, watchlist_names) end def token_transfers(_, _conn, false), do: nil @@ -512,7 +543,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do end @doc """ - Prepares decoded tx info + Prepares decoded transaction info """ @spec decoded_input(any()) :: map() | nil def decoded_input(decoded_input) do @@ -589,14 +620,14 @@ defmodule BlockScoutWeb.API.V2.TransactionView do end @doc """ - Returns array of token types for tx. + Returns array of token types for transaction. """ - @spec tx_types( + @spec transaction_types( Explorer.Chain.Transaction.t(), - [tx_type], - tx_type - ) :: [tx_type] - when tx_type: + [transaction_type], + transaction_type + ) :: [transaction_type] + when transaction_type: :coin_transfer | :contract_call | :contract_creation @@ -606,9 +637,9 @@ defmodule BlockScoutWeb.API.V2.TransactionView do | :token_transfer | :blob_transaction | :set_code_transaction - def tx_types(tx, types \\ [], stage \\ :set_code_transaction) + def transaction_types(transaction, types \\ [], stage \\ :set_code_transaction) - def tx_types(%Transaction{type: type} = tx, types, :set_code_transaction) do + def transaction_types(%Transaction{type: type} = transaction, types, :set_code_transaction) do # EIP-7702 set code transaction type types = if type == 4 do @@ -617,10 +648,10 @@ defmodule BlockScoutWeb.API.V2.TransactionView do types end - tx_types(tx, types, :blob_transaction) + transaction_types(transaction, types, :blob_transaction) end - def tx_types(%Transaction{type: type} = tx, types, :blob_transaction) do + def transaction_types(%Transaction{type: type} = transaction, types, :blob_transaction) do # EIP-2718 blob transaction type types = if type == 3 do @@ -629,22 +660,26 @@ defmodule BlockScoutWeb.API.V2.TransactionView do types end - tx_types(tx, types, :token_transfer) + transaction_types(transaction, types, :token_transfer) end - def tx_types(%Transaction{token_transfers: token_transfers} = tx, types, :token_transfer) do + def transaction_types(%Transaction{token_transfers: token_transfers} = transaction, types, :token_transfer) do types = if (!is_nil(token_transfers) && token_transfers != [] && !match?(%NotLoaded{}, token_transfers)) || - tx.has_token_transfers do + transaction.has_token_transfers do [:token_transfer | types] else types end - tx_types(tx, types, :token_creation) + transaction_types(transaction, types, :token_creation) end - def tx_types(%Transaction{created_contract_address: created_contract_address} = tx, types, :token_creation) do + def transaction_types( + %Transaction{created_contract_address: created_contract_address} = transaction, + types, + :token_creation + ) do types = if match?(%Address{}, created_contract_address) && match?(%Token{}, created_contract_address.token) do [:token_creation | types] @@ -652,11 +687,11 @@ defmodule BlockScoutWeb.API.V2.TransactionView do types end - tx_types(tx, types, :contract_creation) + transaction_types(transaction, types, :contract_creation) end - def tx_types( - %Transaction{to_address_hash: to_address_hash} = tx, + def transaction_types( + %Transaction{to_address_hash: to_address_hash} = transaction, types, :contract_creation ) do @@ -667,10 +702,10 @@ defmodule BlockScoutWeb.API.V2.TransactionView do types end - tx_types(tx, types, :contract_call) + transaction_types(transaction, types, :contract_call) end - def tx_types(%Transaction{to_address: to_address} = tx, types, :contract_call) do + def transaction_types(%Transaction{to_address: to_address} = transaction, types, :contract_call) do types = if Address.smart_contract?(to_address) do [:contract_call | types] @@ -678,10 +713,10 @@ defmodule BlockScoutWeb.API.V2.TransactionView do types end - tx_types(tx, types, :coin_transfer) + transaction_types(transaction, types, :coin_transfer) end - def tx_types(%Transaction{value: value} = tx, types, :coin_transfer) do + def transaction_types(%Transaction{value: value} = transaction, types, :coin_transfer) do types = if Decimal.compare(value.value, 0) == :gt do [:coin_transfer | types] @@ -689,22 +724,22 @@ defmodule BlockScoutWeb.API.V2.TransactionView do types end - tx_types(tx, types, :rootstock_remasc) + transaction_types(transaction, types, :rootstock_remasc) end - def tx_types(tx, types, :rootstock_remasc) do + def transaction_types(transaction, types, :rootstock_remasc) do types = - if Transaction.rootstock_remasc_transaction?(tx) do + if Transaction.rootstock_remasc_transaction?(transaction) do [:rootstock_remasc | types] else types end - tx_types(tx, types, :rootstock_bridge) + transaction_types(transaction, types, :rootstock_bridge) end - def tx_types(tx, types, :rootstock_bridge) do - if Transaction.rootstock_bridge_transaction?(tx) do + def transaction_types(transaction, types, :rootstock_bridge) do + if Transaction.rootstock_bridge_transaction?(transaction) do [:rootstock_bridge | types] else types @@ -776,8 +811,8 @@ defmodule BlockScoutWeb.API.V2.TransactionView do transactions end - defp chain_type_fields(result, transaction, single_tx?, conn, _watchlist_names) do - if single_tx? do + defp chain_type_fields(result, transaction, single_transaction?, conn, _watchlist_names) do + if single_transaction? do # credo:disable-for-next-line Credo.Check.Design.AliasUsage BlockScoutWeb.API.V2.PolygonEdgeView.extend_transaction_json_response(result, transaction.hash, conn) else @@ -790,8 +825,8 @@ defmodule BlockScoutWeb.API.V2.TransactionView do transactions end - defp chain_type_fields(result, transaction, single_tx?, _conn, _watchlist_names) do - if single_tx? do + defp chain_type_fields(result, transaction, single_transaction?, _conn, _watchlist_names) do + if single_transaction? do # credo:disable-for-next-line Credo.Check.Design.AliasUsage BlockScoutWeb.API.V2.PolygonZkevmView.extend_transaction_json_response(result, transaction) else @@ -804,8 +839,8 @@ defmodule BlockScoutWeb.API.V2.TransactionView do transactions end - defp chain_type_fields(result, transaction, single_tx?, _conn, _watchlist_names) do - if single_tx? do + defp chain_type_fields(result, transaction, single_transaction?, _conn, _watchlist_names) do + if single_transaction? do # credo:disable-for-next-line Credo.Check.Design.AliasUsage BlockScoutWeb.API.V2.ZkSyncView.extend_transaction_json_response(result, transaction) else @@ -818,8 +853,8 @@ defmodule BlockScoutWeb.API.V2.TransactionView do transactions end - defp chain_type_fields(result, transaction, single_tx?, _conn, _watchlist_names) do - if single_tx? do + defp chain_type_fields(result, transaction, single_transaction?, _conn, _watchlist_names) do + if single_transaction? do # credo:disable-for-next-line Credo.Check.Design.AliasUsage BlockScoutWeb.API.V2.ArbitrumView.extend_transaction_json_response(result, transaction) else @@ -832,8 +867,8 @@ defmodule BlockScoutWeb.API.V2.TransactionView do transactions end - defp chain_type_fields(result, transaction, single_tx?, _conn, _watchlist_names) do - if single_tx? do + defp chain_type_fields(result, transaction, single_transaction?, _conn, _watchlist_names) do + if single_transaction? do # credo:disable-for-next-line Credo.Check.Design.AliasUsage BlockScoutWeb.API.V2.OptimismView.extend_transaction_json_response(result, transaction) else @@ -846,13 +881,13 @@ defmodule BlockScoutWeb.API.V2.TransactionView do transactions end - defp chain_type_fields(result, transaction, single_tx?, conn, watchlist_names) do - if single_tx? do + defp chain_type_fields(result, transaction, single_transaction?, conn, watchlist_names) do + if single_transaction? do # credo:disable-for-next-line Credo.Check.Design.AliasUsage BlockScoutWeb.API.V2.SuaveView.extend_transaction_json_response( transaction, result, - single_tx?, + single_transaction?, conn, watchlist_names ) @@ -867,7 +902,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do BlockScoutWeb.API.V2.StabilityView.transform_transactions(transactions) end - defp chain_type_fields(result, transaction, _single_tx?, _conn, _watchlist_names) do + defp chain_type_fields(result, transaction, _single_transaction?, _conn, _watchlist_names) do # credo:disable-for-next-line Credo.Check.Design.AliasUsage BlockScoutWeb.API.V2.StabilityView.extend_transaction_json_response(result, transaction) end @@ -877,7 +912,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do transactions end - defp chain_type_fields(result, transaction, _single_tx?, _conn, _watchlist_names) do + defp chain_type_fields(result, transaction, _single_transaction?, _conn, _watchlist_names) do # credo:disable-for-next-line Credo.Check.Design.AliasUsage BlockScoutWeb.API.V2.EthereumView.extend_transaction_json_response(result, transaction) end @@ -887,7 +922,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do transactions end - defp chain_type_fields(result, transaction, _single_tx?, _conn, _watchlist_names) do + defp chain_type_fields(result, transaction, _single_transaction?, _conn, _watchlist_names) do # credo:disable-for-next-line Credo.Check.Design.AliasUsage BlockScoutWeb.API.V2.CeloView.extend_transaction_json_response(result, transaction) end @@ -897,7 +932,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do transactions end - defp chain_type_fields(result, _transaction, _single_tx?, _conn, _watchlist_names) do + defp chain_type_fields(result, _transaction, _single_transaction?, _conn, _watchlist_names) do result end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/zksync_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/zksync_view.ex index a5c418aa8d79..b2eeb59c7b03 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/zksync_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/zksync_view.ex @@ -15,14 +15,18 @@ defmodule BlockScoutWeb.API.V2.ZkSyncView do "number" => batch.number, "timestamp" => batch.timestamp, "root_hash" => batch.root_hash, - "l1_tx_count" => batch.l1_tx_count, - "l2_tx_count" => batch.l2_tx_count, + "l1_transaction_count" => batch.l1_transaction_count, + "l2_transaction_count" => batch.l2_transaction_count, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l1_transaction_count` property + "l1_tx_count" => batch.l1_transaction_count, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l2_transaction_count` property + "l2_tx_count" => batch.l2_transaction_count, "l1_gas_price" => batch.l1_gas_price, "l2_fair_gas_price" => batch.l2_fair_gas_price, "start_block" => batch.start_block, "end_block" => batch.end_block } - |> add_l1_txs_info_and_status(batch) + |> add_l1_transactions_info_and_status(batch) end @doc """ @@ -64,9 +68,11 @@ defmodule BlockScoutWeb.API.V2.ZkSyncView do %{ "number" => batch.number, "timestamp" => batch.timestamp, - "tx_count" => batch.l1_tx_count + batch.l2_tx_count + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_count` property + "tx_count" => batch.l1_transaction_count + batch.l2_transaction_count, + "transaction_count" => batch.l1_transaction_count + batch.l2_transaction_count } - |> add_l1_txs_info_and_status(batch) + |> add_l1_transactions_info_and_status(batch) end) end @@ -119,7 +125,7 @@ defmodule BlockScoutWeb.API.V2.ZkSyncView do defp do_add_zksync_info(out_json, zksync_entity) do res = %{} - |> do_add_l1_txs_info_and_status(%{ + |> do_add_l1_transactions_info_and_status(%{ batch_number: get_batch_number(zksync_entity), commit_transaction: zksync_entity.zksync_commit_transaction, prove_transaction: zksync_entity.zksync_prove_transaction, @@ -138,22 +144,22 @@ defmodule BlockScoutWeb.API.V2.ZkSyncView do end end - defp add_l1_txs_info_and_status(out_json, %TransactionBatch{} = batch) do - do_add_l1_txs_info_and_status(out_json, batch) + defp add_l1_transactions_info_and_status(out_json, %TransactionBatch{} = batch) do + do_add_l1_transactions_info_and_status(out_json, batch) end - defp do_add_l1_txs_info_and_status(out_json, zksync_item) do - l1_txs = get_associated_l1_txs(zksync_item) + defp do_add_l1_transactions_info_and_status(out_json, zksync_item) do + l1_transactions = get_associated_l1_transactions(zksync_item) out_json |> Map.merge(%{ "status" => batch_status(zksync_item), - "commit_transaction_hash" => APIV2Helper.get_2map_data(l1_txs, :commit_transaction, :hash), - "commit_transaction_timestamp" => APIV2Helper.get_2map_data(l1_txs, :commit_transaction, :ts), - "prove_transaction_hash" => APIV2Helper.get_2map_data(l1_txs, :prove_transaction, :hash), - "prove_transaction_timestamp" => APIV2Helper.get_2map_data(l1_txs, :prove_transaction, :ts), - "execute_transaction_hash" => APIV2Helper.get_2map_data(l1_txs, :execute_transaction, :hash), - "execute_transaction_timestamp" => APIV2Helper.get_2map_data(l1_txs, :execute_transaction, :ts) + "commit_transaction_hash" => APIV2Helper.get_2map_data(l1_transactions, :commit_transaction, :hash), + "commit_transaction_timestamp" => APIV2Helper.get_2map_data(l1_transactions, :commit_transaction, :ts), + "prove_transaction_hash" => APIV2Helper.get_2map_data(l1_transactions, :prove_transaction, :hash), + "prove_transaction_timestamp" => APIV2Helper.get_2map_data(l1_transactions, :prove_transaction, :ts), + "execute_transaction_hash" => APIV2Helper.get_2map_data(l1_transactions, :execute_transaction, :hash), + "execute_transaction_timestamp" => APIV2Helper.get_2map_data(l1_transactions, :execute_transaction, :ts) }) end @@ -165,13 +171,13 @@ defmodule BlockScoutWeb.API.V2.ZkSyncView do # # ## Returns # A map containing nesting maps describing corresponding L1 transactions - defp get_associated_l1_txs(zksync_item) do + defp get_associated_l1_transactions(zksync_item) do [:commit_transaction, :prove_transaction, :execute_transaction] - |> Enum.reduce(%{}, fn key, l1_txs -> + |> Enum.reduce(%{}, fn key, l1_transactions -> case Map.get(zksync_item, key) do - nil -> Map.put(l1_txs, key, nil) - %Ecto.Association.NotLoaded{} -> Map.put(l1_txs, key, nil) - value -> Map.put(l1_txs, key, %{hash: value.hash, ts: value.timestamp}) + nil -> Map.put(l1_transactions, key, nil) + %Ecto.Association.NotLoaded{} -> Map.put(l1_transactions, key, nil) + value -> Map.put(l1_transactions, key, %{hash: value.hash, ts: value.timestamp}) end end) end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tab_helper.ex b/apps/block_scout_web/lib/block_scout_web/views/tab_helper.ex index e6b4ff74e9ac..8e2ec70997b2 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/tab_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/tab_helper.ex @@ -24,7 +24,7 @@ defmodule BlockScoutWeb.TabHelper do "active" else case request_path do - "/tx/" <> "0x" <> <<_tx_hash::binary-size(64)>> -> + "/tx/" <> "0x" <> <<_transaction_hash::binary-size(64)>> -> tab_status_selector(tab_name, show_token_transfers) _ -> diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex index 73814009ac8a..88fc2098312b 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex @@ -22,8 +22,8 @@ defmodule BlockScoutWeb.TransactionStateView do Decimal.abs(val) end - def has_state_changes?(tx) do - has_diff?(from_loss(tx)) or has_diff?(to_profit(tx)) + def has_state_changes?(transaction) do + has_diff?(from_loss(transaction)) or has_diff?(to_profit(transaction)) end def display_value(balance, :coin, _token_id) do diff --git a/apps/block_scout_web/test/block_scout_web/channels/websocket_v2_test.exs b/apps/block_scout_web/test/block_scout_web/channels/websocket_v2_test.exs index e4ff798de0a1..77f2332c335f 100644 --- a/apps/block_scout_web/test/block_scout_web/channels/websocket_v2_test.exs +++ b/apps/block_scout_web/test/block_scout_web/channels/websocket_v2_test.exs @@ -237,9 +237,9 @@ defmodule BlockScoutWeb.WebsocketV2Test do Subscriber.to(:transactions, :realtime) Import.all(@import_data) - assert_receive {:chain_event, :transactions, :realtime, txs}, :timer.seconds(5) + assert_receive {:chain_event, :transactions, :realtime, transactions}, :timer.seconds(5) - Notifier.handle_event({:chain_event, :transactions, :realtime, txs}) + Notifier.handle_event({:chain_event, :transactions, :realtime, transactions}) assert_receive %Phoenix.Socket.Message{ payload: %{transaction: 2}, @@ -280,7 +280,7 @@ defmodule BlockScoutWeb.WebsocketV2Test do :timer.seconds(5) end - test "broadcast array of txs to address" do + test "broadcast array of transactions to address" do topic = "addresses:0x8bf38d4764929064f2d4d3a56520a76ab3df415b" {:ok, _reply, _socket} = @@ -291,34 +291,34 @@ defmodule BlockScoutWeb.WebsocketV2Test do Subscriber.to(:transactions, :realtime) Import.all(@import_data) - assert_receive {:chain_event, :transactions, :realtime, txs}, :timer.seconds(5) - Notifier.handle_event({:chain_event, :transactions, :realtime, txs}) + assert_receive {:chain_event, :transactions, :realtime, transactions}, :timer.seconds(5) + Notifier.handle_event({:chain_event, :transactions, :realtime, transactions}) assert_receive %Phoenix.Socket.Message{ - payload: %{transactions: [tx_1, tx_2]}, + payload: %{transactions: [transaction_1, transaction_2]}, event: "transaction", topic: ^topic }, :timer.seconds(5) - tx_1 = tx_1 |> Jason.encode!() |> Jason.decode!() - compare_item(Repo.get_by(Transaction, %{hash: tx_1["hash"]}), tx_1) + transaction_1 = transaction_1 |> Jason.encode!() |> Jason.decode!() + compare_item(Repo.get_by(Transaction, %{hash: transaction_1["hash"]}), transaction_1) - tx_2 = tx_2 |> Jason.encode!() |> Jason.decode!() - compare_item(Repo.get_by(Transaction, %{hash: tx_2["hash"]}), tx_2) + transaction_2 = transaction_2 |> Jason.encode!() |> Jason.decode!() + compare_item(Repo.get_by(Transaction, %{hash: transaction_2["hash"]}), transaction_2) assert_receive %Phoenix.Socket.Message{ - payload: %{transactions: [tx_1, tx_2]}, + payload: %{transactions: [transaction_1, transaction_2]}, event: "pending_transaction", topic: ^topic }, :timer.seconds(5) - tx_1 = tx_1 |> Jason.encode!() |> Jason.decode!() - compare_item(Repo.get_by(Transaction, %{hash: tx_1["hash"]}), tx_1) + transaction_1 = transaction_1 |> Jason.encode!() |> Jason.decode!() + compare_item(Repo.get_by(Transaction, %{hash: transaction_1["hash"]}), transaction_1) - tx_2 = tx_2 |> Jason.encode!() |> Jason.decode!() - compare_item(Repo.get_by(Transaction, %{hash: tx_2["hash"]}), tx_2) + transaction_2 = transaction_2 |> Jason.encode!() |> Jason.decode!() + compare_item(Repo.get_by(Transaction, %{hash: transaction_2["hash"]}), transaction_2) end test "broadcast array of transfers to address" do @@ -369,17 +369,17 @@ defmodule BlockScoutWeb.WebsocketV2Test do defp compare_item(%TokenTransfer{} = token_transfer, json) do assert Address.checksum(token_transfer.from_address_hash) == json["from"]["hash"] assert Address.checksum(token_transfer.to_address_hash) == json["to"]["hash"] - assert to_string(token_transfer.transaction_hash) == json["tx_hash"] + assert to_string(token_transfer.transaction_hash) == json["transaction_hash"] assert json["timestamp"] != nil assert json["method"] != nil assert to_string(token_transfer.block_hash) == json["block_hash"] - assert to_string(token_transfer.log_index) == json["log_index"] + assert token_transfer.log_index == json["log_index"] assert check_total(Repo.preload(token_transfer, [{:token, :contract_address}]).token, json["total"], token_transfer) end defp compare_item(%Transaction{} = transaction, json) do assert to_string(transaction.hash) == json["hash"] - assert transaction.block_number == json["block"] + assert transaction.block_number == json["block_number"] assert to_string(transaction.value.value) == json["value"] assert Address.checksum(transaction.from_address_hash) == json["from"]["hash"] assert Address.checksum(transaction.to_address_hash) == json["to"]["hash"] diff --git a/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs index cfeeaa31012a..ae3e048e178c 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs @@ -274,32 +274,32 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do end test "post private transaction tag", %{conn: conn} do - tx_hash_non_existing = to_string(build(:transaction).hash) - tx_hash = to_string(insert(:transaction).hash) + transaction_hash_non_existing = to_string(build(:transaction).hash) + transaction_hash = to_string(insert(:transaction).hash) assert conn |> post("/api/account/v2/user/tags/transaction", %{ - "transaction_hash" => tx_hash_non_existing, + "transaction_hash" => transaction_hash_non_existing, "name" => "MyName" }) - |> doc(description: "Error on try to create private transaction tag for tx does not exist") - |> json_response(422) == %{"errors" => %{"tx_hash" => ["Transaction does not exist"]}} + |> doc(description: "Error on try to create private transaction tag for transaction does not exist") + |> json_response(422) == %{"errors" => %{"transaction_hash" => ["Transaction does not exist"]}} tag_transaction_response = conn |> post("/api/account/v2/user/tags/transaction", %{ - "transaction_hash" => tx_hash, + "transaction_hash" => transaction_hash, "name" => "MyName" }) |> doc(description: "Create private transaction tag") |> json_response(200) conn - |> get("/api/account/v2/tags/transaction/#{tx_hash}") + |> get("/api/account/v2/tags/transaction/#{transaction_hash}") |> doc(description: "Get tags for transaction") |> json_response(200) - assert tag_transaction_response["transaction_hash"] == tx_hash + assert tag_transaction_response["transaction_hash"] == transaction_hash assert tag_transaction_response["name"] == "MyName" assert tag_transaction_response["id"] end @@ -349,11 +349,11 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do end test "edit private transaction tag", %{conn: conn} do - tx_tag = build(:tag_transaction) + transaction_tag = build(:tag_transaction) tag_response = conn - |> post("/api/account/v2/user/tags/transaction", tx_tag) + |> post("/api/account/v2/user/tags/transaction", transaction_tag) |> json_response(200) _response = @@ -361,20 +361,20 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do |> get("/api/account/v2/user/tags/transaction") |> json_response(200) == [tag_response] - assert tag_response["address_hash"] == tx_tag["address_hash"] - assert tag_response["name"] == tx_tag["name"] + assert tag_response["address_hash"] == transaction_tag["address_hash"] + assert tag_response["name"] == transaction_tag["name"] assert tag_response["id"] - new_tx_tag = build(:tag_transaction) + new_transaction_tag = build(:tag_transaction) new_tag_response = conn - |> put("/api/account/v2/user/tags/transaction/#{tag_response["id"]}", new_tx_tag) + |> put("/api/account/v2/user/tags/transaction/#{tag_response["id"]}", new_transaction_tag) |> doc(description: "Edit private transaction tag") |> json_response(200) - assert new_tag_response["address_hash"] == new_tx_tag["address_hash"] - assert new_tag_response["name"] == new_tx_tag["name"] + assert new_tag_response["address_hash"] == new_transaction_tag["address_hash"] + assert new_tag_response["name"] == new_transaction_tag["name"] assert new_tag_response["id"] == tag_response["id"] end @@ -384,25 +384,25 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do zipped = Enum.zip(transactions, names) created = - Enum.map(zipped, fn {tx_hash, name} -> + Enum.map(zipped, fn {transaction_hash, name} -> id = (conn |> post("/api/account/v2/user/tags/transaction", %{ - "transaction_hash" => tx_hash, + "transaction_hash" => transaction_hash, "name" => name }) |> json_response(200))["id"] - {tx_hash, %{"label" => name}, %{"transaction_hash" => tx_hash, "id" => id, "name" => name}} + {transaction_hash, %{"label" => name}, %{"transaction_hash" => transaction_hash, "id" => id, "name" => name}} end) - assert Enum.all?(created, fn {tx_hash, map_tag, _} -> + assert Enum.all?(created, fn {transaction_hash, map_tag, _} -> response = conn - |> get("/api/account/v2/tags/transaction/#{tx_hash}") + |> get("/api/account/v2/tags/transaction/#{transaction_hash}") |> json_response(200) - response["personal_tx_tag"] == map_tag + response["personal_transaction_tag"] == map_tag end) response = @@ -421,25 +421,25 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do zipped = Enum.zip(transactions, names) created = - Enum.map(zipped, fn {tx_hash, name} -> + Enum.map(zipped, fn {transaction_hash, name} -> id = (conn |> post("/api/account/v2/user/tags/transaction", %{ - "transaction_hash" => tx_hash, + "transaction_hash" => transaction_hash, "name" => name }) |> json_response(200))["id"] - {tx_hash, %{"label" => name}, %{"transaction_hash" => tx_hash, "id" => id, "name" => name}} + {transaction_hash, %{"label" => name}, %{"transaction_hash" => transaction_hash, "id" => id, "name" => name}} end) - assert Enum.all?(created, fn {tx_hash, map_tag, _} -> + assert Enum.all?(created, fn {transaction_hash, map_tag, _} -> response = conn - |> get("/api/account/v2/tags/transaction/#{tx_hash}") + |> get("/api/account/v2/tags/transaction/#{transaction_hash}") |> json_response(200) - response["personal_tx_tag"] == map_tag + response["personal_transaction_tag"] == map_tag end) response = @@ -475,7 +475,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do |> json_response(200) |> Map.get("items") - response["personal_tx_tag"] == nil + response["personal_transaction_tag"] == nil end) end @@ -1227,7 +1227,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do end defp compare_item(%TagTransaction{} = tag_transaction, json) do - assert json["transaction_hash"] == to_string(tag_transaction.tx_hash) + assert json["transaction_hash"] == to_string(tag_transaction.transaction_hash) assert json["name"] == tag_transaction.name assert json["id"] == tag_transaction.id end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs index 0901f55f6f7b..4426ee891d16 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs @@ -2572,12 +2572,12 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do erc_721_tt = for x <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, from_address: address, token_contract_address: erc_721_token.contract_address, token_ids: [x] diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs index 7812d118810f..94e217d62efd 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs @@ -611,7 +611,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do implementation_contract_address_hash_string = Base.encode16(implementation_contract.address_hash.bytes, case: :lower) - proxy_tx_input = + proxy_transaction_input = "0x11b804ab000000000000000000000000" <> implementation_contract_address_hash_string <> "000000000000000000000000000000000000000000000000000000000000006035323031313537360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000284e159163400000000000000000000000034420c13696f4ac650b9fafe915553a1abcd7dd30000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000ff5ae9b0a7522736299d797d80b8fc6f31d61100000000000000000000000000ff5ae9b0a7522736299d797d80b8fc6f31d6110000000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034420c13696f4ac650b9fafe915553a1abcd7dd300000000000000000000000000000000000000000000000000000000000000184f7074696d69736d2053756273637269626572204e465473000000000000000000000000000000000000000000000000000000000000000000000000000000054f504e46540000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037697066733a2f2f516d66544e504839765651334b5952346d6b52325a6b757756424266456f5a5554545064395538666931503332752f300000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c82bbe41f2cf04e3a8efa18f7032bdd7f6d98a81000000000000000000000000efba8a2a82ec1fb1273806174f5e28fbb917cf9500000000000000000000000000000000000000000000000000000000" @@ -732,16 +732,16 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do abi: proxy_abi ) - tx = + transaction = insert(:transaction, created_contract_address_hash: proxy_address.hash, - input: proxy_tx_input + input: proxy_transaction_input ) |> with_block(status: :ok) name = implementation_contract.name - from = Address.checksum(tx.from_address_hash) - tx_hash = to_string(tx.hash) + from = Address.checksum(transaction.from_address_hash) + transaction_hash = to_string(transaction.hash) address_hash = Address.checksum(proxy_address.hash) {:ok, implementation_contract_address_hash} = @@ -1170,7 +1170,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do %{ "contractAddress" => contract_address, "contractCreator" => contract_creator, - "txHash" => tx_hash + "txHash" => transaction_hash } ] } = @@ -1180,7 +1180,7 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do assert contract_address == to_string(address.hash) assert contract_creator == to_string(transaction.from_address_hash) - assert tx_hash == to_string(transaction.hash) + assert transaction_hash == to_string(transaction.hash) end end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs index 5f38ff2bbdb5..94aef36b245d 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs @@ -82,7 +82,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do "public_tags" => [], "watchlist_names" => [], "creator_address_hash" => nil, - "creation_tx_hash" => nil, + "creation_transaction_hash" => nil, "token" => nil, "coin_balance" => nil, "proxy_type" => nil, @@ -115,7 +115,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert pattern_response["public_tags"] == response["public_tags"] assert pattern_response["watchlist_names"] == response["watchlist_names"] assert pattern_response["creator_address_hash"] == response["creator_address_hash"] - assert pattern_response["creation_tx_hash"] == response["creation_tx_hash"] + assert pattern_response["creation_transaction_hash"] == response["creation_transaction_hash"] assert pattern_response["token"] == response["token"] assert pattern_response["coin_balance"] == response["coin_balance"] assert pattern_response["implementation_address"] == response["implementation_address"] @@ -172,7 +172,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do implementation_contract_address_hash_string = Base.encode16(implementation_contract.address_hash.bytes, case: :lower) - proxy_tx_input = + proxy_transaction_input = "0x11b804ab000000000000000000000000" <> implementation_contract_address_hash_string <> "000000000000000000000000000000000000000000000000000000000000006035323031313537360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000284e159163400000000000000000000000034420c13696f4ac650b9fafe915553a1abcd7dd30000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000ff5ae9b0a7522736299d797d80b8fc6f31d61100000000000000000000000000ff5ae9b0a7522736299d797d80b8fc6f31d6110000000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034420c13696f4ac650b9fafe915553a1abcd7dd300000000000000000000000000000000000000000000000000000000000000184f7074696d69736d2053756273637269626572204e465473000000000000000000000000000000000000000000000000000000000000000000000000000000054f504e46540000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037697066733a2f2f516d66544e504839765651334b5952346d6b52325a6b757756424266456f5a5554545064395538666931503332752f300000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c82bbe41f2cf04e3a8efa18f7032bdd7f6d98a81000000000000000000000000efba8a2a82ec1fb1273806174f5e28fbb917cf9500000000000000000000000000000000000000000000000000000000" @@ -185,16 +185,16 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do contract_code: proxy_deployed_bytecode ) - tx = + transaction = insert(:transaction, created_contract_address_hash: proxy_address.hash, - input: proxy_tx_input + input: proxy_transaction_input ) |> with_block(status: :ok) name = implementation_contract.name - from = Address.checksum(tx.from_address_hash) - tx_hash = to_string(tx.hash) + from = Address.checksum(transaction.from_address_hash) + transaction_hash = to_string(transaction.hash) address_hash = Address.checksum(proxy_address.hash) {:ok, implementation_contract_address_hash} = @@ -220,7 +220,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do "public_tags" => [], "watchlist_names" => [], "creator_address_hash" => ^from, - "creation_tx_hash" => ^tx_hash, + "creation_transaction_hash" => ^transaction_hash, "proxy_type" => "eip1167", "implementations" => [ %{"address" => ^checksummed_implementation_contract_address_hash, "name" => ^name} @@ -231,7 +231,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do test "get EIP-1967 proxy contract info", %{conn: conn} do smart_contract = insert(:smart_contract) - tx = + transaction = insert(:transaction, to_address_hash: nil, to_address: nil, @@ -247,8 +247,8 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do ) name = smart_contract.name - from = Address.checksum(tx.from_address_hash) - tx_hash = to_string(tx.hash) + from = Address.checksum(transaction.from_address_hash) + transaction_hash = to_string(transaction.hash) address_hash = Address.checksum(smart_contract.address_hash) implementation_address = insert(:address) @@ -266,7 +266,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do "public_tags" => [], "watchlist_names" => [], "creator_address_hash" => ^from, - "creation_tx_hash" => ^tx_hash, + "creation_transaction_hash" => ^transaction_hash, "proxy_type" => "eip1967", "implementations" => [%{"address" => ^implementation_address_hash_string, "name" => nil}] } = json_response(request, 200) @@ -372,22 +372,22 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do test "get counters", %{conn: conn} do address = insert(:address) - tx_from = insert(:transaction, from_address: address) |> with_block() + transaction_from = insert(:transaction, from_address: address) |> with_block() insert(:transaction, to_address: address) |> with_block() - another_tx = insert(:transaction) |> with_block() + another_transaction = insert(:transaction) |> with_block() insert(:token_transfer, from_address: address, - transaction: another_tx, - block: another_tx.block, - block_number: another_tx.block_number + transaction: another_transaction, + block: another_transaction.block, + block_number: another_transaction.block_number ) insert(:token_transfer, to_address: address, - transaction: another_tx, - block: another_tx.block, - block_number: another_tx.block_number + transaction: another_transaction, + block: another_transaction.block, + block_number: another_transaction.block_number ) insert(:block, miner: address) @@ -398,7 +398,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do request = get(conn, "/api/v2/addresses/#{address.hash}/counters") - gas_used = to_string(tx_from.gas_used) + gas_used = to_string(transaction_from.gas_used) assert %{ "transactions_count" => "2", @@ -427,7 +427,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do test "get relevant transaction", %{conn: conn} do address = insert(:address) - tx = insert(:transaction, from_address: address) |> with_block() + transaction = insert(:transaction, from_address: address) |> with_block() insert(:transaction) |> with_block() @@ -436,14 +436,14 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert response = json_response(request, 200) assert Enum.count(response["items"]) == 1 assert response["next_page_params"] == nil - compare_item(tx, Enum.at(response["items"], 0)) + compare_item(transaction, Enum.at(response["items"], 0)) end test "get pending transaction", %{conn: conn} do address = insert(:address) - tx = insert(:transaction, from_address: address) |> with_block() - pending_tx = insert(:transaction, from_address: address) + transaction = insert(:transaction, from_address: address) |> with_block() + pending_transaction = insert(:transaction, from_address: address) insert(:transaction) |> with_block() insert(:transaction) @@ -453,28 +453,28 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert response = json_response(request, 200) assert Enum.count(response["items"]) == 2 assert response["next_page_params"] == nil - compare_item(pending_tx, Enum.at(response["items"], 0)) - compare_item(tx, Enum.at(response["items"], 1)) + compare_item(pending_transaction, Enum.at(response["items"], 0)) + compare_item(transaction, Enum.at(response["items"], 1)) end test "get only :to transaction", %{conn: conn} do address = insert(:address) insert(:transaction, from_address: address) |> with_block() - tx = insert(:transaction, to_address: address) |> with_block() + transaction = insert(:transaction, to_address: address) |> with_block() request = get(conn, "/api/v2/addresses/#{address.hash}/transactions", %{"filter" => "to"}) assert response = json_response(request, 200) assert Enum.count(response["items"]) == 1 assert response["next_page_params"] == nil - compare_item(tx, Enum.at(response["items"], 0)) + compare_item(transaction, Enum.at(response["items"], 0)) end test "get only :from transactions", %{conn: conn} do address = insert(:address) - tx = insert(:transaction, from_address: address) |> with_block() + transaction = insert(:transaction, from_address: address) |> with_block() insert(:transaction, to_address: address) |> with_block() request = get(conn, "/api/v2/addresses/#{address.hash}/transactions", %{"filter" => "from"}) @@ -482,13 +482,13 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert response = json_response(request, 200) assert Enum.count(response["items"]) == 1 assert response["next_page_params"] == nil - compare_item(tx, Enum.at(response["items"], 0)) + compare_item(transaction, Enum.at(response["items"], 0)) end - test "validated txs can paginate", %{conn: conn} do + test "validated transactions can paginate", %{conn: conn} do address = insert(:address) - txs = insert_list(51, :transaction, from_address: address) |> with_block() + transactions = insert_list(51, :transaction, from_address: address) |> with_block() request = get(conn, "/api/v2/addresses/#{address.hash}/transactions") assert response = json_response(request, 200) @@ -496,13 +496,13 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do request_2nd_page = get(conn, "/api/v2/addresses/#{address.hash}/transactions", response["next_page_params"]) assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, txs) + check_paginated_response(response, response_2nd_page, transactions) end - test "pending txs can paginate", %{conn: conn} do + test "pending transactions can paginate", %{conn: conn} do address = insert(:address) - txs = insert_list(51, :transaction, from_address: address) + transactions = insert_list(51, :transaction, from_address: address) request = get(conn, "/api/v2/addresses/#{address.hash}/transactions") assert response = json_response(request, 200) @@ -510,14 +510,14 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do request_2nd_page = get(conn, "/api/v2/addresses/#{address.hash}/transactions", response["next_page_params"]) assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, txs) + check_paginated_response(response, response_2nd_page, transactions) end - test "pending + validated txs can paginate", %{conn: conn} do + test "pending + validated transactions can paginate", %{conn: conn} do address = insert(:address) - txs_pending = insert_list(51, :transaction, from_address: address) - txs_validated = insert_list(50, :transaction, to_address: address) |> with_block() + transactions_pending = insert_list(51, :transaction, from_address: address) + transactions_validated = insert_list(50, :transaction, to_address: address) |> with_block() request = get(conn, "/api/v2/addresses/#{address.hash}/transactions") assert response = json_response(request, 200) @@ -527,25 +527,29 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert Enum.count(response["items"]) == 50 assert response["next_page_params"] != nil - compare_item(Enum.at(txs_pending, 50), Enum.at(response["items"], 0)) - compare_item(Enum.at(txs_pending, 1), Enum.at(response["items"], 49)) + compare_item(Enum.at(transactions_pending, 50), Enum.at(response["items"], 0)) + compare_item(Enum.at(transactions_pending, 1), Enum.at(response["items"], 49)) assert Enum.count(response_2nd_page["items"]) == 50 assert response_2nd_page["next_page_params"] != nil - compare_item(Enum.at(txs_pending, 0), Enum.at(response_2nd_page["items"], 0)) - compare_item(Enum.at(txs_validated, 49), Enum.at(response_2nd_page["items"], 1)) - compare_item(Enum.at(txs_validated, 1), Enum.at(response_2nd_page["items"], 49)) + compare_item(Enum.at(transactions_pending, 0), Enum.at(response_2nd_page["items"], 0)) + compare_item(Enum.at(transactions_validated, 49), Enum.at(response_2nd_page["items"], 1)) + compare_item(Enum.at(transactions_validated, 1), Enum.at(response_2nd_page["items"], 49)) request = get(conn, "/api/v2/addresses/#{address.hash}/transactions", response_2nd_page["next_page_params"]) assert response = json_response(request, 200) - check_paginated_response(response_2nd_page, response, txs_validated ++ [Enum.at(txs_pending, 0)]) + check_paginated_response( + response_2nd_page, + response, + transactions_validated ++ [Enum.at(transactions_pending, 0)] + ) end - test ":to txs can paginate", %{conn: conn} do + test ":to transactions can paginate", %{conn: conn} do address = insert(:address) - txs = insert_list(51, :transaction, to_address: address) |> with_block() + transactions = insert_list(51, :transaction, to_address: address) |> with_block() insert_list(51, :transaction, from_address: address) |> with_block() filter = %{"filter" => "to"} @@ -557,14 +561,14 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, txs) + check_paginated_response(response, response_2nd_page, transactions) end - test ":from txs can paginate", %{conn: conn} do + test ":from transactions can paginate", %{conn: conn} do address = insert(:address) insert_list(51, :transaction, to_address: address) |> with_block() - txs = insert_list(51, :transaction, from_address: address) |> with_block() + transactions = insert_list(51, :transaction, from_address: address) |> with_block() filter = %{"filter" => "from"} request = get(conn, "/api/v2/addresses/#{address.hash}/transactions", filter) @@ -575,14 +579,14 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, txs) + check_paginated_response(response, response_2nd_page, transactions) end - test ":from + :to txs can paginate", %{conn: conn} do + test ":from + :to transactions can paginate", %{conn: conn} do address = insert(:address) - txs_from = insert_list(50, :transaction, from_address: address) |> with_block() - txs_to = insert_list(51, :transaction, to_address: address) |> with_block() + transactions_from = insert_list(50, :transaction, from_address: address) |> with_block() + transactions_to = insert_list(51, :transaction, to_address: address) |> with_block() request = get(conn, "/api/v2/addresses/#{address.hash}/transactions") assert response = json_response(request, 200) @@ -592,25 +596,25 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert Enum.count(response["items"]) == 50 assert response["next_page_params"] != nil - compare_item(Enum.at(txs_to, 50), Enum.at(response["items"], 0)) - compare_item(Enum.at(txs_to, 1), Enum.at(response["items"], 49)) + compare_item(Enum.at(transactions_to, 50), Enum.at(response["items"], 0)) + compare_item(Enum.at(transactions_to, 1), Enum.at(response["items"], 49)) assert Enum.count(response_2nd_page["items"]) == 50 assert response_2nd_page["next_page_params"] != nil - compare_item(Enum.at(txs_to, 0), Enum.at(response_2nd_page["items"], 0)) - compare_item(Enum.at(txs_from, 49), Enum.at(response_2nd_page["items"], 1)) - compare_item(Enum.at(txs_from, 1), Enum.at(response_2nd_page["items"], 49)) + compare_item(Enum.at(transactions_to, 0), Enum.at(response_2nd_page["items"], 0)) + compare_item(Enum.at(transactions_from, 49), Enum.at(response_2nd_page["items"], 1)) + compare_item(Enum.at(transactions_from, 1), Enum.at(response_2nd_page["items"], 49)) request = get(conn, "/api/v2/addresses/#{address.hash}/transactions", response_2nd_page["next_page_params"]) assert response = json_response(request, 200) - check_paginated_response(response_2nd_page, response, txs_from ++ [Enum.at(txs_to, 0)]) + check_paginated_response(response_2nd_page, response, transactions_from ++ [Enum.at(transactions_to, 0)]) end test "ignores wrong ordering params", %{conn: conn} do address = insert(:address) - txs = insert_list(51, :transaction, from_address: address) |> with_block() + transactions = insert_list(51, :transaction, from_address: address) |> with_block() request = get(conn, "/api/v2/addresses/#{address.hash}/transactions", %{"sort" => "foo", "order" => "bar"}) assert response = json_response(request, 200) @@ -624,16 +628,16 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, txs) + check_paginated_response(response, response_2nd_page, transactions) end test "backward compatible with legacy paging params", %{conn: conn} do address = insert(:address) block = insert(:block) - txs = insert_list(51, :transaction, from_address: address) |> with_block(block) + transactions = insert_list(51, :transaction, from_address: address) |> with_block(block) - [_, tx_before_last | _] = txs + [_, transaction_before_last | _] = transactions request = get(conn, "/api/v2/addresses/#{address.hash}/transactions") assert response = json_response(request, 200) @@ -642,20 +646,20 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do get( conn, "/api/v2/addresses/#{address.hash}/transactions", - %{"block_number" => to_string(block.number), "index" => to_string(tx_before_last.index)} + %{"block_number" => to_string(block.number), "index" => to_string(transaction_before_last.index)} ) assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, txs) + check_paginated_response(response, response_2nd_page, transactions) end test "backward compatible with legacy paging params for pending transactions", %{conn: conn} do address = insert(:address) - txs = insert_list(51, :transaction, from_address: address) + transactions = insert_list(51, :transaction, from_address: address) - [_, tx_before_last | _] = txs + [_, transaction_before_last | _] = transactions request = get(conn, "/api/v2/addresses/#{address.hash}/transactions") assert response = json_response(request, 200) @@ -664,22 +668,25 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do get( conn, "/api/v2/addresses/#{address.hash}/transactions", - %{"inserted_at" => to_string(tx_before_last.inserted_at), "hash" => to_string(tx_before_last.hash)} + %{ + "inserted_at" => to_string(transaction_before_last.inserted_at), + "hash" => to_string(transaction_before_last.hash) + } ) assert response_2nd_page_pending = json_response(request_2nd_page_pending, 200) - check_paginated_response(response, response_2nd_page_pending, txs) + check_paginated_response(response, response_2nd_page_pending, transactions) end test "can order and paginate by fee ascending", %{conn: conn} do address = insert(:address) - txs_from = insert_list(25, :transaction, from_address: address) |> with_block() - txs_to = insert_list(26, :transaction, to_address: address) |> with_block() + transactions_from = insert_list(25, :transaction, from_address: address) |> with_block() + transactions_to = insert_list(26, :transaction, to_address: address) |> with_block() - txs = - (txs_from ++ txs_to) + transactions = + (transactions_from ++ transactions_to) |> Enum.sort( &(Decimal.compare(&1 |> Transaction.fee(:wei) |> elem(1), &2 |> Transaction.fee(:wei) |> elem(1)) in [ :eq, @@ -701,24 +708,24 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert Enum.count(response["items"]) == 50 assert response["next_page_params"] != nil - compare_item(Enum.at(txs, 0), Enum.at(response["items"], 0)) - compare_item(Enum.at(txs, 49), Enum.at(response["items"], 49)) + compare_item(Enum.at(transactions, 0), Enum.at(response["items"], 0)) + compare_item(Enum.at(transactions, 49), Enum.at(response["items"], 49)) assert Enum.count(response_2nd_page["items"]) == 1 assert response_2nd_page["next_page_params"] == nil - compare_item(Enum.at(txs, 50), Enum.at(response_2nd_page["items"], 0)) + compare_item(Enum.at(transactions, 50), Enum.at(response_2nd_page["items"], 0)) - check_paginated_response(response, response_2nd_page, txs |> Enum.reverse()) + check_paginated_response(response, response_2nd_page, transactions |> Enum.reverse()) end test "can order and paginate by fee descending", %{conn: conn} do address = insert(:address) - txs_from = insert_list(25, :transaction, from_address: address) |> with_block() - txs_to = insert_list(26, :transaction, to_address: address) |> with_block() + transactions_from = insert_list(25, :transaction, from_address: address) |> with_block() + transactions_to = insert_list(26, :transaction, to_address: address) |> with_block() - txs = - (txs_from ++ txs_to) + transactions = + (transactions_from ++ transactions_to) |> Enum.sort( &(Decimal.compare(&1 |> Transaction.fee(:wei) |> elem(1), &2 |> Transaction.fee(:wei) |> elem(1)) in [ :eq, @@ -740,24 +747,24 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert Enum.count(response["items"]) == 50 assert response["next_page_params"] != nil - compare_item(Enum.at(txs, 0), Enum.at(response["items"], 0)) - compare_item(Enum.at(txs, 49), Enum.at(response["items"], 49)) + compare_item(Enum.at(transactions, 0), Enum.at(response["items"], 0)) + compare_item(Enum.at(transactions, 49), Enum.at(response["items"], 49)) assert Enum.count(response_2nd_page["items"]) == 1 assert response_2nd_page["next_page_params"] == nil - compare_item(Enum.at(txs, 50), Enum.at(response_2nd_page["items"], 0)) + compare_item(Enum.at(transactions, 50), Enum.at(response_2nd_page["items"], 0)) - check_paginated_response(response, response_2nd_page, txs |> Enum.reverse()) + check_paginated_response(response, response_2nd_page, transactions |> Enum.reverse()) end test "can order and paginate by value ascending", %{conn: conn} do address = insert(:address) - txs_from = insert_list(25, :transaction, from_address: address) |> with_block() - txs_to = insert_list(26, :transaction, to_address: address) |> with_block() + transactions_from = insert_list(25, :transaction, from_address: address) |> with_block() + transactions_to = insert_list(26, :transaction, to_address: address) |> with_block() - txs = - (txs_from ++ txs_to) + transactions = + (transactions_from ++ transactions_to) |> Enum.sort(&(Decimal.compare(Wei.to(&1.value, :wei), Wei.to(&2.value, :wei)) in [:eq, :lt])) request = get(conn, "/api/v2/addresses/#{address.hash}/transactions", %{"sort" => "value", "order" => "asc"}) @@ -774,24 +781,24 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert Enum.count(response["items"]) == 50 assert response["next_page_params"] != nil - compare_item(Enum.at(txs, 0), Enum.at(response["items"], 0)) - compare_item(Enum.at(txs, 49), Enum.at(response["items"], 49)) + compare_item(Enum.at(transactions, 0), Enum.at(response["items"], 0)) + compare_item(Enum.at(transactions, 49), Enum.at(response["items"], 49)) assert Enum.count(response_2nd_page["items"]) == 1 assert response_2nd_page["next_page_params"] == nil - compare_item(Enum.at(txs, 50), Enum.at(response_2nd_page["items"], 0)) + compare_item(Enum.at(transactions, 50), Enum.at(response_2nd_page["items"], 0)) - check_paginated_response(response, response_2nd_page, txs |> Enum.reverse()) + check_paginated_response(response, response_2nd_page, transactions |> Enum.reverse()) end test "can order and paginate by value descending", %{conn: conn} do address = insert(:address) - txs_from = insert_list(25, :transaction, from_address: address) |> with_block() - txs_to = insert_list(26, :transaction, to_address: address) |> with_block() + transactions_from = insert_list(25, :transaction, from_address: address) |> with_block() + transactions_to = insert_list(26, :transaction, to_address: address) |> with_block() - txs = - (txs_from ++ txs_to) + transactions = + (transactions_from ++ transactions_to) |> Enum.sort(&(Decimal.compare(Wei.to(&1.value, :wei), Wei.to(&2.value, :wei)) in [:eq, :gt])) request = get(conn, "/api/v2/addresses/#{address.hash}/transactions", %{"sort" => "value", "order" => "desc"}) @@ -808,14 +815,14 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert Enum.count(response["items"]) == 50 assert response["next_page_params"] != nil - compare_item(Enum.at(txs, 0), Enum.at(response["items"], 0)) - compare_item(Enum.at(txs, 49), Enum.at(response["items"], 49)) + compare_item(Enum.at(transactions, 0), Enum.at(response["items"], 0)) + compare_item(Enum.at(transactions, 49), Enum.at(response["items"], 49)) assert Enum.count(response_2nd_page["items"]) == 1 assert response_2nd_page["next_page_params"] == nil - compare_item(Enum.at(txs, 50), Enum.at(response_2nd_page["items"], 0)) + compare_item(Enum.at(transactions, 50), Enum.at(response_2nd_page["items"], 0)) - check_paginated_response(response, response_2nd_page, txs |> Enum.reverse()) + check_paginated_response(response, response_2nd_page, transactions |> Enum.reverse()) end end @@ -855,12 +862,21 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do test "get relevant token transfer", %{conn: conn} do address = insert(:address) - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() - insert(:token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number) + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number + ) token_transfer = - insert(:token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number, from_address: address) + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, + from_address: address + ) request = get(conn, "/api/v2/addresses/#{address.hash}/token-transfers") @@ -891,17 +907,26 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do address = insert(:address) - tx = + transaction = insert(:transaction, input: "0x731133e9000000000000000000000000bb36c792b9b45aaf8b848a1392b0d6559202729e000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000" ) |> with_block() - insert(:token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number) + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number + ) token_transfer = - insert(:token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number, from_address: address) + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, + from_address: address + ) request = get(conn, "/api/v2/addresses/#{address.hash}/token-transfers") @@ -917,17 +942,26 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do address = insert(:address) - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() - insert(:token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number) + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number + ) - insert(:token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number, from_address: address) + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, + from_address: address + ) token_transfer = insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, from_address: address, token_contract_address: token.contract_address ) @@ -950,19 +984,19 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do token_transfers = for _ <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, from_address: address ) insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, from_address: address, token_contract_address: token.contract_address ) @@ -983,12 +1017,22 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do test "get only :to token transfer", %{conn: conn} do address = insert(:address) - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() - insert(:token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number, from_address: address) + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, + from_address: address + ) token_transfer = - insert(:token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number, to_address: address) + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, + to_address: address + ) request = get(conn, "/api/v2/addresses/#{address.hash}/token-transfers", %{"filter" => "to"}) @@ -1001,12 +1045,23 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do test "get only :from token transfer", %{conn: conn} do address = insert(:address) - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() token_transfer = - insert(:token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number, from_address: address) + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, + from_address: address + ) + + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, + to_address: address + ) - insert(:token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number, to_address: address) request = get(conn, "/api/v2/addresses/#{address.hash}/token-transfers", %{"filter" => "from"}) assert response = json_response(request, 200) @@ -1020,12 +1075,12 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do token_transfers = for _ <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, from_address: address ) end @@ -1043,14 +1098,26 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do address = insert(:address) for _ <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() - insert(:token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number, from_address: address) + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() + + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, + from_address: address + ) end token_transfers = for _ <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() - insert(:token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number, to_address: address) + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() + + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, + to_address: address + ) end filter = %{"filter" => "to"} @@ -1070,19 +1137,25 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do token_transfers = for _ <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, from_address: address ) end for _ <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() - insert(:token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number, to_address: address) + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() + + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, + to_address: address + ) end filter = %{"filter" => "from"} @@ -1102,20 +1175,26 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do tt_from = for _ <- 0..49 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, from_address: address ) end tt_to = for _ <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() - insert(:token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number, to_address: address) + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() + + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, + to_address: address + ) end request = get(conn, "/api/v2/addresses/#{address.hash}/token-transfers") @@ -1148,12 +1227,12 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do erc_20_tt = for _ <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, from_address: address, token_contract_address: erc_20_token.contract_address, token_type: "ERC-20" @@ -1164,12 +1243,12 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do erc_721_tt = for x <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, from_address: address, token_contract_address: erc_721_token.contract_address, token_ids: [x], @@ -1181,12 +1260,12 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do erc_1155_tt = for x <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, from_address: address, token_contract_address: erc_1155_token.contract_address, token_ids: [x], @@ -1276,12 +1355,12 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do erc_20_tt = for _ <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, from_address: address, token_contract_address: erc_20_token.contract_address, token_type: "ERC-20" @@ -1292,12 +1371,12 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do erc_721_tt = for x <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, to_address: address, token_contract_address: erc_721_token.contract_address, token_ids: [x], @@ -1356,13 +1435,13 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do tt = for _ <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, to_address: address, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(0..50, fn _x -> id end), token_type: "ERC-1155", @@ -1392,13 +1471,13 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do token_transfers = for i <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, - transaction: tx, + transaction: transaction, to_address: address, - block: tx.block, - block_number: tx.block_number, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: [i], token_type: "ERC-721" @@ -1419,14 +1498,14 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do address = insert(:address) token = insert(:token, type: "ERC-1155") - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() tt = insert(:token_transfer, - transaction: tx, + transaction: transaction, to_address: address, - block: tx.block, - block_number: tx.block_number, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(0..50, fn x -> x end), token_type: "ERC-1155", @@ -1462,14 +1541,14 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do token = insert(:token, type: "ERC-1155") - tx_1 = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction_1 = insert(:transaction, input: "0xabcd010203040506") |> with_block() tt_1 = insert(:token_transfer, - transaction: tx_1, + transaction: transaction_1, to_address: address, - block: tx_1.block, - block_number: tx_1.block_number, + block: transaction_1.block, + block_number: transaction_1.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(0..24, fn x -> x end), token_type: "ERC-1155", @@ -1481,14 +1560,14 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do %TokenTransfer{tt_1 | token_ids: [i], amount: i} end - tx_2 = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction_2 = insert(:transaction, input: "0xabcd010203040506") |> with_block() tt_2 = insert(:token_transfer, - transaction: tx_2, + transaction: transaction_2, to_address: address, - block: tx_2.block, - block_number: tx_2.block_number, + block: transaction_2.block, + block_number: transaction_2.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(25..49, fn x -> x end), token_type: "ERC-1155", @@ -1502,10 +1581,10 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do tt_3 = insert(:token_transfer, - transaction: tx_2, + transaction: transaction_2, from_address: address, - block: tx_2.block, - block_number: tx_2.block_number, + block: transaction_2.block, + block_number: transaction_2.block_number, token_contract_address: token.contract_address, token_ids: [50], token_type: "ERC-1155", @@ -1527,14 +1606,14 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do token = insert(:token, type: "ERC-1155") - tx_1 = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction_1 = insert(:transaction, input: "0xabcd010203040506") |> with_block() tt_1 = insert(:token_transfer, - transaction: tx_1, + transaction: transaction_1, from_address: address, - block: tx_1.block, - block_number: tx_1.block_number, + block: transaction_1.block, + block_number: transaction_1.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(0..24, fn x -> x end), token_type: "ERC-1155", @@ -1546,14 +1625,14 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do %TokenTransfer{tt_1 | token_ids: [i], amount: i} end - tx_2 = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction_2 = insert(:transaction, input: "0xabcd010203040506") |> with_block() tt_2 = insert(:token_transfer, - transaction: tx_2, + transaction: transaction_2, to_address: address, - block: tx_2.block, - block_number: tx_2.block_number, + block: transaction_2.block, + block_number: transaction_2.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(25..50, fn x -> x end), token_type: "ERC-1155", @@ -1591,32 +1670,32 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert %{"message" => "Invalid parameter(s)"} = json_response(request, 422) end - test "get internal tx and filter working", %{conn: conn} do + test "get internal transaction and filter working", %{conn: conn} do address = insert(:address) - tx = + transaction = :transaction |> insert() |> with_block() - internal_tx_from = + internal_transaction_from = insert(:internal_transaction, - transaction: tx, + transaction: transaction, index: 1, - block_number: tx.block_number, - transaction_index: tx.index, - block_hash: tx.block_hash, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, block_index: 1, from_address: address ) - internal_tx_to = + internal_transaction_to = insert(:internal_transaction, - transaction: tx, + transaction: transaction, index: 2, - block_number: tx.block_number, - transaction_index: tx.index, - block_hash: tx.block_hash, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, block_index: 2, to_address: address ) @@ -1627,40 +1706,40 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert Enum.count(response["items"]) == 2 assert response["next_page_params"] == nil - compare_item(internal_tx_from, Enum.at(response["items"], 1)) - compare_item(internal_tx_to, Enum.at(response["items"], 0)) + compare_item(internal_transaction_from, Enum.at(response["items"], 1)) + compare_item(internal_transaction_to, Enum.at(response["items"], 0)) request = get(conn, "/api/v2/addresses/#{address.hash}/internal-transactions", %{"filter" => "from"}) assert response = json_response(request, 200) assert Enum.count(response["items"]) == 1 assert response["next_page_params"] == nil - compare_item(internal_tx_from, Enum.at(response["items"], 0)) + compare_item(internal_transaction_from, Enum.at(response["items"], 0)) request = get(conn, "/api/v2/addresses/#{address.hash}/internal-transactions", %{"filter" => "to"}) assert response = json_response(request, 200) assert Enum.count(response["items"]) == 1 assert response["next_page_params"] == nil - compare_item(internal_tx_to, Enum.at(response["items"], 0)) + compare_item(internal_transaction_to, Enum.at(response["items"], 0)) end - test "internal txs can paginate", %{conn: conn} do + test "internal transactions can paginate", %{conn: conn} do address = insert(:address) - tx = + transaction = :transaction |> insert() |> with_block() - itxs_from = + internal_transactions_from = for i <- 1..51 do insert(:internal_transaction, - transaction: tx, + transaction: transaction, index: i, - block_number: tx.block_number, - transaction_index: tx.index, - block_hash: tx.block_hash, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, block_index: i, from_address: address ) @@ -1674,16 +1753,16 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, itxs_from) + check_paginated_response(response, response_2nd_page, internal_transactions_from) - itxs_to = + internal_transactions_to = for i <- 52..102 do insert(:internal_transaction, - transaction: tx, + transaction: transaction, index: i, - block_number: tx.block_number, - transaction_index: tx.index, - block_hash: tx.block_hash, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, block_index: i, to_address: address ) @@ -1702,7 +1781,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, itxs_to) + check_paginated_response(response, response_2nd_page, internal_transactions_to) filter = %{"filter" => "from"} request = get(conn, "/api/v2/addresses/#{address.hash}/internal-transactions", filter) @@ -1717,7 +1796,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, itxs_from) + check_paginated_response(response, response_2nd_page, internal_transactions_from) end end @@ -1904,17 +1983,17 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do test "get log", %{conn: conn} do address = insert(:address) - tx = + transaction = :transaction |> insert() |> with_block() log = insert(:log, - transaction: tx, + transaction: transaction, index: 1, - block: tx.block, - block_number: tx.block_number, + block: transaction.block, + block_number: transaction.block_number, address: address ) @@ -1932,16 +2011,16 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do logs = for x <- 0..50 do - tx = + transaction = :transaction |> insert() |> with_block() insert(:log, - transaction: tx, + transaction: transaction, index: x, - block: tx.block, - block_number: tx.block_number, + block: transaction.block, + block_number: transaction.block_number, address: address ) end @@ -1958,17 +2037,17 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do test "regression test for 9926", %{conn: conn} do address = insert(:address, hash: "0x036cec1a199234fC02f72d29e596a09440825f1C") - tx = + transaction = :transaction |> insert() |> with_block() log = insert(:log, - transaction: tx, + transaction: transaction, index: 1, - block: tx.block, - block_number: tx.block_number, + block: transaction.block, + block_number: transaction.block_number, address: address ) @@ -2036,30 +2115,30 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do address = insert(:address) for x <- 0..20 do - tx = + transaction = :transaction |> insert() |> with_block() insert(:log, - transaction: tx, + transaction: transaction, index: x, - block: tx.block, - block_number: tx.block_number, + block: transaction.block, + block_number: transaction.block_number, address: address ) end - tx = + transaction = :transaction |> insert() |> with_block() log = insert(:log, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, address: address, first_topic: topic(@first_topic_hex_string_1) ) @@ -2554,7 +2633,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do "token_balances_count" => 0, "logs_count" => 0, "withdrawals_count" => 0, - "internal_txs_count" => 0 + "internal_transactions_count" => 0 } = json_response(request, 200) end @@ -2563,36 +2642,36 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do insert(:transaction, from_address: address) |> with_block() insert(:transaction, to_address: address) |> with_block() - another_tx = insert(:transaction) |> with_block() + another_transaction = insert(:transaction) |> with_block() insert(:token_transfer, from_address: address, - transaction: another_tx, - block: another_tx.block, - block_number: another_tx.block_number + transaction: another_transaction, + block: another_transaction.block, + block_number: another_transaction.block_number ) insert(:token_transfer, to_address: address, - transaction: another_tx, - block: another_tx.block, - block_number: another_tx.block_number + transaction: another_transaction, + block: another_transaction.block, + block_number: another_transaction.block_number ) insert(:block, miner: address) - tx = + transaction = :transaction |> insert() |> with_block() for x <- 1..2 do insert(:internal_transaction, - transaction: tx, + transaction: transaction, index: x, - block_number: tx.block_number, - transaction_index: tx.index, - block_hash: tx.block_hash, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, block_index: x, from_address: address ) @@ -2603,16 +2682,16 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do end for x <- 0..60 do - tx = + transaction = :transaction |> insert() |> with_block() insert(:log, - transaction: tx, + transaction: transaction, index: x, - block: tx.block, - block_number: tx.block_number, + block: transaction.block, + block_number: transaction.block_number, address: address ) end @@ -2626,16 +2705,16 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do "token_balances_count" => 51, "logs_count" => 51, "withdrawals_count" => 51, - "internal_txs_count" => 2 + "internal_transactions_count" => 2 } = json_response(request, 200) for x <- 3..4 do insert(:internal_transaction, - transaction: tx, + transaction: transaction, index: x, - block_number: tx.block_number, - transaction_index: tx.index, - block_hash: tx.block_hash, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, block_index: x, from_address: address ) @@ -2650,7 +2729,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do "token_balances_count" => 51, "logs_count" => 51, "withdrawals_count" => 51, - "internal_txs_count" => 2 + "internal_transactions_count" => 2 } = json_response(request, 200) end @@ -2659,36 +2738,36 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do insert(:transaction, from_address: address) |> with_block() insert(:transaction, to_address: address) |> with_block() - another_tx = insert(:transaction) |> with_block() + another_transaction = insert(:transaction) |> with_block() insert(:token_transfer, from_address: address, - transaction: another_tx, - block: another_tx.block, - block_number: another_tx.block_number + transaction: another_transaction, + block: another_transaction.block, + block_number: another_transaction.block_number ) insert(:token_transfer, to_address: address, - transaction: another_tx, - block: another_tx.block, - block_number: another_tx.block_number + transaction: another_transaction, + block: another_transaction.block, + block_number: another_transaction.block_number ) insert(:block, miner: address) - tx = + transaction = :transaction |> insert() |> with_block() for x <- 1..2 do insert(:internal_transaction, - transaction: tx, + transaction: transaction, index: x, - block_number: tx.block_number, - transaction_index: tx.index, - block_hash: tx.block_hash, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, block_index: x, from_address: address ) @@ -2699,16 +2778,16 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do end for x <- 0..60 do - tx = + transaction = :transaction |> insert() |> with_block() insert(:log, - transaction: tx, + transaction: transaction, index: x, - block: tx.block, - block_number: tx.block_number, + block: transaction.block, + block_number: transaction.block_number, address: address ) end @@ -2722,7 +2801,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do "token_balances_count" => 51, "logs_count" => 51, "withdrawals_count" => 51, - "internal_txs_count" => 2 + "internal_transactions_count" => 2 } = json_response(request, 200) old_env = Application.get_env(:explorer, Explorer.Chain.Cache.AddressesTabsCounters) @@ -2731,11 +2810,11 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do for x <- 3..4 do insert(:internal_transaction, - transaction: tx, + transaction: transaction, index: x, - block_number: tx.block_number, - transaction_index: tx.index, - block_hash: tx.block_hash, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, block_index: x, from_address: address ) @@ -2753,7 +2832,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do "token_balances_count" => 51, "logs_count" => 51, "withdrawals_count" => 51, - "internal_txs_count" => 4 + "internal_transactions_count" => 4 } = json_response(request, 200) Application.put_env(:explorer, Explorer.Chain.Cache.AddressesTabsCounters, old_env) @@ -3310,12 +3389,12 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do defp compare_item(%Address{} = address, json) do assert Address.checksum(address.hash) == json["hash"] - assert to_string(address.transactions_count) == json["tx_count"] + assert to_string(address.transactions_count) == json["transaction_count"] end defp compare_item(%Transaction{} = transaction, json) do assert to_string(transaction.hash) == json["hash"] - assert transaction.block_number == json["block"] + assert transaction.block_number == json["block_number"] assert to_string(transaction.value.value) == json["value"] assert Address.checksum(transaction.from_address_hash) == json["from"]["hash"] assert Address.checksum(transaction.to_address_hash) == json["to"]["hash"] @@ -3324,21 +3403,21 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do defp compare_item(%TokenTransfer{} = token_transfer, json) do assert Address.checksum(token_transfer.from_address_hash) == json["from"]["hash"] assert Address.checksum(token_transfer.to_address_hash) == json["to"]["hash"] - assert to_string(token_transfer.transaction_hash) == json["tx_hash"] + assert to_string(token_transfer.transaction_hash) == json["transaction_hash"] assert json["timestamp"] != nil assert json["method"] != nil assert to_string(token_transfer.block_hash) == json["block_hash"] - assert to_string(token_transfer.log_index) == json["log_index"] + assert token_transfer.log_index == json["log_index"] assert check_total(Repo.preload(token_transfer, [{:token, :contract_address}]).token, json["total"], token_transfer) end - defp compare_item(%InternalTransaction{} = internal_tx, json) do - assert internal_tx.block_number == json["block"] - assert to_string(internal_tx.gas) == json["gas_limit"] - assert internal_tx.index == json["index"] - assert to_string(internal_tx.transaction_hash) == json["transaction_hash"] - assert Address.checksum(internal_tx.from_address_hash) == json["from"]["hash"] - assert Address.checksum(internal_tx.to_address_hash) == json["to"]["hash"] + defp compare_item(%InternalTransaction{} = internal_transaction, json) do + assert internal_transaction.block_number == json["block_number"] + assert to_string(internal_transaction.gas) == json["gas_limit"] + assert internal_transaction.index == json["index"] + assert to_string(internal_transaction.transaction_hash) == json["transaction_hash"] + assert Address.checksum(internal_transaction.from_address_hash) == json["from"]["hash"] + assert Address.checksum(internal_transaction.to_address_hash) == json["to"]["hash"] end defp compare_item(%Block{} = block, json) do @@ -3374,7 +3453,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do assert log.index == json["index"] assert to_string(log.data) == json["data"] assert Address.checksum(log.address_hash) == json["address"]["hash"] - assert to_string(log.transaction_hash) == json["tx_hash"] + assert to_string(log.transaction_hash) == json["transaction_hash"] assert json["block_number"] == log.block_number assert json["block_hash"] == to_string(log.block_hash) end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/advanced_filter_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/advanced_filter_controller_test.exs index 4e14bc95970f..9f85979f33a3 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/advanced_filter_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/advanced_filter_controller_test.exs @@ -15,13 +15,13 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do end test "get and paginate advanced filter (transactions split between pages)", %{conn: conn} do - first_tx = :transaction |> insert() |> with_block() - insert_list(3, :token_transfer, transaction: first_tx) + first_transaction = :transaction |> insert() |> with_block() + insert_list(3, :token_transfer, transaction: first_transaction) for i <- 0..2 do insert(:internal_transaction, - transaction: first_tx, - block_hash: first_tx.block_hash, + transaction: first_transaction, + block_hash: first_transaction.block_hash, index: i, block_index: i ) @@ -38,20 +38,20 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do end test "get and paginate advanced filter (token transfers split between pages)", %{conn: conn} do - first_tx = :transaction |> insert() |> with_block() - insert_list(3, :token_transfer, transaction: first_tx) + first_transaction = :transaction |> insert() |> with_block() + insert_list(3, :token_transfer, transaction: first_transaction) for i <- 0..2 do insert(:internal_transaction, - transaction: first_tx, - block_hash: first_tx.block_hash, + transaction: first_transaction, + block_hash: first_transaction.block_hash, index: i, block_index: i ) end - second_tx = :transaction |> insert() |> with_block() - insert_list(50, :token_transfer, transaction: second_tx, block_number: second_tx.block_number) + second_transaction = :transaction |> insert() |> with_block() + insert_list(50, :token_transfer, transaction: second_transaction, block_number: second_transaction.block_number) request = get(conn, "/api/v2/advanced-filters") assert response = json_response(request, 200) @@ -62,23 +62,23 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do end test "get and paginate advanced filter (batch token transfers split between pages)", %{conn: conn} do - first_tx = :transaction |> insert() |> with_block() - insert_list(3, :token_transfer, transaction: first_tx) + first_transaction = :transaction |> insert() |> with_block() + insert_list(3, :token_transfer, transaction: first_transaction) for i <- 0..2 do insert(:internal_transaction, - transaction: first_tx, - block_hash: first_tx.block_hash, + transaction: first_transaction, + block_hash: first_transaction.block_hash, index: i, block_index: i ) end - second_tx = :transaction |> insert() |> with_block() + second_transaction = :transaction |> insert() |> with_block() insert_list(5, :token_transfer, - transaction: second_tx, - block_number: second_tx.block_number, + transaction: second_transaction, + block_number: second_transaction.block_number, token_type: "ERC-1155", token_ids: 0..10 |> Enum.to_list(), amounts: 10..20 |> Enum.to_list() @@ -93,24 +93,24 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do end test "get and paginate advanced filter (internal transactions split between pages)", %{conn: conn} do - first_tx = :transaction |> insert() |> with_block() - insert_list(3, :token_transfer, transaction: first_tx) + first_transaction = :transaction |> insert() |> with_block() + insert_list(3, :token_transfer, transaction: first_transaction) for i <- 0..2 do insert(:internal_transaction, - transaction: first_tx, - block_hash: first_tx.block_hash, + transaction: first_transaction, + block_hash: first_transaction.block_hash, index: i, block_index: i ) end - second_tx = :transaction |> insert() |> with_block() + second_transaction = :transaction |> insert() |> with_block() for i <- 0..49 do insert(:internal_transaction, - transaction: second_tx, - block_hash: second_tx.block_hash, + transaction: second_transaction, + block_hash: second_transaction.block_hash, index: i, block_index: i ) @@ -124,49 +124,51 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do check_paginated_response(AdvancedFilter.list(), response["items"], response_2nd_page["items"]) end - test "filter by tx_type", %{conn: conn} do + test "filter by transaction_type", %{conn: conn} do 30 |> insert_list(:transaction) |> with_block() - tx = insert(:transaction) |> with_block() + transaction = insert(:transaction) |> with_block() for token_type <- ~w(ERC-20 ERC-404 ERC-721 ERC-1155), _ <- 0..4 do - insert(:token_transfer, transaction: tx, token_type: token_type) + insert(:token_transfer, transaction: transaction, token_type: token_type) end - tx = :transaction |> insert() |> with_block() + transaction = :transaction |> insert() |> with_block() for i <- 0..29 do insert(:internal_transaction, - transaction: tx, - block_hash: tx.block_hash, + transaction: transaction, + block_hash: transaction.block_hash, index: i, block_index: i ) end - for tx_type_filter_string <- + for transaction_type_filter_string <- ~w(COIN_TRANSFER COIN_TRANSFER,ERC-404 ERC-721,ERC-1155 ERC-20,COIN_TRANSFER,ERC-1155) do - tx_type_filter = tx_type_filter_string |> String.split(",") - request = get(conn, "/api/v2/advanced-filters", %{"tx_types" => tx_type_filter_string}) + transaction_type_filter = transaction_type_filter_string |> String.split(",") + request = get(conn, "/api/v2/advanced-filters", %{"transaction_types" => transaction_type_filter_string}) assert response = json_response(request, 200) - assert Enum.all?(response["items"], fn item -> String.upcase(item["type"]) in tx_type_filter end) + assert Enum.all?(response["items"], fn item -> String.upcase(item["type"]) in transaction_type_filter end) if response["next_page_params"] do request_2nd_page = get( conn, "/api/v2/advanced-filters", - Map.merge(%{"tx_types" => tx_type_filter_string}, response["next_page_params"]) + Map.merge(%{"transaction_types" => transaction_type_filter_string}, response["next_page_params"]) ) assert response_2nd_page = json_response(request_2nd_page, 200) - assert Enum.all?(response_2nd_page["items"], fn item -> String.upcase(item["type"]) in tx_type_filter end) + assert Enum.all?(response_2nd_page["items"], fn item -> + String.upcase(item["type"]) in transaction_type_filter + end) check_paginated_response( - AdvancedFilter.list(tx_types: tx_type_filter), + AdvancedFilter.list(transaction_types: transaction_type_filter), response["items"], response_2nd_page["items"] ) @@ -177,7 +179,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do test "filter by methods", %{conn: conn} do TestHelper.get_eip1967_implementation_zero_addresses() - tx = :transaction |> insert() |> with_block() + transaction = :transaction |> insert() |> with_block() smart_contract = build(:smart_contract) @@ -201,10 +203,10 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do for i <- 0..4 do insert(:internal_transaction, - transaction: tx, + transaction: transaction, to_address_hash: contract_address.hash, to_address: contract_address, - block_hash: tx.block_hash, + block_hash: transaction.block_hash, index: i, block_index: i, input: method1 @@ -213,10 +215,10 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do for i <- 5..9 do insert(:internal_transaction, - transaction: tx, + transaction: transaction, to_address_hash: contract_address.hash, to_address: contract_address, - block_hash: tx.block_hash, + block_hash: transaction.block_hash, index: i, block_index: i, input: method2 @@ -258,16 +260,16 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do first_timestamp = ~U[2023-12-12 00:00:00.000000Z] for i <- 0..4 do - tx = :transaction |> insert() |> with_block(block_timestamp: Timex.shift(first_timestamp, days: i)) + transaction = :transaction |> insert() |> with_block(block_timestamp: Timex.shift(first_timestamp, days: i)) insert(:internal_transaction, - transaction: tx, - block_hash: tx.block_hash, + transaction: transaction, + block_hash: transaction.block_hash, index: i, block_index: i ) - insert(:token_transfer, transaction: tx, block_number: tx.block_number, log_index: i) + insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) end request = @@ -285,16 +287,16 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do address = insert(:address) for i <- 0..4 do - tx = :transaction |> insert() |> with_block() + transaction = :transaction |> insert() |> with_block() if i < 2 do :transaction |> insert(from_address_hash: address.hash, from_address: address) |> with_block() insert(:internal_transaction, - transaction: tx, + transaction: transaction, from_address_hash: address.hash, from_address: address, - block_hash: tx.block_hash, + block_hash: transaction.block_hash, index: i, block_index: i ) @@ -302,19 +304,19 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:token_transfer, from_address_hash: address.hash, from_address: address, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: i ) else insert(:internal_transaction, - transaction: tx, - block_hash: tx.block_hash, + transaction: transaction, + block_hash: transaction.block_hash, index: i, block_index: i ) - insert(:token_transfer, transaction: tx, block_number: tx.block_number, log_index: i) + insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) end end @@ -329,16 +331,16 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do address = insert(:address) for i <- 0..4 do - tx = :transaction |> insert() |> with_block() + transaction = :transaction |> insert() |> with_block() if i < 4 do :transaction |> insert(from_address_hash: address.hash, from_address: address) |> with_block() insert(:internal_transaction, - transaction: tx, + transaction: transaction, from_address_hash: address.hash, from_address: address, - block_hash: tx.block_hash, + block_hash: transaction.block_hash, index: i, block_index: i ) @@ -346,19 +348,19 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:token_transfer, from_address_hash: address.hash, from_address: address, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: i ) else insert(:internal_transaction, - transaction: tx, - block_hash: tx.block_hash, + transaction: transaction, + block_hash: transaction.block_hash, index: i, block_index: i ) - insert(:token_transfer, transaction: tx, block_number: tx.block_number, log_index: i) + insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) end end @@ -374,7 +376,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do address_to_exclude = insert(:address) for i <- 0..2 do - tx = + transaction = :transaction |> insert(from_address_hash: address_to_exclude.hash, from_address: address_to_exclude) |> with_block() @@ -385,10 +387,10 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do |> with_block() insert(:internal_transaction, - transaction: tx, + transaction: transaction, from_address_hash: address_to_include.hash, from_address: address_to_include, - block_hash: tx.block_hash, + block_hash: transaction.block_hash, index: i, block_index: i ) @@ -396,19 +398,19 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:token_transfer, from_address_hash: address_to_include.hash, from_address: address_to_include, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: i ) else insert(:internal_transaction, - transaction: tx, - block_hash: tx.block_hash, + transaction: transaction, + block_hash: transaction.block_hash, index: i, block_index: i ) - insert(:token_transfer, transaction: tx, block_number: tx.block_number, log_index: i) + insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) end end @@ -427,16 +429,16 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do address = insert(:address) for i <- 0..4 do - tx = :transaction |> insert() |> with_block() + transaction = :transaction |> insert() |> with_block() if i < 2 do :transaction |> insert(to_address_hash: address.hash, to_address: address) |> with_block() insert(:internal_transaction, - transaction: tx, + transaction: transaction, to_address_hash: address.hash, to_address: address, - block_hash: tx.block_hash, + block_hash: transaction.block_hash, index: i, block_index: i ) @@ -444,19 +446,19 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:token_transfer, to_address_hash: address.hash, to_address: address, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: i ) else insert(:internal_transaction, - transaction: tx, - block_hash: tx.block_hash, + transaction: transaction, + block_hash: transaction.block_hash, index: i, block_index: i ) - insert(:token_transfer, transaction: tx, block_number: tx.block_number, log_index: i) + insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) end end @@ -471,16 +473,16 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do address = insert(:address) for i <- 0..4 do - tx = :transaction |> insert() |> with_block() + transaction = :transaction |> insert() |> with_block() if i < 4 do :transaction |> insert(to_address_hash: address.hash, to_address: address) |> with_block() insert(:internal_transaction, - transaction: tx, + transaction: transaction, to_address_hash: address.hash, to_address: address, - block_hash: tx.block_hash, + block_hash: transaction.block_hash, index: i, block_index: i ) @@ -488,19 +490,19 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:token_transfer, to_address_hash: address.hash, to_address: address, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: i ) else insert(:internal_transaction, - transaction: tx, - block_hash: tx.block_hash, + transaction: transaction, + block_hash: transaction.block_hash, index: i, block_index: i ) - insert(:token_transfer, transaction: tx, block_number: tx.block_number, log_index: i) + insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) end end @@ -516,7 +518,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do address_to_exclude = insert(:address) for i <- 0..2 do - tx = + transaction = :transaction |> insert(to_address_hash: address_to_exclude.hash, to_address: address_to_exclude) |> with_block() @@ -527,10 +529,10 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do |> with_block() insert(:internal_transaction, - transaction: tx, + transaction: transaction, to_address_hash: address_to_include.hash, to_address: address_to_include, - block_hash: tx.block_hash, + block_hash: transaction.block_hash, index: i, block_index: i ) @@ -538,19 +540,19 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:token_transfer, to_address_hash: address_to_include.hash, to_address: address_to_include, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: i ) else insert(:internal_transaction, - transaction: tx, - block_hash: tx.block_hash, + transaction: transaction, + block_hash: transaction.block_hash, index: i, block_index: i ) - insert(:token_transfer, transaction: tx, block_number: tx.block_number, log_index: i) + insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) end end @@ -570,17 +572,17 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do to_address = insert(:address) for i <- 0..8 do - tx = :transaction |> insert() |> with_block() + transaction = :transaction |> insert() |> with_block() cond do i < 2 -> :transaction |> insert(from_address_hash: from_address.hash, from_address: from_address) |> with_block() insert(:internal_transaction, - transaction: tx, + transaction: transaction, from_address_hash: from_address.hash, from_address: from_address, - block_hash: tx.block_hash, + block_hash: transaction.block_hash, index: i, block_index: i ) @@ -588,8 +590,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:token_transfer, from_address_hash: from_address.hash, from_address: from_address, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: i ) @@ -597,10 +599,10 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do :transaction |> insert(to_address_hash: to_address.hash, to_address: to_address) |> with_block() insert(:internal_transaction, - transaction: tx, + transaction: transaction, to_address_hash: to_address.hash, to_address: to_address, - block_hash: tx.block_hash, + block_hash: transaction.block_hash, index: i, block_index: i ) @@ -608,8 +610,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:token_transfer, to_address_hash: to_address.hash, to_address: to_address, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: i ) @@ -624,12 +626,12 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do |> with_block() insert(:internal_transaction, - transaction: tx, + transaction: transaction, to_address_hash: to_address.hash, to_address: to_address, from_address_hash: from_address.hash, from_address: from_address, - block_hash: tx.block_hash, + block_hash: transaction.block_hash, index: i, block_index: i ) @@ -639,20 +641,20 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do to_address: to_address, from_address_hash: from_address.hash, from_address: from_address, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: i ) true -> insert(:internal_transaction, - transaction: tx, - block_hash: tx.block_hash, + transaction: transaction, + block_hash: transaction.block_hash, index: i, block_index: i ) - insert(:token_transfer, transaction: tx, block_number: tx.block_number, log_index: i) + insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) end end @@ -673,17 +675,17 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do to_address = insert(:address) for i <- 0..8 do - tx = :transaction |> insert() |> with_block() + transaction = :transaction |> insert() |> with_block() cond do i < 2 -> :transaction |> insert(from_address_hash: from_address.hash, from_address: from_address) |> with_block() insert(:internal_transaction, - transaction: tx, + transaction: transaction, from_address_hash: from_address.hash, from_address: from_address, - block_hash: tx.block_hash, + block_hash: transaction.block_hash, index: i, block_index: i ) @@ -691,8 +693,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:token_transfer, from_address_hash: from_address.hash, from_address: from_address, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: i ) @@ -700,10 +702,10 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do :transaction |> insert(to_address_hash: to_address.hash, to_address: to_address) |> with_block() insert(:internal_transaction, - transaction: tx, + transaction: transaction, to_address_hash: to_address.hash, to_address: to_address, - block_hash: tx.block_hash, + block_hash: transaction.block_hash, index: i, block_index: i ) @@ -711,8 +713,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:token_transfer, to_address_hash: to_address.hash, to_address: to_address, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: i ) @@ -727,12 +729,12 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do |> with_block() insert(:internal_transaction, - transaction: tx, + transaction: transaction, to_address_hash: to_address.hash, to_address: to_address, from_address_hash: from_address.hash, from_address: from_address, - block_hash: tx.block_hash, + block_hash: transaction.block_hash, index: i, block_index: i ) @@ -742,20 +744,20 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do to_address: to_address, from_address_hash: from_address.hash, from_address: from_address, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: i ) true -> insert(:internal_transaction, - transaction: tx, - block_hash: tx.block_hash, + transaction: transaction, + block_hash: transaction.block_hash, index: i, block_index: i ) - insert(:token_transfer, transaction: tx, block_number: tx.block_number, log_index: i) + insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) end end @@ -772,11 +774,11 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do test "filter by amount", %{conn: conn} do for i <- 0..4 do - tx = :transaction |> insert(value: i * 10 ** 18) |> with_block() + transaction = :transaction |> insert(value: i * 10 ** 18) |> with_block() insert(:internal_transaction, - transaction: tx, - block_hash: tx.block_hash, + transaction: transaction, + block_hash: transaction.block_hash, index: 0, block_index: 0, value: i * 10 ** 18 @@ -787,8 +789,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:token_transfer, amount: i * 10 ** 10, token_contract_address: token.contract_address, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: 0 ) end @@ -804,13 +806,13 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do token_b = insert(:token) token_c = insert(:token) - tx = :transaction |> insert() |> with_block() + transaction = :transaction |> insert() |> with_block() for token <- [token_a, token_b, token_c, token_a, token_b, token_c, token_a, token_b, token_c] do insert(:token_transfer, token_contract_address: token.contract_address, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: 0 ) end @@ -831,13 +833,13 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do token_b = insert(:token) token_c = insert(:token) - tx = :transaction |> insert() |> with_block() + transaction = :transaction |> insert() |> with_block() for token <- [token_a, token_b, token_c, token_a, token_b, token_c, token_a, token_b, token_c] do insert(:token_transfer, token_contract_address: token.contract_address, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: 0 ) end @@ -858,13 +860,13 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do token_b = insert(:token) token_c = insert(:token) - tx = :transaction |> insert() |> with_block() + transaction = :transaction |> insert() |> with_block() for token <- [token_a, token_b, token_c, token_a, token_b, token_c, token_a, token_b, token_c] do insert(:token_transfer, token_contract_address: token.contract_address, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: 0 ) end @@ -885,13 +887,13 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do token_b = insert(:token) token_c = insert(:token) - tx = :transaction |> insert() |> with_block() + transaction = :transaction |> insert() |> with_block() for token <- [token_a, token_b, token_c, token_a, token_b, token_c, token_a, token_b, token_c] do insert(:token_transfer, token_contract_address: token.contract_address, - transaction: tx, - block_number: tx.block_number, + transaction: transaction, + block_number: transaction.block_number, log_index: 0 ) end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/block_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/block_controller_test.exs index bfb690c04d60..cdbd5461871e 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/block_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/block_controller_test.exs @@ -282,14 +282,14 @@ defmodule BlockScoutWeb.API.V2.BlockControllerTest do assert response["next_page_params"] == nil end - test "get relevant tx", %{conn: conn} do + test "get relevant transaction", %{conn: conn} do 10 |> insert_list(:transaction) |> with_block() block = insert(:block) - tx = + transaction = :transaction |> insert() |> with_block(block) @@ -298,21 +298,21 @@ defmodule BlockScoutWeb.API.V2.BlockControllerTest do assert response = json_response(request, 200) assert Enum.count(response["items"]) == 1 assert response["next_page_params"] == nil - compare_item(tx, Enum.at(response["items"], 0)) + compare_item(transaction, Enum.at(response["items"], 0)) request = get(conn, "/api/v2/blocks/#{block.hash}/transactions") assert response_1 = json_response(request, 200) assert response_1 == response end - test "get txs with working next_page_params", %{conn: conn} do + test "get transactions with working next_page_params", %{conn: conn} do 2 |> insert_list(:transaction) |> with_block() block = insert(:block) - txs = + transactions = 51 |> insert_list(:transaction) |> with_block(block) @@ -324,7 +324,7 @@ defmodule BlockScoutWeb.API.V2.BlockControllerTest do request_2nd_page = get(conn, "/api/v2/blocks/#{block.number}/transactions", response["next_page_params"]) assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, txs) + check_paginated_response(response, response_2nd_page, transactions) request_1 = get(conn, "/api/v2/blocks/#{block.hash}/transactions") assert response_1 = json_response(request_1, 200) @@ -443,34 +443,34 @@ defmodule BlockScoutWeb.API.V2.BlockControllerTest do request = get(conn, "/api/v2/blocks/#{block.hash}/internal-transactions") assert %{"items" => [], "next_page_params" => nil} = json_response(request, 200) - tx = + transaction = :transaction |> insert() |> with_block(block) insert(:internal_transaction, - transaction: tx, + transaction: transaction, index: 0, - block_number: tx.block_number, - transaction_index: tx.index, - block_hash: tx.block_hash, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, block_index: 0 ) - internal_txs = + internal_transactions = 51..1 |> Enum.map(fn index -> - tx = + transaction = :transaction |> insert() |> with_block(block) insert(:internal_transaction, - transaction: tx, + transaction: transaction, index: index, - block_number: tx.block_number, - transaction_index: tx.index, - block_hash: tx.block_hash, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, block_index: index ) end) @@ -482,7 +482,7 @@ defmodule BlockScoutWeb.API.V2.BlockControllerTest do assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, internal_txs) + check_paginated_response(response, response_2nd_page, internal_transactions) end end @@ -493,7 +493,7 @@ defmodule BlockScoutWeb.API.V2.BlockControllerTest do defp compare_item(%Transaction{} = transaction, json) do assert to_string(transaction.hash) == json["hash"] - assert transaction.block_number == json["block"] + assert transaction.block_number == json["block_number"] assert to_string(transaction.value.value) == json["value"] assert Address.checksum(transaction.from_address_hash) == json["from"]["hash"] assert Address.checksum(transaction.to_address_hash) == json["to"]["hash"] @@ -503,13 +503,13 @@ defmodule BlockScoutWeb.API.V2.BlockControllerTest do assert withdrawal.index == json["index"] end - defp compare_item(%InternalTransaction{} = internal_tx, json) do - assert internal_tx.block_number == json["block"] - assert to_string(internal_tx.gas) == json["gas_limit"] - assert internal_tx.index == json["index"] - assert to_string(internal_tx.transaction_hash) == json["transaction_hash"] - assert Address.checksum(internal_tx.from_address_hash) == json["from"]["hash"] - assert Address.checksum(internal_tx.to_address_hash) == json["to"]["hash"] + defp compare_item(%InternalTransaction{} = internal_transaction, json) do + assert internal_transaction.block_number == json["block_number"] + assert to_string(internal_transaction.gas) == json["gas_limit"] + assert internal_transaction.index == json["index"] + assert to_string(internal_transaction.transaction_hash) == json["transaction_hash"] + assert Address.checksum(internal_transaction.from_address_hash) == json["from"]["hash"] + assert Address.checksum(internal_transaction.to_address_hash) == json["to"]["hash"] end defp check_paginated_response(first_page_resp, second_page_resp, list) do diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/main_page_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/main_page_controller_test.exs index 1ea1c334ceb2..0b56c7411a80 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/main_page_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/main_page_controller_test.exs @@ -37,20 +37,20 @@ defmodule BlockScoutWeb.API.V2.MainPageControllerTest do end describe "/main-page/transactions" do - test "get empty list when no txs", %{conn: conn} do + test "get empty list when no transactions", %{conn: conn} do request = get(conn, "/api/v2/main-page/transactions") assert [] = json_response(request, 200) end - test "get last 6 txs", %{conn: conn} do - txs = insert_list(10, :transaction) |> with_block() |> Enum.take(-6) |> Enum.reverse() + test "get last 6 transactions", %{conn: conn} do + transactions = insert_list(10, :transaction) |> with_block() |> Enum.take(-6) |> Enum.reverse() request = get(conn, "/api/v2/main-page/transactions") assert response = json_response(request, 200) assert Enum.count(response) == 6 for i <- 0..5 do - compare_item(Enum.at(txs, i), Enum.at(response, i)) + compare_item(Enum.at(transactions, i), Enum.at(response, i)) end end end @@ -61,7 +61,7 @@ defmodule BlockScoutWeb.API.V2.MainPageControllerTest do assert %{"message" => "Unauthorized"} = json_response(request, 401) end - test "get last 6 txs", %{conn: conn} do + test "get last 6 transactions", %{conn: conn} do insert_list(10, :transaction) |> with_block() auth = build(:auth) @@ -107,17 +107,17 @@ defmodule BlockScoutWeb.API.V2.MainPageControllerTest do notify_email: true }) - txs_1 = insert_list(2, :transaction, from_address: address_1) |> with_block() - txs_2 = insert_list(1, :transaction, from_address: address_2, to_address: address_1) |> with_block() - txs_3 = insert_list(3, :transaction, to_address: address_2) |> with_block() - txs = (txs_1 ++ txs_2 ++ txs_3) |> Enum.reverse() + transactions_1 = insert_list(2, :transaction, from_address: address_1) |> with_block() + transactions_2 = insert_list(1, :transaction, from_address: address_2, to_address: address_1) |> with_block() + transactions_3 = insert_list(3, :transaction, to_address: address_2) |> with_block() + transactions = (transactions_1 ++ transactions_2 ++ transactions_3) |> Enum.reverse() request = get(conn, "/api/v2/main-page/transactions/watchlist") assert response = json_response(request, 200) assert Enum.count(response) == 6 for i <- 0..5 do - compare_item(Enum.at(txs, i), Enum.at(response, i), %{ + compare_item(Enum.at(transactions, i), Enum.at(response, i), %{ address_1.hash => watchlist_address_1.name, address_2.hash => watchlist_address_2.name }) @@ -144,7 +144,7 @@ defmodule BlockScoutWeb.API.V2.MainPageControllerTest do defp compare_item(%Transaction{} = transaction, json) do assert to_string(transaction.hash) == json["hash"] - assert transaction.block_number == json["block"] + assert transaction.block_number == json["block_number"] assert to_string(transaction.value.value) == json["value"] assert Address.checksum(transaction.from_address_hash) == json["from"]["hash"] assert Address.checksum(transaction.to_address_hash) == json["to"]["hash"] @@ -152,7 +152,7 @@ defmodule BlockScoutWeb.API.V2.MainPageControllerTest do defp compare_item(%Transaction{} = transaction, json, wl_names) do assert to_string(transaction.hash) == json["hash"] - assert transaction.block_number == json["block"] + assert transaction.block_number == json["block_number"] assert to_string(transaction.value.value) == json["value"] assert Address.checksum(transaction.from_address_hash) == json["from"]["hash"] assert Address.checksum(transaction.to_address_hash) == json["to"]["hash"] 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 f5d15a76ba92..2f66211bb46f 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 @@ -202,9 +202,9 @@ defmodule BlockScoutWeb.API.V2.SearchControllerTest do end test "search transaction", %{conn: conn} do - tx = insert(:transaction, block_timestamp: nil) + transaction = insert(:transaction, block_timestamp: nil) - request = get(conn, "/api/v2/search?q=#{tx.hash}") + request = get(conn, "/api/v2/search?q=#{transaction.hash}") assert response = json_response(request, 200) assert Enum.count(response["items"]) == 1 @@ -213,15 +213,15 @@ defmodule BlockScoutWeb.API.V2.SearchControllerTest do item = Enum.at(response["items"], 0) assert item["type"] == "transaction" - assert item["tx_hash"] == to_string(tx.hash) - assert item["url"] =~ to_string(tx.hash) + assert item["transaction_hash"] == to_string(transaction.hash) + assert item["url"] =~ to_string(transaction.hash) assert item["timestamp"] == nil end test "search transaction with timestamp", %{conn: conn} do - tx = :transaction |> insert() |> with_block() + transaction = :transaction |> insert() |> with_block() - request = get(conn, "/api/v2/search?q=#{tx.hash}") + request = get(conn, "/api/v2/search?q=#{transaction.hash}") assert response = json_response(request, 200) assert Enum.count(response["items"]) == 1 @@ -230,9 +230,11 @@ defmodule BlockScoutWeb.API.V2.SearchControllerTest do item = Enum.at(response["items"], 0) assert item["type"] == "transaction" - assert item["tx_hash"] == to_string(tx.hash) - assert item["url"] =~ to_string(tx.hash) - assert item["timestamp"] == Repo.preload(tx, [:block]).block.timestamp |> to_string() |> String.replace(" ", "T") + assert item["transaction_hash"] == to_string(transaction.hash) + assert item["url"] =~ to_string(transaction.hash) + + assert item["timestamp"] == + Repo.preload(transaction, [:block]).block.timestamp |> to_string() |> String.replace(" ", "T") end test "search tags", %{conn: conn} do diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs index 58b7a38ca140..6f94f4da8518 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs @@ -455,7 +455,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do implementation_contract_address_hash_string = Base.encode16(implementation_contract.address_hash.bytes, case: :lower) - proxy_tx_input = + proxy_transaction_input = "0x11b804ab000000000000000000000000" <> implementation_contract_address_hash_string <> "000000000000000000000000000000000000000000000000000000000000006035323031313537360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000284e159163400000000000000000000000034420c13696f4ac650b9fafe915553a1abcd7dd30000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000ff5ae9b0a7522736299d797d80b8fc6f31d61100000000000000000000000000ff5ae9b0a7522736299d797d80b8fc6f31d6110000000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034420c13696f4ac650b9fafe915553a1abcd7dd300000000000000000000000000000000000000000000000000000000000000184f7074696d69736d2053756273637269626572204e465473000000000000000000000000000000000000000000000000000000000000000000000000000000054f504e46540000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037697066733a2f2f516d66544e504839765651334b5952346d6b52325a6b757756424266456f5a5554545064395538666931503332752f300000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c82bbe41f2cf04e3a8efa18f7032bdd7f6d98a81000000000000000000000000efba8a2a82ec1fb1273806174f5e28fbb917cf9500000000000000000000000000000000000000000000000000000000" @@ -470,7 +470,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do insert(:transaction, created_contract_address_hash: proxy_address.hash, - input: proxy_tx_input + input: proxy_transaction_input ) |> with_block(status: :ok) @@ -479,7 +479,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "has_custom_methods_write" => false, "is_self_destructed" => false, "deployed_bytecode" => proxy_deployed_bytecode, - "creation_bytecode" => proxy_tx_input, + "creation_bytecode" => proxy_transaction_input, "proxy_type" => "eip1167", "implementations" => [ %{ @@ -618,7 +618,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do implementation_contract_address_hash_string = Base.encode16(implementation_contract.address_hash.bytes, case: :lower) - proxy_tx_input = + proxy_transaction_input = "0x684fbe55000000000000000000000000af1caf51d49b0e63d1ff7e5d4ed6ea26d15f3f9d000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003" proxy_deployed_bytecode = @@ -633,7 +633,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do insert(:transaction, created_contract_address_hash: proxy_address.hash, - input: proxy_tx_input + input: proxy_transaction_input ) |> with_block(status: :ok) @@ -648,7 +648,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do "has_custom_methods_write" => false, "is_self_destructed" => false, "deployed_bytecode" => proxy_deployed_bytecode, - "creation_bytecode" => proxy_tx_input + "creation_bytecode" => proxy_transaction_input } request = get(conn, "/api/v2/smart-contracts/#{Address.checksum(proxy_address.hash)}") @@ -3580,7 +3580,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do end |> Enum.reverse() - ordering_params = %{"sort" => "txs_count", "order" => "asc"} + ordering_params = %{"sort" => "transactions_count", "order" => "asc"} request = get(conn, "/api/v2/smart-contracts", ordering_params) assert response = json_response(request, 200) @@ -3600,7 +3600,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do insert(:smart_contract, address_hash: address.hash, address: address) end - ordering_params = %{"sort" => "txs_count", "order" => "desc"} + ordering_params = %{"sort" => "transactions_count", "order" => "desc"} request = get(conn, "/api/v2/smart-contracts", ordering_params) assert response = json_response(request, 200) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs index daefa03d40d6..b40da67f8c2d 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs @@ -131,12 +131,12 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do token_transfers = for _ <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address ) end @@ -161,12 +161,12 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do tt = for _ <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(0..50, fn _x -> id end), token_type: "ERC-1155", @@ -195,12 +195,12 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do token_transfers = for i <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: [i], token_type: "ERC-721" @@ -220,13 +220,13 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do test "check that pagination works fine with 1155 batches #1 (large batch)", %{conn: conn} do token = insert(:token, type: "ERC-1155") - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() tt = insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(0..50, fn x -> x end), token_type: "ERC-1155", @@ -253,13 +253,13 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do %{conn: conn} do token = insert(:token, type: "ERC-1155") - tx_1 = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction_1 = insert(:transaction, input: "0xabcd010203040506") |> with_block() tt_1 = insert(:token_transfer, - transaction: tx_1, - block: tx_1.block, - block_number: tx_1.block_number, + transaction: transaction_1, + block: transaction_1.block, + block_number: transaction_1.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(0..24, fn x -> x end), token_type: "ERC-1155", @@ -271,13 +271,13 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do %TokenTransfer{tt_1 | token_ids: [i], amount: i} end - tx_2 = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction_2 = insert(:transaction, input: "0xabcd010203040506") |> with_block() tt_2 = insert(:token_transfer, - transaction: tx_2, - block: tx_2.block, - block_number: tx_2.block_number, + transaction: transaction_2, + block: transaction_2.block, + block_number: transaction_2.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(25..49, fn x -> x end), token_type: "ERC-1155", @@ -291,9 +291,9 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do tt_3 = insert(:token_transfer, - transaction: tx_2, - block: tx_2.block, - block_number: tx_2.block_number, + transaction: transaction_2, + block: transaction_2.block, + block_number: transaction_2.block_number, token_contract_address: token.contract_address, token_ids: [50], token_type: "ERC-1155", @@ -314,13 +314,13 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do test "check that pagination works fine with 1155 batches #3", %{conn: conn} do token = insert(:token, type: "ERC-1155") - tx_1 = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction_1 = insert(:transaction, input: "0xabcd010203040506") |> with_block() tt_1 = insert(:token_transfer, - transaction: tx_1, - block: tx_1.block, - block_number: tx_1.block_number, + transaction: transaction_1, + block: transaction_1.block, + block_number: transaction_1.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(0..24, fn x -> x end), token_type: "ERC-1155", @@ -332,13 +332,13 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do %TokenTransfer{tt_1 | token_ids: [i], amount: i} end - tx_2 = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction_2 = insert(:transaction, input: "0xabcd010203040506") |> with_block() tt_2 = insert(:token_transfer, - transaction: tx_2, - block: tx_2.block, - block_number: tx_2.block_number, + transaction: transaction_2, + block: transaction_2.block, + block_number: transaction_2.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(25..50, fn x -> x end), token_type: "ERC-1155", @@ -1237,12 +1237,12 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do token_transfers = for _i <- 0..50 do - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: [id], token_type: "ERC-721" @@ -1271,13 +1271,13 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do insert(:token_instance, token_id: id, token_contract_address_hash: token.contract_address_hash) - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() tt = insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(0..50, fn _x -> id end), token_type: "ERC-1155", @@ -1299,14 +1299,14 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do amount = 101 insert(:token_instance, token_id: id, token_contract_address_hash: token.contract_address_hash) - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() tt = for _ <- 0..50 do insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(0..50, fn x -> x end) ++ [id], token_type: "ERC-1155", @@ -1636,11 +1636,11 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do def compare_item(%TokenTransfer{} = token_transfer, json) do assert Address.checksum(token_transfer.from_address_hash) == json["from"]["hash"] assert Address.checksum(token_transfer.to_address_hash) == json["to"]["hash"] - assert to_string(token_transfer.transaction_hash) == json["tx_hash"] + assert to_string(token_transfer.transaction_hash) == json["transaction_hash"] assert json["timestamp"] != nil assert json["method"] != nil assert to_string(token_transfer.block_hash) == json["block_hash"] - assert to_string(token_transfer.log_index) == json["log_index"] + assert token_transfer.log_index == json["log_index"] assert check_total(Repo.preload(token_transfer, [{:token, :contract_address}]).token, json["total"], token_transfer) end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_transfer_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_transfer_controller_test.exs index a1a46b07ab3d..b3944a618423 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_transfer_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_transfer_controller_test.exs @@ -13,12 +13,12 @@ defmodule BlockScoutWeb.API.V2.TokenTransferControllerTest do end test "non empty list", %{conn: conn} do - tx = + transaction = :transaction |> insert() |> with_block() - 1 |> insert_list(:token_transfer, transaction: tx) + 1 |> insert_list(:token_transfer, transaction: transaction) request = get(conn, "/api/v2/token-transfers") @@ -28,7 +28,7 @@ defmodule BlockScoutWeb.API.V2.TokenTransferControllerTest do end test "filters by type", %{conn: conn} do - tx = + transaction = :transaction |> insert() |> with_block() @@ -36,7 +36,7 @@ defmodule BlockScoutWeb.API.V2.TokenTransferControllerTest do token = insert(:token, type: "ERC-721") insert(:token_transfer, - transaction: tx, + transaction: transaction, token: token, token_type: "ERC-721" ) @@ -49,7 +49,7 @@ defmodule BlockScoutWeb.API.V2.TokenTransferControllerTest do end test "returns all transfers if filter is incorrect", %{conn: conn} do - tx = + transaction = :transaction |> insert() |> with_block() @@ -57,13 +57,13 @@ defmodule BlockScoutWeb.API.V2.TokenTransferControllerTest do token = insert(:token, type: "ERC-100500") insert(:token_transfer, - transaction: tx, + transaction: transaction, token: token, token_type: "ERC-721" ) insert(:token_transfer, - transaction: tx, + transaction: transaction, token: token, token_type: "ERC-20" ) @@ -78,12 +78,12 @@ defmodule BlockScoutWeb.API.V2.TokenTransferControllerTest do test "token transfers with next_page_params", %{conn: conn} do token_transfers = for _i <- 0..50 do - tx = insert(:transaction) |> with_block() + transaction = insert(:transaction) |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number ) end @@ -97,12 +97,12 @@ defmodule BlockScoutWeb.API.V2.TokenTransferControllerTest do end test "flatten erc1155 batch token transfer", %{conn: conn} do - tx = insert(:transaction) |> with_block() + transaction = insert(:transaction) |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_ids: [1, 2, 3], amounts: [500, 600, 700], token_type: "ERC-1155" @@ -117,12 +117,12 @@ defmodule BlockScoutWeb.API.V2.TokenTransferControllerTest do test "paginates erc1155 batch token transfers", %{conn: conn} do token_transfers = for _i <- 0..50 do - tx = insert(:transaction) |> with_block() + transaction = insert(:transaction) |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_ids: [1, 2], amounts: [500, 600], token_type: "ERC-1155" @@ -145,11 +145,11 @@ defmodule BlockScoutWeb.API.V2.TokenTransferControllerTest do defp compare_item(%TokenTransfer{} = token_transfer, json) do assert Address.checksum(token_transfer.from_address_hash) == json["from"]["hash"] assert Address.checksum(token_transfer.to_address_hash) == json["to"]["hash"] - assert to_string(token_transfer.transaction_hash) == json["tx_hash"] + assert to_string(token_transfer.transaction_hash) == json["transaction_hash"] assert token_transfer.transaction.block_timestamp == Timex.parse!(json["timestamp"], "{ISO:Extended:Z}") assert json["method"] == nil - assert to_string(token_transfer.block_number) == json["block_number"] - assert to_string(token_transfer.log_index) == json["log_index"] + assert token_transfer.block_number == json["block_number"] + assert token_transfer.log_index == json["log_index"] end defp check_paginated_response(first_page_resp, second_page_resp, third_page_resp, token_transfers) do diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs index 8d783b4d2dc0..d83dc7a40a5b 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs @@ -37,8 +37,8 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do assert response["next_page_params"] == nil end - test "txs with next_page_params", %{conn: conn} do - txs = + test "transactions with next_page_params", %{conn: conn} do + transactions = 51 |> insert_list(:transaction) |> with_block() @@ -49,15 +49,15 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do request_2nd_page = get(conn, "/api/v2/transactions", response["next_page_params"]) assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, txs) + check_paginated_response(response, response_2nd_page, transactions) end test "filter=pending", %{conn: conn} do - pending_txs = + pending_transactions = 51 |> insert_list(:transaction) - _mined_txs = + _mined_transactions = 51 |> insert_list(:transaction) |> with_block() @@ -70,15 +70,15 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do request_2nd_page = get(conn, "/api/v2/transactions", Map.merge(response["next_page_params"], filter)) assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, pending_txs) + check_paginated_response(response, response_2nd_page, pending_transactions) end test "filter=validated", %{conn: conn} do - _pending_txs = + _pending_transactions = 51 |> insert_list(:transaction) - mined_txs = + mined_transactions = 51 |> insert_list(:transaction) |> with_block() @@ -91,7 +91,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do request_2nd_page = get(conn, "/api/v2/transactions", Map.merge(response["next_page_params"], filter)) assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, mined_txs) + check_paginated_response(response, response_2nd_page, mined_transactions) end end @@ -120,7 +120,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do assert response["next_page_params"] == nil end - test "watchlist txs can paginate", %{conn: conn} do + test "watchlist transactions can paginate", %{conn: conn} do auth = build(:auth) {:ok, user} = UserFromAuth.find_or_create(auth) @@ -171,17 +171,17 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do |> insert_list(:transaction) |> with_block() - txs_1 = + transactions_1 = 25 |> insert_list(:transaction, from_address: address_1) |> with_block() - txs_2 = + transactions_2 = 1 |> insert_list(:transaction, from_address: address_2, to_address: address_1) |> with_block() - txs_3 = + transactions_3 = 25 |> insert_list(:transaction, from_address: address_2) |> with_block() @@ -192,61 +192,61 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do request_2nd_page = get(conn, "/api/v2/transactions/watchlist", response["next_page_params"]) assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, txs_1 ++ txs_2 ++ txs_3, %{ + check_paginated_response(response, response_2nd_page, transactions_1 ++ transactions_2 ++ transactions_3, %{ address_1.hash => watchlist_address_1.name, address_2.hash => watchlist_address_2.name }) end end - describe "/transactions/{tx_hash}" do - test "return 404 on non existing tx", %{conn: conn} do - tx = build(:transaction) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}") + describe "/transactions/{transaction_hash}" do + test "return 404 on non existing transaction", %{conn: conn} do + transaction = build(:transaction) + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}") assert %{"message" => "Not found"} = json_response(request, 404) end - test "return 422 on invalid tx hash", %{conn: conn} do + test "return 422 on invalid transaction hash", %{conn: conn} do request = get(conn, "/api/v2/transactions/0x") assert %{"message" => "Invalid parameter(s)"} = json_response(request, 422) end - test "return existing tx", %{conn: conn} do - tx = + test "return existing transaction", %{conn: conn} do + transaction = :transaction |> insert() |> with_block() - request = get(conn, "/api/v2/transactions/" <> to_string(tx.hash)) + request = get(conn, "/api/v2/transactions/" <> to_string(transaction.hash)) assert response = json_response(request, 200) - compare_item(tx, response) + compare_item(transaction, response) end test "batch 1155 flattened", %{conn: conn} do token = insert(:token, type: "ERC-1155") - tx = + transaction = :transaction |> insert() |> with_block() insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(0..50, fn x -> x end), token_type: "ERC-1155", amounts: Enum.map(0..50, fn x -> x end) ) - request = get(conn, "/api/v2/transactions/" <> to_string(tx.hash)) + request = get(conn, "/api/v2/transactions/" <> to_string(transaction.hash)) assert response = json_response(request, 200) - compare_item(tx, response) + compare_item(transaction, response) assert Enum.count(response["token_transfers"]) == 10 end @@ -254,16 +254,16 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do test "single 1155 flattened", %{conn: conn} do token = insert(:token, type: "ERC-1155") - tx = + transaction = :transaction |> insert() |> with_block() tt = insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: [1], token_type: "ERC-1155", @@ -271,10 +271,10 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do amount: nil ) - request = get(conn, "/api/v2/transactions/" <> to_string(tx.hash)) + request = get(conn, "/api/v2/transactions/" <> to_string(transaction.hash)) assert response = json_response(request, 200) - compare_item(tx, response) + compare_item(transaction, response) assert Enum.count(response["token_transfers"]) == 1 assert is_map(Enum.at(response["token_transfers"], 0)["total"]) @@ -282,27 +282,27 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end end - describe "/transactions/{tx_hash}/internal-transactions" do - test "return 404 on non existing tx", %{conn: conn} do - tx = build(:transaction) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/internal-transactions") + describe "/transactions/{transaction_hash}/internal-transactions" do + test "return 404 on non existing transaction", %{conn: conn} do + transaction = build(:transaction) + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/internal-transactions") assert %{"message" => "Not found"} = json_response(request, 404) end - test "return 422 on invalid tx hash", %{conn: conn} do + test "return 422 on invalid transaction hash", %{conn: conn} do request = get(conn, "/api/v2/transactions/0x/internal-transactions") assert %{"message" => "Invalid parameter(s)"} = json_response(request, 422) end test "return empty list", %{conn: conn} do - tx = + transaction = :transaction |> insert() |> with_block() - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/internal-transactions") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/internal-transactions") assert response = json_response(request, 200) assert response["items"] == [] @@ -310,31 +310,31 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end test "return relevant internal transaction", %{conn: conn} do - tx = + transaction = :transaction |> insert() |> with_block() insert(:internal_transaction, - transaction: tx, + transaction: transaction, index: 0, - block_number: tx.block_number, - transaction_index: tx.index, - block_hash: tx.block_hash, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, block_index: 0 ) - internal_tx = + internal_transaction = insert(:internal_transaction, - transaction: tx, + transaction: transaction, index: 1, - block_number: tx.block_number, - transaction_index: tx.index, - block_hash: tx.block_hash, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, block_index: 1 ) - tx_1 = + transaction_1 = :transaction |> insert() |> with_block() @@ -342,84 +342,88 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do 0..5 |> Enum.map(fn index -> insert(:internal_transaction, - transaction: tx_1, + transaction: transaction_1, index: index, - block_number: tx_1.block_number, - transaction_index: tx_1.index, - block_hash: tx_1.block_hash, + block_number: transaction_1.block_number, + transaction_index: transaction_1.index, + block_hash: transaction_1.block_hash, block_index: index ) end) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/internal-transactions") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/internal-transactions") assert response = json_response(request, 200) assert Enum.count(response["items"]) == 1 assert response["next_page_params"] == nil - compare_item(internal_tx, Enum.at(response["items"], 0)) + compare_item(internal_transaction, Enum.at(response["items"], 0)) end test "return list with next_page_params", %{conn: conn} do - tx = + transaction = :transaction |> insert() |> with_block() insert(:internal_transaction, - transaction: tx, + transaction: transaction, index: 0, - block_number: tx.block_number, - transaction_index: tx.index, - block_hash: tx.block_hash, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, block_index: 0 ) - internal_txs = + internal_transactions = 51..1 |> Enum.map(fn index -> insert(:internal_transaction, - transaction: tx, + transaction: transaction, index: index, - block_number: tx.block_number, - transaction_index: tx.index, - block_hash: tx.block_hash, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, block_index: index ) end) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/internal-transactions") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/internal-transactions") assert response = json_response(request, 200) request_2nd_page = - get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/internal-transactions", response["next_page_params"]) + get( + conn, + "/api/v2/transactions/#{to_string(transaction.hash)}/internal-transactions", + response["next_page_params"] + ) assert response_2nd_page = json_response(request_2nd_page, 200) - check_paginated_response(response, response_2nd_page, internal_txs) + check_paginated_response(response, response_2nd_page, internal_transactions) end end - describe "/transactions/{tx_hash}/logs" do - test "return 404 on non existing tx", %{conn: conn} do - tx = build(:transaction) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/logs") + describe "/transactions/{transaction_hash}/logs" do + test "return 404 on non existing transaction", %{conn: conn} do + transaction = build(:transaction) + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/logs") assert %{"message" => "Not found"} = json_response(request, 404) end - test "return 422 on invalid tx hash", %{conn: conn} do + test "return 422 on invalid transaction hash", %{conn: conn} do request = get(conn, "/api/v2/transactions/0x/logs") assert %{"message" => "Invalid parameter(s)"} = json_response(request, 422) end test "return empty list", %{conn: conn} do - tx = + transaction = :transaction |> insert() |> with_block() - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/logs") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/logs") assert response = json_response(request, 200) assert response["items"] == [] @@ -427,20 +431,20 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end test "return relevant log", %{conn: conn} do - tx = + transaction = :transaction |> insert() |> with_block() log = insert(:log, - transaction: tx, + transaction: transaction, index: 1, - block: tx.block, - block_number: tx.block_number + block: transaction.block, + block_number: transaction.block_number ) - tx_1 = + transaction_1 = :transaction |> insert() |> with_block() @@ -448,14 +452,14 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do 0..5 |> Enum.map(fn index -> insert(:log, - transaction: tx_1, + transaction: transaction_1, index: index, - block: tx_1.block, - block_number: tx_1.block_number + block: transaction_1.block, + block_number: transaction_1.block_number ) end) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/logs") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/logs") assert response = json_response(request, 200) assert Enum.count(response["items"]) == 1 @@ -464,7 +468,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end test "return list with next_page_params", %{conn: conn} do - tx = + transaction = :transaction |> insert() |> with_block() @@ -473,17 +477,18 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do 50..0 |> Enum.map(fn index -> insert(:log, - transaction: tx, + transaction: transaction, index: index, - block: tx.block, - block_number: tx.block_number + block: transaction.block, + block_number: transaction.block_number ) end) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/logs") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/logs") assert response = json_response(request, 200) - request_2nd_page = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/logs", response["next_page_params"]) + request_2nd_page = + get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/logs", response["next_page_params"]) assert response_2nd_page = json_response(request_2nd_page, 200) @@ -491,27 +496,27 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end end - describe "/transactions/{tx_hash}/token-transfers" do - test "return 404 on non existing tx", %{conn: conn} do - tx = build(:transaction) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers") + describe "/transactions/{transaction_hash}/token-transfers" do + test "return 404 on non existing transaction", %{conn: conn} do + transaction = build(:transaction) + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers") assert %{"message" => "Not found"} = json_response(request, 404) end - test "return 422 on invalid tx hash", %{conn: conn} do + test "return 422 on invalid transaction hash", %{conn: conn} do request = get(conn, "/api/v2/transactions/0x/token-transfers") assert %{"message" => "Invalid parameter(s)"} = json_response(request, 422) end test "return empty list", %{conn: conn} do - tx = + transaction = :transaction |> insert() |> with_block() - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers") assert response = json_response(request, 200) assert response["items"] == [] @@ -519,21 +524,30 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end test "return relevant token transfer", %{conn: conn} do - tx = + transaction = :transaction |> insert() |> with_block() - token_transfer = insert(:token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number) + token_transfer = + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number + ) - tx_1 = + transaction_1 = :transaction |> insert() |> with_block() - insert_list(6, :token_transfer, transaction: tx_1, block: tx_1.block, block_number: tx_1.block_number) + insert_list(6, :token_transfer, + transaction: transaction_1, + block: transaction_1.block, + block_number: transaction_1.block_number + ) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers") assert response = json_response(request, 200) assert Enum.count(response["items"]) == 1 @@ -542,20 +556,24 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end test "return list with next_page_params", %{conn: conn} do - tx = + transaction = :transaction |> insert() |> with_block() token_transfers = - insert_list(51, :token_transfer, transaction: tx, block: tx.block, block_number: tx.block_number) + insert_list(51, :token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number + ) |> Enum.reverse() - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers") assert response = json_response(request, 200) request_2nd_page = - get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers", response["next_page_params"]) + get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers", response["next_page_params"]) assert response_2nd_page = json_response(request_2nd_page, 200) @@ -563,7 +581,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end test "check filters", %{conn: conn} do - tx = + transaction = :transaction |> insert() |> with_block() @@ -573,9 +591,9 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do erc_1155_tt = for x <- 0..50 do insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: erc_1155_token.contract_address, token_ids: [x], token_type: "ERC-1155" @@ -588,9 +606,9 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do erc_721_tt = for x <- 0..50 do insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: erc_721_token.contract_address, token_ids: [x], token_type: "ERC-721" @@ -603,9 +621,9 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do erc_20_tt = for _ <- 0..50 do insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: erc_20_token.contract_address, token_type: "ERC-20" ) @@ -614,13 +632,13 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do # -- ERC-20 -- filter = %{"type" => "ERC-20"} - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers", filter) + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers", filter) assert response = json_response(request, 200) request_2nd_page = get( conn, - "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers", + "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers", Map.merge(response["next_page_params"], filter) ) @@ -631,13 +649,13 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do # -- ERC-721 -- filter = %{"type" => "ERC-721"} - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers", filter) + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers", filter) assert response = json_response(request, 200) request_2nd_page = get( conn, - "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers", + "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers", Map.merge(response["next_page_params"], filter) ) @@ -648,13 +666,13 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do # -- ERC-1155 -- filter = %{"type" => "ERC-1155"} - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers", filter) + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers", filter) assert response = json_response(request, 200) request_2nd_page = get( conn, - "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers", + "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers", Map.merge(response["next_page_params"], filter) ) @@ -665,13 +683,13 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do # two filters simultaneously filter = %{"type" => "ERC-1155,ERC-20"} - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers", filter) + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers", filter) assert response = json_response(request, 200) request_2nd_page = get( conn, - "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers", + "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers", Map.merge(response["next_page_params"], filter) ) @@ -691,7 +709,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do request_3rd_page = get( conn, - "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers", + "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers", Map.merge(response_2nd_page["next_page_params"], filter) ) @@ -709,7 +727,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do insert(:token_instance, token_id: id, token_contract_address_hash: token.contract_address_hash) - tx = + transaction = :transaction |> insert() |> with_block() @@ -717,9 +735,9 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do tt = for _ <- 0..50 do insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(0..50, fn _x -> id end), token_type: "ERC-1155", @@ -732,11 +750,11 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do %TokenTransfer{i | token_ids: [id], amount: Decimal.new(1275)} end - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers") assert response = json_response(request, 200) request_2nd_page = - get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers", response["next_page_params"]) + get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers", response["next_page_params"]) assert response_2nd_page = json_response(request_2nd_page, 200) @@ -746,7 +764,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do test "check that pagination works for 721 tokens", %{conn: conn} do token = insert(:token, type: "ERC-721") - tx = + transaction = :transaction |> insert() |> with_block() @@ -754,20 +772,20 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do token_transfers = for i <- 0..50 do insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: [i], token_type: "ERC-721" ) end - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers") assert response = json_response(request, 200) request_2nd_page = - get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers", response["next_page_params"]) + get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers", response["next_page_params"]) assert response_2nd_page = json_response(request_2nd_page, 200) @@ -777,16 +795,16 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do test "check that pagination works fine with 1155 batches #1 (large batch)", %{conn: conn} do token = insert(:token, type: "ERC-1155") - tx = + transaction = :transaction |> insert() |> with_block() tt = insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(0..50, fn x -> x end), token_type: "ERC-1155", @@ -798,11 +816,11 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do %TokenTransfer{tt | token_ids: [i], amount: i} end - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers") assert response = json_response(request, 200) request_2nd_page = - get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers", response["next_page_params"]) + get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers", response["next_page_params"]) assert response_2nd_page = json_response(request_2nd_page, 200) @@ -813,16 +831,16 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do %{conn: conn} do token = insert(:token, type: "ERC-1155") - tx = + transaction = :transaction |> insert() |> with_block() tt_1 = insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(0..24, fn x -> x end), token_type: "ERC-1155", @@ -836,9 +854,9 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do tt_2 = insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(25..49, fn x -> x end), token_type: "ERC-1155", @@ -852,20 +870,20 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do tt_3 = insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: [50], token_type: "ERC-1155", amounts: [50] ) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers") assert response = json_response(request, 200) request_2nd_page = - get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers", response["next_page_params"]) + get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers", response["next_page_params"]) assert response_2nd_page = json_response(request_2nd_page, 200) @@ -875,13 +893,13 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do test "check that pagination works fine with 1155 batches #3", %{conn: conn} do token = insert(:token, type: "ERC-1155") - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() tt_1 = insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(0..24, fn x -> x end), token_type: "ERC-1155", @@ -895,9 +913,9 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do tt_2 = insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, token_contract_address: token.contract_address, token_ids: Enum.map(25..50, fn x -> x end), token_type: "ERC-1155", @@ -909,11 +927,11 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do %TokenTransfer{tt_2 | token_ids: [i], amount: i} end - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers") assert response = json_response(request, 200) request_2nd_page = - get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers", response["next_page_params"]) + get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/token-transfers", response["next_page_params"]) assert response_2nd_page = json_response(request_2nd_page, 200) @@ -921,21 +939,21 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end end - describe "/transactions/{tx_hash}/state-changes" do - test "return 404 on non existing tx", %{conn: conn} do - tx = build(:transaction) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/state-changes") + describe "/transactions/{transaction_hash}/state-changes" do + test "return 404 on non existing transaction", %{conn: conn} do + transaction = build(:transaction) + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}/state-changes") assert %{"message" => "Not found"} = json_response(request, 404) end - test "return 422 on invalid tx hash", %{conn: conn} do + test "return 422 on invalid transaction hash", %{conn: conn} do request = get(conn, "/api/v2/transactions/0x/state-changes") assert %{"message" => "Invalid parameter(s)"} = json_response(request, 422) end - test "return existing tx", %{conn: conn} do + test "return existing transaction", %{conn: conn} do block_before = insert(:block) transaction = @@ -1135,7 +1153,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do test "when gas is paid with token and token is present in db", %{conn: conn} do token = insert(:token) - tx = + transaction = :transaction |> insert(gas_token_contract_address: token.contract_address) |> with_block() @@ -1162,7 +1180,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do ] } = json_response(request, 200) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}") assert %{ "celo" => %{ @@ -1175,7 +1193,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do } } = json_response(request, 200) - request = get(conn, "/api/v2/addresses/#{to_string(tx.from_address_hash)}/transactions") + request = get(conn, "/api/v2/addresses/#{to_string(transaction.from_address_hash)}/transactions") assert %{ "items" => [ @@ -1211,7 +1229,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do test "when gas is paid with token and token is not present in db", %{conn: conn} do unknown_token_address = insert(:address) - tx = + transaction = :transaction |> insert(gas_token_contract_address: unknown_token_address) |> with_block() @@ -1232,7 +1250,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do ] } = json_response(request, 200) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}") assert %{ "celo" => %{ @@ -1242,7 +1260,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do } } = json_response(request, 200) - request = get(conn, "/api/v2/addresses/#{to_string(tx.from_address_hash)}/transactions") + request = get(conn, "/api/v2/addresses/#{to_string(transaction.from_address_hash)}/transactions") assert %{ "items" => [ @@ -1270,7 +1288,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end test "when gas is paid in native coin", %{conn: conn} do - tx = :transaction |> insert() |> with_block() + transaction = :transaction |> insert() |> with_block() request = get(conn, "/api/v2/transactions") @@ -1282,13 +1300,13 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do ] } = json_response(request, 200) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}") assert %{ "celo" => %{"gas_token" => nil} } = json_response(request, 200) - request = get(conn, "/api/v2/addresses/#{to_string(tx.from_address_hash)}/transactions") + request = get(conn, "/api/v2/addresses/#{to_string(transaction.from_address_hash)}/transactions") assert %{ "items" => [ @@ -1309,7 +1327,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end end - describe "/transactions/{tx_hash}/raw-trace" do + describe "/transactions/{transaction_hash}/raw-trace" do test "returns raw trace from node", %{conn: conn} do transaction = :transaction @@ -1363,14 +1381,14 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do describe "stability fees" do test "check stability fees", %{conn: conn} do - tx = insert(:transaction) |> with_block() + transaction = insert(:transaction) |> with_block() _log = insert(:log, - transaction: tx, + transaction: transaction, index: 1, - block: tx.block, - block_number: tx.block_number, + block: transaction.block, + block_number: transaction.block_number, first_topic: topic(@first_topic_hex_string_1), data: "0x000000000000000000000000dc2b93f3291030f3f7a6d9363ac37757f7ad5c4300000000000000000000000000000000000000000000000000002824369a100000000000000000000000000046b555cb3962bf9533c437cbd04a2f702dfdb999000000000000000000000000000000000000000000000000000014121b4d0800000000000000000000000000faf7a981360c2fab3a5ab7b3d6d8d0cf97a91eb9000000000000000000000000000000000000000000000000000014121b4d0800" @@ -1394,7 +1412,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do ] } = json_response(request, 200) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}") assert %{ "stability_fee" => %{ @@ -1407,7 +1425,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do } } = json_response(request, 200) - request = get(conn, "/api/v2/addresses/#{to_string(tx.from_address_hash)}/transactions") + request = get(conn, "/api/v2/addresses/#{to_string(transaction.from_address_hash)}/transactions") assert %{ "items" => [ @@ -1426,14 +1444,14 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end test "check stability if token absent in DB", %{conn: conn} do - tx = insert(:transaction) |> with_block() + transaction = insert(:transaction) |> with_block() _log = insert(:log, - transaction: tx, + transaction: transaction, index: 1, - block: tx.block, - block_number: tx.block_number, + block: transaction.block, + block_number: transaction.block_number, first_topic: topic(@first_topic_hex_string_1), data: "0x000000000000000000000000dc2b93f3291030f3f7a6d9363ac37757f7ad5c4300000000000000000000000000000000000000000000000000002824369a100000000000000000000000000046b555cb3962bf9533c437cbd04a2f702dfdb999000000000000000000000000000000000000000000000000000014121b4d0800000000000000000000000000faf7a981360c2fab3a5ab7b3d6d8d0cf97a91eb9000000000000000000000000000000000000000000000000000014121b4d0800" @@ -1456,7 +1474,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do ] } = json_response(request, 200) - request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}") + request = get(conn, "/api/v2/transactions/#{to_string(transaction.hash)}") assert %{ "stability_fee" => %{ @@ -1469,7 +1487,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do } } = json_response(request, 200) - request = get(conn, "/api/v2/addresses/#{to_string(tx.from_address_hash)}/transactions") + request = get(conn, "/api/v2/addresses/#{to_string(transaction.from_address_hash)}/transactions") assert %{ "items" => [ @@ -1491,43 +1509,43 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do defp compare_item(%Transaction{} = transaction, json) do assert to_string(transaction.hash) == json["hash"] - assert transaction.block_number == json["block"] + assert transaction.block_number == json["block_number"] assert to_string(transaction.value.value) == json["value"] assert Address.checksum(transaction.from_address_hash) == json["from"]["hash"] assert Address.checksum(transaction.to_address_hash) == json["to"]["hash"] end - defp compare_item(%InternalTransaction{} = internal_tx, json) do - assert internal_tx.block_number == json["block"] - assert to_string(internal_tx.gas) == json["gas_limit"] - assert internal_tx.index == json["index"] - assert to_string(internal_tx.transaction_hash) == json["transaction_hash"] - assert Address.checksum(internal_tx.from_address_hash) == json["from"]["hash"] - assert Address.checksum(internal_tx.to_address_hash) == json["to"]["hash"] + defp compare_item(%InternalTransaction{} = internal_transaction, json) do + assert internal_transaction.block_number == json["block_number"] + assert to_string(internal_transaction.gas) == json["gas_limit"] + assert internal_transaction.index == json["index"] + assert to_string(internal_transaction.transaction_hash) == json["transaction_hash"] + assert Address.checksum(internal_transaction.from_address_hash) == json["from"]["hash"] + assert Address.checksum(internal_transaction.to_address_hash) == json["to"]["hash"] end defp compare_item(%Log{} = log, json) do assert to_string(log.data) == json["data"] assert log.index == json["index"] assert Address.checksum(log.address_hash) == json["address"]["hash"] - assert to_string(log.transaction_hash) == json["tx_hash"] + assert to_string(log.transaction_hash) == json["transaction_hash"] assert json["block_number"] == log.block_number end defp compare_item(%TokenTransfer{} = token_transfer, json) do assert Address.checksum(token_transfer.from_address_hash) == json["from"]["hash"] assert Address.checksum(token_transfer.to_address_hash) == json["to"]["hash"] - assert to_string(token_transfer.transaction_hash) == json["tx_hash"] + assert to_string(token_transfer.transaction_hash) == json["transaction_hash"] assert json["timestamp"] == nil assert json["method"] == nil assert to_string(token_transfer.block_hash) == json["block_hash"] - assert to_string(token_transfer.log_index) == json["log_index"] + assert token_transfer.log_index == json["log_index"] assert check_total(Repo.preload(token_transfer, [{:token, :contract_address}]).token, json["total"], token_transfer) end defp compare_item(%Transaction{} = transaction, json, wl_names) do assert to_string(transaction.hash) == json["hash"] - assert transaction.block_number == json["block"] + assert transaction.block_number == json["block_number"] assert to_string(transaction.value.value) == json["value"] assert Address.checksum(transaction.from_address_hash) == json["from"]["hash"] assert Address.checksum(transaction.to_address_hash) == json["to"]["hash"] @@ -1555,26 +1573,26 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do ) end - defp check_paginated_response(first_page_resp, second_page_resp, txs) do + defp check_paginated_response(first_page_resp, second_page_resp, transactions) do assert Enum.count(first_page_resp["items"]) == 50 assert first_page_resp["next_page_params"] != nil - compare_item(Enum.at(txs, 50), Enum.at(first_page_resp["items"], 0)) - compare_item(Enum.at(txs, 1), Enum.at(first_page_resp["items"], 49)) + compare_item(Enum.at(transactions, 50), Enum.at(first_page_resp["items"], 0)) + compare_item(Enum.at(transactions, 1), Enum.at(first_page_resp["items"], 49)) assert Enum.count(second_page_resp["items"]) == 1 assert second_page_resp["next_page_params"] == nil - compare_item(Enum.at(txs, 0), Enum.at(second_page_resp["items"], 0)) + compare_item(Enum.at(transactions, 0), Enum.at(second_page_resp["items"], 0)) end - defp check_paginated_response(first_page_resp, second_page_resp, txs, wl_names) do + defp check_paginated_response(first_page_resp, second_page_resp, transactions, wl_names) do assert Enum.count(first_page_resp["items"]) == 50 assert first_page_resp["next_page_params"] != nil - compare_item(Enum.at(txs, 50), Enum.at(first_page_resp["items"], 0), wl_names) - compare_item(Enum.at(txs, 1), Enum.at(first_page_resp["items"], 49), wl_names) + compare_item(Enum.at(transactions, 50), Enum.at(first_page_resp["items"], 0), wl_names) + compare_item(Enum.at(transactions, 1), Enum.at(first_page_resp["items"], 49), wl_names) assert Enum.count(second_page_resp["items"]) == 1 assert second_page_resp["next_page_params"] == nil - compare_item(Enum.at(txs, 0), Enum.at(second_page_resp["items"], 0), wl_names) + compare_item(Enum.at(transactions, 0), Enum.at(second_page_resp["items"], 0), wl_names) end # with the current implementation no transfers should come with list in totals diff --git a/apps/block_scout_web/test/block_scout_web/controllers/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/transaction_controller_test.exs index 65d8f8946911..99d09fcb44a8 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/transaction_controller_test.exs @@ -127,7 +127,7 @@ defmodule BlockScoutWeb.TransactionControllerTest do assert html_response(conn, 422) end - test "no redirect from tx page", %{conn: conn} do + test "no redirect from transaction page", %{conn: conn} do transaction = insert(:transaction) conn = get(conn, transaction_path(BlockScoutWeb.Endpoint, :show, transaction)) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/verified_contracts_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/verified_contracts_controller_test.exs index 7e5a91e064b6..0a07b1d6a368 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/verified_contracts_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/verified_contracts_controller_test.exs @@ -67,7 +67,7 @@ defmodule BlockScoutWeb.VerifiedContractsControllerTest do coin_balance: nil, items_count: "50", smart_contract_id: id, - tx_count: nil + transaction_count: nil ) assert Map.get(json_response(conn, 200), "next_page_path") == expected_path diff --git a/apps/block_scout_web/test/block_scout_web/features/viewing_transactions_test.exs b/apps/block_scout_web/test/block_scout_web/features/viewing_transactions_test.exs index 2d6eb7158305..9931bd43a5d9 100644 --- a/apps/block_scout_web/test/block_scout_web/features/viewing_transactions_test.exs +++ b/apps/block_scout_web/test/block_scout_web/features/viewing_transactions_test.exs @@ -28,7 +28,7 @@ defmodule BlockScoutWeb.ViewingTransactionsTest do taft = insert(:address) # From Lincoln to Taft. - txn_from_lincoln = + transaction_from_lincoln = :transaction |> insert(from_address: lincoln, to_address: taft) |> with_block(block) @@ -66,7 +66,7 @@ defmodule BlockScoutWeb.ViewingTransactionsTest do lincoln: lincoln, taft: taft, transaction: transaction, - txn_from_lincoln: txn_from_lincoln + transaction_from_lincoln: transaction_from_lincoln }} end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/filecoin.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/filecoin.ex index 2d2e17822b9c..f9395a1321a4 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/filecoin.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/filecoin.ex @@ -617,23 +617,23 @@ defmodule EthereumJSONRPC.Filecoin do end defp to_transactions_params(blocks_responses, id_to_params) do - Enum.reduce(blocks_responses, [], fn %{id: id, result: tx_result}, blocks_acc -> - extract_transactions_params(Map.fetch!(id_to_params, id), tx_result) ++ blocks_acc + Enum.reduce(blocks_responses, [], fn %{id: id, result: transaction_result}, blocks_acc -> + extract_transactions_params(Map.fetch!(id_to_params, id), transaction_result) ++ blocks_acc end) end - defp extract_transactions_params(block_number, tx_result) do - tx_result + defp extract_transactions_params(block_number, transaction_result) do + transaction_result |> Enum.reduce( {[], 0}, # counter is the index of the internal transaction in transaction - fn %{"transactionHash" => tx_hash, "transactionPosition" => transaction_index} = calls_result, - {tx_acc, counter} -> - last_tx_response_from_accumulator = List.first(tx_acc) + fn %{"transactionHash" => transaction_hash, "transactionPosition" => transaction_index} = calls_result, + {transaction_acc, counter} -> + last_transaction_response_from_accumulator = List.first(transaction_acc) next_counter = - with {:empty_accumulator, false} <- {:empty_accumulator, is_nil(last_tx_response_from_accumulator)}, - true <- tx_hash !== last_tx_response_from_accumulator["transactionHash"] do + with {:empty_accumulator, false} <- {:empty_accumulator, is_nil(last_transaction_response_from_accumulator)}, + true <- transaction_hash !== last_transaction_response_from_accumulator["transactionHash"] do 0 else {:empty_accumulator, true} -> @@ -648,13 +648,13 @@ defmodule EthereumJSONRPC.Filecoin do Map.merge( %{ "blockNumber" => block_number, - "transactionHash" => tx_hash, + "transactionHash" => transaction_hash, "transactionIndex" => transaction_index, "index" => next_counter }, calls_result ) - | tx_acc + | transaction_acc ], next_counter } diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex index d30131f1284c..7c019ded0469 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex @@ -152,23 +152,23 @@ defmodule EthereumJSONRPC.Geth do def to_transactions_params(blocks_responses, id_to_params) do blocks_responses - |> Enum.reduce({[], 0}, fn %{id: id, result: tx_result}, {blocks_acc, counter} -> + |> Enum.reduce({[], 0}, fn %{id: id, result: transaction_result}, {blocks_acc, counter} -> {transactions_params, _, new_counter} = - extract_transactions_params(Map.fetch!(id_to_params, id), tx_result, counter) + extract_transactions_params(Map.fetch!(id_to_params, id), transaction_result, counter) {transactions_params ++ blocks_acc, new_counter} end) |> elem(0) end - defp extract_transactions_params(block_number, tx_result, counter) do - Enum.reduce(tx_result, {[], 0, counter}, fn %{"txHash" => tx_hash, "result" => calls_result}, - {tx_acc, inner_counter, counter} -> + defp extract_transactions_params(block_number, transaction_result, counter) do + Enum.reduce(transaction_result, {[], 0, counter}, fn %{"txHash" => transaction_hash, "result" => calls_result}, + {transaction_acc, inner_counter, counter} -> { [ - {%{block_number: block_number, hash_data: tx_hash, transaction_index: inner_counter, id: counter}, + {%{block_number: block_number, hash_data: transaction_hash, transaction_index: inner_counter, id: counter}, %{id: counter, result: calls_result}} - | tx_acc + | transaction_acc ], inner_counter + 1, counter + 1 @@ -261,14 +261,14 @@ defmodule EthereumJSONRPC.Geth do request(%{id: id, method: "eth_getTransactionReceipt", params: [hash_data]}) end) |> json_rpc(json_rpc_named_arguments), - {:ok, txs} <- + {:ok, transactions} <- id_to_params |> Enum.map(fn {id, %{hash_data: hash_data}} -> request(%{id: id, method: "eth_getTransactionByHash", params: [hash_data]}) end) |> json_rpc(json_rpc_named_arguments) do receipts_map = Enum.into(receipts, %{}, fn %{id: id, result: receipt} -> {id, receipt} end) - txs_map = Enum.into(txs, %{}, fn %{id: id, result: tx} -> {id, tx} end) + transactions_map = Enum.into(transactions, %{}, fn %{id: id, result: transaction} -> {id, transaction} end) tracer = if Application.get_env(:ethereum_jsonrpc, __MODULE__)[:tracer] == "polygon_edge", @@ -282,7 +282,7 @@ defmodule EthereumJSONRPC.Geth do %{id: id, result: %{"structLogs" => _} = result} -> debug_trace_transaction_response_to_internal_transactions_params( - %{id: id, result: tracer.replay(result, Map.fetch!(receipts_map, id), Map.fetch!(txs_map, id))}, + %{id: id, result: tracer.replay(result, Map.fetch!(receipts_map, id), Map.fetch!(transactions_map, id))}, id_to_params ) end) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth/tracer.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth/tracer.ex index 92aa18fc3d4c..cc17cc1cb00d 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth/tracer.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth/tracer.ex @@ -6,10 +6,14 @@ defmodule EthereumJSONRPC.Geth.Tracer do import EthereumJSONRPC, only: [integer_to_quantity: 1, quantity_to_integer: 1] - def replay(%{"structLogs" => logs, "gas" => top_call_gas, "returnValue" => return_value} = result, receipt, tx) + def replay( + %{"structLogs" => logs, "gas" => top_call_gas, "returnValue" => return_value} = result, + receipt, + transaction + ) when is_list(logs) do %{"contractAddress" => contract_address} = receipt - %{"from" => from, "to" => to, "value" => value, "input" => input} = tx + %{"from" => from, "to" => to, "value" => value, "input" => input} = transaction top = to diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex index c61e74f4c612..7a1745f4f0a0 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex @@ -30,7 +30,7 @@ defmodule EthereumJSONRPC.Transaction do :optimism -> @chain_type_fields quote( do: [ - l1_tx_origin: EthereumJSONRPC.hash(), + l1_transaction_origin: EthereumJSONRPC.hash(), l1_block_number: non_neg_integer() ] ) @@ -343,7 +343,7 @@ defmodule EthereumJSONRPC.Transaction do ]) end - # txpool_content method on Erigon node returns tx data + # txpool_content method on Erigon node returns transaction data # without gas price def do_elixir_to_params( %{ @@ -390,7 +390,7 @@ defmodule EthereumJSONRPC.Transaction do ]) end - # for legacy txs without maxPriorityFeePerGas and maxFeePerGas + # for legacy transactions without maxPriorityFeePerGas and maxFeePerGas def do_elixir_to_params( %{ "blockHash" => block_hash, @@ -432,7 +432,7 @@ defmodule EthereumJSONRPC.Transaction do ]) end - # for legacy txs without type, maxPriorityFeePerGas and maxFeePerGas + # for legacy transactions without type, maxPriorityFeePerGas and maxFeePerGas def do_elixir_to_params( %{ "blockHash" => block_hash, @@ -472,7 +472,7 @@ defmodule EthereumJSONRPC.Transaction do ]) end - # for txs without gasPrice, maxPriorityFeePerGas and maxFeePerGas + # for transactions without gasPrice, maxPriorityFeePerGas and maxFeePerGas def do_elixir_to_params( %{ "blockHash" => block_hash, @@ -522,9 +522,9 @@ defmodule EthereumJSONRPC.Transaction do ]) :optimism -> - # we need to put blobVersionedHashes for Indexer.Fetcher.Optimism.TxnBatch module + # we need to put blobVersionedHashes for Indexer.Fetcher.Optimism.TransactionBatch module put_if_present(params, elixir, [ - {"l1TxOrigin", :l1_tx_origin}, + {"l1TxOrigin", :l1_transaction_origin}, {"l1BlockNumber", :l1_block_number}, {"blobVersionedHashes", :blob_versioned_hashes} ]) diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth/tracer_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth/tracer_test.exs index bc57c176f7b2..f2a8da987e2b 100644 --- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth/tracer_test.exs +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth/tracer_test.exs @@ -8,7 +8,7 @@ defmodule EthereumJSONRPC.Geth.TracerTest do test "same as callTracer" do struct_logs = File.read!(File.cwd!() <> "/test/support/fixture/geth/trace/struct_logger.json") |> Jason.decode!() - tx = "0xa0a5c30c5c5ec22b3346e0ae5ce09f8f41faf54f68a2a113eb15e363af90e9ab" + transaction = "0xa0a5c30c5c5ec22b3346e0ae5ce09f8f41faf54f68a2a113eb15e363af90e9ab" sl_calls = Tracer.replay(struct_logs["result"], struct_logs["receipt"], struct_logs["tx"]) @@ -18,7 +18,7 @@ defmodule EthereumJSONRPC.Geth.TracerTest do "blockNumber" => 0, "index" => index, "transactionIndex" => 0, - "transactionHash" => tx + "transactionHash" => transaction }) end) |> Calls.to_internal_transactions_params() @@ -40,7 +40,7 @@ defmodule EthereumJSONRPC.Geth.TracerTest do "blockNumber" => 0, "index" => index, "transactionIndex" => 0, - "transactionHash" => tx + "transactionHash" => transaction }) end) |> Calls.to_internal_transactions_params() diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth_test.exs index 61195d8c3234..6617bbc2423a 100644 --- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth_test.exs +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth_test.exs @@ -359,11 +359,12 @@ defmodule EthereumJSONRPC.GethTest do Application.put_env(:ethereum_jsonrpc, Geth, tracer: "call_tracer", debug_trace_timeout: "5s") - call_tracer_internal_txs = Geth.fetch_internal_transactions([transaction_params], json_rpc_named_arguments) + call_tracer_internal_transactions = + Geth.fetch_internal_transactions([transaction_params], json_rpc_named_arguments) Application.put_env(:ethereum_jsonrpc, Geth, tracer: "js", debug_trace_timeout: "5s") - assert call_tracer_internal_txs == + assert call_tracer_internal_transactions == Geth.fetch_internal_transactions([transaction_params], json_rpc_named_arguments) end @@ -431,11 +432,12 @@ defmodule EthereumJSONRPC.GethTest do Application.put_env(:ethereum_jsonrpc, Geth, tracer: "call_tracer", debug_trace_timeout: "5s") - call_tracer_internal_txs = Geth.fetch_internal_transactions([transaction_params], json_rpc_named_arguments) + call_tracer_internal_transactions = + Geth.fetch_internal_transactions([transaction_params], json_rpc_named_arguments) Application.put_env(:ethereum_jsonrpc, Geth, tracer: "js", debug_trace_timeout: "5s") - assert call_tracer_internal_txs == + assert call_tracer_internal_transactions == Geth.fetch_internal_transactions([transaction_params], json_rpc_named_arguments) end diff --git a/apps/explorer/lib/encrypt.ex b/apps/explorer/lib/encrypt.ex index e105feb11d7b..ba8073ba4e62 100644 --- a/apps/explorer/lib/encrypt.ex +++ b/apps/explorer/lib/encrypt.ex @@ -54,8 +54,8 @@ defmodule Mix.Tasks.Encrypt do element |> Changeset.change(%{ encrypted_name: element.name, - encrypted_tx_hash: element.tx_hash, - tx_hash_hash: element.tx_hash |> to_string() |> String.downcase() + encrypted_transaction_hash: element.transaction_hash, + transaction_hash_hash: element.transaction_hash |> to_string() |> String.downcase() }) |> Account.update!() end) diff --git a/apps/explorer/lib/explorer/account/notifier/email.ex b/apps/explorer/lib/explorer/account/notifier/email.ex index 98e33be3778a..b1f9606936bf 100644 --- a/apps/explorer/lib/explorer/account/notifier/email.ex +++ b/apps/explorer/lib/explorer/account/notifier/email.ex @@ -35,7 +35,7 @@ defmodule Explorer.Account.Notifier.Email do |> add_dynamic_field("block_number", notification.block_number) |> add_dynamic_field("amount", amount(notification)) |> add_dynamic_field("name", notification.name) - |> add_dynamic_field("tx_fee", notification.tx_fee) + |> add_dynamic_field("transaction_fee", notification.transaction_fee) |> add_dynamic_field("direction", direction(notification)) |> add_dynamic_field("method", notification.method) |> add_dynamic_field("transaction_url", transaction_url(notification)) diff --git a/apps/explorer/lib/explorer/account/notifier/notify.ex b/apps/explorer/lib/explorer/account/notifier/notify.ex index 3ad9e041c297..1214b865d51b 100644 --- a/apps/explorer/lib/explorer/account/notifier/notify.ex +++ b/apps/explorer/lib/explorer/account/notifier/notify.ex @@ -129,7 +129,7 @@ defmodule Explorer.Account.Notifier.Notify do block_number: summary.block_number, amount: summary.amount, subject: summary.subject, - tx_fee: summary.tx_fee, + transaction_fee: summary.transaction_fee, name: summary.name, type: summary.type, from_address_hash_hash: hash_to_lower_case_string(summary.from_address_hash), diff --git a/apps/explorer/lib/explorer/account/notifier/summary.ex b/apps/explorer/lib/explorer/account/notifier/summary.ex index 568c9773ea73..d6231cfe62b6 100644 --- a/apps/explorer/lib/explorer/account/notifier/summary.ex +++ b/apps/explorer/lib/explorer/account/notifier/summary.ex @@ -19,7 +19,7 @@ defmodule Explorer.Account.Notifier.Summary do :method, :block_number, :amount, - :tx_fee, + :transaction_fee, :name, :subject, :type @@ -81,7 +81,7 @@ defmodule Explorer.Account.Notifier.Summary do to_address_hash: transaction.to_address_hash, block_number: transaction.block_number, amount: amount(transaction), - tx_fee: fee(transaction), + transaction_fee: fee(transaction), name: Explorer.coin_name(), subject: "Coin transaction", type: "COIN" @@ -96,7 +96,7 @@ defmodule Explorer.Account.Notifier.Summary do to_address_hash: transaction.created_contract_address_hash, block_number: transaction.block_number, amount: amount(transaction), - tx_fee: fee(transaction), + transaction_fee: fee(transaction), name: Explorer.coin_name(), subject: "Contract creation", type: "COIN" @@ -121,7 +121,7 @@ defmodule Explorer.Account.Notifier.Summary do block_number: transfer.block_number, amount: amount(transfer), subject: transfer.token.type, - tx_fee: fee(transaction), + transaction_fee: fee(transaction), name: token_name(transfer), type: transfer.token.type } @@ -135,7 +135,7 @@ defmodule Explorer.Account.Notifier.Summary do to_address_hash: transfer.to_address_hash, block_number: transfer.block_number, subject: to_string(transfer.token_ids && List.first(transfer.token_ids)), - tx_fee: fee(transaction), + transaction_fee: fee(transaction), name: token_name(transfer), type: transfer.token.type } @@ -149,7 +149,7 @@ defmodule Explorer.Account.Notifier.Summary do to_address_hash: transfer.to_address_hash, block_number: transfer.block_number, subject: token_ids(transfer), - tx_fee: fee(transaction), + transaction_fee: fee(transaction), name: token_name(transfer), type: transfer.token.type } @@ -165,7 +165,7 @@ defmodule Explorer.Account.Notifier.Summary do to_address_hash: transfer.to_address_hash, block_number: transfer.block_number, subject: if(token_ids_string == "", do: transfer.token.type, else: token_ids_string), - tx_fee: fee(transaction), + transaction_fee: fee(transaction), name: token_name(transfer), type: transfer.token.type } diff --git a/apps/explorer/lib/explorer/account/tag_transaction.ex b/apps/explorer/lib/explorer/account/tag_transaction.ex index a11b770e03be..510610e59113 100644 --- a/apps/explorer/lib/explorer/account/tag_transaction.ex +++ b/apps/explorer/lib/explorer/account/tag_transaction.ex @@ -13,16 +13,16 @@ defmodule Explorer.Account.TagTransaction do import Explorer.Chain, only: [hash_to_lower_case_string: 1] typed_schema "account_tag_transactions" do - field(:tx_hash_hash, Cloak.Ecto.SHA256) :: binary() | nil + field(:transaction_hash_hash, Cloak.Ecto.SHA256) :: binary() | nil field(:name, Explorer.Encrypted.Binary, null: false) - field(:tx_hash, Explorer.Encrypted.TransactionHash, null: false) + field(:transaction_hash, Explorer.Encrypted.TransactionHash, null: false) belongs_to(:identity, Identity, null: false) timestamps() end - @attrs ~w(name identity_id tx_hash)a + @attrs ~w(name identity_id transaction_hash)a def changeset do %__MODULE__{} @@ -36,7 +36,7 @@ defmodule Explorer.Account.TagTransaction do |> validate_required(@attrs, message: "Required") |> validate_length(:name, min: 1, max: 35) |> put_hashed_fields() - |> unique_constraint([:identity_id, :tx_hash_hash], message: "Transaction tag already exists") + |> unique_constraint([:identity_id, :transaction_hash_hash], message: "Transaction tag already exists") |> tag_transaction_count_constraint() |> check_transaction_existence() end @@ -50,20 +50,20 @@ defmodule Explorer.Account.TagTransaction do defp put_hashed_fields(changeset) do # Using force_change instead of put_change due to https://github.com/danielberkompas/cloak_ecto/issues/53 changeset - |> force_change(:tx_hash_hash, hash_to_lower_case_string(get_field(changeset, :tx_hash))) + |> force_change(:transaction_hash_hash, hash_to_lower_case_string(get_field(changeset, :transaction_hash))) end - defp check_transaction_existence(%Changeset{changes: %{tx_hash: tx_hash}} = changeset) do - check_transaction_existence_inner(changeset, tx_hash) + defp check_transaction_existence(%Changeset{changes: %{transaction_hash: transaction_hash}} = changeset) do + check_transaction_existence_inner(changeset, transaction_hash) end defp check_transaction_existence(changeset), do: changeset - defp check_transaction_existence_inner(changeset, tx_hash) do - if match?({:ok, _}, Chain.hash_to_transaction(tx_hash)) do + defp check_transaction_existence_inner(changeset, transaction_hash) do + if match?({:ok, _}, Chain.hash_to_transaction(transaction_hash)) do changeset else - add_error(changeset, :tx_hash, "Transaction does not exist") + add_error(changeset, :transaction_hash, "Transaction does not exist") end end @@ -122,17 +122,17 @@ defmodule Explorer.Account.TagTransaction do defp page_transaction_tags(query, _), do: query - def tag_transaction_by_transaction_hash_and_identity_id_query(tx_hash, identity_id) - when not is_nil(tx_hash) and not is_nil(identity_id) do + def tag_transaction_by_transaction_hash_and_identity_id_query(transaction_hash, identity_id) + when not is_nil(transaction_hash) and not is_nil(identity_id) do __MODULE__ - |> where([tag], tag.identity_id == ^identity_id and tag.tx_hash == ^tx_hash) + |> where([tag], tag.identity_id == ^identity_id and tag.transaction_hash == ^transaction_hash) end def tag_transaction_by_transaction_hash_and_identity_id_query(_, _), do: nil - def get_tag_transaction_by_transaction_hash_and_identity_id(tx_hash, identity_id) - when not is_nil(tx_hash) and not is_nil(identity_id) do - tx_hash + def get_tag_transaction_by_transaction_hash_and_identity_id(transaction_hash, identity_id) + when not is_nil(transaction_hash) and not is_nil(identity_id) do + transaction_hash |> hash_to_lower_case_string() |> tag_transaction_by_transaction_hash_and_identity_id_query(identity_id) |> Repo.account_repo().one() @@ -179,7 +179,7 @@ defmodule Explorer.Account.TagTransaction do end defimpl Jason.Encoder, for: Explorer.Account.TagTransaction do - def encode(tx_tag, opts) do - Jason.Encode.string(tx_tag.name, opts) + def encode(transaction_tag, opts) do + Jason.Encode.string(transaction_tag.name, opts) end end diff --git a/apps/explorer/lib/explorer/account/watchlist_notification.ex b/apps/explorer/lib/explorer/account/watchlist_notification.ex index bf747c32a1b1..2b7d129a0156 100644 --- a/apps/explorer/lib/explorer/account/watchlist_notification.ex +++ b/apps/explorer/lib/explorer/account/watchlist_notification.ex @@ -17,7 +17,7 @@ defmodule Explorer.Account.WatchlistNotification do field(:block_number, :integer, null: false) field(:direction, :string, null: false) field(:method, :string, null: false) - field(:tx_fee, :decimal, null: false) + field(:transaction_fee, :decimal, null: false) field(:type, :string, null: false) field(:viewed_at, :integer, null: false) field(:name, Explorer.Encrypted.Binary, null: false) @@ -41,7 +41,18 @@ defmodule Explorer.Account.WatchlistNotification do @doc false def changeset(watchlist_notifications, attrs) do watchlist_notifications - |> cast(attrs, [:amount, :direction, :name, :type, :method, :block_number, :tx_fee, :value, :decimals, :viewed_at]) + |> cast(attrs, [ + :amount, + :direction, + :name, + :type, + :method, + :block_number, + :transaction_fee, + :value, + :decimals, + :viewed_at + ]) |> validate_required([ :amount, :direction, @@ -49,7 +60,7 @@ defmodule Explorer.Account.WatchlistNotification do :type, :method, :block_number, - :tx_fee, + :transaction_fee, :value, :decimals, :viewed_at diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 6472fe13b0fb..385791447a12 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -257,6 +257,7 @@ defmodule Explorer.Chain do defp common_where_limit_order(query, paging_options) do query |> InternalTransaction.where_is_different_from_parent_transaction() + # todo: replace `index_int_tx_desc_order` with `index_internal_transaction_desc_order` in the next line when new frontend is bound to `index_internal_transaction_desc_order` property |> page_internal_transaction(paging_options, %{index_int_tx_desc_order: true}) |> limit(^paging_options.page_size) |> order_by( @@ -286,7 +287,7 @@ defmodule Explorer.Chain do |> Transaction.address_to_transactions_tasks_query(true) |> Transaction.not_pending_transactions() |> join_associations(necessity_by_association) - |> Transaction.put_has_token_transfers_to_tx(false) + |> Transaction.put_has_token_transfers_to_transaction(false) |> Transaction.matching_address_queries_list(direction, address_hashes) |> Enum.map(fn query -> Task.async(fn -> select_repo(options).all(query) end) end) end @@ -569,15 +570,17 @@ defmodule Explorer.Chain do |> fetch_transactions_in_ascending_order_by_index() |> join(:inner, [transaction], block in assoc(transaction, :block)) |> where([_, block], block.hash == ^block_hash) - |> apply_filter_by_tx_type_to_transactions(type_filter) + |> apply_filter_by_type_to_transactions(type_filter) |> join_associations(necessity_by_association) - |> Transaction.put_has_token_transfers_to_tx(old_ui?) + |> Transaction.put_has_token_transfers_to_transaction(old_ui?) |> (&if(old_ui?, do: preload(&1, [{:token_transfers, [:token, :from_address, :to_address]}]), else: &1)).() |> select_repo(options).all() |> (&if(old_ui?, do: &1, else: - Enum.map(&1, fn tx -> preload_token_transfers(tx, @token_transfers_necessity_by_association, options) end) + Enum.map(&1, fn transaction -> + preload_token_transfers(transaction, @token_transfers_necessity_by_association, options) + end) )).() end @@ -591,10 +594,12 @@ defmodule Explorer.Chain do |> fetch_transactions_in_descending_order_by_block_and_index() |> where(execution_node_hash: ^execution_node_hash) |> join_associations(necessity_by_association) - |> Transaction.put_has_token_transfers_to_tx(false) + |> Transaction.put_has_token_transfers_to_transaction(false) |> (& &1).() |> select_repo(options).all() - |> (&Enum.map(&1, fn tx -> preload_token_transfers(tx, @token_transfers_necessity_by_association, options) end)).() + |> (&Enum.map(&1, fn transaction -> + preload_token_transfers(transaction, @token_transfers_necessity_by_association, options) + end)).() end @spec block_to_withdrawals( @@ -613,15 +618,15 @@ defmodule Explorer.Chain do end @doc """ - Finds sum of gas_used for new (EIP-1559) txs belongs to block + Finds sum of gas_used for new (EIP-1559) transactions belongs to block """ - @spec block_to_gas_used_by_1559_txs(Hash.Full.t()) :: non_neg_integer() - def block_to_gas_used_by_1559_txs(block_hash) do + @spec block_to_gas_used_by_1559_transactions(Hash.Full.t()) :: non_neg_integer() + def block_to_gas_used_by_1559_transactions(block_hash) do query = from( - tx in Transaction, - where: tx.block_hash == ^block_hash, - select: sum(tx.gas_used) + transaction in Transaction, + where: transaction.block_hash == ^block_hash, + select: sum(transaction.gas_used) ) result = Repo.one(query) @@ -629,18 +634,18 @@ defmodule Explorer.Chain do end @doc """ - Finds sum of priority fee for new (EIP-1559) txs belongs to block + Finds sum of priority fee for new (EIP-1559) transactions belongs to block """ - @spec block_to_priority_fee_of_1559_txs(Hash.Full.t()) :: Decimal.t() - def block_to_priority_fee_of_1559_txs(block_hash) do + @spec block_to_priority_fee_of_1559_transactions(Hash.Full.t()) :: Decimal.t() + def block_to_priority_fee_of_1559_transactions(block_hash) do block = Repo.get_by(Block, hash: block_hash) case block.base_fee_per_gas do %Wei{value: base_fee_per_gas} -> query = from( - tx in Transaction, - where: tx.block_hash == ^block_hash, + transaction in Transaction, + where: transaction.block_hash == ^block_hash, select: sum( fragment( @@ -648,20 +653,20 @@ defmodule Explorer.Chain do WHEN COALESCE(?,?) = 0 THEN 0 WHEN COALESCE(?,?) - ? < COALESCE(?,?) THEN (COALESCE(?,?) - ?) * ? ELSE COALESCE(?,?) * ? END", - tx.max_fee_per_gas, - tx.gas_price, - tx.max_fee_per_gas, - tx.gas_price, + transaction.max_fee_per_gas, + transaction.gas_price, + transaction.max_fee_per_gas, + transaction.gas_price, ^base_fee_per_gas, - tx.max_priority_fee_per_gas, - tx.gas_price, - tx.max_fee_per_gas, - tx.gas_price, + transaction.max_priority_fee_per_gas, + transaction.gas_price, + transaction.max_fee_per_gas, + transaction.gas_price, ^base_fee_per_gas, - tx.gas_used, - tx.max_priority_fee_per_gas, - tx.gas_price, - tx.gas_used + transaction.gas_used, + transaction.max_priority_fee_per_gas, + transaction.gas_price, + transaction.gas_used ) ) ) @@ -1348,9 +1353,9 @@ defmodule Explorer.Chain do end end - # preload_to_detect_tt?: we don't need to preload more than one token transfer in case the tx inside the list (we don't show any token transfers on tx tile in new UI) + # preload_to_detect_tt?: we don't need to preload more than one token transfer in case the transaction inside the list (we don't show any token transfers on transaction tile in new UI) def preload_token_transfers( - %Transaction{hash: tx_hash, block_hash: block_hash} = transaction, + %Transaction{hash: transaction_hash, block_hash: block_hash} = transaction, necessity_by_association, options, preload_to_detect_tt? \\ true @@ -1363,12 +1368,12 @@ defmodule Explorer.Chain do token_transfers = TokenTransfer |> (&if(is_nil(block_hash), - do: where(&1, [token_transfer], token_transfer.transaction_hash == ^tx_hash), + do: where(&1, [token_transfer], token_transfer.transaction_hash == ^transaction_hash), else: where( &1, [token_transfer], - token_transfer.transaction_hash == ^tx_hash and token_transfer.block_hash == ^block_hash + token_transfer.transaction_hash == ^transaction_hash and token_transfer.block_hash == ^block_hash ) )).() |> limit(^limit) @@ -1505,9 +1510,9 @@ defmodule Explorer.Chain do full_blocks_range = max_saved_block_number - min_blockchain_trace_block_number - BlockNumberHelper.null_rounds_count() + 1 - processed_int_txs_for_blocks_count = max(0, full_blocks_range - pbo_count) + processed_int_transactions_for_blocks_count = max(0, full_blocks_range - pbo_count) - ratio = get_ratio(processed_int_txs_for_blocks_count, full_blocks_range) + ratio = get_ratio(processed_int_transactions_for_blocks_count, full_blocks_range) ratio |> (&if( @@ -2683,16 +2688,16 @@ defmodule Explorer.Chain do |> Transaction.fetch_transactions() |> where([transaction], not is_nil(transaction.block_number) and not is_nil(transaction.index)) |> apply_filter_by_method_id_to_transactions(method_id_filter) - |> apply_filter_by_tx_type_to_transactions(type_filter) + |> apply_filter_by_type_to_transactions(type_filter) |> join_associations(necessity_by_association) - |> Transaction.put_has_token_transfers_to_tx(old_ui?) + |> Transaction.put_has_token_transfers_to_transaction(old_ui?) |> (&if(old_ui?, do: preload(&1, [{:token_transfers, [:token, :from_address, :to_address]}]), else: &1)).() |> select_repo(options).all() |> (&if(old_ui?, do: &1, else: - Enum.map(&1, fn tx -> - preload_token_transfers(tx, @token_transfers_necessity_by_association, options) + Enum.map(&1, fn transaction -> + preload_token_transfers(transaction, @token_transfers_necessity_by_association, options) end) )).() end @@ -2737,7 +2742,7 @@ defmodule Explorer.Chain do |> limit(^paging_options.page_size) |> pending_transactions_query() |> apply_filter_by_method_id_to_transactions(method_id_filter) - |> apply_filter_by_tx_type_to_transactions(type_filter) + |> apply_filter_by_type_to_transactions(type_filter) |> order_by([transaction], desc: transaction.inserted_at, asc: transaction.hash) |> join_associations(necessity_by_association) |> (&if(old_ui?, do: preload(&1, [{:token_transfers, [:token, :from_address, :to_address]}]), else: &1)).() @@ -2976,13 +2981,13 @@ defmodule Explorer.Chain do %Transaction{revert_reason: revert_reason} = transaction if revert_reason == nil do - fetch_tx_revert_reason(transaction) + fetch_transaction_revert_reason(transaction) else revert_reason end end - def fetch_tx_revert_reason(transaction) do + def fetch_transaction_revert_reason(transaction) do json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) hash_string = to_string(transaction.hash) @@ -3007,13 +3012,13 @@ defmodule Explorer.Chain do {:error, reason} -> Logger.error(fn -> - ["Error while fetching first trace for tx: #{hash_string} error reason: ", inspect(reason)] + ["Error while fetching first trace for transaction: #{hash_string} error reason: ", inspect(reason)] end) - fetch_tx_revert_reason_using_call(transaction) + fetch_transaction_revert_reason_using_call(transaction) :ignore -> - fetch_tx_revert_reason_using_call(transaction) + fetch_transaction_revert_reason_using_call(transaction) end if !is_nil(revert_reason) do @@ -3025,7 +3030,7 @@ defmodule Explorer.Chain do revert_reason end - defp fetch_tx_revert_reason_using_call(%Transaction{ + defp fetch_transaction_revert_reason_using_call(%Transaction{ block_number: block_number, to_address_hash: to_address_hash, from_address_hash: from_address_hash, @@ -3142,29 +3147,29 @@ defmodule Explorer.Chain do |> Data.to_string() end - def smart_contract_creation_tx_bytecode(address_hash) do - creation_tx_query = + def smart_contract_creation_transaction_bytecode(address_hash) do + creation_transaction_query = from( - tx in Transaction, + transaction in Transaction, left_join: a in Address, - on: tx.created_contract_address_hash == a.hash, - where: tx.created_contract_address_hash == ^address_hash, - where: tx.status == ^1, - select: %{init: tx.input, created_contract_code: a.contract_code}, - order_by: [desc: tx.block_number], + on: transaction.created_contract_address_hash == a.hash, + where: transaction.created_contract_address_hash == ^address_hash, + where: transaction.status == ^1, + select: %{init: transaction.input, created_contract_code: a.contract_code}, + order_by: [desc: transaction.block_number], limit: ^1 ) - tx_input = - creation_tx_query + transaction_input = + creation_transaction_query |> Repo.one() - if tx_input do - with %{init: input, created_contract_code: created_contract_code} <- tx_input do + if transaction_input do + with %{init: input, created_contract_code: created_contract_code} <- transaction_input do %{init: Data.to_string(input), created_contract_code: Data.to_string(created_contract_code)} end else - creation_int_tx_query = + creation_int_transaction_query = from( itx in InternalTransaction, join: t in assoc(itx, :transaction), @@ -3175,7 +3180,7 @@ defmodule Explorer.Chain do limit: ^1 ) - res = creation_int_tx_query |> Repo.one() + res = creation_int_transaction_query |> Repo.one() case res do %{init: init, created_contract_code: created_contract_code} -> @@ -3430,16 +3435,25 @@ defmodule Explorer.Chain do where(query, [coin_balance], coin_balance.block_number < ^block_number) end + # todo: replace `index_int_tx_desc_order` with `index_internal_transaction_desc_order` in the next clause when new frontend is bound to `index_internal_transaction_desc_order` property def page_internal_transaction(_, _, _ \\ %{index_int_tx_desc_order: false}) def page_internal_transaction(query, %PagingOptions{key: nil}, _), do: query + # todo: keep next clause for compatibility with frontend and remove when new frontend is bound to `index_internal_transaction_desc_order` property def page_internal_transaction(query, %PagingOptions{key: {block_number, transaction_index, index}}, %{ index_int_tx_desc_order: desc }) do - hardcoded_where_for_page_int_tx(query, block_number, transaction_index, index, desc) + hardcoded_where_for_page_internal_transaction(query, block_number, transaction_index, index, desc) + end + + def page_internal_transaction(query, %PagingOptions{key: {block_number, transaction_index, index}}, %{ + index_internal_transaction_desc_order: desc + }) do + hardcoded_where_for_page_internal_transaction(query, block_number, transaction_index, index, desc) end + # todo: keep next clause for compatibility with frontend and remove when new frontend is bound to `index_internal_transaction_desc_order` property def page_internal_transaction(query, %PagingOptions{key: {0}}, %{index_int_tx_desc_order: desc}) do if desc do query @@ -3448,6 +3462,15 @@ defmodule Explorer.Chain do end end + def page_internal_transaction(query, %PagingOptions{key: {0}}, %{index_internal_transaction_desc_order: desc}) do + if desc do + query + else + where(query, [internal_transaction], internal_transaction.index > 0) + end + end + + # todo: keep next clause for compatibility with frontend and remove when new frontend is bound to `index_internal_transaction_desc_order` property def page_internal_transaction(query, %PagingOptions{key: {index}}, %{index_int_tx_desc_order: desc}) do if desc do where(query, [internal_transaction], internal_transaction.index < ^index) @@ -3456,7 +3479,15 @@ defmodule Explorer.Chain do end end - defp hardcoded_where_for_page_int_tx(query, 0, 0, index, false), + def page_internal_transaction(query, %PagingOptions{key: {index}}, %{index_internal_transaction_desc_order: desc}) do + if desc do + where(query, [internal_transaction], internal_transaction.index < ^index) + else + where(query, [internal_transaction], internal_transaction.index > ^index) + end + end + + defp hardcoded_where_for_page_internal_transaction(query, 0, 0, index, false), do: where( query, @@ -3465,7 +3496,7 @@ defmodule Explorer.Chain do internal_transaction.transaction_index == 0 and internal_transaction.index > ^index ) - defp hardcoded_where_for_page_int_tx(query, block_number, 0, index, false), + defp hardcoded_where_for_page_internal_transaction(query, block_number, 0, index, false), do: where( query, @@ -3475,7 +3506,7 @@ defmodule Explorer.Chain do internal_transaction.transaction_index == 0 and internal_transaction.index > ^index) ) - defp hardcoded_where_for_page_int_tx(query, block_number, transaction_index, index, false), + defp hardcoded_where_for_page_internal_transaction(query, block_number, transaction_index, index, false), do: where( query, @@ -3487,7 +3518,7 @@ defmodule Explorer.Chain do internal_transaction.transaction_index == ^transaction_index and internal_transaction.index > ^index) ) - defp hardcoded_where_for_page_int_tx(query, 0, 0, index, true), + defp hardcoded_where_for_page_internal_transaction(query, 0, 0, index, true), do: where( query, @@ -3496,7 +3527,7 @@ defmodule Explorer.Chain do internal_transaction.transaction_index == 0 and internal_transaction.index < ^index ) - defp hardcoded_where_for_page_int_tx(query, block_number, 0, 0, true), + defp hardcoded_where_for_page_internal_transaction(query, block_number, 0, 0, true), do: where( query, @@ -3504,7 +3535,7 @@ defmodule Explorer.Chain do internal_transaction.block_number < ^block_number ) - defp hardcoded_where_for_page_int_tx(query, block_number, 0, index, true), + defp hardcoded_where_for_page_internal_transaction(query, block_number, 0, index, true), do: where( query, @@ -3514,7 +3545,7 @@ defmodule Explorer.Chain do internal_transaction.transaction_index == 0 and internal_transaction.index < ^index) ) - defp hardcoded_where_for_page_int_tx(query, block_number, transaction_index, 0, true), + defp hardcoded_where_for_page_internal_transaction(query, block_number, transaction_index, 0, true), do: where( query, @@ -3524,7 +3555,7 @@ defmodule Explorer.Chain do internal_transaction.transaction_index < ^transaction_index) ) - defp hardcoded_where_for_page_int_tx(query, block_number, transaction_index, index, true), + defp hardcoded_where_for_page_internal_transaction(query, block_number, transaction_index, index, true), do: where( query, @@ -4194,18 +4225,20 @@ defmodule Explorer.Chain do end end - # Here we fetch from DB one tx per one coin balance. It's much more faster than LEFT OUTER JOIN which was before. + # Here we fetch from DB one transaction per one coin balance. It's much more faster than LEFT OUTER JOIN which was before. defp preload_transactions(balances, options) do tasks = Enum.map(balances, fn balance -> Task.async(fn -> Transaction |> where( - [tx], - tx.block_number == ^balance.block_number and (tx.value > ^0 or (tx.gas_price > ^0 and tx.gas_used > ^0)) and - (tx.to_address_hash == ^balance.address_hash or tx.from_address_hash == ^balance.address_hash) + [transaction], + transaction.block_number == ^balance.block_number and + (transaction.value > ^0 or (transaction.gas_price > ^0 and transaction.gas_used > ^0)) and + (transaction.to_address_hash == ^balance.address_hash or + transaction.from_address_hash == ^balance.address_hash) ) - |> select([tx], tx.hash) + |> select([transaction], transaction.hash) |> limit(1) |> select_repo(options).one() end) @@ -4217,7 +4250,7 @@ defmodule Explorer.Chain do |> Enum.map(fn {{task, res}, balance} -> case res do {:ok, hash} -> - put_tx_hash(hash, balance) + put_transaction_hash(hash, balance) {:exit, _reason} -> balance @@ -4229,7 +4262,7 @@ defmodule Explorer.Chain do end) end - defp put_tx_hash(hash, coin_balance), + defp put_transaction_hash(hash, coin_balance), do: if(hash, do: %CoinBalance{coin_balance | transaction_hash: hash}, else: coin_balance) defp add_block_timestamp_to_balances( @@ -4840,7 +4873,7 @@ defmodule Explorer.Chain do def fetch_first_trace(transactions_params, json_rpc_named_arguments) do case EthereumJSONRPC.fetch_first_trace(transactions_params, json_rpc_named_arguments) do {:ok, [%{first_trace: first_trace, block_hash: block_hash, json_rpc_named_arguments: json_rpc_named_arguments}]} -> - format_tx_first_trace(first_trace, block_hash, json_rpc_named_arguments) + format_transaction_first_trace(first_trace, block_hash, json_rpc_named_arguments) {:error, error} -> {:error, error} @@ -4850,7 +4883,7 @@ defmodule Explorer.Chain do end end - defp format_tx_first_trace(first_trace, block_hash, json_rpc_named_arguments) do + defp format_transaction_first_trace(first_trace, block_hash, json_rpc_named_arguments) do {:ok, to_address_hash} = if Map.has_key?(first_trace, :to_address_hash) do Chain.string_to_address_hash(first_trace.to_address_hash) @@ -5089,7 +5122,7 @@ defmodule Explorer.Chain do if method_ids != [] do query - |> where([tx], fragment("SUBSTRING(? FOR 4)", tx.input) in ^method_ids) + |> where([transaction], fragment("SUBSTRING(? FOR 4)", transaction.input) in ^method_ids) else query end @@ -5120,49 +5153,49 @@ defmodule Explorer.Chain do end end - def apply_filter_by_tx_type_to_transactions(query, [_ | _] = filter) do - {dynamic, modified_query} = apply_filter_by_tx_type_to_transactions_inner(filter, query) + def apply_filter_by_type_to_transactions(query, [_ | _] = filter) do + {dynamic, modified_query} = apply_filter_by_type_to_transactions_inner(filter, query) modified_query |> where(^dynamic) end - def apply_filter_by_tx_type_to_transactions(query, _filter), do: query + def apply_filter_by_type_to_transactions(query, _filter), do: query - def apply_filter_by_tx_type_to_transactions_inner(dynamic \\ dynamic(false), filter, query) + def apply_filter_by_type_to_transactions_inner(dynamic \\ dynamic(false), filter, query) - def apply_filter_by_tx_type_to_transactions_inner(dynamic, [type | remain], query) do + def apply_filter_by_type_to_transactions_inner(dynamic, [type | remain], query) do case type do :contract_call -> dynamic |> filter_contract_call_dynamic() - |> apply_filter_by_tx_type_to_transactions_inner( + |> apply_filter_by_type_to_transactions_inner( remain, - join(query, :inner, [tx], address in assoc(tx, :to_address), as: :to_address) + join(query, :inner, [transaction], address in assoc(transaction, :to_address), as: :to_address) ) :contract_creation -> dynamic |> filter_contract_creation_dynamic() - |> apply_filter_by_tx_type_to_transactions_inner(remain, query) + |> apply_filter_by_type_to_transactions_inner(remain, query) :coin_transfer -> dynamic |> filter_transaction_dynamic() - |> apply_filter_by_tx_type_to_transactions_inner(remain, query) + |> apply_filter_by_type_to_transactions_inner(remain, query) :token_transfer -> dynamic |> filter_token_transfer_dynamic() - |> apply_filter_by_tx_type_to_transactions_inner(remain, query) + |> apply_filter_by_type_to_transactions_inner(remain, query) :token_creation -> dynamic |> filter_token_creation_dynamic() - |> apply_filter_by_tx_type_to_transactions_inner( + |> apply_filter_by_type_to_transactions_inner( remain, - join(query, :inner, [tx], token in Token, - on: token.contract_address_hash == tx.created_contract_address_hash, + join(query, :inner, [transaction], token in Token, + on: token.contract_address_hash == transaction.created_contract_address_hash, as: :created_token ) ) @@ -5170,43 +5203,43 @@ defmodule Explorer.Chain do :blob_transaction -> dynamic |> filter_blob_transaction_dynamic() - |> apply_filter_by_tx_type_to_transactions_inner(remain, query) + |> apply_filter_by_type_to_transactions_inner(remain, query) end end - def apply_filter_by_tx_type_to_transactions_inner(dynamic_query, _, query), do: {dynamic_query, query} + def apply_filter_by_type_to_transactions_inner(dynamic_query, _, query), do: {dynamic_query, query} def filter_contract_creation_dynamic(dynamic) do - dynamic([tx], ^dynamic or is_nil(tx.to_address_hash)) + dynamic([transaction], ^dynamic or is_nil(transaction.to_address_hash)) end def filter_transaction_dynamic(dynamic) do - dynamic([tx], ^dynamic or tx.value > ^0) + dynamic([transaction], ^dynamic or transaction.value > ^0) end def filter_contract_call_dynamic(dynamic) do - dynamic([tx, to_address: to_address], ^dynamic or not is_nil(to_address.contract_code)) + dynamic([transaction, to_address: to_address], ^dynamic or not is_nil(to_address.contract_code)) end def filter_token_transfer_dynamic(dynamic) do # TokenTransfer.__struct__.__meta__.source dynamic( - [tx], + [transaction], ^dynamic or fragment( "NOT (SELECT transaction_hash FROM token_transfers WHERE transaction_hash = ? LIMIT 1) IS NULL", - tx.hash + transaction.hash ) ) end def filter_token_creation_dynamic(dynamic) do - dynamic([tx, created_token: created_token], ^dynamic or not is_nil(created_token)) + dynamic([transaction, created_token: created_token], ^dynamic or not is_nil(created_token)) end def filter_blob_transaction_dynamic(dynamic) do # EIP-2718 blob transaction type - dynamic([tx], ^dynamic or tx.type == 3) + dynamic([transaction], ^dynamic or transaction.type == 3) end def count_verified_contracts do @@ -5237,11 +5270,14 @@ defmodule Explorer.Chain do def count_new_contracts do query = - from(tx in Transaction, - select: tx, + from(transaction in Transaction, + select: transaction, where: - tx.status == ^:ok and - fragment("NOW() - ? at time zone 'UTC' <= interval '24 hours'", tx.created_contract_code_indexed_at) + transaction.status == ^:ok and + fragment( + "NOW() - ? at time zone 'UTC' <= interval '24 hours'", + transaction.created_contract_code_indexed_at + ) ) query diff --git a/apps/explorer/lib/explorer/chain/address/counters.ex b/apps/explorer/lib/explorer/chain/address/counters.ex index 3fdb09acb1d1..5b873f0e7569 100644 --- a/apps/explorer/lib/explorer/chain/address/counters.ex +++ b/apps/explorer/lib/explorer/chain/address/counters.ex @@ -38,8 +38,8 @@ defmodule Explorer.Chain.Address.Counters do @typep counter :: non_neg_integer() | nil @counters_limit 51 - @types [:validations, :txs, :token_transfers, :token_balances, :logs, :withdrawals, :internal_txs] - @txs_types [:txs_from, :txs_to, :txs_contract] + @types [:validations, :transactions, :token_transfers, :token_balances, :logs, :withdrawals, :internal_transactions] + @transactions_types [:transactions_from, :transactions_to, :transactions_contract] defp address_hash_to_logs_query(address_hash) do from(l in Log, where: l.address_hash == ^address_hash) @@ -293,7 +293,7 @@ defmodule Explorer.Chain.Address.Counters do end end - defp address_hash_to_internal_txs_limited_count_query(address_hash) do + defp address_hash_to_internal_transactions_limited_count_query(address_hash) do query_to_address_hash_wrapped = InternalTransaction |> InternalTransaction.where_nonpending_block() @@ -400,7 +400,7 @@ defmodule Explorer.Chain.Address.Counters do ) transactions_from_count_task = - run_or_ignore(cached_counters[:txs], :txs_from, address_hash, fn -> + run_or_ignore(cached_counters[:transactions], :transactions_from, address_hash, fn -> result = Transaction |> where([t], t.from_address_hash == ^address_hash) @@ -414,14 +414,18 @@ defmodule Explorer.Chain.Address.Counters do Logger.info("Time consumed for transactions_from_count_task for #{address_hash} is #{diff}ms") - AddressesTabsCounters.save_txs_counter_progress(address_hash, %{txs_types: [:txs_from], txs_from: result}) - AddressesTabsCounters.drop_task(:txs_from, address_hash) + AddressesTabsCounters.save_transactions_counter_progress(address_hash, %{ + transactions_types: [:transactions_from], + transactions_from: result + }) + + AddressesTabsCounters.drop_task(:transactions_from, address_hash) - {:txs_from, result} + {:transactions_from, result} end) transactions_to_count_task = - run_or_ignore(cached_counters[:txs], :txs_to, address_hash, fn -> + run_or_ignore(cached_counters[:transactions], :transactions_to, address_hash, fn -> result = Transaction |> where([t], t.to_address_hash == ^address_hash) @@ -435,14 +439,18 @@ defmodule Explorer.Chain.Address.Counters do Logger.info("Time consumed for transactions_to_count_task for #{address_hash} is #{diff}ms") - AddressesTabsCounters.save_txs_counter_progress(address_hash, %{txs_types: [:txs_to], txs_to: result}) - AddressesTabsCounters.drop_task(:txs_to, address_hash) + AddressesTabsCounters.save_transactions_counter_progress(address_hash, %{ + transactions_types: [:transactions_to], + transactions_to: result + }) + + AddressesTabsCounters.drop_task(:transactions_to, address_hash) - {:txs_to, result} + {:transactions_to, result} end) transactions_created_contract_count_task = - run_or_ignore(cached_counters[:txs], :txs_contract, address_hash, fn -> + run_or_ignore(cached_counters[:transactions], :transactions_contract, address_hash, fn -> result = Transaction |> where([t], t.created_contract_address_hash == ^address_hash) @@ -456,14 +464,14 @@ defmodule Explorer.Chain.Address.Counters do Logger.info("Time consumed for transactions_created_contract_count_task for #{address_hash} is #{diff}ms") - AddressesTabsCounters.save_txs_counter_progress(address_hash, %{ - txs_types: [:txs_contract], - txs_contract: result + AddressesTabsCounters.save_transactions_counter_progress(address_hash, %{ + transactions_types: [:transactions_contract], + transactions_contract: result }) - AddressesTabsCounters.drop_task(:txs_contract, address_hash) + AddressesTabsCounters.drop_task(:transactions_contract, address_hash) - {:txs_contract, result} + {:transactions_contract, result} end) token_transfers_count_task = @@ -502,11 +510,11 @@ defmodule Explorer.Chain.Address.Counters do options ) - internal_txs_count_task = + internal_transactions_count_task = configure_task( - :internal_txs, + :internal_transactions, cached_counters, - address_hash_to_internal_txs_limited_count_query(address_hash), + address_hash_to_internal_transactions_limited_count_query(address_hash), address_hash, options ) @@ -534,44 +542,46 @@ defmodule Explorer.Chain.Address.Counters do token_balances_count_task, logs_count_task, withdrawals_count_task, - internal_txs_count_task, + internal_transactions_count_task, celo_election_rewards_count_task ] |> Enum.reject(&is_nil/1) |> Task.yield_many(:timer.seconds(1)) - |> Enum.reduce(Map.merge(prepare_cache_values(cached_counters), %{txs_types: [], txs_hashes: []}), fn {task, res}, - acc -> - case res do - {:ok, {txs_type, txs_hashes}} when txs_type in @txs_types -> - acc - |> (&Map.put(&1, :txs_types, [txs_type | &1[:txs_types]])).() - |> (&Map.put(&1, :txs_hashes, &1[:txs_hashes] ++ txs_hashes)).() - - {:ok, {type, counter}} -> - Map.put(acc, type, counter) - - {:exit, reason} -> - Logger.warning(fn -> - [ - "Query fetching address counters for #{address_hash} terminated: #{inspect(reason)}" - ] - end) - - acc - - nil -> - Logger.warning(fn -> - [ - "Query fetching address counters for #{address_hash} timed out." - ] - end) - - Task.ignore(task) - - acc + |> Enum.reduce( + Map.merge(prepare_cache_values(cached_counters), %{transactions_types: [], transactions_hashes: []}), + fn {task, res}, acc -> + case res do + {:ok, {transactions_type, transactions_hashes}} when transactions_type in @transactions_types -> + acc + |> (&Map.put(&1, :transactions_types, [transactions_type | &1[:transactions_types]])).() + |> (&Map.put(&1, :transactions_hashes, &1[:transactions_hashes] ++ transactions_hashes)).() + + {:ok, {type, counter}} -> + Map.put(acc, type, counter) + + {:exit, reason} -> + Logger.warning(fn -> + [ + "Query fetching address counters for #{address_hash} terminated: #{inspect(reason)}" + ] + end) + + acc + + nil -> + Logger.warning(fn -> + [ + "Query fetching address counters for #{address_hash} timed out." + ] + end) + + Task.ignore(task) + + acc + end end - end) - |> process_txs_counter() + ) + |> process_transactions_counter() map end @@ -608,17 +618,19 @@ defmodule Explorer.Chain.Address.Counters do end) end - defp process_txs_counter(%{txs_types: [_ | _] = txs_types, txs_hashes: hashes} = map) do + defp process_transactions_counter( + %{transactions_types: [_ | _] = transactions_types, transactions_hashes: hashes} = map + ) do counter = hashes |> Enum.uniq() |> Enum.count() |> min(@counters_limit) - if Enum.count(txs_types) == 3 || counter == @counters_limit do - map |> Map.put(:txs, counter) + if Enum.count(transactions_types) == 3 || counter == @counters_limit do + map |> Map.put(:transactions, counter) else map end end - defp process_txs_counter(map), do: map + defp process_transactions_counter(map), do: map defp prepare_cache_values(cached_counters) do Enum.reduce(cached_counters, %{}, fn @@ -633,8 +645,8 @@ defmodule Explorer.Chain.Address.Counters do @doc """ Returns all possible transactions type """ - @spec txs_types :: list(atom) - def txs_types, do: @txs_types + @spec transactions_types :: list(atom) + def transactions_types, do: @transactions_types @doc """ Returns max counter value diff --git a/apps/explorer/lib/explorer/chain/address/metadata_preloader.ex b/apps/explorer/lib/explorer/chain/address/metadata_preloader.ex index 2ee81f28438d..01595915310a 100644 --- a/apps/explorer/lib/explorer/chain/address/metadata_preloader.ex +++ b/apps/explorer/lib/explorer/chain/address/metadata_preloader.ex @@ -200,12 +200,12 @@ defmodule Explorer.Chain.Address.MetadataPreloader do to_address_hash: to_address_hash, created_contract_address_hash: created_contract_address_hash, from_address_hash: from_address_hash - } = tx, + } = transaction, names, field_to_put_info ) do token_transfers = - case tx.token_transfers do + case transaction.token_transfers do token_transfers_list when is_list(token_transfers_list) -> Enum.map(token_transfers_list, &put_meta_to_item(&1, names, field_to_put_info)) @@ -214,11 +214,11 @@ defmodule Explorer.Chain.Address.MetadataPreloader do end %Transaction{ - tx - | to_address: alter_address(tx.to_address, to_address_hash, names, field_to_put_info), + transaction + | to_address: alter_address(transaction.to_address, to_address_hash, names, field_to_put_info), created_contract_address: - alter_address(tx.created_contract_address, created_contract_address_hash, names, field_to_put_info), - from_address: alter_address(tx.from_address, from_address_hash, names, field_to_put_info), + alter_address(transaction.created_contract_address, created_contract_address_hash, names, field_to_put_info), + from_address: alter_address(transaction.from_address, from_address_hash, names, field_to_put_info), token_transfers: token_transfers } end @@ -243,16 +243,16 @@ defmodule Explorer.Chain.Address.MetadataPreloader do to_address_hash: to_address_hash, created_contract_address_hash: created_contract_address_hash, from_address_hash: from_address_hash - } = tx, + } = transaction, names, field_to_put_info ) do %InternalTransaction{ - tx - | to_address: alter_address(tx.to_address, to_address_hash, names, field_to_put_info), + transaction + | to_address: alter_address(transaction.to_address, to_address_hash, names, field_to_put_info), created_contract_address: - alter_address(tx.created_contract_address, created_contract_address_hash, names, field_to_put_info), - from_address: alter_address(tx.from_address, from_address_hash, names, field_to_put_info) + alter_address(transaction.created_contract_address, created_contract_address_hash, names, field_to_put_info), + from_address: alter_address(transaction.from_address, from_address_hash, names, field_to_put_info) } end diff --git a/apps/explorer/lib/explorer/chain/advanced_filter.ex b/apps/explorer/lib/explorer/chain/advanced_filter.ex index ec7b9305b868..b8b2d910ec50 100644 --- a/apps/explorer/lib/explorer/chain/advanced_filter.ex +++ b/apps/explorer/lib/explorer/chain/advanced_filter.ex @@ -54,7 +54,7 @@ defmodule Explorer.Chain.AdvancedFilter do field(:token_transfer_batch_index, :integer, null: true) end - @typep tx_types :: {:tx_types, [String.t()] | nil} + @typep transaction_types :: {:transaction_types, [String.t()] | nil} @typep methods :: {:methods, [String.t()] | nil} @typep age :: {:age, [{:from, DateTime.t() | nil} | {:to, DateTime.t() | nil}] | nil} @typep from_address_hashes :: {:from_address_hashes, [Hash.Address.t()] | nil} @@ -64,7 +64,7 @@ defmodule Explorer.Chain.AdvancedFilter do @typep token_contract_address_hashes :: {:token_contract_address_hashes, [{:include, [Hash.Address.t()]} | {:include, [Hash.Address.t()]}] | nil} @type options :: [ - tx_types() + transaction_types() | methods() | age() | from_address_hashes() @@ -122,14 +122,14 @@ defmodule Explorer.Chain.AdvancedFilter do end defp only_transactions?(options) do - transaction_types = options[:tx_types] + transaction_types = options[:transaction_types] tokens_to_include = options[:token_contract_address_hashes][:include] transaction_types == ["COIN_TRANSFER"] or tokens_to_include == ["native"] end defp only_token_transfers?(options) do - transaction_types = options[:tx_types] + transaction_types = options[:transaction_types] tokens_to_include = options[:token_contract_address_hashes][:include] tokens_to_exclude = options[:token_contract_address_hashes][:exclude] @@ -270,11 +270,14 @@ defmodule Explorer.Chain.AdvancedFilter do defp page_transactions(query, %PagingOptions{ key: %{ block_number: block_number, - transaction_index: tx_index + transaction_index: transaction_index } }) do dynamic_condition = - dynamic(^page_block_number_dynamic(:transaction, block_number) or ^page_tx_index_dynamic(block_number, tx_index)) + dynamic( + ^page_block_number_dynamic(:transaction, block_number) or + ^page_transaction_index_dynamic(block_number, transaction_index) + ) query |> where(^dynamic_condition) end @@ -309,17 +312,17 @@ defmodule Explorer.Chain.AdvancedFilter do defp page_internal_transactions(query, %PagingOptions{ key: %{ block_number: block_number, - transaction_index: tx_index, + transaction_index: transaction_index, internal_transaction_index: nil } }) do - case {block_number, tx_index} do + case {block_number, transaction_index} do {0, 0} -> - query |> where(as(:transaction).block_number == ^block_number and as(:transaction).index == ^tx_index) + query |> where(as(:transaction).block_number == ^block_number and as(:transaction).index == ^transaction_index) - {0, tx_index} -> + {0, transaction_index} -> query - |> where(as(:transaction).block_number == ^block_number and as(:transaction).index <= ^tx_index) + |> where(as(:transaction).block_number == ^block_number and as(:transaction).index <= ^transaction_index) {block_number, 0} -> query |> where(as(:transaction).block_number < ^block_number) @@ -328,7 +331,7 @@ defmodule Explorer.Chain.AdvancedFilter do query |> where( as(:transaction).block_number < ^block_number or - (as(:transaction).block_number == ^block_number and as(:transaction).index <= ^tx_index) + (as(:transaction).block_number == ^block_number and as(:transaction).index <= ^transaction_index) ) end end @@ -336,14 +339,15 @@ defmodule Explorer.Chain.AdvancedFilter do defp page_internal_transactions(query, %PagingOptions{ key: %{ block_number: block_number, - transaction_index: tx_index, + transaction_index: transaction_index, internal_transaction_index: it_index } }) do dynamic_condition = dynamic( - ^page_block_number_dynamic(:transaction, block_number) or ^page_tx_index_dynamic(block_number, tx_index) or - ^page_it_index_dynamic(block_number, tx_index, it_index) + ^page_block_number_dynamic(:transaction, block_number) or + ^page_transaction_index_dynamic(block_number, transaction_index) or + ^page_it_index_dynamic(block_number, transaction_index, it_index) ) query @@ -386,28 +390,31 @@ defmodule Explorer.Chain.AdvancedFilter do defp page_token_transfers(query, %PagingOptions{ key: %{ block_number: block_number, - transaction_index: tx_index, + transaction_index: transaction_index, token_transfer_index: nil, internal_transaction_index: nil } }) do - case {block_number, tx_index} do + case {block_number, transaction_index} do {0, 0} -> - query |> where(as(:transaction).block_number == ^block_number and as(:transaction).index == ^tx_index) + query |> where(as(:transaction).block_number == ^block_number and as(:transaction).index == ^transaction_index) - {0, tx_index} -> + {0, transaction_index} -> query - |> where([token_transfer], token_transfer.block_number == ^block_number and as(:transaction).index < ^tx_index) + |> where( + [token_transfer], + token_transfer.block_number == ^block_number and as(:transaction).index < ^transaction_index + ) {block_number, 0} -> query |> where([token_transfer], token_transfer.block_number < ^block_number) - {block_number, tx_index} -> + {block_number, transaction_index} -> query |> where( [token_transfer], token_transfer.block_number < ^block_number or - (token_transfer.block_number == ^block_number and as(:transaction).index <= ^tx_index) + (token_transfer.block_number == ^block_number and as(:transaction).index <= ^transaction_index) ) end end @@ -415,13 +422,14 @@ defmodule Explorer.Chain.AdvancedFilter do defp page_token_transfers(query, %PagingOptions{ key: %{ block_number: block_number, - transaction_index: tx_index, + transaction_index: transaction_index, token_transfer_index: nil } }) do dynamic_condition = dynamic( - ^page_block_number_dynamic(:token_transfer, block_number) or ^page_tx_index_dynamic(block_number, tx_index) + ^page_block_number_dynamic(:token_transfer, block_number) or + ^page_transaction_index_dynamic(block_number, transaction_index) ) query |> where(^dynamic_condition) @@ -463,18 +471,21 @@ defmodule Explorer.Chain.AdvancedFilter do dynamic(false) end - defp page_tx_index_dynamic(block_number, tx_index) when tx_index > 0 do - dynamic([transaction: tx], tx.block_number == ^block_number and tx.index < ^tx_index) + defp page_transaction_index_dynamic(block_number, transaction_index) when transaction_index > 0 do + dynamic( + [transaction: transaction], + transaction.block_number == ^block_number and transaction.index < ^transaction_index + ) end - defp page_tx_index_dynamic(_, _) do + defp page_transaction_index_dynamic(_, _) do dynamic(false) end - defp page_it_index_dynamic(block_number, tx_index, it_index) when it_index > 0 do + defp page_it_index_dynamic(block_number, transaction_index, it_index) when it_index > 0 do dynamic( - [transaction: tx, internal_transaction: it], - tx.block_number == ^block_number and tx.index == ^tx_index and + [transaction: transaction, internal_transaction: it], + transaction.block_number == ^block_number and transaction.index == ^transaction_index and it.index < ^it_index ) end @@ -520,7 +531,7 @@ defmodule Explorer.Chain.AdvancedFilter do defp apply_token_transfers_filters(query, options) do query - |> filter_by_tx_type(options[:tx_types]) + |> filter_by_transaction_type(options[:transaction_types]) |> filter_token_transfers_by_methods(options[:methods]) |> filter_by_token(options[:token_contract_address_hashes][:include], :include) |> filter_by_token(options[:token_contract_address_hashes][:exclude], :exclude) @@ -545,11 +556,11 @@ defmodule Explorer.Chain.AdvancedFilter do query |> where(not is_nil(as(:transaction).block_number) and not is_nil(as(:transaction).index)) end - defp filter_by_tx_type(query, [_ | _] = tx_types) do - query |> where([token_transfer], token_transfer.token_type in ^tx_types) + defp filter_by_transaction_type(query, [_ | _] = transaction_types) do + query |> where([token_transfer], token_transfer.token_type in ^transaction_types) end - defp filter_by_tx_type(query, _), do: query + defp filter_by_transaction_type(query, _), do: query defp filter_transactions_by_methods(query, [_ | _] = methods) do prepared_methods = prepare_methods(methods) diff --git a/apps/explorer/lib/explorer/chain/arbitrum/batch_transaction.ex b/apps/explorer/lib/explorer/chain/arbitrum/batch_transaction.ex index d1487ced2965..dca5ed99b716 100644 --- a/apps/explorer/lib/explorer/chain/arbitrum/batch_transaction.ex +++ b/apps/explorer/lib/explorer/chain/arbitrum/batch_transaction.ex @@ -14,21 +14,21 @@ defmodule Explorer.Chain.Arbitrum.BatchTransaction do alias Explorer.Chain.Arbitrum.L1Batch alias Explorer.Chain.{Hash, Transaction} - @required_attrs ~w(batch_number tx_hash)a + @required_attrs ~w(batch_number transaction_hash)a @typedoc """ Descriptor of the rollup transaction included in an Arbitrum batch: * `batch_number` - The number of the Arbitrum batch. - * `tx_hash` - The hash of the rollup transaction. + * `transaction_hash` - The hash of the rollup transaction. """ @type to_import :: %{ :batch_number => non_neg_integer(), - :tx_hash => binary() + :transaction_hash => binary() } @typedoc """ - * `tx_hash` - The hash of the rollup transaction. - * `l2_transaction` - An instance of `Explorer.Chain.Transaction` referenced by `tx_hash`. + * `transaction_hash` - The hash of the rollup transaction. + * `l2_transaction` - An instance of `Explorer.Chain.Transaction` referenced by `transaction_hash`. * `batch_number` - The number of the Arbitrum batch. * `batch` - An instance of `Explorer.Chain.Arbitrum.L1Batch` referenced by `batch_number`. """ @@ -37,7 +37,7 @@ defmodule Explorer.Chain.Arbitrum.BatchTransaction do belongs_to(:batch, L1Batch, foreign_key: :batch_number, references: :number, type: :integer) belongs_to(:l2_transaction, Transaction, - foreign_key: :tx_hash, + foreign_key: :transaction_hash, primary_key: true, references: :hash, type: Hash.Full @@ -56,6 +56,6 @@ defmodule Explorer.Chain.Arbitrum.BatchTransaction do |> validate_required(@required_attrs) |> foreign_key_constraint(:batch_number) |> foreign_key_constraint(:block_hash) - |> unique_constraint(:tx_hash) + |> unique_constraint(:transaction_hash) end end diff --git a/apps/explorer/lib/explorer/chain/arbitrum/da_multi_purpose_record.ex b/apps/explorer/lib/explorer/chain/arbitrum/da_multi_purpose_record.ex index 36409e66d4f9..c7ef42e98e60 100644 --- a/apps/explorer/lib/explorer/chain/arbitrum/da_multi_purpose_record.ex +++ b/apps/explorer/lib/explorer/chain/arbitrum/da_multi_purpose_record.ex @@ -84,22 +84,23 @@ defmodule Explorer.Chain.Arbitrum.DaMultiPurposeRecord.Helper do ## Parameters - `height`: The height of the block in the Celestia network. - - `tx_commitment`: The transaction commitment. + - `transaction_commitment`: The transaction commitment. ## Returns - A binary representing the calculated data key for the record containing Celestia blob data. """ @spec calculate_celestia_data_key(binary() | non_neg_integer(), binary() | Explorer.Chain.Hash.t()) :: binary() - def calculate_celestia_data_key(height, tx_commitment) when is_binary(height) do - calculate_celestia_data_key(String.to_integer(height), tx_commitment) + def calculate_celestia_data_key(height, transaction_commitment) when is_binary(height) do + calculate_celestia_data_key(String.to_integer(height), transaction_commitment) end - def calculate_celestia_data_key(height, %Hash{} = tx_commitment) when is_integer(height) do - calculate_celestia_data_key(height, tx_commitment.bytes) + def calculate_celestia_data_key(height, %Hash{} = transaction_commitment) when is_integer(height) do + calculate_celestia_data_key(height, transaction_commitment.bytes) end - def calculate_celestia_data_key(height, tx_commitment) when is_integer(height) and is_binary(tx_commitment) do - :crypto.hash(:sha256, :binary.encode_unsigned(height) <> tx_commitment) + def calculate_celestia_data_key(height, transaction_commitment) + when is_integer(height) and is_binary(transaction_commitment) do + :crypto.hash(:sha256, :binary.encode_unsigned(height) <> transaction_commitment) end end diff --git a/apps/explorer/lib/explorer/chain/arbitrum/reader.ex b/apps/explorer/lib/explorer/chain/arbitrum/reader.ex index abfc5b420a39..cdb7093611c5 100644 --- a/apps/explorer/lib/explorer/chain/arbitrum/reader.ex +++ b/apps/explorer/lib/explorer/chain/arbitrum/reader.ex @@ -23,10 +23,10 @@ defmodule Explorer.Chain.Arbitrum.Reader do # https://github.com/OffchainLabs/go-ethereum/blob/dff302de66598c36b964b971f72d35a95148e650/core/types/transaction.go#L44C2-L50 @message_to_l2_eth_deposit 100 - @message_to_l2_submit_retryable_tx 105 + @message_to_l2_submit_retryable_transaction 105 @to_l2_messages_transaction_types [ @message_to_l2_eth_deposit, - @message_to_l2_submit_retryable_tx + @message_to_l2_submit_retryable_transaction ] @doc """ @@ -112,7 +112,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do missed_messages_to_l2_query() |> order_by(desc: :block_number) |> limit(1) - |> select([rollup_tx], rollup_tx.block_number) + |> select([rollup_transaction], rollup_transaction.block_number) |> Repo.one(timeout: :infinity) end @@ -195,7 +195,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do Reads a list of L1 transactions by their hashes from the `arbitrum_lifecycle_l1_transactions` table and returns their IDs. ## Parameters - - `l1_tx_hashes`: A list of hashes to retrieve L1 transactions for. + - `l1_transaction_hashes`: A list of hashes to retrieve L1 transactions for. ## Returns - A list of tuples containing transaction hashes and IDs for the transaction @@ -203,12 +203,12 @@ defmodule Explorer.Chain.Arbitrum.Reader do list. """ @spec lifecycle_transaction_ids([binary()]) :: [{Hash.t(), non_neg_integer}] - def lifecycle_transaction_ids(l1_tx_hashes) when is_list(l1_tx_hashes) do + def lifecycle_transaction_ids(l1_transaction_hashes) when is_list(l1_transaction_hashes) do query = from( lt in LifecycleTransaction, select: {lt.hash, lt.id}, - where: lt.hash in ^l1_tx_hashes + where: lt.hash in ^l1_transaction_hashes ) Repo.all(query) @@ -218,7 +218,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do Reads a list of L1 transactions by their hashes from the `arbitrum_lifecycle_l1_transactions` table. ## Parameters - - `l1_tx_hashes`: A list of hashes to retrieve L1 transactions for. + - `l1_transaction_hashes`: A list of hashes to retrieve L1 transactions for. ## Returns - A list of `Explorer.Chain.Arbitrum.LifecycleTransaction` corresponding to the @@ -226,11 +226,11 @@ defmodule Explorer.Chain.Arbitrum.Reader do list. """ @spec lifecycle_transactions([binary()]) :: [LifecycleTransaction.t()] - def lifecycle_transactions(l1_tx_hashes) when is_list(l1_tx_hashes) do + def lifecycle_transactions(l1_transaction_hashes) when is_list(l1_transaction_hashes) do query = from( lt in LifecycleTransaction, - where: lt.hash in ^l1_tx_hashes + where: lt.hash in ^l1_transaction_hashes ) Repo.all(query) @@ -477,11 +477,11 @@ defmodule Explorer.Chain.Arbitrum.Reader do def l1_block_of_latest_execution do query = from( - tx in LifecycleTransaction, + transaction in LifecycleTransaction, inner_join: ex in L1Execution, - on: tx.id == ex.execution_id, - select: tx.block_number, - order_by: [desc: tx.block_number], + on: transaction.id == ex.execution_id, + select: transaction.block_number, + order_by: [desc: transaction.block_number], limit: 1 ) @@ -499,11 +499,11 @@ defmodule Explorer.Chain.Arbitrum.Reader do def l1_block_of_earliest_execution do query = from( - tx in LifecycleTransaction, + transaction in LifecycleTransaction, inner_join: ex in L1Execution, - on: tx.id == ex.execution_id, - select: tx.block_number, - order_by: [asc: tx.block_number], + on: transaction.id == ex.execution_id, + select: transaction.block_number, + order_by: [asc: transaction.block_number], limit: 1 ) @@ -672,11 +672,11 @@ defmodule Explorer.Chain.Arbitrum.Reader do main_query = from( subquery in subquery(confirmed_combined_ranges_query), - inner_join: tx_cur in LifecycleTransaction, - on: subquery.confirmation_id == tx_cur.id, - left_join: tx_prev in LifecycleTransaction, - on: subquery.prev_confirmation_id == tx_prev.id, - select: {tx_prev.block_number, tx_cur.block_number}, + inner_join: current_transaction in LifecycleTransaction, + on: subquery.confirmation_id == current_transaction.id, + left_join: previous_transaction in LifecycleTransaction, + on: subquery.prev_confirmation_id == previous_transaction.id, + select: {previous_transaction.block_number, current_transaction.block_number}, where: subquery.min_block_num - 1 != subquery.prev_max_number or is_nil(subquery.prev_max_number), order_by: [desc: subquery.min_block_num], limit: 1 @@ -826,9 +826,12 @@ defmodule Explorer.Chain.Arbitrum.Reader do @spec transactions_for_missed_messages_to_l2(non_neg_integer(), non_neg_integer()) :: [Hash.t()] def transactions_for_missed_messages_to_l2(start_block, end_block) do missed_messages_to_l2_query() - |> where([rollup_tx], rollup_tx.block_number >= ^start_block and rollup_tx.block_number <= ^end_block) + |> where( + [rollup_transaction], + rollup_transaction.block_number >= ^start_block and rollup_transaction.block_number <= ^end_block + ) |> order_by(desc: :block_timestamp) - |> select([rollup_tx], rollup_tx.hash) + |> select([rollup_transaction], rollup_transaction.hash) |> Repo.all() end @@ -844,10 +847,10 @@ defmodule Explorer.Chain.Arbitrum.Reader do # - A query to retrieve missed L1-to-L2 messages. @spec missed_messages_to_l2_query() :: Ecto.Query.t() defp missed_messages_to_l2_query do - from(rollup_tx in Transaction, + from(rollup_transaction in Transaction, left_join: msg in Message, - on: rollup_tx.hash == msg.completion_transaction_hash and msg.direction == :to_l2, - where: rollup_tx.type in @to_l2_messages_transaction_types and is_nil(msg.completion_transaction_hash) + on: rollup_transaction.hash == msg.completion_transaction_hash and msg.direction == :to_l2, + where: rollup_transaction.type in @to_l2_messages_transaction_types and is_nil(msg.completion_transaction_hash) ) end @@ -1052,7 +1055,7 @@ defmodule Explorer.Chain.Arbitrum.Reader do """ @spec batch_transactions(non_neg_integer() | binary(), api?: boolean()) :: [BatchTransaction.t()] def batch_transactions(batch_number, options) do - query = from(tx in BatchTransaction, where: tx.batch_number == ^batch_number) + query = from(transaction in BatchTransaction, where: transaction.batch_number == ^batch_number) select_repo(options).all(query) end diff --git a/apps/explorer/lib/explorer/chain/block.ex b/apps/explorer/lib/explorer/chain/block.ex index 60f60251cdb6..6cc8e049d39b 100644 --- a/apps/explorer/lib/explorer/chain/block.ex +++ b/apps/explorer/lib/explorer/chain/block.ex @@ -18,7 +18,7 @@ defmodule Explorer.Chain.Block.Schema do alias Explorer.Chain.Arbitrum.BatchBlock, as: ArbitrumBatchBlock alias Explorer.Chain.Block.{Reward, SecondDegreeRelation} alias Explorer.Chain.Celo.EpochReward, as: CeloEpochReward - alias Explorer.Chain.Optimism.TxnBatch, as: OptimismTxnBatch + alias Explorer.Chain.Optimism.TransactionBatch, as: OptimismTransactionBatch alias Explorer.Chain.ZkSync.BatchBlock, as: ZkSyncBatchBlock @chain_type_fields (case Application.compile_env(:explorer, :chain_type) do @@ -34,7 +34,7 @@ defmodule Explorer.Chain.Block.Schema do :optimism -> elem( quote do - has_one(:op_transaction_batch, OptimismTxnBatch, + has_one(:op_transaction_batch, OptimismTransactionBatch, foreign_key: :l2_block_number, references: :number ) diff --git a/apps/explorer/lib/explorer/chain/bridged_token.ex b/apps/explorer/lib/explorer/chain/bridged_token.ex index 64beb981c8de..8e5c62e81940 100644 --- a/apps/explorer/lib/explorer/chain/bridged_token.ex +++ b/apps/explorer/lib/explorer/chain/bridged_token.ex @@ -213,7 +213,7 @@ defmodule Explorer.Chain.BridgedToken do """ def fetch_omni_bridged_tokens_metadata(token_addresses) do Enum.each(token_addresses, fn token_address_hash -> - created_from_int_tx_success_query = + created_from_internal_transaction_success_query = from( it in InternalTransaction, inner_join: t in assoc(it, :transaction), @@ -221,42 +221,42 @@ defmodule Explorer.Chain.BridgedToken do where: t.status == ^1 ) - created_from_int_tx_success = - created_from_int_tx_success_query + created_from_internal_transaction_success = + created_from_internal_transaction_success_query |> limit(1) |> Repo.one() - created_from_tx_query = + created_from_transaction_query = from( t in Transaction, where: t.created_contract_address_hash == ^token_address_hash ) - created_from_tx = - created_from_tx_query + created_from_transaction = + created_from_transaction_query |> Repo.all() |> Enum.count() > 0 - created_from_int_tx_query = + created_from_internal_transaction_query = from( it in InternalTransaction, where: it.created_contract_address_hash == ^token_address_hash ) - created_from_int_tx = - created_from_int_tx_query + created_from_internal_transaction = + created_from_internal_transaction_query |> Repo.all() |> Enum.count() > 0 cond do - created_from_tx -> + created_from_transaction -> set_token_bridged_status(token_address_hash, false) - created_from_int_tx && !created_from_int_tx_success -> + created_from_internal_transaction && !created_from_internal_transaction_success -> set_token_bridged_status(token_address_hash, false) - created_from_int_tx && created_from_int_tx_success -> - proceed_with_set_omni_status(token_address_hash, created_from_int_tx_success) + created_from_internal_transaction && created_from_internal_transaction_success -> + proceed_with_set_omni_status(token_address_hash, created_from_internal_transaction_success) true -> :ok @@ -266,11 +266,11 @@ defmodule Explorer.Chain.BridgedToken do :ok end - defp proceed_with_set_omni_status(token_address_hash, created_from_int_tx_success) do + defp proceed_with_set_omni_status(token_address_hash, created_from_internal_transaction_success) do {:ok, eth_omni_status} = extract_omni_bridged_token_metadata_wrapper( token_address_hash, - created_from_int_tx_success, + created_from_internal_transaction_success, :eth_omni_bridge_mediator ) @@ -280,7 +280,7 @@ defmodule Explorer.Chain.BridgedToken do else extract_omni_bridged_token_metadata_wrapper( token_address_hash, - created_from_int_tx_success, + created_from_internal_transaction_success, :bsc_omni_bridge_mediator ) end @@ -291,7 +291,7 @@ defmodule Explorer.Chain.BridgedToken do else extract_omni_bridged_token_metadata_wrapper( token_address_hash, - created_from_int_tx_success, + created_from_internal_transaction_success, :poa_omni_bridge_mediator ) end @@ -301,9 +301,13 @@ defmodule Explorer.Chain.BridgedToken do end end - defp extract_omni_bridged_token_metadata_wrapper(token_address_hash, created_from_int_tx_success, mediator) do + defp extract_omni_bridged_token_metadata_wrapper( + token_address_hash, + created_from_internal_transaction_success, + mediator + ) do omni_bridge_mediator = Application.get_env(:explorer, __MODULE__)[mediator] - %{transaction_hash: transaction_hash} = created_from_int_tx_success + %{transaction_hash: transaction_hash} = created_from_internal_transaction_success if omni_bridge_mediator && omni_bridge_mediator !== "" do {:ok, omni_bridge_mediator_hash} = Chain.string_to_address_hash(omni_bridge_mediator) diff --git a/apps/explorer/lib/explorer/chain/cache/addresses_tabs_counters.ex b/apps/explorer/lib/explorer/chain/cache/addresses_tabs_counters.ex index 64801ca24fd4..e144f886902a 100644 --- a/apps/explorer/lib/explorer/chain/cache/addresses_tabs_counters.ex +++ b/apps/explorer/lib/explorer/chain/cache/addresses_tabs_counters.ex @@ -11,7 +11,14 @@ defmodule Explorer.Chain.Cache.AddressesTabsCounters do @cache_name :addresses_tabs_counters - @typep counter_type :: :validations | :txs | :token_transfers | :token_balances | :logs | :withdrawals | :internal_txs + @typep counter_type :: + :validations + | :transactions + | :token_transfers + | :token_balances + | :logs + | :withdrawals + | :internal_transactions @typep response_status :: :limit_value | :stale | :up_to_date @spec get_counter(counter_type, String.t()) :: {DateTime.t(), non_neg_integer(), response_status} | nil @@ -41,8 +48,8 @@ defmodule Explorer.Chain.Cache.AddressesTabsCounters do address_hash |> task_cache_key(counter_type) |> fetch_from_ets_cache(@cache_name, nil) end - def save_txs_counter_progress(address_hash, results) do - GenServer.cast(__MODULE__, {:set_txs_state, address_hash, results}) + def save_transactions_counter_progress(address_hash, results) do + GenServer.cast(__MODULE__, {:set_transactions_state, address_hash, results}) end def start_link(_) do @@ -63,21 +70,21 @@ defmodule Explorer.Chain.Cache.AddressesTabsCounters do end @impl true - def handle_cast({:set_txs_state, address_hash, %{txs_types: txs_types} = results}, state) do + def handle_cast({:set_transactions_state, address_hash, %{transactions_types: transactions_types} = results}, state) do address_hash = lowercased_string(address_hash) if ignored?(state[address_hash]) do {:noreply, state} else address_state = - txs_types - |> Enum.reduce(state[address_hash] || %{}, fn tx_type, acc -> - Map.put(acc, tx_type, results[tx_type]) + transactions_types + |> Enum.reduce(state[address_hash] || %{}, fn transaction_type, acc -> + Map.put(acc, transaction_type, results[transaction_type]) end) - |> (&Map.put(&1, :txs_types, (txs_types ++ (&1[:txs_types] || [])) |> Enum.uniq())).() + |> (&Map.put(&1, :transactions_types, (transactions_types ++ (&1[:transactions_types] || [])) |> Enum.uniq())).() counter = - Counters.txs_types() + Counters.transactions_types() |> Enum.reduce([], fn type, acc -> (address_state[type] || []) ++ acc end) @@ -86,12 +93,12 @@ defmodule Explorer.Chain.Cache.AddressesTabsCounters do |> min(Counters.counters_limit()) cond do - Enum.count(address_state[:txs_types]) == 3 -> - set_counter(:txs, address_hash, counter) + Enum.count(address_state[:transactions_types]) == 3 -> + set_counter(:transactions, address_hash, counter) {:noreply, Map.put(state, address_hash, nil)} counter == Counters.counters_limit() -> - set_counter(:txs, address_hash, counter) + set_counter(:transactions, address_hash, counter) {:noreply, Map.put(state, address_hash, :limit_value)} true -> diff --git a/apps/explorer/lib/explorer/chain/cache/state_changes.ex b/apps/explorer/lib/explorer/chain/cache/state_changes.ex index f11f48efedcb..5c9abc41f54e 100644 --- a/apps/explorer/lib/explorer/chain/cache/state_changes.ex +++ b/apps/explorer/lib/explorer/chain/cache/state_changes.ex @@ -21,8 +21,8 @@ defmodule Explorer.Chain.Cache.StateChanges do @type id :: Hash.t() - def element_to_id(%__MODULE__{transaction_hash: tx_hash}) do - tx_hash + def element_to_id(%__MODULE__{transaction_hash: transaction_hash}) do + transaction_hash end # in order to always keep just requested changes diff --git a/apps/explorer/lib/explorer/chain/cache/transaction_action_tokens_data.ex b/apps/explorer/lib/explorer/chain/cache/transaction_action_tokens_data.ex index 27fc043c33c9..976bfbae4a35 100644 --- a/apps/explorer/lib/explorer/chain/cache/transaction_action_tokens_data.ex +++ b/apps/explorer/lib/explorer/chain/cache/transaction_action_tokens_data.ex @@ -4,7 +4,7 @@ defmodule Explorer.Chain.Cache.TransactionActionTokensData do """ use GenServer - @cache_name :tx_actions_tokens_data_cache + @cache_name :transaction_actions_tokens_data_cache @spec start_link(term()) :: GenServer.on_start() def start_link(_) do diff --git a/apps/explorer/lib/explorer/chain/cache/transaction_action_uniswap_pools.ex b/apps/explorer/lib/explorer/chain/cache/transaction_action_uniswap_pools.ex index f254e95f382b..4e7719768f2f 100644 --- a/apps/explorer/lib/explorer/chain/cache/transaction_action_uniswap_pools.ex +++ b/apps/explorer/lib/explorer/chain/cache/transaction_action_uniswap_pools.ex @@ -4,7 +4,7 @@ defmodule Explorer.Chain.Cache.TransactionActionUniswapPools do """ use GenServer - @cache_name :tx_actions_uniswap_pools_cache + @cache_name :transaction_actions_uniswap_pools_cache @spec start_link(term()) :: GenServer.on_start() def start_link(_) do diff --git a/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex b/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex index f9676e18688d..d1dc032a06da 100644 --- a/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex +++ b/apps/explorer/lib/explorer/chain/csv_export/address_transaction_csv_exporter.ex @@ -60,8 +60,8 @@ defmodule Explorer.Chain.CSVExport.AddressTransactionCsvExporter do ] date_to_prices = - Enum.reduce(transactions_with_decoded_data, %{}, fn {_decoded_data, tx}, acc -> - date = tx |> Transaction.block_timestamp() |> DateTime.to_date() + Enum.reduce(transactions_with_decoded_data, %{}, fn {_decoded_data, transaction}, acc -> + date = transaction |> Transaction.block_timestamp() |> DateTime.to_date() if Map.has_key?(acc, date) do acc diff --git a/apps/explorer/lib/explorer/chain/fetcher/look_up_smart_contract_sources_on_demand.ex b/apps/explorer/lib/explorer/chain/fetcher/look_up_smart_contract_sources_on_demand.ex index 1394f8aa0fc6..d46b3f6d66f9 100644 --- a/apps/explorer/lib/explorer/chain/fetcher/look_up_smart_contract_sources_on_demand.ex +++ b/apps/explorer/lib/explorer/chain/fetcher/look_up_smart_contract_sources_on_demand.ex @@ -40,11 +40,11 @@ defmodule Explorer.Chain.Fetcher.LookUpSmartContractSourcesOnDemand do defp fetch_sources(address_hash_string, address_contract_code, only_full?) do Publisher.broadcast(%{eth_bytecode_db_lookup_started: [address_hash_string]}, :on_demand) - creation_tx_input = contract_creation_input(address_hash_string) + creation_transaction_input = contract_creation_input(address_hash_string) with {:ok, %{"sourceType" => type, "matchType" => match_type} = source} <- %{} - |> prepare_bytecode_for_microservice(creation_tx_input, Data.to_string(address_contract_code)) + |> prepare_bytecode_for_microservice(creation_transaction_input, Data.to_string(address_contract_code)) |> EthBytecodeDBInterface.search_contract(address_hash_string), :ok <- check_match_type(match_type, only_full?), {:ok, _} <- process_contract_source(type, source, address_hash_string) do diff --git a/apps/explorer/lib/explorer/chain/import/runner/arbitrum/batch_transactions.ex b/apps/explorer/lib/explorer/chain/import/runner/arbitrum/batch_transactions.ex index f4cda6473b6b..17cda3fb909a 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/arbitrum/batch_transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/arbitrum/batch_transactions.ex @@ -60,7 +60,7 @@ defmodule Explorer.Chain.Import.Runner.Arbitrum.BatchTransactions do | {:error, [Changeset.t()]} def insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = _options) when is_list(changes_list) do # Enforce Arbitrum.BatchTransaction ShareLocks order (see docs: sharelock.md) - ordered_changes_list = Enum.sort_by(changes_list, & &1.tx_hash) + ordered_changes_list = Enum.sort_by(changes_list, & &1.transaction_hash) {:ok, inserted} = Import.insert_changes_list( @@ -70,7 +70,7 @@ defmodule Explorer.Chain.Import.Runner.Arbitrum.BatchTransactions do returning: true, timeout: timeout, timestamps: timestamps, - conflict_target: :tx_hash, + conflict_target: :transaction_hash, on_conflict: :nothing ) diff --git a/apps/explorer/lib/explorer/chain/import/runner/arbitrum/lifecycle_transactions.ex b/apps/explorer/lib/explorer/chain/import/runner/arbitrum/lifecycle_transactions.ex index f5a2c07a3249..b6ab03b73cee 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/arbitrum/lifecycle_transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/arbitrum/lifecycle_transactions.ex @@ -83,24 +83,24 @@ defmodule Explorer.Chain.Import.Runner.Arbitrum.LifecycleTransactions do defp default_on_conflict do from( - tx in LifecycleTransaction, + transaction in LifecycleTransaction, update: [ set: [ # don't update `id` as it is a primary key # don't update `hash` as it is a unique index and used for the conflict target timestamp: fragment("EXCLUDED.timestamp"), block_number: fragment("EXCLUDED.block_number"), - status: fragment("GREATEST(?, EXCLUDED.status)", tx.status), - inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", tx.inserted_at), - updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", tx.updated_at) + status: fragment("GREATEST(?, EXCLUDED.status)", transaction.status), + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", transaction.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", transaction.updated_at) ] ], where: fragment( "(EXCLUDED.timestamp, EXCLUDED.block_number, EXCLUDED.status) IS DISTINCT FROM (?, ?, ?)", - tx.timestamp, - tx.block_number, - tx.status + transaction.timestamp, + transaction.block_number, + transaction.status ) ) end 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 da81e2df5109..55f67f35d19f 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex @@ -65,15 +65,15 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do :acquire_blocks ) end) - |> Multi.run(:acquire_pending_internal_txs, fn repo, %{acquire_blocks: block_hashes} -> + |> Multi.run(:acquire_pending_internal_transactions, fn repo, %{acquire_blocks: block_hashes} -> Instrumenter.block_import_stage_runner( - fn -> acquire_pending_internal_txs(repo, block_hashes) end, + fn -> acquire_pending_internal_transactions(repo, block_hashes) end, :block_pending, :internal_transactions, - :acquire_pending_internal_txs + :acquire_pending_internal_transactions ) end) - |> Multi.run(:acquire_transactions, fn repo, %{acquire_pending_internal_txs: pending_block_hashes} -> + |> Multi.run(:acquire_transactions, fn repo, %{acquire_pending_internal_transactions: pending_block_hashes} -> Instrumenter.block_import_stage_runner( fn -> acquire_transactions(repo, pending_block_hashes) end, :block_pending, @@ -171,7 +171,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do end) |> Multi.run(:update_pending_blocks_status, fn repo, %{ - acquire_pending_internal_txs: pending_block_hashes, + acquire_pending_internal_transactions: pending_block_hashes, set_refetch_needed_for_invalid_blocks: invalid_block_hashes } -> Instrumenter.block_import_stage_runner( @@ -309,7 +309,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do {:ok, repo.all(query)} end - defp acquire_pending_internal_txs(repo, block_hashes) do + defp acquire_pending_internal_transactions(repo, block_hashes) do query = from( pending_ops in PendingBlockOperation, @@ -340,8 +340,8 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do defp invalid_block_numbers(transactions, internal_transactions_params) do # Finds all mismatches between transactions and internal transactions # for a block number: - # - there are no internal txs for some transactions - # - there are internal txs with a different block number than their transactions + # - there are no internal transactions for some transactions + # - there are internal transactions with a different block number than their transactions # Returns block numbers where any of these issues is found # Note: the case "# - there are no transactions for some internal transactions" was removed because it caused the issue https://github.com/blockscout/blockscout/issues/3367 @@ -350,7 +350,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do # |> MapSet.difference(internal_transactions_tuples) should be replaced with |> MapSet.difference(common_tuples) # Note: for zetachain or if empty traces are explicitly allowed, - # the case "# - there are no internal txs for some transactions" is removed since + # the case "# - there are no internal transactions for some transactions" is removed since # there are may be non-traceable transactions transactions_tuples = MapSet.new(transactions, &{&1.hash, &1.block_number}) @@ -391,7 +391,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do else blocks_map = Map.new(transactions, &{&1.block_number, &1.block_hash}) - valid_internal_txs = + valid_internal_transactions = internal_transactions_params |> Enum.group_by(& &1.block_number) |> Map.drop(invalid_block_numbers) @@ -399,7 +399,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do compose_entry_wrapper(item, blocks_map) end) - {:ok, valid_internal_txs} + {:ok, valid_internal_transactions} end end @@ -484,8 +484,8 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do else params = valid_internal_transactions - |> Enum.filter(fn internal_tx -> - internal_tx[:index] == 0 + |> Enum.filter(fn internal_transaction -> + internal_transaction[:index] == 0 end) |> Enum.map(fn trace -> %{ @@ -602,12 +602,12 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do end end - defp get_trivial_tx_hashes_with_error_in_internal_tx(internal_transactions) do + defp get_trivial_transaction_hashes_with_error_in_internal_transaction(internal_transactions) do internal_transactions - |> Enum.filter(fn internal_tx -> - internal_tx[:index] != 0 && !is_nil(internal_tx[:error]) + |> Enum.filter(fn internal_transaction -> + internal_transaction[:index] != 0 && !is_nil(internal_transaction[:error]) end) - |> Enum.map(fn internal_tx -> internal_tx[:transaction_hash] end) + |> Enum.map(fn internal_transaction -> internal_transaction[:transaction_hash] end) |> MapSet.new() end @@ -650,7 +650,9 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do transaction_receipt_from_node \\ nil ) do valid_internal_transactions_count = Enum.count(valid_internal_transactions) - txs_with_error_in_internal_txs = get_trivial_tx_hashes_with_error_in_internal_tx(valid_internal_transactions) + + transactions_with_error_in_internal_transactions = + get_trivial_transaction_hashes_with_error_in_internal_transaction(valid_internal_transactions) set = generate_transaction_set_to_update( @@ -658,7 +660,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do transaction_from_db, transaction_receipt_from_node, timestamps, - txs_with_error_in_internal_txs + transactions_with_error_in_internal_transactions ) update_query = @@ -692,7 +694,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do transaction_from_db, transaction_receipt_from_node, timestamps, - txs_with_error_in_internal_txs + transactions_with_error_in_internal_transactions ) do default_set = [ created_contract_address_hash: first_trace.created_contract_address_hash, @@ -712,8 +714,11 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do transaction_receipt_from_node && transaction_receipt_from_node.cumulative_gas_used ) |> Keyword.put_new( - :has_error_in_internal_txs, - if(Enum.member?(txs_with_error_in_internal_txs, first_trace.transaction_hash), do: true, else: false) + :has_error_in_internal_transactions, + if(Enum.member?(transactions_with_error_in_internal_transactions, first_trace.transaction_hash), + do: true, + else: false + ) ) set_with_gas_used = @@ -794,7 +799,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do ) try do - # ShareLocks order already enforced by `acquire_pending_internal_txs` (see docs: sharelocks.md) + # ShareLocks order already enforced by `acquire_pending_internal_transactions` (see docs: sharelocks.md) {_count, deleted} = repo.delete_all(delete_query, []) {:ok, deleted} diff --git a/apps/explorer/lib/explorer/chain/import/runner/optimism/txn_batches.ex b/apps/explorer/lib/explorer/chain/import/runner/optimism/transaction_batches.ex similarity index 78% rename from apps/explorer/lib/explorer/chain/import/runner/optimism/txn_batches.ex rename to apps/explorer/lib/explorer/chain/import/runner/optimism/transaction_batches.ex index 0814b3439dd9..ae846119fd34 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/optimism/txn_batches.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/optimism/transaction_batches.ex @@ -1,13 +1,13 @@ -defmodule Explorer.Chain.Import.Runner.Optimism.TxnBatches do +defmodule Explorer.Chain.Import.Runner.Optimism.TransactionBatches do @moduledoc """ - Bulk imports `t:Explorer.Chain.Optimism.TxnBatch.t/0`. + Bulk imports `t:Explorer.Chain.Optimism.TransactionBatch.t/0`. """ require Ecto.Query alias Ecto.{Changeset, Multi, Repo} alias Explorer.Chain.Import - alias Explorer.Chain.Optimism.TxnBatch + alias Explorer.Chain.Optimism.TransactionBatch alias Explorer.Prometheus.Instrumenter import Ecto.Query, only: [from: 2] @@ -17,13 +17,13 @@ defmodule Explorer.Chain.Import.Runner.Optimism.TxnBatches do # milliseconds @timeout 60_000 - @type imported :: [TxnBatch.t()] + @type imported :: [TransactionBatch.t()] @impl Import.Runner - def ecto_schema_module, do: TxnBatch + def ecto_schema_module, do: TransactionBatch @impl Import.Runner - def option_key, do: :optimism_txn_batches + def option_key, do: :optimism_transaction_batches @impl Import.Runner def imported_table_row do @@ -42,12 +42,12 @@ defmodule Explorer.Chain.Import.Runner.Optimism.TxnBatches do |> Map.put_new(:timeout, @timeout) |> Map.put(:timestamps, timestamps) - Multi.run(multi, :insert_txn_batches, fn repo, _ -> + Multi.run(multi, :insert_transaction_batches, fn repo, _ -> Instrumenter.block_import_stage_runner( fn -> insert(repo, changes_list, insert_options) end, :block_referencing, - :optimism_txn_batches, - :optimism_txn_batches + :optimism_transaction_batches, + :optimism_transaction_batches ) end) end @@ -56,19 +56,19 @@ defmodule Explorer.Chain.Import.Runner.Optimism.TxnBatches do def timeout, do: @timeout @spec insert(Repo.t(), [map()], %{required(:timeout) => timeout(), required(:timestamps) => Import.timestamps()}) :: - {:ok, [TxnBatch.t()]} + {:ok, [TransactionBatch.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) - # Enforce TxnBatch ShareLocks order (see docs: sharelock.md) + # Enforce TransactionBatch ShareLocks order (see docs: sharelock.md) ordered_changes_list = Enum.sort_by(changes_list, & &1.l2_block_number) {:ok, inserted} = Import.insert_changes_list( repo, ordered_changes_list, - for: TxnBatch, + for: TransactionBatch, returning: true, timeout: timeout, timestamps: timestamps, @@ -81,7 +81,7 @@ defmodule Explorer.Chain.Import.Runner.Optimism.TxnBatches do defp default_on_conflict do from( - tb in TxnBatch, + tb in TransactionBatch, update: [ set: [ # don't update `l2_block_number` as it is a primary key and used for the conflict target diff --git a/apps/explorer/lib/explorer/chain/import/runner/polygon_zkevm/lifecycle_transactions.ex b/apps/explorer/lib/explorer/chain/import/runner/polygon_zkevm/lifecycle_transactions.ex index 3b12c4cd19d9..a7260a787c01 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/polygon_zkevm/lifecycle_transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/polygon_zkevm/lifecycle_transactions.ex @@ -83,20 +83,20 @@ defmodule Explorer.Chain.Import.Runner.PolygonZkevm.LifecycleTransactions do defp default_on_conflict do from( - tx in LifecycleTransaction, + transaction in LifecycleTransaction, update: [ set: [ # don't update `id` as it is a primary key # don't update `hash` as it is a unique index and used for the conflict target is_verify: fragment("EXCLUDED.is_verify"), - inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", tx.inserted_at), - updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", tx.updated_at) + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", transaction.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", transaction.updated_at) ] ], where: fragment( "(EXCLUDED.is_verify) IS DISTINCT FROM (?)", - tx.is_verify + transaction.is_verify ) ) end diff --git a/apps/explorer/lib/explorer/chain/import/runner/transactions.ex b/apps/explorer/lib/explorer/chain/import/runner/transactions.ex index aee467977f3e..83f7388548d2 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/transactions.ex @@ -244,7 +244,7 @@ defmodule Explorer.Chain.Import.Runner.Transactions do l1_fee_scalar: fragment("EXCLUDED.l1_fee_scalar"), l1_gas_price: fragment("EXCLUDED.l1_gas_price"), l1_gas_used: fragment("EXCLUDED.l1_gas_used"), - l1_tx_origin: fragment("EXCLUDED.l1_tx_origin"), + l1_transaction_origin: fragment("EXCLUDED.l1_transaction_origin"), l1_block_number: fragment("EXCLUDED.l1_block_number"), # Don't update `hash` as it is part of the primary key and used for the conflict target inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", transaction.inserted_at), @@ -253,7 +253,7 @@ defmodule Explorer.Chain.Import.Runner.Transactions do ], where: fragment( - "(EXCLUDED.block_hash, EXCLUDED.block_number, EXCLUDED.block_consensus, EXCLUDED.block_timestamp, EXCLUDED.created_contract_address_hash, EXCLUDED.created_contract_code_indexed_at, EXCLUDED.cumulative_gas_used, EXCLUDED.from_address_hash, EXCLUDED.gas, EXCLUDED.gas_price, EXCLUDED.gas_used, EXCLUDED.index, EXCLUDED.input, EXCLUDED.nonce, EXCLUDED.r, EXCLUDED.s, EXCLUDED.status, EXCLUDED.to_address_hash, EXCLUDED.v, EXCLUDED.value, EXCLUDED.earliest_processing_start, EXCLUDED.revert_reason, EXCLUDED.max_priority_fee_per_gas, EXCLUDED.max_fee_per_gas, EXCLUDED.type, EXCLUDED.l1_fee, EXCLUDED.l1_fee_scalar, EXCLUDED.l1_gas_price, EXCLUDED.l1_gas_used, EXCLUDED.l1_tx_origin, EXCLUDED.l1_block_number) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + "(EXCLUDED.block_hash, EXCLUDED.block_number, EXCLUDED.block_consensus, EXCLUDED.block_timestamp, EXCLUDED.created_contract_address_hash, EXCLUDED.created_contract_code_indexed_at, EXCLUDED.cumulative_gas_used, EXCLUDED.from_address_hash, EXCLUDED.gas, EXCLUDED.gas_price, EXCLUDED.gas_used, EXCLUDED.index, EXCLUDED.input, EXCLUDED.nonce, EXCLUDED.r, EXCLUDED.s, EXCLUDED.status, EXCLUDED.to_address_hash, EXCLUDED.v, EXCLUDED.value, EXCLUDED.earliest_processing_start, EXCLUDED.revert_reason, EXCLUDED.max_priority_fee_per_gas, EXCLUDED.max_fee_per_gas, EXCLUDED.type, EXCLUDED.l1_fee, EXCLUDED.l1_fee_scalar, EXCLUDED.l1_gas_price, EXCLUDED.l1_gas_used, EXCLUDED.l1_transaction_origin, EXCLUDED.l1_block_number) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", transaction.block_hash, transaction.block_number, transaction.block_consensus, @@ -283,7 +283,7 @@ defmodule Explorer.Chain.Import.Runner.Transactions do transaction.l1_fee_scalar, transaction.l1_gas_price, transaction.l1_gas_used, - transaction.l1_tx_origin, + transaction.l1_transaction_origin, transaction.l1_block_number ) ) diff --git a/apps/explorer/lib/explorer/chain/import/runner/zksync/batch_transactions.ex b/apps/explorer/lib/explorer/chain/import/runner/zksync/batch_transactions.ex index 39804aa0f97f..a7dca2298b86 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/zksync/batch_transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/zksync/batch_transactions.ex @@ -60,7 +60,7 @@ defmodule Explorer.Chain.Import.Runner.ZkSync.BatchTransactions do | {:error, [Changeset.t()]} def insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = _options) when is_list(changes_list) do # Enforce ZkSync.BatchTransaction ShareLocks order (see docs: sharelock.md) - ordered_changes_list = Enum.sort_by(changes_list, & &1.tx_hash) + ordered_changes_list = Enum.sort_by(changes_list, & &1.transaction_hash) {:ok, inserted} = Import.insert_changes_list( @@ -70,7 +70,7 @@ defmodule Explorer.Chain.Import.Runner.ZkSync.BatchTransactions do returning: true, timeout: timeout, timestamps: timestamps, - conflict_target: :tx_hash, + conflict_target: :transaction_hash, on_conflict: :nothing ) diff --git a/apps/explorer/lib/explorer/chain/import/runner/zksync/lifecycle_transactions.ex b/apps/explorer/lib/explorer/chain/import/runner/zksync/lifecycle_transactions.ex index b5b5e74ee89d..7011423601ee 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/zksync/lifecycle_transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/zksync/lifecycle_transactions.ex @@ -83,20 +83,20 @@ defmodule Explorer.Chain.Import.Runner.ZkSync.LifecycleTransactions do defp default_on_conflict do from( - tx in LifecycleTransaction, + transaction in LifecycleTransaction, update: [ set: [ # don't update `id` as it is a primary key # don't update `hash` as it is a unique index and used for the conflict target timestamp: fragment("EXCLUDED.timestamp"), - inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", tx.inserted_at), - updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", tx.updated_at) + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", transaction.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", transaction.updated_at) ] ], where: fragment( "(EXCLUDED.timestamp) IS DISTINCT FROM (?)", - tx.timestamp + transaction.timestamp ) ) end diff --git a/apps/explorer/lib/explorer/chain/import/runner/zksync/transaction_batches.ex b/apps/explorer/lib/explorer/chain/import/runner/zksync/transaction_batches.ex index 2c4639a43a63..11c85fff7601 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/zksync/transaction_batches.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/zksync/transaction_batches.ex @@ -88,8 +88,8 @@ defmodule Explorer.Chain.Import.Runner.ZkSync.TransactionBatches do set: [ # don't update `number` as it is a primary key and used for the conflict target timestamp: fragment("EXCLUDED.timestamp"), - l1_tx_count: fragment("EXCLUDED.l1_tx_count"), - l2_tx_count: fragment("EXCLUDED.l2_tx_count"), + l1_transaction_count: fragment("EXCLUDED.l1_transaction_count"), + l2_transaction_count: fragment("EXCLUDED.l2_transaction_count"), root_hash: fragment("EXCLUDED.root_hash"), l1_gas_price: fragment("EXCLUDED.l1_gas_price"), l2_fair_gas_price: fragment("EXCLUDED.l2_fair_gas_price"), @@ -104,10 +104,10 @@ defmodule Explorer.Chain.Import.Runner.ZkSync.TransactionBatches do ], where: fragment( - "(EXCLUDED.timestamp, EXCLUDED.l1_tx_count, EXCLUDED.l2_tx_count, EXCLUDED.root_hash, EXCLUDED.l1_gas_price, EXCLUDED.l2_fair_gas_price, EXCLUDED.start_block, EXCLUDED.end_block, EXCLUDED.commit_id, EXCLUDED.prove_id, EXCLUDED.execute_id) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + "(EXCLUDED.timestamp, EXCLUDED.l1_transaction_count, EXCLUDED.l2_transaction_count, EXCLUDED.root_hash, EXCLUDED.l1_gas_price, EXCLUDED.l2_fair_gas_price, EXCLUDED.start_block, EXCLUDED.end_block, EXCLUDED.commit_id, EXCLUDED.prove_id, EXCLUDED.execute_id) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", tb.timestamp, - tb.l1_tx_count, - tb.l2_tx_count, + tb.l1_transaction_count, + tb.l2_transaction_count, tb.root_hash, tb.l1_gas_price, tb.l2_fair_gas_price, diff --git a/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex b/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex index 621a712168b4..5f28a40b01bc 100644 --- a/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex +++ b/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex @@ -22,7 +22,7 @@ defmodule Explorer.Chain.Import.Stage.BlockReferencing do optimism: [ Runner.Optimism.FrameSequences, Runner.Optimism.FrameSequenceBlobs, - Runner.Optimism.TxnBatches, + Runner.Optimism.TransactionBatches, Runner.Optimism.OutputRoots, Runner.Optimism.DisputeGames, Runner.Optimism.Deposits, diff --git a/apps/explorer/lib/explorer/chain/log.ex b/apps/explorer/lib/explorer/chain/log.ex index 7ef35888444b..92018eb1bf74 100644 --- a/apps/explorer/lib/explorer/chain/log.ex +++ b/apps/explorer/lib/explorer/chain/log.ex @@ -394,9 +394,9 @@ defmodule Explorer.Chain.Log do |> Base.decode16!(case: :lower) end - def fetch_log_by_tx_hash_and_first_topic(tx_hash, first_topic, options \\ []) do + def fetch_log_by_transaction_hash_and_first_topic(transaction_hash, first_topic, options \\ []) do __MODULE__ - |> where([l], l.transaction_hash == ^tx_hash and l.first_topic == ^first_topic) + |> where([l], l.transaction_hash == ^transaction_hash and l.first_topic == ^first_topic) |> limit(1) |> Chain.select_repo(options).one() end diff --git a/apps/explorer/lib/explorer/chain/metrics/queries.ex b/apps/explorer/lib/explorer/chain/metrics/queries.ex index c68b829422b7..496b6ea4d4a0 100644 --- a/apps/explorer/lib/explorer/chain/metrics/queries.ex +++ b/apps/explorer/lib/explorer/chain/metrics/queries.ex @@ -35,17 +35,17 @@ defmodule Explorer.Chain.Metrics.Queries do def success_transactions_number_query do if DenormalizationHelper.transactions_denormalization_finished?() do Transaction - |> where([tx], tx.block_timestamp >= ago(^update_period_hours(), "hour")) - |> where([tx], tx.block_consensus == true) - |> where([tx], tx.status == ^1) - |> select([tx], count(tx.hash)) + |> where([transaction], transaction.block_timestamp >= ago(^update_period_hours(), "hour")) + |> where([transaction], transaction.block_consensus == true) + |> where([transaction], transaction.status == ^1) + |> select([transaction], count(transaction.hash)) else Transaction - |> join(:inner, [tx], block in assoc(tx, :block)) - |> where([tx, block], block.timestamp >= ago(^update_period_hours(), "hour")) - |> where([tx, block], block.consensus == true) - |> where([tx, block], tx.status == ^1) - |> select([tx, block], count(tx.hash)) + |> join(:inner, [transaction], block in assoc(transaction, :block)) + |> where([transaction, block], block.timestamp >= ago(^update_period_hours(), "hour")) + |> where([transaction, block], block.consensus == true) + |> where([transaction, block], transaction.status == ^1) + |> select([transaction, block], count(transaction.hash)) end end @@ -57,30 +57,30 @@ defmodule Explorer.Chain.Metrics.Queries do transactions_query = if DenormalizationHelper.transactions_denormalization_finished?() do Transaction - |> where([tx], not is_nil(tx.created_contract_address_hash)) - |> where([tx], tx.block_timestamp >= ago(^update_period_hours(), "hour")) - |> where([tx], tx.block_consensus == true) - |> where([tx], tx.status == ^1) - |> select([tx], tx.created_contract_address_hash) + |> where([transaction], not is_nil(transaction.created_contract_address_hash)) + |> where([transaction], transaction.block_timestamp >= ago(^update_period_hours(), "hour")) + |> where([transaction], transaction.block_consensus == true) + |> where([transaction], transaction.status == ^1) + |> select([transaction], transaction.created_contract_address_hash) else Transaction - |> join(:inner, [tx], block in assoc(tx, :block)) - |> where([tx], not is_nil(tx.created_contract_address_hash)) - |> where([tx, block], block.consensus == true) - |> where([tx, block], block.timestamp >= ago(^update_period_hours(), "hour")) - |> where([tx, block], tx.status == ^1) - |> select([tx, block], tx.created_contract_address_hash) + |> join(:inner, [transaction], block in assoc(transaction, :block)) + |> where([transaction], not is_nil(transaction.created_contract_address_hash)) + |> where([transaction, block], block.consensus == true) + |> where([transaction, block], block.timestamp >= ago(^update_period_hours(), "hour")) + |> where([transaction, block], transaction.status == ^1) + |> select([transaction, block], transaction.created_contract_address_hash) end # todo: this part is too slow, need to optimize # internal_transactions_query = # InternalTransaction # |> join(:inner, [it], transaction in assoc(it, :transaction)) - # |> where([it, tx], not is_nil(it.created_contract_address_hash)) - # |> where([it, tx], tx.block_timestamp >= ago(^update_period_hours(), "hour")) - # |> where([it, tx], tx.block_consensus == true) - # |> where([it, tx], tx.status == ^1) - # |> select([it, tx], it.created_contract_address_hash) + # |> where([it, transaction], not is_nil(it.created_contract_address_hash)) + # |> where([it, transaction], transaction.block_timestamp >= ago(^update_period_hours(), "hour")) + # |> where([it, transaction], transaction.block_consensus == true) + # |> where([it, transaction], transaction.status == ^1) + # |> select([it, transaction], it.created_contract_address_hash) # |> wrapped_union_subquery() # query = @@ -143,15 +143,15 @@ defmodule Explorer.Chain.Metrics.Queries do def simplified_active_addresses_number_query do if DenormalizationHelper.transactions_denormalization_finished?() do Transaction - |> where([tx], tx.block_timestamp >= ago(^update_period_hours(), "hour")) - |> where([tx], tx.block_consensus == true) - |> select([tx], fragment("COUNT(DISTINCT(?))", tx.from_address_hash)) + |> where([transaction], transaction.block_timestamp >= ago(^update_period_hours(), "hour")) + |> where([transaction], transaction.block_consensus == true) + |> select([transaction], fragment("COUNT(DISTINCT(?))", transaction.from_address_hash)) else Transaction - |> join(:inner, [tx], block in assoc(tx, :block)) - |> where([tx, block], block.timestamp >= ago(^update_period_hours(), "hour")) - |> where([tx, block], block.consensus == true) - |> select([tx], fragment("COUNT(DISTINCT(?))", tx.from_address_hash)) + |> join(:inner, [transaction], block in assoc(transaction, :block)) + |> where([transaction, block], block.timestamp >= ago(^update_period_hours(), "hour")) + |> where([transaction, block], block.consensus == true) + |> select([transaction], fragment("COUNT(DISTINCT(?))", transaction.from_address_hash)) end end @@ -164,31 +164,31 @@ defmodule Explorer.Chain.Metrics.Queries do transactions_query = if DenormalizationHelper.transactions_denormalization_finished?() do Transaction - |> where([tx], tx.block_timestamp >= ago(^update_period_hours(), "hour")) - |> where([tx], tx.block_consensus == true) + |> where([transaction], transaction.block_timestamp >= ago(^update_period_hours(), "hour")) + |> where([transaction], transaction.block_consensus == true) |> distinct(true) - |> select([tx], %{ + |> select([transaction], %{ address_hash: fragment( "UNNEST(ARRAY[?, ?, ?])", - tx.from_address_hash, - tx.to_address_hash, - tx.created_contract_address_hash + transaction.from_address_hash, + transaction.to_address_hash, + transaction.created_contract_address_hash ) }) else Transaction - |> join(:inner, [tx], block in assoc(tx, :block)) - |> where([tx, block], block.timestamp >= ago(^update_period_hours(), "hour")) - |> where([tx, block], block.consensus == true) + |> join(:inner, [transaction], block in assoc(transaction, :block)) + |> where([transaction, block], block.timestamp >= ago(^update_period_hours(), "hour")) + |> where([transaction, block], block.consensus == true) |> distinct(true) - |> select([tx, block], %{ + |> select([transaction, block], %{ address_hash: fragment( "UNNEST(ARRAY[?, ?, ?])", - tx.from_address_hash, - tx.to_address_hash, - tx.created_contract_address_hash + transaction.from_address_hash, + transaction.to_address_hash, + transaction.created_contract_address_hash ) }) end @@ -197,10 +197,10 @@ defmodule Explorer.Chain.Metrics.Queries do if DenormalizationHelper.transactions_denormalization_finished?() do InternalTransaction |> join(:inner, [it], transaction in assoc(it, :transaction)) - |> where([it, tx], tx.block_timestamp >= ago(^update_period_hours(), "hour")) - |> where([it, tx], tx.block_consensus == true) - |> where([it, tx], tx.status == ^1) - |> select([it, tx], %{ + |> where([it, transaction], transaction.block_timestamp >= ago(^update_period_hours(), "hour")) + |> where([it, transaction], transaction.block_consensus == true) + |> where([it, transaction], transaction.status == ^1) + |> select([it, transaction], %{ address_hash: fragment( "UNNEST(ARRAY[?, ?, ?])", @@ -213,11 +213,11 @@ defmodule Explorer.Chain.Metrics.Queries do else InternalTransaction |> join(:inner, [it], transaction in assoc(it, :transaction)) - |> join(:inner, [tx], block in assoc(tx, :block)) - |> where([it, tx, block], tx.block_timestamp >= ago(^update_period_hours(), "hour")) - |> where([it, tx, block], block.consensus == true) - |> where([it, tx, block], tx.status == ^1) - |> select([it, tx, block], %{ + |> join(:inner, [transaction], block in assoc(transaction, :block)) + |> where([it, transaction, block], transaction.block_timestamp >= ago(^update_period_hours(), "hour")) + |> where([it, transaction, block], block.consensus == true) + |> where([it, transaction, block], transaction.status == ^1) + |> select([it, transaction, block], %{ address_hash: fragment( "UNNEST(ARRAY[?, ?, ?])", @@ -233,10 +233,10 @@ defmodule Explorer.Chain.Metrics.Queries do if DenormalizationHelper.transactions_denormalization_finished?() do TokenTransfer |> join(:inner, [tt], transaction in assoc(tt, :transaction)) - |> where([tt, tx], tx.block_timestamp >= ago(^update_period_hours(), "hour")) - |> where([tt, tx], tx.block_consensus == true) - |> where([tt, tx], tx.status == ^1) - |> select([tt, tx], %{ + |> where([tt, transaction], transaction.block_timestamp >= ago(^update_period_hours(), "hour")) + |> where([tt, transaction], transaction.block_consensus == true) + |> where([tt, transaction], transaction.status == ^1) + |> select([tt, transaction], %{ address_hash: fragment("UNNEST(ARRAY[?, ?, ?])", tt.from_address_hash, tt.to_address_hash, tt.token_contract_address_hash) }) @@ -244,11 +244,11 @@ defmodule Explorer.Chain.Metrics.Queries do else TokenTransfer |> join(:inner, [tt], transaction in assoc(tt, :transaction)) - |> join(:inner, [tx], block in assoc(tx, :block)) - |> where([tt, tx, block], tx.block_timestamp >= ago(^update_period_hours(), "hour")) - |> where([tt, tx, block], block.consensus == true) - |> where([tt, tx, block], tx.status == ^1) - |> select([tt, tx, block], %{ + |> join(:inner, [transaction], block in assoc(transaction, :block)) + |> where([tt, transaction, block], transaction.block_timestamp >= ago(^update_period_hours(), "hour")) + |> where([tt, transaction, block], block.consensus == true) + |> where([tt, transaction, block], transaction.status == ^1) + |> select([tt, transaction, block], %{ address_hash: fragment("UNNEST(ARRAY[?, ?, ?])", tt.from_address_hash, tt.to_address_hash, tt.token_contract_address_hash) }) diff --git a/apps/explorer/lib/explorer/chain/optimism/deposit.ex b/apps/explorer/lib/explorer/chain/optimism/deposit.ex index 48c6dc32237c..36543fb59d84 100644 --- a/apps/explorer/lib/explorer/chain/optimism/deposit.ex +++ b/apps/explorer/lib/explorer/chain/optimism/deposit.ex @@ -61,7 +61,7 @@ defmodule Explorer.Chain.Optimism.Deposit do paging_options = Keyword.get(options, :paging_options, default_paging_options()) case paging_options do - %PagingOptions{key: {0, _l2_tx_hash}} -> + %PagingOptions{key: {0, _l2_transaction_hash}} -> [] _ -> @@ -80,10 +80,10 @@ defmodule Explorer.Chain.Optimism.Deposit do defp page_deposits(query, %PagingOptions{key: nil}), do: query - defp page_deposits(query, %PagingOptions{key: {block_number, l2_tx_hash}}) do + defp page_deposits(query, %PagingOptions{key: {block_number, l2_transaction_hash}}) do from(d in query, where: d.l1_block_number < ^block_number, - or_where: d.l1_block_number == ^block_number and d.l2_transaction_hash < ^l2_tx_hash + or_where: d.l1_block_number == ^block_number and d.l2_transaction_hash < ^l2_transaction_hash ) end end diff --git a/apps/explorer/lib/explorer/chain/optimism/frame_sequence.ex b/apps/explorer/lib/explorer/chain/optimism/frame_sequence.ex index 8a5788e40da4..d78646c5a177 100644 --- a/apps/explorer/lib/explorer/chain/optimism/frame_sequence.ex +++ b/apps/explorer/lib/explorer/chain/optimism/frame_sequence.ex @@ -16,7 +16,7 @@ defmodule Explorer.Chain.Optimism.FrameSequence do import Explorer.Chain, only: [default_paging_options: 0, select_repo: 1] alias Explorer.Chain.{Hash, Transaction} - alias Explorer.Chain.Optimism.{FrameSequenceBlob, TxnBatch} + alias Explorer.Chain.Optimism.{FrameSequenceBlob, TransactionBatch} alias Explorer.PagingOptions @required_attrs ~w(id l1_transaction_hashes l1_timestamp)a @@ -25,7 +25,7 @@ defmodule Explorer.Chain.Optimism.FrameSequence do * `l1_transaction_hashes` - The list of L1 transaction hashes where the frame sequence is stored. * `l1_timestamp` - UTC timestamp of the last L1 transaction of `l1_transaction_hashes` list. * `view_ready` - Boolean flag indicating if the frame sequence is ready for displaying on UI. - * `transaction_batches` - Instances of `Explorer.Chain.Optimism.TxnBatch` bound with this frame sequence. + * `transaction_batches` - Instances of `Explorer.Chain.Optimism.TransactionBatch` bound with this frame sequence. * `blobs` - Instances of `Explorer.Chain.Optimism.FrameSequenceBlob` bound with this frame sequence. """ @primary_key {:id, :integer, autogenerate: false} @@ -34,7 +34,7 @@ defmodule Explorer.Chain.Optimism.FrameSequence do field(:l1_timestamp, :utc_datetime_usec) field(:view_ready, :boolean) - has_many(:transaction_batches, TxnBatch, foreign_key: :frame_sequence_id) + has_many(:transaction_batches, TransactionBatch, foreign_key: :frame_sequence_id) has_many(:blobs, FrameSequenceBlob, foreign_key: :frame_sequence_id) timestamps() @@ -104,9 +104,9 @@ defmodule Explorer.Chain.Optimism.FrameSequence do batch = select_repo(options).one(query) if not is_nil(batch) do - l2_block_number_from = TxnBatch.edge_l2_block_number(internal_id, :min) - l2_block_number_to = TxnBatch.edge_l2_block_number(internal_id, :max) - tx_count = Transaction.tx_count_for_block_range(l2_block_number_from..l2_block_number_to) + l2_block_number_from = TransactionBatch.edge_l2_block_number(internal_id, :min) + l2_block_number_to = TransactionBatch.edge_l2_block_number(internal_id, :max) + transaction_count = Transaction.transaction_count_for_block_range(l2_block_number_from..l2_block_number_to) {batch_data_container, blobs} = FrameSequenceBlob.list(internal_id, options) @@ -115,8 +115,10 @@ defmodule Explorer.Chain.Optimism.FrameSequence do "l1_timestamp" => batch.l1_timestamp, "l2_block_start" => l2_block_number_from, "l2_block_end" => l2_block_number_to, - "tx_count" => tx_count, - "l1_tx_hashes" => batch.l1_transaction_hashes, + "transaction_count" => transaction_count, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_count` property + "tx_count" => transaction_count, + "l1_transaction_hashes" => batch.l1_transaction_hashes, "batch_data_container" => batch_data_container } diff --git a/apps/explorer/lib/explorer/chain/optimism/txn_batch.ex b/apps/explorer/lib/explorer/chain/optimism/transaction_batch.ex similarity index 95% rename from apps/explorer/lib/explorer/chain/optimism/txn_batch.ex rename to apps/explorer/lib/explorer/chain/optimism/transaction_batch.ex index a94ef6d3c98b..2ce8032da299 100644 --- a/apps/explorer/lib/explorer/chain/optimism/txn_batch.ex +++ b/apps/explorer/lib/explorer/chain/optimism/transaction_batch.ex @@ -1,9 +1,9 @@ -defmodule Explorer.Chain.Optimism.TxnBatch do +defmodule Explorer.Chain.Optimism.TransactionBatch do @moduledoc """ Models a batch of transactions for Optimism. Changes in the schema should be reflected in the bulk import module: - - Explorer.Chain.Import.Runner.Optimism.TxnBatches + - Explorer.Chain.Import.Runner.Optimism.TransactionBatches Migrations: - Explorer.Repo.Migrations.AddOpTransactionBatchesTable @@ -96,7 +96,7 @@ defmodule Explorer.Chain.Optimism.TxnBatch do end @doc """ - Lists `t:Explorer.Chain.Optimism.TxnBatch.t/0`'s' in descending order based on l2_block_number. + Lists `t:Explorer.Chain.Optimism.TransactionBatch.t/0`'s' in descending order based on l2_block_number. ## Parameters - `options`: A keyword list of options that may include whether to use a replica database, @@ -131,7 +131,7 @@ defmodule Explorer.Chain.Optimism.TxnBatch do base_query |> join_association(:frame_sequence, :required) - |> page_txn_batches(paging_options) + |> page_transaction_batches(paging_options) |> limit(^paging_options.page_size) |> select_repo(options).all() end @@ -275,9 +275,9 @@ defmodule Explorer.Chain.Optimism.TxnBatch do bytes_before <> <> <> bytes_after end - defp page_txn_batches(query, %PagingOptions{key: nil}), do: query + defp page_transaction_batches(query, %PagingOptions{key: nil}), do: query - defp page_txn_batches(query, %PagingOptions{key: {block_number}}) do + defp page_transaction_batches(query, %PagingOptions{key: {block_number}}) do from(tb in query, where: tb.l2_block_number < ^block_number) end end diff --git a/apps/explorer/lib/explorer/chain/optimism/withdrawal.ex b/apps/explorer/lib/explorer/chain/optimism/withdrawal.ex index d397329c5844..011273f092fc 100644 --- a/apps/explorer/lib/explorer/chain/optimism/withdrawal.ex +++ b/apps/explorer/lib/explorer/chain/optimism/withdrawal.ex @@ -64,8 +64,8 @@ defmodule Explorer.Chain.Optimism.Withdrawal do base_query = from(w in __MODULE__, order_by: [desc: w.msg_nonce], - left_join: l2_tx in Transaction, - on: w.l2_transaction_hash == l2_tx.hash, + left_join: l2_transaction in Transaction, + on: w.l2_transaction_hash == l2_transaction.hash, left_join: l2_block in Block, on: w.l2_block_number == l2_block.number, left_join: we in WithdrawalEvent, @@ -77,7 +77,7 @@ defmodule Explorer.Chain.Optimism.Withdrawal do l2_timestamp: l2_block.timestamp, l2_transaction_hash: w.l2_transaction_hash, l1_transaction_hash: we.l1_transaction_hash, - from: l2_tx.from_address_hash + from: l2_transaction.from_address_hash } ) diff --git a/apps/explorer/lib/explorer/chain/polygon_zkevm/lifecycle_transaction.ex b/apps/explorer/lib/explorer/chain/polygon_zkevm/lifecycle_transaction.ex index 504cdce41663..e1ec0a7abc30 100644 --- a/apps/explorer/lib/explorer/chain/polygon_zkevm/lifecycle_transaction.ex +++ b/apps/explorer/lib/explorer/chain/polygon_zkevm/lifecycle_transaction.ex @@ -24,8 +24,8 @@ defmodule Explorer.Chain.PolygonZkevm.LifecycleTransaction do Validates that the `attrs` are valid. """ @spec changeset(Ecto.Schema.t(), map()) :: Ecto.Schema.t() - def changeset(%__MODULE__{} = txn, attrs \\ %{}) do - txn + def changeset(%__MODULE__{} = transaction, attrs \\ %{}) do + transaction |> cast(attrs, @required_attrs) |> validate_required(@required_attrs) |> unique_constraint(:id) diff --git a/apps/explorer/lib/explorer/chain/polygon_zkevm/reader.ex b/apps/explorer/lib/explorer/chain/polygon_zkevm/reader.ex index 4f71a2c3644b..e34301b83ab9 100644 --- a/apps/explorer/lib/explorer/chain/polygon_zkevm/reader.ex +++ b/apps/explorer/lib/explorer/chain/polygon_zkevm/reader.ex @@ -190,12 +190,12 @@ defmodule Explorer.Chain.PolygonZkevm.Reader do Reads a list of L1 transactions by their hashes from `polygon_zkevm_lifecycle_l1_transactions` table. """ @spec lifecycle_transactions(list()) :: list() - def lifecycle_transactions(l1_tx_hashes) do + def lifecycle_transactions(l1_transaction_hashes) do query = from( lt in LifecycleTransaction, select: {lt.hash, lt.id}, - where: lt.hash in ^l1_tx_hashes + where: lt.hash in ^l1_transaction_hashes ) Repo.all(query, timeout: :infinity) diff --git a/apps/explorer/lib/explorer/chain/search.ex b/apps/explorer/lib/explorer/chain/search.ex index 5731f94cd889..20d0d35225fc 100644 --- a/apps/explorer/lib/explorer/chain/search.ex +++ b/apps/explorer/lib/explorer/chain/search.ex @@ -104,30 +104,30 @@ defmodule Explorer.Chain.Search do |> union(^address_query) valid_full_hash?(string) -> - tx_query = search_tx_query(string) + transaction_query = search_transaction_query(string) - tx_block_query = + transaction_block_query = basic_query - |> union(^tx_query) + |> union(^transaction_query) |> union(^block_query) - tx_block_op_query = + transaction_block_op_query = if UserOperation.enabled?() do user_operation_query = search_user_operation_query(string) - tx_block_query + transaction_block_query |> union(^user_operation_query) else - tx_block_query + transaction_block_query end if Application.get_env(:explorer, :chain_type) == :ethereum do blob_query = search_blob_query(string) - tx_block_op_query + transaction_block_op_query |> union(^blob_query) else - tx_block_op_query + transaction_block_op_query end block_query -> @@ -190,10 +190,10 @@ defmodule Explorer.Chain.Search do |> limit(^paging_options.page_size) |> select_repo(options).all() - tx_result = + transaction_result = if valid_full_hash?(search_query) do search_query - |> search_tx_query() + |> search_transaction_query() |> select_repo(options).all() else [] @@ -242,7 +242,7 @@ defmodule Explorer.Chain.Search do tokens_result, contracts_result, labels_result, - tx_result, + transaction_result, op_result, blob_result, address_result, @@ -410,16 +410,16 @@ defmodule Explorer.Chain.Search do defp valid_full_hash?(string_input) do case Chain.string_to_transaction_hash(string_input) do - {:ok, _tx_hash} -> true + {:ok, _transaction_hash} -> true _ -> false end end - defp search_tx_query(term) do + defp search_transaction_query(term) do if DenormalizationHelper.transactions_denormalization_finished?() do transaction_search_fields = search_fields() - |> Map.put(:tx_hash, dynamic([transaction], transaction.hash)) + |> Map.put(:transaction_hash, dynamic([transaction], transaction.hash)) |> Map.put(:block_hash, dynamic([transaction], transaction.block_hash)) |> Map.put(:type, "transaction") |> Map.put(:block_number, dynamic([transaction], transaction.block_number)) @@ -433,7 +433,7 @@ defmodule Explorer.Chain.Search do else transaction_search_fields = search_fields() - |> Map.put(:tx_hash, dynamic([transaction, _], transaction.hash)) + |> Map.put(:transaction_hash, dynamic([transaction, _], transaction.hash)) |> Map.put(:block_hash, dynamic([transaction, _], transaction.block_hash)) |> Map.put(:type, "transaction") |> Map.put(:block_number, dynamic([transaction, _], transaction.block_number)) @@ -513,7 +513,7 @@ defmodule Explorer.Chain.Search do defp page_search_results(query, %PagingOptions{key: nil}), do: query defp page_search_results(query, %PagingOptions{ - key: {_address_hash, _tx_hash, _block_hash, holder_count, name, inserted_at, item_type} + key: {_address_hash, _transaction_hash, _block_hash, holder_count, name, inserted_at, item_type} }) when holder_count in [nil, ""] do where( @@ -528,7 +528,7 @@ defmodule Explorer.Chain.Search do # credo:disable-for-next-line defp page_search_results(query, %PagingOptions{ - key: {_address_hash, _tx_hash, _block_hash, holder_count, name, inserted_at, item_type} + key: {_address_hash, _transaction_hash, _block_hash, holder_count, name, inserted_at, item_type} }) do where( query, @@ -592,7 +592,7 @@ defmodule Explorer.Chain.Search do end end - # For some reasons timestamp for blocks and txs returns as ~N[2023-06-25 19:39:47.339493] + # For some reasons timestamp for blocks and transactions returns as ~N[2023-06-25 19:39:47.339493] defp format_timestamp(result) do if result.timestamp do result @@ -653,7 +653,7 @@ defmodule Explorer.Chain.Search do defp search_fields do %{ address_hash: dynamic([_], type(^nil, :binary)), - tx_hash: dynamic([_], type(^nil, :binary)), + transaction_hash: dynamic([_], type(^nil, :binary)), user_operation_hash: dynamic([_], type(^nil, :binary)), blob_hash: dynamic([_], type(^nil, :binary)), block_hash: dynamic([_], type(^nil, :binary)), diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index c79d2fe278b8..2da936337bfa 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -566,30 +566,32 @@ defmodule Explorer.Chain.SmartContract do @doc """ Extracts creation bytecode (`init`) and transaction (`tx`) or - internal transaction (`internal_tx`) where the contract was created. + internal transaction (`internal_transaction`) where the contract was created. """ - @spec creation_tx_with_bytecode(binary() | Hash.t()) :: - %{init: binary(), tx: Transaction.t()} | %{init: binary(), internal_tx: InternalTransaction.t()} | nil - def creation_tx_with_bytecode(address_hash) do - creation_tx_query = + @spec creation_transaction_with_bytecode(binary() | Hash.t()) :: + %{init: binary(), transaction: Transaction.t()} + | %{init: binary(), internal_transaction: InternalTransaction.t()} + | nil + def creation_transaction_with_bytecode(address_hash) do + creation_transaction_query = from( - tx in Transaction, - where: tx.created_contract_address_hash == ^address_hash, - where: tx.status == ^1, - order_by: [desc: tx.block_number], + transaction in Transaction, + where: transaction.created_contract_address_hash == ^address_hash, + where: transaction.status == ^1, + order_by: [desc: transaction.block_number], limit: ^1 ) - tx = - creation_tx_query + transaction = + creation_transaction_query |> Repo.one() - if tx do - with %{input: input} <- tx do - %{init: Data.to_string(input), tx: tx} + if transaction do + with %{input: input} <- transaction do + %{init: Data.to_string(input), transaction: transaction} end else - creation_int_tx_query = + creation_int_transaction_query = from( itx in InternalTransaction, join: t in assoc(itx, :transaction), @@ -597,12 +599,12 @@ defmodule Explorer.Chain.SmartContract do where: t.status == ^1 ) - internal_tx = creation_int_tx_query |> Repo.one() + internal_transaction = creation_int_transaction_query |> Repo.one() - case internal_tx do + case internal_transaction do %{init: init} -> init_str = Data.to_string(init) - %{init: init_str, internal_tx: internal_tx} + %{init: init_str, internal_transaction: internal_transaction} _ -> nil diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index 75883500c023..e4c0d71defa0 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -45,7 +45,7 @@ defmodule Explorer.Chain.Transaction.Schema do field(:l1_fee_scalar, :decimal) field(:l1_gas_price, Wei) field(:l1_gas_used, :decimal) - field(:l1_tx_origin, Hash.Full) + field(:l1_transaction_origin, Hash.Full) field(:l1_block_number, :integer) end, 2 @@ -113,7 +113,7 @@ defmodule Explorer.Chain.Transaction.Schema do elem( quote do has_one(:zksync_batch_transaction, ZkSyncBatchTransaction, - foreign_key: :tx_hash, + foreign_key: :transaction_hash, references: :hash ) @@ -153,7 +153,7 @@ defmodule Explorer.Chain.Transaction.Schema do field(:gas_used_for_l1, :decimal) has_one(:arbitrum_batch_transaction, ArbitrumBatchTransaction, - foreign_key: :tx_hash, + foreign_key: :transaction_hash, references: :hash ) @@ -217,7 +217,7 @@ defmodule Explorer.Chain.Transaction.Schema do field(:max_priority_fee_per_gas, Wei) field(:max_fee_per_gas, Wei) field(:type, :integer) - field(:has_error_in_internal_txs, :boolean) + field(:has_error_in_internal_transactions, :boolean) field(:has_token_transfers, :boolean, virtual: true) # stability virtual fields @@ -317,11 +317,11 @@ defmodule Explorer.Chain.Transaction do block_consensus block_timestamp created_contract_address_hash cumulative_gas_used earliest_processing_start error gas_price gas_used index created_contract_code_indexed_at status - to_address_hash revert_reason type has_error_in_internal_txs r s v)a + to_address_hash revert_reason type has_error_in_internal_transactions r s v)a @chain_type_optional_attrs (case Application.compile_env(:explorer, :chain_type) do :optimism -> - ~w(l1_fee l1_fee_scalar l1_gas_price l1_gas_used l1_tx_origin l1_block_number)a + ~w(l1_fee l1_fee_scalar l1_gas_price l1_gas_used l1_transaction_origin l1_block_number)a :suave -> ~w(execution_node_hash wrapped_type wrapped_nonce wrapped_to_address_hash wrapped_gas wrapped_gas_price wrapped_max_priority_fee_per_gas wrapped_max_fee_per_gas wrapped_value wrapped_input wrapped_v wrapped_r wrapped_s wrapped_hash)a @@ -482,7 +482,7 @@ defmodule Explorer.Chain.Transaction do * `max_priority_fee_per_gas` - User defined maximum fee (tip) per unit of gas paid to validator for transaction prioritization. * `max_fee_per_gas` - Maximum total amount per unit of gas a user is willing to pay for a transaction, including base fee and priority fee. * `type` - New transaction type identifier introduced in EIP 2718 (Berlin HF) - * `has_error_in_internal_txs` - shows if the internal transactions related to transaction have errors + * `has_error_in_internal_transactions` - shows if the internal transactions related to transaction have errors * `execution_node` - execution node address (used by Suave) * `execution_node_hash` - foreign key of `execution_node` (used by Suave) * `wrapped_type` - transaction type from the `wrapped` field (used by Suave) @@ -764,7 +764,7 @@ defmodule Explorer.Chain.Transaction do error_type: {:error, any()} | {:error, :contract_not_verified | :contract_verified, list()}, success_type: {:ok | binary(), any()} | {:ok, binary(), binary(), list()} def decoded_input_data( - tx, + transaction, skip_sig_provider? \\ false, options, methods_map \\ %{}, @@ -1501,7 +1501,7 @@ defmodule Explorer.Chain.Transaction do |> address_to_transactions_tasks_query(false, old_ui?) |> not_dropped_or_replaced_transactions() |> Chain.join_associations(necessity_by_association) - |> put_has_token_transfers_to_tx(old_ui?) + |> put_has_token_transfers_to_transaction(old_ui?) |> matching_address_queries_list(direction, address_hash) |> Enum.map(fn query -> Task.async(fn -> Chain.select_repo(options).all(query) end) end) end @@ -1680,7 +1680,7 @@ defmodule Explorer.Chain.Transaction do @spec page_transaction(Ecto.Query.t() | atom, Explorer.PagingOptions.t()) :: Ecto.Query.t() def page_transaction(query, %PagingOptions{key: nil}), do: query - def page_transaction(query, %PagingOptions{is_pending_tx: true} = options), + def page_transaction(query, %PagingOptions{is_pending_transaction: true} = options), do: page_pending_transaction(query, options) def page_transaction(query, %PagingOptions{key: {0, index}, is_index_in_asc_order: true}) do @@ -1750,16 +1750,16 @@ defmodule Explorer.Chain.Transaction do Adds a `has_token_transfers` field to the query via `select_merge` if second argument is `false` and returns the query untouched otherwise. """ - @spec put_has_token_transfers_to_tx(Ecto.Query.t() | atom, boolean) :: Ecto.Query.t() - def put_has_token_transfers_to_tx(query, true), do: query + @spec put_has_token_transfers_to_transaction(Ecto.Query.t() | atom, boolean) :: Ecto.Query.t() + def put_has_token_transfers_to_transaction(query, true), do: query - def put_has_token_transfers_to_tx(query, false) do - from(tx in query, + def put_has_token_transfers_to_transaction(query, false) do + from(transaction in query, select_merge: %{ has_token_transfers: fragment( "(SELECT transaction_hash FROM token_transfers WHERE transaction_hash = ? LIMIT 1) IS NOT NULL", - tx.hash + transaction.hash ) } ) @@ -1770,7 +1770,7 @@ defmodule Explorer.Chain.Transaction do """ @spec dynamic_fee :: Ecto.Query.dynamic_expr() def dynamic_fee do - dynamic([tx], tx.gas_price * fragment("COALESCE(?, ?)", tx.gas_used, tx.gas)) + dynamic([transaction], transaction.gas_price * fragment("COALESCE(?, ?)", transaction.gas_used, transaction.gas)) end @doc """ @@ -1780,10 +1780,11 @@ defmodule Explorer.Chain.Transaction do required(String.t()) => Decimal.t() | Wei.t() | non_neg_integer | DateTime.t() | Hash.t() } def address_transactions_next_page_params( - %__MODULE__{block_number: block_number, index: index, inserted_at: inserted_at, hash: hash, value: value} = tx + %__MODULE__{block_number: block_number, index: index, inserted_at: inserted_at, hash: hash, value: value} = + transaction ) do %{ - "fee" => tx |> fee(:wei) |> elem(1), + "fee" => transaction |> fee(:wei) |> elem(1), "value" => value, "block_number" => block_number, "index" => index, @@ -1824,8 +1825,8 @@ defmodule Explorer.Chain.Transaction do @spec fee(Transaction.t(), :ether | :gwei | :wei) :: {:maximum, Decimal.t()} | {:actual, Decimal.t() | nil} def fee(%Transaction{gas: _gas, gas_price: nil, gas_used: nil}, _unit), do: {:maximum, nil} - def fee(%Transaction{gas: gas, gas_price: gas_price, gas_used: nil} = tx, unit) do - {:maximum, fee(tx, gas_price, gas, unit)} + def fee(%Transaction{gas: gas, gas_price: gas_price, gas_used: nil} = transaction, unit) do + {:maximum, fee(transaction, gas_price, gas, unit)} end def fee(%Transaction{gas_price: nil, gas_used: gas_used} = transaction, unit) do @@ -1842,13 +1843,13 @@ defmodule Explorer.Chain.Transaction do end end - def fee(%Transaction{gas_price: gas_price, gas_used: gas_used} = tx, unit) do - {:actual, fee(tx, gas_price, gas_used, unit)} + def fee(%Transaction{gas_price: gas_price, gas_used: gas_used} = transaction, unit) do + {:actual, fee(transaction, gas_price, gas_used, unit)} end - defp fee(tx, gas_price, gas, unit) do + defp fee(transaction, gas_price, gas, unit) do l1_fee = - case Map.get(tx, :l1_fee) do + case Map.get(transaction, :l1_fee) do nil -> Wei.from(Decimal.new(0), :wei) value -> value end @@ -1926,8 +1927,8 @@ defmodule Explorer.Chain.Transaction do Returns the number of transactions included into the blocks of the specified block range. Only consensus blocks are taken into account. """ - @spec tx_count_for_block_range(Range.t()) :: non_neg_integer() - def tx_count_for_block_range(from..to//_) do + @spec transaction_count_for_block_range(Range.t()) :: non_neg_integer() + def transaction_count_for_block_range(from..to//_) do Repo.replica().aggregate( from( t in Transaction, diff --git a/apps/explorer/lib/explorer/chain/transaction/history/historian.ex b/apps/explorer/lib/explorer/chain/transaction/history/historian.ex index bb6e25ab624a..fe09465b7abe 100644 --- a/apps/explorer/lib/explorer/chain/transaction/history/historian.ex +++ b/apps/explorer/lib/explorer/chain/transaction/history/historian.ex @@ -17,7 +17,7 @@ defmodule Explorer.Chain.Transaction.History.Historian do @impl Historian def compile_records(num_days, records \\ []) do - Logger.info("tx/per day chart: collect records for txs per day stats") + Logger.info("tx/per day chart: collect records for transactions per day stats") if num_days == 1 do Logger.info("tx/per day chart: records collected #{inspect(records)}") diff --git a/apps/explorer/lib/explorer/chain/transaction/state_change.ex b/apps/explorer/lib/explorer/chain/transaction/state_change.ex index d9b8bd93d579..3acf6eadc92a 100644 --- a/apps/explorer/lib/explorer/chain/transaction/state_change.ex +++ b/apps/explorer/lib/explorer/chain/transaction/state_change.ex @@ -24,58 +24,59 @@ defmodule Explorer.Chain.Transaction.StateChange do @zero_wei %Wei{value: Decimal.new(0)} @spec coin_balances_before(Transaction.t(), [Transaction.t()], coin_balances_map()) :: coin_balances_map() - def coin_balances_before(tx, block_txs, coin_balances_before_block) do - block = tx.block + def coin_balances_before(transaction, block_transactions, coin_balances_before_block) do + block = transaction.block - block_txs + block_transactions |> Enum.reduce_while( coin_balances_before_block, - fn block_tx, acc -> - if block_tx.index < tx.index do - {:cont, update_coin_balances_from_tx(acc, block_tx, block)} + fn block_transaction, acc -> + if block_transaction.index < transaction.index do + {:cont, update_coin_balances_from_transaction(acc, block_transaction, block)} else - # txs ordered by index ascending, so we can halt after facing index greater or equal than index of our tx + # transactions ordered by index ascending, so we can halt after facing index greater or equal than index of our transaction {:halt, acc} end end ) end - @spec update_coin_balances_from_tx(coin_balances_map(), Transaction.t(), Block.t()) :: coin_balances_map() - def update_coin_balances_from_tx(coin_balances, tx, block) do + @spec update_coin_balances_from_transaction(coin_balances_map(), Transaction.t(), Block.t()) :: coin_balances_map() + def update_coin_balances_from_transaction(coin_balances, transaction, block) do coin_balances = coin_balances - |> update_balance(tx.from_address_hash, &Wei.sub(&1, from_loss(tx))) - |> update_balance(tx.to_address_hash, &Wei.sum(&1, to_profit(tx))) - |> update_balance(block.miner_hash, &Wei.sum(&1, miner_profit(tx, block))) + |> update_balance(transaction.from_address_hash, &Wei.sub(&1, from_loss(transaction))) + |> update_balance(transaction.to_address_hash, &Wei.sum(&1, to_profit(transaction))) + |> update_balance(block.miner_hash, &Wei.sum(&1, miner_profit(transaction, block))) - if error?(tx) do + if error?(transaction) do coin_balances else - tx.internal_transactions |> Enum.reduce(coin_balances, &update_coin_balances_from_internal_tx(&1, &2)) + transaction.internal_transactions + |> Enum.reduce(coin_balances, &update_coin_balances_from_internal_transaction(&1, &2)) end end - defp update_coin_balances_from_internal_tx(%InternalTransaction{call_type: :delegatecall}, coin_balances), + defp update_coin_balances_from_internal_transaction(%InternalTransaction{call_type: :delegatecall}, coin_balances), do: coin_balances - defp update_coin_balances_from_internal_tx(%InternalTransaction{index: 0}, coin_balances), do: coin_balances + defp update_coin_balances_from_internal_transaction(%InternalTransaction{index: 0}, coin_balances), do: coin_balances - defp update_coin_balances_from_internal_tx(internal_tx, coin_balances) do + defp update_coin_balances_from_internal_transaction(internal_transaction, coin_balances) do coin_balances - |> update_balance(internal_tx.from_address_hash, &Wei.sub(&1, from_loss(internal_tx))) - |> update_balance(internal_tx.to_address_hash, &Wei.sum(&1, to_profit(internal_tx))) + |> update_balance(internal_transaction.from_address_hash, &Wei.sub(&1, from_loss(internal_transaction))) + |> update_balance(internal_transaction.to_address_hash, &Wei.sum(&1, to_profit(internal_transaction))) end - def token_balances_before(balances_before, tx, block_txs) do - block_txs + def token_balances_before(balances_before, transaction, block_transactions) do + block_transactions |> Enum.reduce_while( balances_before, - fn block_tx, state -> - if block_tx.index < tx.index do - {:cont, do_update_token_balances_from_token_transfers(block_tx.token_transfers, state)} + fn block_transaction, state -> + if block_transaction.index < transaction.index do + {:cont, do_update_token_balances_from_token_transfers(block_transaction.token_transfers, state)} else - # txs ordered by index ascending, so we can halt after facing index greater or equal than index of our tx + # transactions ordered by index ascending, so we can halt after facing index greater or equal than index of our transaction {:halt, state} end end @@ -161,18 +162,18 @@ defmodule Explorer.Chain.Transaction.StateChange do or an internal transaction. """ @spec from_loss(Transaction.t() | InternalTransaction.t()) :: Wei.t() - def from_loss(%Transaction{} = tx) do - {_, fee} = Transaction.fee(tx, :wei) + def from_loss(%Transaction{} = transaction) do + {_, fee} = Transaction.fee(transaction, :wei) - if error?(tx) do + if error?(transaction) do %Wei{value: fee} else - Wei.sum(tx.value, %Wei{value: fee}) + Wei.sum(transaction.value, %Wei{value: fee}) end end - def from_loss(%InternalTransaction{} = tx) do - tx.value + def from_loss(%InternalTransaction{} = transaction) do + transaction.value end @doc """ @@ -180,33 +181,33 @@ defmodule Explorer.Chain.Transaction.StateChange do or an internal transaction. """ @spec to_profit(Transaction.t() | InternalTransaction.t()) :: Wei.t() - def to_profit(%Transaction{} = tx) do - if error?(tx) do + def to_profit(%Transaction{} = transaction) do + if error?(transaction) do %Wei{value: 0} else - tx.value + transaction.value end end - def to_profit(%InternalTransaction{} = tx) do - tx.value + def to_profit(%InternalTransaction{} = transaction) do + transaction.value end - defp miner_profit(tx, block) do + defp miner_profit(transaction, block) do base_fee_per_gas = block.base_fee_per_gas || %Wei{value: Decimal.new(0)} - max_priority_fee_per_gas = tx.max_priority_fee_per_gas || tx.gas_price - max_fee_per_gas = tx.max_fee_per_gas || tx.gas_price + max_priority_fee_per_gas = transaction.max_priority_fee_per_gas || transaction.gas_price + max_fee_per_gas = transaction.max_fee_per_gas || transaction.gas_price priority_fee_per_gas = Enum.min_by([max_priority_fee_per_gas, Wei.sub(max_fee_per_gas, base_fee_per_gas)], fn x -> Wei.to(x, :wei) end) - Wei.mult(priority_fee_per_gas, tx.gas_used) + Wei.mult(priority_fee_per_gas, transaction.gas_used) end - defp error?(tx) do - case Chain.transaction_to_status(tx) do + defp error?(transaction) do + case Chain.transaction_to_status(transaction) do {:error, _} -> true _ -> false end @@ -236,14 +237,15 @@ defmodule Explorer.Chain.Transaction.StateChange do taking into account state changes from previous transactions in the same block. """ @spec native_coin_entries(Transaction.t(), coin_balances_map()) :: [t()] - def native_coin_entries(transaction, coin_balances_before_tx) do + def native_coin_entries(transaction, coin_balances_before_transaction) do block = transaction.block - coin_balances_after_tx = update_coin_balances_from_tx(coin_balances_before_tx, transaction, block) + coin_balances_after_transaction = + update_coin_balances_from_transaction(coin_balances_before_transaction, transaction, block) - coin_balances_before_tx + coin_balances_before_transaction |> Enum.reduce([], fn {address_hash, {address, coin_balance_before}}, acc -> - {_, coin_balance_after} = coin_balances_after_tx[address_hash] + {_, coin_balance_after} = coin_balances_after_transaction[address_hash] coin_entry = coin_entry(address, coin_balance_before, coin_balance_after, address_hash == block.miner_hash) if coin_entry do diff --git a/apps/explorer/lib/explorer/chain/transaction_action.ex b/apps/explorer/lib/explorer/chain/transaction_action.ex index a8aba0bc5a59..795892a6f167 100644 --- a/apps/explorer/lib/explorer/chain/transaction_action.ex +++ b/apps/explorer/lib/explorer/chain/transaction_action.ex @@ -63,8 +63,8 @@ defmodule Explorer.Chain.TransactionAction do timestamps() end - def changeset(%__MODULE__{} = tx_actions, attrs \\ %{}) do - tx_actions + def changeset(%__MODULE__{} = transaction_actions, attrs \\ %{}) do + transaction_actions |> cast(attrs, @required_attrs) |> validate_required(@required_attrs) |> foreign_key_constraint(:hash) diff --git a/apps/explorer/lib/explorer/chain/zksync/batch_transaction.ex b/apps/explorer/lib/explorer/chain/zksync/batch_transaction.ex index e6fb1fc64af8..d9c95f42f6b4 100644 --- a/apps/explorer/lib/explorer/chain/zksync/batch_transaction.ex +++ b/apps/explorer/lib/explorer/chain/zksync/batch_transaction.ex @@ -15,11 +15,11 @@ defmodule Explorer.Chain.ZkSync.BatchTransaction do alias Explorer.Chain.{Hash, Transaction} alias Explorer.Chain.ZkSync.TransactionBatch - @required_attrs ~w(batch_number tx_hash)a + @required_attrs ~w(batch_number transaction_hash)a @typedoc """ - * `tx_hash` - The hash of the rollup transaction. - * `l2_transaction` - An instance of `Explorer.Chain.Transaction` referenced by `tx_hash`. + * `transaction_hash` - The hash of the rollup transaction. + * `l2_transaction` - An instance of `Explorer.Chain.Transaction` referenced by `transaction_hash`. * `batch_number` - The number of the ZkSync batch. * `batch` - An instance of `Explorer.Chain.ZkSync.TransactionBatch` referenced by `batch_number`. """ @@ -28,7 +28,7 @@ defmodule Explorer.Chain.ZkSync.BatchTransaction do belongs_to(:batch, TransactionBatch, foreign_key: :batch_number, references: :number, type: :integer) belongs_to(:l2_transaction, Transaction, - foreign_key: :tx_hash, + foreign_key: :transaction_hash, primary_key: true, references: :hash, type: Hash.Full @@ -46,6 +46,6 @@ defmodule Explorer.Chain.ZkSync.BatchTransaction do |> cast(attrs, @required_attrs) |> validate_required(@required_attrs) |> foreign_key_constraint(:batch_number) - |> unique_constraint(:tx_hash) + |> unique_constraint(:transaction_hash) end end diff --git a/apps/explorer/lib/explorer/chain/zksync/reader.ex b/apps/explorer/lib/explorer/chain/zksync/reader.ex index ca61e25a285c..ed632db585dd 100644 --- a/apps/explorer/lib/explorer/chain/zksync/reader.ex +++ b/apps/explorer/lib/explorer/chain/zksync/reader.ex @@ -297,18 +297,18 @@ defmodule Explorer.Chain.ZkSync.Reader do Reads a list of L1 transactions by their hashes from the `zksync_lifecycle_l1_transactions` table. ## Parameters - - `l1_tx_hashes`: A list of hashes to retrieve L1 transactions for. + - `l1_transaction_hashes`: A list of hashes to retrieve L1 transactions for. ## Returns - A list of `Explorer.Chain.ZkSync.LifecycleTransaction` corresponding to the hashes from the input list. The output list may be smaller than the input list. """ @spec lifecycle_transactions(maybe_improper_list(binary(), [])) :: [Explorer.Chain.ZkSync.LifecycleTransaction] - def lifecycle_transactions(l1_tx_hashes) do + def lifecycle_transactions(l1_transaction_hashes) do query = from( lt in LifecycleTransaction, select: {lt.hash, lt.id}, - where: lt.hash in ^l1_tx_hashes + where: lt.hash in ^l1_transaction_hashes ) Repo.all(query, timeout: :infinity) diff --git a/apps/explorer/lib/explorer/chain/zksync/transaction_batch.ex b/apps/explorer/lib/explorer/chain/zksync/transaction_batch.ex index 3f6ac409cee3..d19f22af61c3 100644 --- a/apps/explorer/lib/explorer/chain/zksync/transaction_batch.ex +++ b/apps/explorer/lib/explorer/chain/zksync/transaction_batch.ex @@ -13,13 +13,13 @@ defmodule Explorer.Chain.ZkSync.TransactionBatch do @optional_attrs ~w(commit_id prove_id execute_id)a - @required_attrs ~w(number timestamp l1_tx_count l2_tx_count root_hash l1_gas_price l2_fair_gas_price start_block end_block)a + @required_attrs ~w(number timestamp l1_transaction_count l2_transaction_count root_hash l1_gas_price l2_fair_gas_price start_block end_block)a @type t :: %__MODULE__{ number: non_neg_integer(), timestamp: DateTime.t(), - l1_tx_count: non_neg_integer(), - l2_tx_count: non_neg_integer(), + l1_transaction_count: non_neg_integer(), + l2_transaction_count: non_neg_integer(), root_hash: Hash.t(), l1_gas_price: Wei.t(), l2_fair_gas_price: Wei.t(), @@ -36,8 +36,8 @@ defmodule Explorer.Chain.ZkSync.TransactionBatch do @primary_key {:number, :integer, autogenerate: false} schema "zksync_transaction_batches" do field(:timestamp, :utc_datetime_usec) - field(:l1_tx_count, :integer) - field(:l2_tx_count, :integer) + field(:l1_transaction_count, :integer) + field(:l2_transaction_count, :integer) field(:root_hash, Hash.Full) field(:l1_gas_price, Wei) field(:l2_fair_gas_price, Wei) diff --git a/apps/explorer/lib/explorer/counters/block_burnt_fee_counter.ex b/apps/explorer/lib/explorer/counters/block_burnt_fee_counter.ex index aa925f39e026..64c02d50fc09 100644 --- a/apps/explorer/lib/explorer/counters/block_burnt_fee_counter.ex +++ b/apps/explorer/lib/explorer/counters/block_burnt_fee_counter.ex @@ -57,7 +57,7 @@ defmodule Explorer.Counters.BlockBurntFeeCounter do defp update_cache(block_hash) do block_hash_string = get_block_hash_string(block_hash) - new_data = Chain.block_to_gas_used_by_1559_txs(block_hash) + new_data = Chain.block_to_gas_used_by_1559_transactions(block_hash) Helper.put_into_ets_cache(@cache_name, "#{block_hash_string}", new_data) end diff --git a/apps/explorer/lib/explorer/counters/block_priority_fee_counter.ex b/apps/explorer/lib/explorer/counters/block_priority_fee_counter.ex index 42a4a552048a..79cd92fb8d3a 100644 --- a/apps/explorer/lib/explorer/counters/block_priority_fee_counter.ex +++ b/apps/explorer/lib/explorer/counters/block_priority_fee_counter.ex @@ -57,7 +57,7 @@ defmodule Explorer.Counters.BlockPriorityFeeCounter do defp update_cache(block_hash) do block_hash_string = get_block_hash_string(block_hash) - new_data = Chain.block_to_priority_fee_of_1559_txs(block_hash) + new_data = Chain.block_to_priority_fee_of_1559_transactions(block_hash) Helper.put_into_ets_cache(@cache_name, "#{block_hash_string}", new_data) end diff --git a/apps/explorer/lib/explorer/counters/transactions_24h_stats.ex b/apps/explorer/lib/explorer/counters/transactions_24h_stats.ex index 80bef49ec046..e82df0530e96 100644 --- a/apps/explorer/lib/explorer/counters/transactions_24h_stats.ex +++ b/apps/explorer/lib/explorer/counters/transactions_24h_stats.ex @@ -12,9 +12,9 @@ defmodule Explorer.Counters.Transactions24hStats do alias Explorer.{Chain, Repo} alias Explorer.Chain.Transaction - @tx_count_name "transaction_count_24h" - @tx_fee_sum_name "transaction_fee_sum_24h" - @tx_fee_average_name "transaction_fee_average_24h" + @transaction_count_name "transaction_count_24h" + @transaction_fee_sum_name "transaction_fee_sum_24h" + @transaction_fee_average_name "transaction_fee_average_24h" @doc """ Starts a process to periodically update the counters. @@ -55,24 +55,24 @@ defmodule Explorer.Counters.Transactions24hStats do end @doc """ - Fetches the value for a `#{@tx_count_name}` counter type from the `last_fetched_counters` table. + Fetches the value for a `#{@transaction_count_name}` counter type from the `last_fetched_counters` table. """ def fetch_count(options) do - Chain.get_last_fetched_counter(@tx_count_name, options) + Chain.get_last_fetched_counter(@transaction_count_name, options) end @doc """ - Fetches the value for a `#{@tx_fee_sum_name}` counter type from the `last_fetched_counters` table. + Fetches the value for a `#{@transaction_fee_sum_name}` counter type from the `last_fetched_counters` table. """ def fetch_fee_sum(options) do - Chain.get_last_fetched_counter(@tx_fee_sum_name, options) + Chain.get_last_fetched_counter(@transaction_fee_sum_name, options) end @doc """ - Fetches the value for a `#{@tx_fee_average_name}` counter type from the `last_fetched_counters` table. + Fetches the value for a `#{@transaction_fee_average_name}` counter type from the `last_fetched_counters` table. """ def fetch_fee_average(options) do - Chain.get_last_fetched_counter(@tx_fee_average_name, options) + Chain.get_last_fetched_counter(@transaction_fee_average_name, options) end @doc """ @@ -110,17 +110,17 @@ defmodule Explorer.Counters.Transactions24hStats do } = Repo.one!(query, timeout: :infinity) Chain.upsert_last_fetched_counter(%{ - counter_type: @tx_count_name, + counter_type: @transaction_count_name, value: count }) Chain.upsert_last_fetched_counter(%{ - counter_type: @tx_fee_sum_name, + counter_type: @transaction_fee_sum_name, value: fee_sum }) Chain.upsert_last_fetched_counter(%{ - counter_type: @tx_fee_average_name, + counter_type: @transaction_fee_average_name, value: fee_average }) end diff --git a/apps/explorer/lib/explorer/etherscan.ex b/apps/explorer/lib/explorer/etherscan.ex index f7a44bd8bc2e..1daac488682d 100644 --- a/apps/explorer/lib/explorer/etherscan.ex +++ b/apps/explorer/lib/explorer/etherscan.ex @@ -787,9 +787,9 @@ defmodule Explorer.Etherscan do {:ok, date} -> query = from( - tx_stats in TransactionStats, - where: tx_stats.date == ^date, - select: tx_stats.total_fee + transaction_stats in TransactionStats, + where: transaction_stats.date == ^date, + select: transaction_stats.total_fee ) total_fees = Repo.replica().one(query) diff --git a/apps/explorer/lib/explorer/graphql/celo.ex b/apps/explorer/lib/explorer/graphql/celo.ex index 4705274ca0fe..6f82167619dc 100644 --- a/apps/explorer/lib/explorer/graphql/celo.ex +++ b/apps/explorer/lib/explorer/graphql/celo.ex @@ -20,8 +20,8 @@ defmodule Explorer.GraphQL.Celo do @doc """ Constructs a paginated query for token transfers involving a specific address. """ - @spec token_tx_transfers_query_for_address(Hash.Address.t(), integer(), integer()) :: Ecto.Query.t() - def token_tx_transfers_query_for_address(address_hash, offset, limit) do + @spec token_transaction_transfers_query_for_address(Hash.Address.t(), integer(), integer()) :: Ecto.Query.t() + def token_transaction_transfers_query_for_address(address_hash, offset, limit) do page = floor(offset / limit) + 1 growing_limit = limit * (page + 1) @@ -51,26 +51,26 @@ defmodule Explorer.GraphQL.Celo do from( tt in subquery(tokens), as: :token_transfer, - inner_join: tx in Transaction, + inner_join: transaction in Transaction, as: :transaction, - on: tx.hash == tt.transaction_hash, + on: transaction.hash == tt.transaction_hash, inner_join: b in Block, - on: tx.block_hash == b.hash, + on: transaction.block_hash == b.hash, left_join: token in Token, - on: tx.gas_token_contract_address_hash == token.contract_address_hash, + on: transaction.gas_token_contract_address_hash == token.contract_address_hash, select: %{ transaction_hash: tt.transaction_hash, to_address_hash: tt.to_address_hash, from_address_hash: tt.from_address_hash, - gas_used: tx.gas_used, - gas_price: tx.gas_price, - fee_currency: tx.gas_token_contract_address_hash, + gas_used: transaction.gas_used, + gas_price: transaction.gas_price, + fee_currency: transaction.gas_token_contract_address_hash, fee_token: fragment("coalesce(?, 'CELO')", token.symbol), - # gateway_fee: tx.gateway_fee, - # gateway_fee_recipient: tx.gas_fee_recipient_hash, + # gateway_fee: transaction.gateway_fee, + # gateway_fee_recipient: transaction.gas_fee_recipient_hash, timestamp: b.timestamp, - input: tx.input, - nonce: tx.nonce, + input: transaction.input, + nonce: transaction.nonce, block_number: tt.block_number } ) @@ -90,18 +90,18 @@ defmodule Explorer.GraphQL.Celo do Constructs a query to fetch token transfers within a given transaction. ## Parameters - - tx_hash: the hash of the transaction + - transaction_hash: the hash of the transaction ## Returns - Ecto query """ - @spec token_tx_transfers_query_by_txhash(Hash.Full.t()) :: Ecto.Query.t() - def token_tx_transfers_query_by_txhash(tx_hash) do - query = token_tx_transfers_query() + @spec token_transaction_transfers_query_by_transaction_hash(Hash.Full.t()) :: Ecto.Query.t() + def token_transaction_transfers_query_by_transaction_hash(transaction_hash) do + query = token_transaction_transfers_query() from( t in subquery(query), - where: t.transaction_hash == ^tx_hash, + where: t.transaction_hash == ^transaction_hash, order_by: [t.log_index] ) end @@ -109,9 +109,9 @@ defmodule Explorer.GraphQL.Celo do @doc """ Constructs a query for token transfers filtered by a specific address. """ - @spec token_tx_transfers_query_by_address(Hash.Address.t()) :: Ecto.Query.t() - def token_tx_transfers_query_by_address(address_hash) do - token_tx_transfers_query() + @spec token_transaction_transfers_query_by_address(Hash.Address.t()) :: Ecto.Query.t() + def token_transaction_transfers_query_by_address(address_hash) do + token_transaction_transfers_query() |> where([t], t.from_address_hash == ^address_hash or t.to_address_hash == ^address_hash) |> order_by([transaction: t], desc: t.block_number, asc: t.nonce) end @@ -119,13 +119,13 @@ defmodule Explorer.GraphQL.Celo do @doc """ Constructs a query to fetch detailed token transfer information. """ - @spec token_tx_transfers_query() :: Ecto.Query.t() - def token_tx_transfers_query do + @spec token_transaction_transfers_query() :: Ecto.Query.t() + def token_transaction_transfers_query do from( tt in TokenTransfer, - inner_join: tx in Transaction, + inner_join: transaction in Transaction, as: :transaction, - on: tx.hash == tt.transaction_hash, + on: transaction.hash == tt.transaction_hash, inner_join: b in Block, on: tt.block_number == b.number, # left_join: wf in CeloWalletAccounts, @@ -135,10 +135,10 @@ defmodule Explorer.GraphQL.Celo do left_join: token in Token, on: tt.token_contract_address_hash == token.contract_address_hash, select: %{ - gas_used: tx.gas_used, - gas_price: tx.gas_price, + gas_used: transaction.gas_used, + gas_price: transaction.gas_price, timestamp: b.timestamp, - input: tx.input, + input: transaction.input, transaction_hash: tt.transaction_hash, from_address_hash: tt.from_address_hash, to_address_hash: tt.to_address_hash, @@ -149,7 +149,7 @@ defmodule Explorer.GraphQL.Celo do # comment: tt.comment, token: token.symbol, token_address: token.contract_address_hash, - nonce: tx.nonce, + nonce: transaction.nonce, block_number: tt.block_number, token_type: token.type, token_id: fragment("(COALESCE(?, ARRAY[]::Decimal[]))[1]", tt.token_ids) diff --git a/apps/explorer/lib/explorer/migrator/restore_omitted_weth_transfers.ex b/apps/explorer/lib/explorer/migrator/restore_omitted_weth_transfers.ex index edc89f2c50b8..a635b3103761 100644 --- a/apps/explorer/lib/explorer/migrator/restore_omitted_weth_transfers.ex +++ b/apps/explorer/lib/explorer/migrator/restore_omitted_weth_transfers.ex @@ -207,7 +207,7 @@ defmodule Explorer.Migrator.RestoreOmittedWETHTransfers do else _ -> Logger.error( - "Failed to decode log: (tx_hash, block_hash, index) = #{to_string(log.transaction_hash)}, #{to_string(log.block_hash)}, #{to_string(log.index)}" + "Failed to decode log: (transaction_hash, block_hash, index) = #{to_string(log.transaction_hash)}, #{to_string(log.block_hash)}, #{to_string(log.index)}" ) nil diff --git a/apps/explorer/lib/explorer/paging_options.ex b/apps/explorer/lib/explorer/paging_options.ex index 081d160c3d15..08239758abc9 100644 --- a/apps/explorer/lib/explorer/paging_options.ex +++ b/apps/explorer/lib/explorer/paging_options.ex @@ -8,7 +8,7 @@ defmodule Explorer.PagingOptions do key: key, page_size: page_size, page_number: page_number, - is_pending_tx: is_pending_tx, + is_pending_transaction: is_pending_transaction, is_index_in_asc_order: is_index_in_asc_order, asc_order: asc_order, batch_key: batch_key @@ -17,7 +17,7 @@ defmodule Explorer.PagingOptions do @typep key :: any() @typep page_size :: non_neg_integer() | nil @typep page_number :: pos_integer() - @typep is_pending_tx :: atom() + @typep is_pending_transaction :: atom() @typep is_index_in_asc_order :: atom() @typep asc_order :: atom() @typep batch_key :: any() @@ -26,7 +26,7 @@ defmodule Explorer.PagingOptions do :key, :page_size, page_number: 1, - is_pending_tx: false, + is_pending_transaction: false, is_index_in_asc_order: false, asc_order: false, batch_key: nil diff --git a/apps/explorer/lib/explorer/smart_contract/helper.ex b/apps/explorer/lib/explorer/smart_contract/helper.ex index 51e7d86817bb..765c04d6be5c 100644 --- a/apps/explorer/lib/explorer/smart_contract/helper.ex +++ b/apps/explorer/lib/explorer/smart_contract/helper.ex @@ -144,7 +144,7 @@ defmodule Explorer.SmartContract.Helper do def cast_libraries(_value, map), do: map def contract_creation_input(address_hash) do - case Chain.smart_contract_creation_tx_bytecode(address_hash) do + case Chain.smart_contract_creation_transaction_bytecode(address_hash) do %{init: init, created_contract_code: _created_contract_code} -> init @@ -181,12 +181,13 @@ defmodule Explorer.SmartContract.Helper do if Application.get_env(:explorer, :chain_type) == :zksync do {nil, deployed_bytecode, metadata} else - case SmartContract.creation_tx_with_bytecode(address_hash) do - %{init: init, tx: tx} -> - {init, deployed_bytecode, tx |> tx_to_metadata(init) |> Map.merge(metadata)} + case SmartContract.creation_transaction_with_bytecode(address_hash) do + %{init: init, transaction: transaction} -> + {init, deployed_bytecode, transaction |> transaction_to_metadata(init) |> Map.merge(metadata)} - %{init: init, internal_tx: internal_tx} -> - {init, deployed_bytecode, internal_tx |> internal_tx_to_metadata(init) |> Map.merge(metadata)} + %{init: init, internal_transaction: internal_transaction} -> + {init, deployed_bytecode, + internal_transaction |> internal_transaction_to_metadata(init) |> Map.merge(metadata)} _ -> {nil, deployed_bytecode, metadata} @@ -194,22 +195,22 @@ defmodule Explorer.SmartContract.Helper do end end - defp tx_to_metadata(tx, init) do + defp transaction_to_metadata(transaction, init) do %{ - "blockNumber" => to_string(tx.block_number), - "transactionHash" => to_string(tx.hash), - "transactionIndex" => to_string(tx.index), - "deployer" => to_string(tx.from_address_hash), + "blockNumber" => to_string(transaction.block_number), + "transactionHash" => to_string(transaction.hash), + "transactionIndex" => to_string(transaction.index), + "deployer" => to_string(transaction.from_address_hash), "creationCode" => to_string(init) } end - defp internal_tx_to_metadata(internal_tx, init) do + defp internal_transaction_to_metadata(internal_transaction, init) do %{ - "blockNumber" => to_string(internal_tx.block_number), - "transactionHash" => to_string(internal_tx.transaction_hash), - "transactionIndex" => to_string(internal_tx.transaction_index), - "deployer" => to_string(internal_tx.from_address_hash), + "blockNumber" => to_string(internal_transaction.block_number), + "transactionHash" => to_string(internal_transaction.transaction_hash), + "transactionIndex" => to_string(internal_transaction.transaction_index), + "deployer" => to_string(internal_transaction.from_address_hash), "creationCode" => to_string(init) } end diff --git a/apps/explorer/lib/explorer/smart_contract/sig_provider_interface.ex b/apps/explorer/lib/explorer/smart_contract/sig_provider_interface.ex index b97f9bcd1edf..0cc621a1150a 100644 --- a/apps/explorer/lib/explorer/smart_contract/sig_provider_interface.ex +++ b/apps/explorer/lib/explorer/smart_contract/sig_provider_interface.ex @@ -10,7 +10,7 @@ defmodule Explorer.SmartContract.SigProviderInterface do @request_error_msg "Error while sending request to sig-provider" def decode_function_call(input) do - base_url = tx_input_decode_url() + base_url = transaction_input_decode_url() url = base_url @@ -74,7 +74,7 @@ defmodule Explorer.SmartContract.SigProviderInterface do def process_sig_provider_response(other_responses), do: {:error, other_responses} - def tx_input_decode_url, do: "#{base_api_url()}" <> "/function" + def transaction_input_decode_url, do: "#{base_api_url()}" <> "/function" def event_decode_url, do: "#{base_api_url()}" <> "/event" diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex b/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex index 79b4f34d7c2f..afc322dca0ca 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex @@ -45,10 +45,10 @@ defmodule Explorer.SmartContract.Solidity.Verifier do end defp evaluate_authenticity_inner(true, address_hash, params) do - {creation_tx_input, deployed_bytecode, verifier_metadata} = fetch_data_for_verification(address_hash) + {creation_transaction_input, deployed_bytecode, verifier_metadata} = fetch_data_for_verification(address_hash) %{} - |> prepare_bytecode_for_microservice(creation_tx_input, deployed_bytecode) + |> prepare_bytecode_for_microservice(creation_transaction_input, deployed_bytecode) |> Map.put("sourceFiles", %{ "#{params["name"]}.#{smart_contract_source_file_extension(parse_boolean(params["is_yul"]))}" => params["contract_source_code"] @@ -127,7 +127,7 @@ defmodule Explorer.SmartContract.Solidity.Verifier do end def evaluate_authenticity_via_standard_json_input_inner(true, address_hash, params, json_input) do - {creation_tx_input, deployed_bytecode, verifier_metadata} = fetch_data_for_verification(address_hash) + {creation_transaction_input, deployed_bytecode, verifier_metadata} = fetch_data_for_verification(address_hash) compiler_version_map = if Application.get_env(:explorer, :chain_type) == :zksync do @@ -140,7 +140,7 @@ defmodule Explorer.SmartContract.Solidity.Verifier do end compiler_version_map - |> prepare_bytecode_for_microservice(creation_tx_input, deployed_bytecode) + |> prepare_bytecode_for_microservice(creation_transaction_input, deployed_bytecode) |> Map.put("input", json_input) |> (&if(Application.get_env(:explorer, :chain_type) == :zksync, do: RustVerifierInterface.zksync_verify_standard_json_input(&1, verifier_metadata), @@ -153,10 +153,10 @@ defmodule Explorer.SmartContract.Solidity.Verifier do end def evaluate_authenticity_via_multi_part_files(address_hash, params, files) do - {creation_tx_input, deployed_bytecode, verifier_metadata} = fetch_data_for_verification(address_hash) + {creation_transaction_input, deployed_bytecode, verifier_metadata} = fetch_data_for_verification(address_hash) %{} - |> prepare_bytecode_for_microservice(creation_tx_input, deployed_bytecode) + |> prepare_bytecode_for_microservice(creation_transaction_input, deployed_bytecode) |> Map.put("sourceFiles", files) |> Map.put("libraries", params["external_libraries"]) |> Map.put("optimizationRuns", prepare_optimization_runs(params["optimization"], params["optimization_runs"])) @@ -359,8 +359,8 @@ defmodule Explorer.SmartContract.Solidity.Verifier do bc_deployed_bytecode = Chain.smart_contract_bytecode(address_hash) - bc_creation_tx_input = - case Chain.smart_contract_creation_tx_bytecode(address_hash) do + bc_creation_transaction_input = + case Chain.smart_contract_creation_transaction_bytecode(address_hash) do %{init: init, created_contract_code: _created_contract_code} -> "0x" <> init_without_0x = init init_without_0x @@ -371,12 +371,12 @@ defmodule Explorer.SmartContract.Solidity.Verifier do %{ "metadata_hash_with_length" => bc_meta, - "trimmed_bytecode" => bc_creation_tx_input_without_meta, + "trimmed_bytecode" => bc_creation_transaction_input_without_meta, "compiler_version" => solc_bc - } = extract_bytecode_and_metadata_hash(bc_creation_tx_input, bc_deployed_bytecode) + } = extract_bytecode_and_metadata_hash(bc_creation_transaction_input, bc_deployed_bytecode) bc_replaced_local = - String.replace(bc_creation_tx_input_without_meta, local_bytecode_without_meta, "", global: false) + String.replace(bc_creation_transaction_input_without_meta, local_bytecode_without_meta, "", global: false) has_constructor_with_params? = has_constructor_with_params?(abi) @@ -386,16 +386,16 @@ defmodule Explorer.SmartContract.Solidity.Verifier do empty_constructor_arguments = arguments_data == "" or arguments_data == nil cond do - bc_creation_tx_input == "" -> + bc_creation_transaction_input == "" -> {:error, :no_creation_data} - !String.contains?(bc_creation_tx_input, bc_meta) || bc_deployed_bytecode in ["", "0x"] -> + !String.contains?(bc_creation_transaction_input, bc_meta) || bc_deployed_bytecode in ["", "0x"] -> {:error, :deployed_bytecode} solc_local != solc_bc -> {:error, :compiler_version} - !String.contains?(bc_creation_tx_input_without_meta, local_bytecode_without_meta) -> + !String.contains?(bc_creation_transaction_input_without_meta, local_bytecode_without_meta) -> {:error, :generated_bytecode} bc_replaced_local == "" && !has_constructor_with_params? -> @@ -410,15 +410,21 @@ defmodule Explorer.SmartContract.Solidity.Verifier do {:error, :autodetect_constructor_arguments_failed} has_constructor_with_params? && - (empty_constructor_arguments || !String.contains?(bc_creation_tx_input, arguments_data)) -> + (empty_constructor_arguments || !String.contains?(bc_creation_transaction_input, arguments_data)) -> {:error, :constructor_arguments} has_constructor_with_params? && is_constructor_args_valid?.(arguments_data) && (bc_replaced_local == arguments_data || - check_users_constructor_args_validity(bc_creation_tx_input, bytecode, bc_meta, local_meta, arguments_data)) -> + check_users_constructor_args_validity( + bc_creation_transaction_input, + bytecode, + bc_meta, + local_meta, + arguments_data + )) -> {:ok, %{abi: abi, constructor_arguments: arguments_data}} - try_library_verification(local_bytecode_without_meta, bc_creation_tx_input_without_meta) -> + try_library_verification(local_bytecode_without_meta, bc_creation_transaction_input_without_meta) -> {:ok, %{abi: abi}} true -> @@ -556,7 +562,7 @@ defmodule Explorer.SmartContract.Solidity.Verifier do @doc """ Function tries to parse constructor args from smart contract creation input. 1. using `extract_meta_from_deployed_bytecode/1` we derive CBOR metadata string - 2. using metadata we split creation_tx_input and try to decode resulting constructor arguments + 2. using metadata we split creation_transaction_input and try to decode resulting constructor arguments 2.1. if we successfully decoded args using constructor's abi, then return constructor args 2.2 otherwise return nil """ @@ -575,8 +581,8 @@ defmodule Explorer.SmartContract.Solidity.Verifier do ) :: nil | binary def parse_constructor_arguments_for_sourcify_contract(address_hash, abi, deployed_bytecode) when is_binary(deployed_bytecode) do - creation_tx_input = - case Chain.smart_contract_creation_tx_bytecode(address_hash) do + creation_transaction_input = + case Chain.smart_contract_creation_transaction_bytecode(address_hash) do %{init: init, created_contract_code: _created_contract_code} -> "0x" <> init_without_0x = init init_without_0x @@ -587,9 +593,9 @@ defmodule Explorer.SmartContract.Solidity.Verifier do with true <- has_constructor_with_params?(abi), check_function <- parse_constructor_and_return_check_function(abi), - false <- is_nil(creation_tx_input) || deployed_bytecode == "0x", + false <- is_nil(creation_transaction_input) || deployed_bytecode == "0x", {meta, meta_length} <- extract_meta_from_deployed_bytecode(deployed_bytecode), - [_bytecode, constructor_args] <- String.split(creation_tx_input, meta <> meta_length), + [_bytecode, constructor_args] <- String.split(creation_transaction_input, meta <> meta_length), ^constructor_args <- check_function.(constructor_args) do constructor_args else diff --git a/apps/explorer/lib/explorer/smart_contract/vyper/verifier.ex b/apps/explorer/lib/explorer/smart_contract/vyper/verifier.ex index 3ca5ac90994c..049c9f544524 100644 --- a/apps/explorer/lib/explorer/smart_contract/vyper/verifier.ex +++ b/apps/explorer/lib/explorer/smart_contract/vyper/verifier.ex @@ -117,10 +117,10 @@ defmodule Explorer.SmartContract.Vyper.Verifier do end defp vyper_verify_multipart(params, evm_version, files, address_hash) do - {creation_tx_input, deployed_bytecode, verifier_metadata} = fetch_data_for_verification(address_hash) + {creation_transaction_input, deployed_bytecode, verifier_metadata} = fetch_data_for_verification(address_hash) %{} - |> prepare_bytecode_for_microservice(creation_tx_input, deployed_bytecode) + |> prepare_bytecode_for_microservice(creation_transaction_input, deployed_bytecode) |> Map.put("evmVersion", evm_version) |> Map.put("sourceFiles", files) |> Map.put("compilerVersion", params["compiler_version"]) @@ -129,10 +129,10 @@ defmodule Explorer.SmartContract.Vyper.Verifier do end defp vyper_verify_standard_json(params, address_hash) do - {creation_tx_input, deployed_bytecode, verifier_metadata} = fetch_data_for_verification(address_hash) + {creation_transaction_input, deployed_bytecode, verifier_metadata} = fetch_data_for_verification(address_hash) %{} - |> prepare_bytecode_for_microservice(creation_tx_input, deployed_bytecode) + |> prepare_bytecode_for_microservice(creation_transaction_input, deployed_bytecode) |> Map.put("compilerVersion", params["compiler_version"]) |> Map.put("input", params["input"]) |> RustVerifierInterface.vyper_verify_standard_json(verifier_metadata) diff --git a/apps/explorer/lib/explorer/third_party_integrations/noves_fi.ex b/apps/explorer/lib/explorer/third_party_integrations/noves_fi.ex index 8d0a9fdcd406..d0fda30068f9 100644 --- a/apps/explorer/lib/explorer/third_party_integrations/noves_fi.ex +++ b/apps/explorer/lib/explorer/third_party_integrations/noves_fi.ex @@ -55,25 +55,25 @@ defmodule Explorer.ThirdPartyIntegrations.NovesFi do @doc """ Noves.fi /evm/{chain}/tx/{txHash} endpoint """ - @spec tx_url(String.t()) :: String.t() - def tx_url(transaction_hash_string) do + @spec transaction_url(String.t()) :: String.t() + def transaction_url(transaction_hash_string) do "#{base_url()}/evm/#{chain_name()}/tx/#{transaction_hash_string}" end @doc """ Noves.fi /evm/{chain}/describeTxs endpoint """ - @spec describe_txs_url() :: String.t() - def describe_txs_url do + @spec describe_transactions_url() :: String.t() + def describe_transactions_url do "#{base_url()}/evm/#{chain_name()}/describeTxs" end @doc """ - Noves.fi /evm/{chain}/txs/{accountAddress} endpoint + Noves.fi /evm/{chain}/transactions/{accountAddress} endpoint """ - @spec address_txs_url(String.t()) :: String.t() - def address_txs_url(address_hash_string) do - "#{base_url()}/evm/#{chain_name()}/txs/#{address_hash_string}" + @spec address_transactions_url(String.t()) :: String.t() + def address_transactions_url(address_hash_string) do + "#{base_url()}/evm/#{chain_name()}/transactions/#{address_hash_string}" end defp base_url do diff --git a/apps/explorer/priv/account/migrations/20241015091450_rename_tx_hash_field.exs b/apps/explorer/priv/account/migrations/20241015091450_rename_tx_hash_field.exs new file mode 100644 index 000000000000..61e1b43dc0a1 --- /dev/null +++ b/apps/explorer/priv/account/migrations/20241015091450_rename_tx_hash_field.exs @@ -0,0 +1,9 @@ +defmodule Explorer.Repo.Account.Migrations.RenameTxHashField do + use Ecto.Migration + + def change do + rename(table(:account_tag_transactions), :tx_hash, to: :transaction_hash) + rename(table(:account_tag_transactions), :tx_hash_hash, to: :transaction_hash_hash) + rename(table(:account_watchlist_notifications), :tx_fee, to: :transaction_fee) + end +end diff --git a/apps/explorer/priv/arbitrum/migrations/20241015093220_rename_tx_hash_field_arbitrum.exs b/apps/explorer/priv/arbitrum/migrations/20241015093220_rename_tx_hash_field_arbitrum.exs new file mode 100644 index 000000000000..2e6aa5028e04 --- /dev/null +++ b/apps/explorer/priv/arbitrum/migrations/20241015093220_rename_tx_hash_field_arbitrum.exs @@ -0,0 +1,7 @@ +defmodule Explorer.Repo.Arbitrum.Migrations.RenameTxHashFieldArbitrum do + use Ecto.Migration + + def change do + rename(table(:arbitrum_batch_l2_transactions), :tx_hash, to: :transaction_hash) + end +end diff --git a/apps/explorer/priv/optimism/migrations/20241015140121_rename_tx_related_field_optimism.exs b/apps/explorer/priv/optimism/migrations/20241015140121_rename_tx_related_field_optimism.exs new file mode 100644 index 000000000000..6f2b4a493540 --- /dev/null +++ b/apps/explorer/priv/optimism/migrations/20241015140121_rename_tx_related_field_optimism.exs @@ -0,0 +1,7 @@ +defmodule Explorer.Repo.Optimism.Migrations.RenameTxRelatedFieldOptimism do + use Ecto.Migration + + def change do + rename(table(:transactions), :l1_tx_origin, to: :l1_transaction_origin) + end +end diff --git a/apps/explorer/priv/repo/migrations/20190613065856_add_tx_hash_inserted_at_index.exs b/apps/explorer/priv/repo/migrations/20190613065856_add_transaction_hash_inserted_at_index.exs similarity index 59% rename from apps/explorer/priv/repo/migrations/20190613065856_add_tx_hash_inserted_at_index.exs rename to apps/explorer/priv/repo/migrations/20190613065856_add_transaction_hash_inserted_at_index.exs index 4596b1e4d4b1..6402ee966706 100644 --- a/apps/explorer/priv/repo/migrations/20190613065856_add_tx_hash_inserted_at_index.exs +++ b/apps/explorer/priv/repo/migrations/20190613065856_add_transaction_hash_inserted_at_index.exs @@ -1,4 +1,4 @@ -defmodule Explorer.Repo.Migrations.AddTxHashInsertedAtIndex do +defmodule Explorer.Repo.Migrations.AddTransactionHashInsertedAtIndex do use Ecto.Migration def change do diff --git a/apps/explorer/priv/repo/migrations/20191018140054_add_pending_internal_txs_operation.exs b/apps/explorer/priv/repo/migrations/20191018140054_add_pending_internal_transactions_operation.exs similarity index 96% rename from apps/explorer/priv/repo/migrations/20191018140054_add_pending_internal_txs_operation.exs rename to apps/explorer/priv/repo/migrations/20191018140054_add_pending_internal_transactions_operation.exs index 13d46ab0139e..d7a7234026f4 100644 --- a/apps/explorer/priv/repo/migrations/20191018140054_add_pending_internal_txs_operation.exs +++ b/apps/explorer/priv/repo/migrations/20191018140054_add_pending_internal_transactions_operation.exs @@ -1,4 +1,4 @@ -defmodule Explorer.Repo.Migrations.AddPendingInternalTxsOperation do +defmodule Explorer.Repo.Migrations.AddPendingInternalTransactionsOperation do use Ecto.Migration def change do diff --git a/apps/explorer/priv/repo/migrations/20200421102450_pending_txs.exs b/apps/explorer/priv/repo/migrations/20200421102450_pending_transactions.exs similarity index 89% rename from apps/explorer/priv/repo/migrations/20200421102450_pending_txs.exs rename to apps/explorer/priv/repo/migrations/20200421102450_pending_transactions.exs index 4f699815e79a..93619991f0f6 100644 --- a/apps/explorer/priv/repo/migrations/20200421102450_pending_txs.exs +++ b/apps/explorer/priv/repo/migrations/20200421102450_pending_transactions.exs @@ -1,4 +1,4 @@ -defmodule Explorer.Repo.Migrations.PendingTxs do +defmodule Explorer.Repo.Migrations.PendingTransactions do use Ecto.Migration def up do diff --git a/apps/explorer/priv/repo/migrations/20211217201759_add_has_error_in_iternal_txs_field_to_transaction.exs b/apps/explorer/priv/repo/migrations/20211217201759_add_has_error_in_internal_txs_field_to_transaction.exs similarity index 100% rename from apps/explorer/priv/repo/migrations/20211217201759_add_has_error_in_iternal_txs_field_to_transaction.exs rename to apps/explorer/priv/repo/migrations/20211217201759_add_has_error_in_internal_txs_field_to_transaction.exs diff --git a/apps/explorer/priv/repo/migrations/20221126103223_add_txs_indexes.exs b/apps/explorer/priv/repo/migrations/20221126103223_add_transactions_indexes.exs similarity index 96% rename from apps/explorer/priv/repo/migrations/20221126103223_add_txs_indexes.exs rename to apps/explorer/priv/repo/migrations/20221126103223_add_transactions_indexes.exs index 241ed059688c..9cc38fb2e7aa 100644 --- a/apps/explorer/priv/repo/migrations/20221126103223_add_txs_indexes.exs +++ b/apps/explorer/priv/repo/migrations/20221126103223_add_transactions_indexes.exs @@ -1,4 +1,4 @@ -defmodule Explorer.Repo.Migrations.AddTxsIndexes do +defmodule Explorer.Repo.Migrations.AddTransactionsIndexes do use Ecto.Migration @disable_ddl_transaction true @disable_migration_lock true diff --git a/apps/explorer/priv/repo/migrations/20230417093914_allow_nil_tx_gas_price.exs b/apps/explorer/priv/repo/migrations/20230417093914_allow_nil_transaction_gas_price.exs similarity index 82% rename from apps/explorer/priv/repo/migrations/20230417093914_allow_nil_tx_gas_price.exs rename to apps/explorer/priv/repo/migrations/20230417093914_allow_nil_transaction_gas_price.exs index 01a984cfb09c..14dbd678fe48 100644 --- a/apps/explorer/priv/repo/migrations/20230417093914_allow_nil_tx_gas_price.exs +++ b/apps/explorer/priv/repo/migrations/20230417093914_allow_nil_transaction_gas_price.exs @@ -1,4 +1,4 @@ -defmodule Explorer.Repo.Migrations.AllowNilTxGasPrice do +defmodule Explorer.Repo.Migrations.AllowNilTransactionGasPrice do use Ecto.Migration def change do diff --git a/apps/explorer/priv/repo/migrations/20241015140214_rename_tx_related_field.exs b/apps/explorer/priv/repo/migrations/20241015140214_rename_tx_related_field.exs new file mode 100644 index 000000000000..948ae164c51c --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20241015140214_rename_tx_related_field.exs @@ -0,0 +1,7 @@ +defmodule Explorer.Repo.Migrations.RenameTxRelatedField do + use Ecto.Migration + + def change do + rename(table(:transactions), :has_error_in_internal_txs, to: :has_error_in_internal_transactions) + end +end diff --git a/apps/explorer/priv/zk_sync/migrations/20241015093336_rename_tx_hash_field_zksync.exs b/apps/explorer/priv/zk_sync/migrations/20241015093336_rename_tx_hash_field_zksync.exs new file mode 100644 index 000000000000..e62c62816547 --- /dev/null +++ b/apps/explorer/priv/zk_sync/migrations/20241015093336_rename_tx_hash_field_zksync.exs @@ -0,0 +1,7 @@ +defmodule Explorer.Repo.ZkSync.Migrations.RenameTxHashFieldArbitrum do + use Ecto.Migration + + def change do + rename(table(:zksync_batch_l2_transactions), :tx_hash, to: :transaction_hash) + end +end diff --git a/apps/explorer/test/explorer/account/notifier/email_test.exs b/apps/explorer/test/explorer/account/notifier/email_test.exs index 184e7d33b8a2..387aab7e6fab 100644 --- a/apps/explorer/test/explorer/account/notifier/email_test.exs +++ b/apps/explorer/test/explorer/account/notifier/email_test.exs @@ -42,7 +42,8 @@ defmodule Explorer.Account.Notifier.EmailTest do describe "composing email" do test "compose_email" do - {:ok, tx_hash} = string_to_transaction_hash("0x5d5ff210261f1b2d6e4af22ea494f428f9997d4ab614a629d4f1390004b3e80d") + {:ok, transaction_hash} = + string_to_transaction_hash("0x5d5ff210261f1b2d6e4af22ea494f428f9997d4ab614a629d4f1390004b3e80d") {:ok, from_hash} = string_to_address_hash("0x092D537737E767Dae48c28aE509f34094496f030") @@ -67,14 +68,14 @@ defmodule Explorer.Account.Notifier.EmailTest do watchlist_notification = %WatchlistNotification{ watchlist_address: watchlist_address, - transaction_hash: tx_hash, + transaction_hash: transaction_hash, from_address_hash: from_hash, to_address_hash: to_hash, direction: "incoming", method: "transfer", block_number: 24_121_177, amount: Decimal.new(1), - tx_fee: Decimal.new(210_000), + transaction_fee: Decimal.new(210_000), name: "wallet", type: "COIN" } @@ -108,7 +109,7 @@ defmodule Explorer.Account.Notifier.EmailTest do "transaction_hash" => "0x5d5ff210261f1b2d6e4af22ea494f428f9997d4ab614a629d4f1390004b3e80d", "transaction_url" => "https://eth.blockscout.com/tx/0x5d5ff210261f1b2d6e4af22ea494f428f9997d4ab614a629d4f1390004b3e80d", - "tx_fee" => Decimal.new(210_000), + "transaction_fee" => Decimal.new(210_000), "username" => "John Snow" }, template_id: "d-666" diff --git a/apps/explorer/test/explorer/account/notifier/notify_test.exs b/apps/explorer/test/explorer/account/notifier/notify_test.exs index 25a7fd34a3ee..1187837791fc 100644 --- a/apps/explorer/test/explorer/account/notifier/notify_test.exs +++ b/apps/explorer/test/explorer/account/notifier/notify_test.exs @@ -38,9 +38,9 @@ defmodule Explorer.Account.Notifier.NotifyTest do describe "notify" do test "when address not in any watchlist" do - tx = with_block(insert(:transaction)) + transaction = with_block(insert(:transaction)) - notify = Notify.call([tx]) + notify = Notify.call([transaction]) wn = WatchlistNotification @@ -60,17 +60,17 @@ defmodule Explorer.Account.Notifier.NotifyTest do _watchlist_address = Repo.preload(wa, watchlist: :identity) - tx = + transaction = %Transaction{ from_address: _from_address, to_address: _to_address, block_number: _block_number, - hash: _tx_hash + hash: _transaction_hash } = with_block(insert(:transaction, to_address: %Chain.Address{hash: address_hash})) - {_, fee} = Transaction.fee(tx, :gwei) - amount = Wei.to(tx.value, :ether) - notify = Notify.call([tx]) + {_, fee} = Transaction.fee(transaction, :gwei) + amount = Wei.to(transaction.value, :ether) + notify = Notify.call([transaction]) wn = WatchlistNotification @@ -83,7 +83,7 @@ defmodule Explorer.Account.Notifier.NotifyTest do assert wn.direction == "incoming" assert wn.method == "transfer" assert wn.subject == "Coin transaction" - assert wn.tx_fee == fee + assert wn.transaction_fee == fee assert wn.type == "COIN" end @@ -99,17 +99,17 @@ defmodule Explorer.Account.Notifier.NotifyTest do _watchlist_address = Repo.preload(wa, watchlist: :identity) - tx = + transaction = %Transaction{ from_address: _from_address, to_address: _to_address, block_number: _block_number, - hash: _tx_hash + hash: _transaction_hash } = with_block(insert(:transaction, to_address: %Chain.Address{hash: address_hash})) - {_, fee} = Transaction.fee(tx, :gwei) - amount = Wei.to(tx.value, :ether) - notify = Notify.call([tx]) + {_, fee} = Transaction.fee(transaction, :gwei) + amount = Wei.to(transaction.value, :ether) + notify = Notify.call([transaction]) wn = WatchlistNotification @@ -122,19 +122,19 @@ defmodule Explorer.Account.Notifier.NotifyTest do assert wn.direction == "incoming" assert wn.method == "transfer" assert wn.subject == "Coin transaction" - assert wn.tx_fee == fee + assert wn.transaction_fee == fee assert wn.type == "COIN" address = Repo.get(Chain.Address, address_hash) - tx = + transaction = %Transaction{ from_address: _from_address, to_address: _to_address, block_number: _block_number, - hash: _tx_hash + hash: _transaction_hash } = with_block(insert(:transaction, to_address: address)) - Notify.call([tx]) + Notify.call([transaction]) WatchlistNotification |> first diff --git a/apps/explorer/test/explorer/account/notifier/summary_test.exs b/apps/explorer/test/explorer/account/notifier/summary_test.exs index ebc82d5cd63b..9cb52e021aa0 100644 --- a/apps/explorer/test/explorer/account/notifier/summary_test.exs +++ b/apps/explorer/test/explorer/account/notifier/summary_test.exs @@ -9,18 +9,18 @@ defmodule Explorer.Account.Notifier.SummaryTest do describe "call" do test "Coin transaction" do - tx = + transaction = %Transaction{ from_address: from_address, to_address: to_address, block_number: block_number, - hash: tx_hash + hash: transaction_hash } = with_block(insert(:transaction)) - {_, fee} = Transaction.fee(tx, :gwei) - amount = Wei.to(tx.value, :ether) + {_, fee} = Transaction.fee(transaction, :gwei) + amount = Wei.to(transaction.value, :ether) - assert Summary.process(tx) == [ + assert Summary.process(transaction) == [ %Summary{ amount: amount, block_number: block_number, @@ -29,22 +29,22 @@ defmodule Explorer.Account.Notifier.SummaryTest do name: "ETH", subject: "Coin transaction", to_address_hash: to_address.hash, - transaction_hash: tx_hash, - tx_fee: fee, + transaction_hash: transaction_hash, + transaction_fee: fee, type: "COIN" } ] end test "Pending Coin transaction (w/o block)" do - tx = + transaction = %Transaction{ from_address: _from_address, to_address: _to_address, - hash: _tx_hash + hash: _transaction_hash } = insert(:transaction) - assert Summary.process(tx) == [] + assert Summary.process(transaction) == [] end test "Contract creation transaction" do @@ -53,21 +53,21 @@ defmodule Explorer.Account.Notifier.SummaryTest do block = insert(:block) - tx = + transaction = %Transaction{ from_address: _from_address, block_number: _block_number, - hash: tx_hash + hash: transaction_hash } = :transaction |> insert(from_address: address, to_address: nil) |> with_contract_creation(contract_address) |> with_block(block) - {_, fee} = Transaction.fee(tx, :gwei) - amount = Wei.to(tx.value, :ether) + {_, fee} = Transaction.fee(transaction, :gwei) + amount = Wei.to(transaction.value, :ether) - assert Summary.process(tx) == [ + assert Summary.process(transaction) == [ %Summary{ amount: amount, block_number: block.number, @@ -76,20 +76,20 @@ defmodule Explorer.Account.Notifier.SummaryTest do name: "ETH", subject: "Contract creation", to_address_hash: contract_address.hash, - transaction_hash: tx_hash, - tx_fee: fee, + transaction_hash: transaction_hash, + transaction_fee: fee, type: "COIN" } ] end test "ERC-20 Token transfer" do - tx = + transaction = %Transaction{ from_address: _from_address, to_address: _to_address, block_number: _block_number, - hash: _tx_hash + hash: _transaction_hash } = with_block(insert(:transaction)) transfer = @@ -101,12 +101,12 @@ defmodule Explorer.Account.Notifier.SummaryTest do token: token } = :token_transfer - |> insert(transaction: tx) + |> insert(transaction: transaction) |> Repo.preload([ :token ]) - {_, fee} = Transaction.fee(tx, :gwei) + {_, fee} = Transaction.fee(transaction, :gwei) token_decimals = Decimal.to_integer(token.decimals) @@ -123,8 +123,8 @@ defmodule Explorer.Account.Notifier.SummaryTest do name: "Infinite Token", subject: "ERC-20", to_address_hash: to_address.hash, - transaction_hash: tx.hash, - tx_fee: fee, + transaction_hash: transaction.hash, + transaction_fee: fee, type: "ERC-20" } ] @@ -133,12 +133,12 @@ defmodule Explorer.Account.Notifier.SummaryTest do test "ERC-721 Token transfer" do token = insert(:token, type: "ERC-721") - tx = + transaction = %Transaction{ from_address: _from_address, to_address: _to_address, block_number: _block_number, - hash: _tx_hash + hash: _transaction_hash } = with_block(insert(:transaction)) transfer = @@ -150,7 +150,7 @@ defmodule Explorer.Account.Notifier.SummaryTest do } = :token_transfer |> insert( - transaction: tx, + transaction: transaction, token_ids: [42], token_contract_address: token.contract_address ) @@ -158,7 +158,7 @@ defmodule Explorer.Account.Notifier.SummaryTest do :token ]) - {_, fee} = Transaction.fee(tx, :gwei) + {_, fee} = Transaction.fee(transaction, :gwei) assert Summary.process(transfer) == [ %Summary{ @@ -169,8 +169,8 @@ defmodule Explorer.Account.Notifier.SummaryTest do name: "Infinite Token", subject: "42", to_address_hash: to_address.hash, - transaction_hash: tx.hash, - tx_fee: fee, + transaction_hash: transaction.hash, + transaction_fee: fee, type: "ERC-721" } ] @@ -179,12 +179,12 @@ defmodule Explorer.Account.Notifier.SummaryTest do test "ERC-1155 single Token transfer" do token = insert(:token, type: "ERC-1155") - tx = + transaction = %Transaction{ from_address: _from_address, to_address: _to_address, block_number: _block_number, - hash: _tx_hash + hash: _transaction_hash } = with_block(insert(:transaction)) transfer = @@ -196,7 +196,7 @@ defmodule Explorer.Account.Notifier.SummaryTest do } = :token_transfer |> insert( - transaction: tx, + transaction: transaction, token_ids: [42], token_contract_address: token.contract_address ) @@ -204,7 +204,7 @@ defmodule Explorer.Account.Notifier.SummaryTest do :token ]) - {_, fee} = Transaction.fee(tx, :gwei) + {_, fee} = Transaction.fee(transaction, :gwei) assert Summary.process(transfer) == [ %Summary{ @@ -215,8 +215,8 @@ defmodule Explorer.Account.Notifier.SummaryTest do name: "Infinite Token", subject: "42", to_address_hash: to_address.hash, - transaction_hash: tx.hash, - tx_fee: fee, + transaction_hash: transaction.hash, + transaction_fee: fee, type: "ERC-1155" } ] @@ -225,12 +225,12 @@ defmodule Explorer.Account.Notifier.SummaryTest do test "ERC-1155 multiple Token transfer" do token = insert(:token, type: "ERC-1155") - tx = + transaction = %Transaction{ from_address: _from_address, to_address: _to_address, block_number: _block_number, - hash: _tx_hash + hash: _transaction_hash } = with_block(insert(:transaction)) transfer = @@ -242,7 +242,7 @@ defmodule Explorer.Account.Notifier.SummaryTest do } = :token_transfer |> insert( - transaction: tx, + transaction: transaction, token_ids: [23, 42], token_contract_address: token.contract_address ) @@ -250,7 +250,7 @@ defmodule Explorer.Account.Notifier.SummaryTest do :token ]) - {_, fee} = Transaction.fee(tx, :gwei) + {_, fee} = Transaction.fee(transaction, :gwei) assert Summary.process(transfer) == [ %Summary{ @@ -261,8 +261,8 @@ defmodule Explorer.Account.Notifier.SummaryTest do name: "Infinite Token", subject: "23, 42", to_address_hash: to_address.hash, - transaction_hash: tx.hash, - tx_fee: fee, + transaction_hash: transaction.hash, + transaction_fee: fee, type: "ERC-1155" } ] @@ -271,12 +271,12 @@ defmodule Explorer.Account.Notifier.SummaryTest do test "ERC-404 Token transfer with token id" do token = insert(:token, type: "ERC-404") - tx = + transaction = %Transaction{ from_address: _from_address, to_address: _to_address, block_number: _block_number, - hash: _tx_hash + hash: _transaction_hash } = with_block(insert(:transaction)) transfer = @@ -288,7 +288,7 @@ defmodule Explorer.Account.Notifier.SummaryTest do } = :token_transfer |> insert( - transaction: tx, + transaction: transaction, token_ids: [42], token_contract_address: token.contract_address ) @@ -296,7 +296,7 @@ defmodule Explorer.Account.Notifier.SummaryTest do :token ]) - {_, fee} = Transaction.fee(tx, :gwei) + {_, fee} = Transaction.fee(transaction, :gwei) token_decimals = Decimal.to_integer(token.decimals) @@ -313,8 +313,8 @@ defmodule Explorer.Account.Notifier.SummaryTest do name: "Infinite Token", subject: "42", to_address_hash: to_address.hash, - transaction_hash: tx.hash, - tx_fee: fee, + transaction_hash: transaction.hash, + transaction_fee: fee, type: "ERC-404" } ] @@ -323,12 +323,12 @@ defmodule Explorer.Account.Notifier.SummaryTest do test "ERC-404 Token transfer without token id" do token = insert(:token, type: "ERC-404") - tx = + transaction = %Transaction{ from_address: _from_address, to_address: _to_address, block_number: _block_number, - hash: _tx_hash + hash: _transaction_hash } = with_block(insert(:transaction)) transfer = @@ -340,7 +340,7 @@ defmodule Explorer.Account.Notifier.SummaryTest do } = :token_transfer |> insert( - transaction: tx, + transaction: transaction, token_ids: [], token_contract_address: token.contract_address ) @@ -348,7 +348,7 @@ defmodule Explorer.Account.Notifier.SummaryTest do :token ]) - {_, fee} = Transaction.fee(tx, :gwei) + {_, fee} = Transaction.fee(transaction, :gwei) token_decimals = Decimal.to_integer(token.decimals) @@ -365,8 +365,8 @@ defmodule Explorer.Account.Notifier.SummaryTest do name: "Infinite Token", subject: "ERC-404", to_address_hash: to_address.hash, - transaction_hash: tx.hash, - tx_fee: fee, + transaction_hash: transaction.hash, + transaction_fee: fee, type: "ERC-404" } ] diff --git a/apps/explorer/test/explorer/chain/cache/gas_price_oracle_test.exs b/apps/explorer/test/explorer/chain/cache/gas_price_oracle_test.exs index 669dfded465e..3fc1493ed2ad 100644 --- a/apps/explorer/test/explorer/chain/cache/gas_price_oracle_test.exs +++ b/apps/explorer/test/explorer/chain/cache/gas_price_oracle_test.exs @@ -28,7 +28,7 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do }}, []} = GasPriceOracle.get_average_gas_price(3, 35, 60, 90) end - test "returns gas prices for blocks with failed txs in the DB" do + test "returns gas prices for blocks with failed transactions in the DB" do block = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391") :transaction diff --git a/apps/explorer/test/explorer/chain/csv_export/address_token_transfer_csv_exporter_test.exs b/apps/explorer/test/explorer/chain/csv_export/address_token_transfer_csv_exporter_test.exs index 87bc3e37c9e6..c559b39dce4b 100644 --- a/apps/explorer/test/explorer/chain/csv_export/address_token_transfer_csv_exporter_test.exs +++ b/apps/explorer/test/explorer/chain/csv_export/address_token_transfer_csv_exporter_test.exs @@ -26,7 +26,7 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporterTest do |> Enum.to_list() |> Enum.drop(1) |> Enum.map(fn [ - [[], tx_hash], + [[], transaction_hash], _, [[], block_number], _, @@ -54,7 +54,7 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporterTest do _ ] -> %{ - tx_hash: tx_hash, + transaction_hash: transaction_hash, block_number: block_number, timestamp: timestamp, from_address: from_address, @@ -71,7 +71,7 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporterTest do end) assert result.block_number == to_string(transaction.block_number) - assert result.tx_hash == to_string(transaction.hash) + assert result.transaction_hash == to_string(transaction.hash) assert result.from_address == Address.checksum(token_transfer.from_address_hash) assert result.to_address == Address.checksum(token_transfer.to_address_hash) assert result.timestamp == to_string(transaction.block_timestamp) 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 86353db3e423..278034f2fd31 100644 --- a/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs +++ b/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs @@ -648,7 +648,7 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do consensus_block_2 = insert(:block, %{hash: hash_2, number: block_number - 2}) for _ <- 0..10 do - tx = + transaction = :transaction |> insert() |> with_block(consensus_block_2) @@ -656,7 +656,7 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do insert(:token_transfer, token_ids: [id], token_type: "ERC-721", - transaction: tx, + transaction: transaction, token_contract_address: tt.token_contract_address, block_number: consensus_block_2.number, block: consensus_block_2 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 17144ccd2fa5..2b04d60b9086 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 @@ -22,12 +22,12 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do 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 + test "transaction's has_error_in_internal_transactions become true when its internal_transaction (where index != 0) 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) assert :ok == transaction.status - assert nil == transaction.has_error_in_internal_txs + assert nil == transaction.has_error_in_internal_transactions index = 0 error = nil @@ -40,18 +40,18 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do internal_transaction_changes_1 = make_internal_transaction_changes(transaction, index, error) assert {:ok, _} = run_internal_transactions([internal_transaction_changes, internal_transaction_changes_1]) - tx = Repo.get(Transaction, transaction.hash) + transaction = Repo.get(Transaction, transaction.hash) - assert :ok == tx.status - assert true == tx.has_error_in_internal_txs + assert :ok == transaction.status + assert true == transaction.has_error_in_internal_transactions end - test "transaction's has_error_in_internal_txs become false when its internal_transaction (where index == 0) has an error" do + test "transaction's has_error_in_internal_transactions become false when its internal_transaction (where index == 0) 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) assert :ok == transaction.status - assert nil == transaction.has_error_in_internal_txs + assert nil == transaction.has_error_in_internal_transactions index = 0 error = "Reverted" @@ -59,18 +59,18 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do internal_transaction_changes = make_internal_transaction_changes(transaction, index, error) assert {:ok, _} = run_internal_transactions([internal_transaction_changes]) - tx = Repo.get(Transaction, transaction.hash) + transaction = Repo.get(Transaction, transaction.hash) - assert :ok == tx.status - assert false == tx.has_error_in_internal_txs + assert :ok == transaction.status + assert false == transaction.has_error_in_internal_transactions end - test "transaction's has_error_in_internal_txs become false when its internal_transaction has no error" do + test "transaction's has_error_in_internal_transactions become false when its internal_transaction has no error" do transaction = insert(:transaction) |> with_block(status: :ok) insert(:pending_block_operation, block_hash: transaction.block_hash, block_number: transaction.block_number) assert :ok == transaction.status - assert nil == transaction.has_error_in_internal_txs + assert nil == transaction.has_error_in_internal_transactions index = 0 error = nil @@ -84,10 +84,10 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do assert {:ok, _} = run_internal_transactions([internal_transaction_changes, internal_transaction_changes_1]) - tx = Repo.get(Transaction, transaction.hash) + transaction = Repo.get(Transaction, transaction.hash) - assert :ok == tx.status - assert false == tx.has_error_in_internal_txs + assert :ok == transaction.status + assert false == transaction.has_error_in_internal_transactions end test "simple coin transfer's status doesn't become :error when its internal_transaction has an error" do @@ -132,7 +132,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do assert :ok == Repo.get(Transaction, transaction2.hash).status end - test "for block with simple coin transfer and method calls, method calls internal txs have correct block_index" do + test "for block with simple coin transfer and method calls, method calls internal transactions have correct block_index" do a_block = insert(:block, number: 1000) transaction0 = insert(:transaction) |> with_block(a_block, status: :ok) transaction1 = insert(:transaction) |> with_block(a_block, status: :ok) diff --git a/apps/explorer/test/explorer/chain/import_test.exs b/apps/explorer/test/explorer/chain/import_test.exs index d79e0869b289..098d116fe91d 100644 --- a/apps/explorer/test/explorer/chain/import_test.exs +++ b/apps/explorer/test/explorer/chain/import_test.exs @@ -655,7 +655,7 @@ defmodule Explorer.Chain.ImportTest do } } - internal_txs_options = %{ + internal_transactions_options = %{ internal_transactions: %{ params: [ %{ @@ -685,7 +685,7 @@ defmodule Explorer.Chain.ImportTest do {:ok, block_hash_casted} = Explorer.Chain.Hash.Full.cast(block_hash) assert [^block_hash_casted] = Explorer.Repo.all(PendingBlockOperation.block_hashes()) - assert {:ok, _} = Import.all(internal_txs_options) + assert {:ok, _} = Import.all(internal_transactions_options) assert [] == Explorer.Repo.all(PendingBlockOperation.block_hashes()) end @@ -746,7 +746,7 @@ defmodule Explorer.Chain.ImportTest do } } - internal_txs_options = %{ + internal_transactions_options = %{ internal_transactions: %{ params: [ %{ @@ -775,7 +775,7 @@ defmodule Explorer.Chain.ImportTest do {:ok, block_hash_casted} = Explorer.Chain.Hash.Full.cast(block_hash) assert [^block_hash_casted] = Explorer.Repo.all(PendingBlockOperation.block_hashes()) - assert {:ok, _} = Import.all(internal_txs_options) + assert {:ok, _} = Import.all(internal_transactions_options) assert [] == Explorer.Repo.all(PendingBlockOperation.block_hashes()) end diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index bf8a8bb17d1c..ffd74e6520f7 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -482,7 +482,7 @@ defmodule Explorer.ChainTest do end end - describe "block_to_gas_used_by_1559_txs/1" do + describe "block_to_gas_used_by_1559_transactions/1" do test "sum of gas_usd from all transactions including legacy" do block = insert(:block, base_fee_per_gas: 4) @@ -504,12 +504,12 @@ defmodule Explorer.ChainTest do index: 2 ) - assert Decimal.new(10) == Chain.block_to_gas_used_by_1559_txs(block.hash) + assert Decimal.new(10) == Chain.block_to_gas_used_by_1559_transactions(block.hash) end end - describe "block_to_priority_fee_of_1559_txs/1" do - test "with transactions: tx.max_fee_per_gas = 0" do + describe "block_to_priority_fee_of_1559_transactions/1" do + test "with transactions: transaction.max_fee_per_gas = 0" do block = insert(:block, base_fee_per_gas: 4) insert(:transaction, @@ -522,10 +522,10 @@ defmodule Explorer.ChainTest do max_priority_fee_per_gas: 3 ) - assert Decimal.new(0) == Chain.block_to_priority_fee_of_1559_txs(block.hash) + assert Decimal.new(0) == Chain.block_to_priority_fee_of_1559_transactions(block.hash) end - test "with transactions: tx.max_fee_per_gas - block.base_fee_per_gas >= tx.max_priority_fee_per_gas" do + test "with transactions: transaction.max_fee_per_gas - block.base_fee_per_gas >= transaction.max_priority_fee_per_gas" do block = insert(:block, base_fee_per_gas: 1) insert(:transaction, @@ -538,10 +538,10 @@ defmodule Explorer.ChainTest do max_priority_fee_per_gas: 1 ) - assert Decimal.new(3) == Chain.block_to_priority_fee_of_1559_txs(block.hash) + assert Decimal.new(3) == Chain.block_to_priority_fee_of_1559_transactions(block.hash) end - test "with transactions: tx.max_fee_per_gas - block.base_fee_per_gas < tx.max_priority_fee_per_gas" do + test "with transactions: transaction.max_fee_per_gas - block.base_fee_per_gas < transaction.max_priority_fee_per_gas" do block = insert(:block, base_fee_per_gas: 4) insert(:transaction, @@ -554,7 +554,7 @@ defmodule Explorer.ChainTest do max_priority_fee_per_gas: 3 ) - assert Decimal.new(4) == Chain.block_to_priority_fee_of_1559_txs(block.hash) + assert Decimal.new(4) == Chain.block_to_priority_fee_of_1559_transactions(block.hash) end test "with legacy transactions" do @@ -569,7 +569,7 @@ defmodule Explorer.ChainTest do index: 1 ) - assert Decimal.new(24) == Chain.block_to_priority_fee_of_1559_txs(block.hash) + assert Decimal.new(24) == Chain.block_to_priority_fee_of_1559_transactions(block.hash) end test "0 in blockchain with no EIP-1559 implemented" do @@ -584,7 +584,7 @@ defmodule Explorer.ChainTest do index: 1 ) - assert 0 == Chain.block_to_priority_fee_of_1559_txs(block.hash) + assert 0 == Chain.block_to_priority_fee_of_1559_transactions(block.hash) end end @@ -743,7 +743,7 @@ defmodule Explorer.ChainTest do assert Chain.finished_indexing_internal_transactions?() end - test "finished indexing (no txs)" do + test "finished indexing (no transactions)" do assert Chain.finished_indexing_internal_transactions?() end diff --git a/apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs index a5500ecb84da..89dbd97ba48c 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs @@ -529,7 +529,7 @@ if Application.compile_env(:explorer, :chain_type) !== :zksync do assert abi != nil end - test "verifies smart-contract created from another contract using successful tx" do + test "verifies smart-contract created from another contract using successful transaction" do path = File.cwd!() <> "/test/support/fixture/smart_contract/contract_from_factory.sol" contract = File.read!(path) diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index 7d823a81c0f9..31754fff3885 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -186,7 +186,7 @@ defmodule Explorer.Factory do end def tag_transaction_db_factory(%{user: user}) do - %TagTransaction{name: sequence("name"), tx_hash: insert(:transaction).hash, identity_id: user.id} + %TagTransaction{name: sequence("name"), transaction_hash: insert(:transaction).hash, identity_id: user.id} end def address_to_tag_factory do diff --git a/apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_txn_without_block.json b/apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_transaction_without_block.json similarity index 100% rename from apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_txn_without_block.json rename to apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_transaction_without_block.json diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 7ab0f66905eb..1d11987a66bd 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -188,7 +188,7 @@ defmodule Indexer.Block.Fetcher do do: PolygonZkevmBridge.parse(blocks, logs), else: [] ), - {arbitrum_xlevel_messages, arbitrum_txs_for_further_handling} = + {arbitrum_xlevel_messages, arbitrum_transactions_for_further_handling} = ArbitrumMessaging.parse(transactions_with_receipts, logs), %FetchedBeneficiaries{params_set: beneficiary_params_set, errors: beneficiaries_errors} = fetch_beneficiaries(blocks, transactions_with_receipts, json_rpc_named_arguments), @@ -255,13 +255,13 @@ defmodule Indexer.Block.Fetcher do state, basic_import_options |> Map.merge(additional_options) |> import_options(chain_type_import_options) ), - {:tx_actions, {:ok, inserted_tx_actions}} <- - {:tx_actions, + {:transaction_actions, {:ok, inserted_transaction_actions}} <- + {:transaction_actions, Chain.import(%{ transaction_actions: %{params: transaction_actions}, timeout: :infinity })} do - inserted = Map.merge(inserted, inserted_tx_actions) + inserted = Map.merge(inserted, inserted_transaction_actions) Prometheus.Instrumenter.block_batch_fetch(fetch_time, callback_module) result = {:ok, %{inserted: inserted, errors: blocks_errors}} update_block_cache(inserted[:blocks]) @@ -270,7 +270,7 @@ defmodule Indexer.Block.Fetcher do update_uncles_cache(inserted[:block_second_degree_relations]) update_withdrawals_cache(inserted[:withdrawals]) - async_match_arbitrum_messages_to_l2(arbitrum_txs_for_further_handling) + async_match_arbitrum_messages_to_l2(arbitrum_transactions_for_further_handling) result else @@ -751,7 +751,7 @@ defmodule Indexer.Block.Fetcher do @spec async_match_arbitrum_messages_to_l2([map()]) :: :ok defp async_match_arbitrum_messages_to_l2([]), do: :ok - defp async_match_arbitrum_messages_to_l2(txs_with_messages_from_l1) do - ArbitrumMessagesToL2Matcher.async_discover_match(txs_with_messages_from_l1) + defp async_match_arbitrum_messages_to_l2(transactions_with_messages_from_l1) do + ArbitrumMessagesToL2Matcher.async_discover_match(transactions_with_messages_from_l1) end end diff --git a/apps/indexer/lib/indexer/block/realtime/fetcher.ex b/apps/indexer/lib/indexer/block/realtime/fetcher.ex index 598a24865535..d2c6e7ab351e 100644 --- a/apps/indexer/lib/indexer/block/realtime/fetcher.ex +++ b/apps/indexer/lib/indexer/block/realtime/fetcher.ex @@ -37,7 +37,7 @@ defmodule Indexer.Block.Realtime.Fetcher do alias Explorer.Utility.MissingRangesManipulator alias Indexer.{Block, Tracer} alias Indexer.Block.Realtime.TaskSupervisor - alias Indexer.Fetcher.Optimism.TxnBatch, as: OptimismTxnBatch + alias Indexer.Fetcher.Optimism.TransactionBatch, as: OptimismTransactionBatch alias Indexer.Fetcher.Optimism.Withdrawal, as: OptimismWithdrawal alias Indexer.Fetcher.PolygonEdge.{DepositExecute, Withdrawal} alias Indexer.Fetcher.PolygonZkevm.BridgeL2, as: PolygonZkevmBridgeL2 @@ -322,7 +322,7 @@ defmodule Indexer.Block.Realtime.Fetcher do defp remove_optimism_assets_by_number(block_number_to_fetch) do if Application.get_env(:explorer, :chain_type) == :optimism do - OptimismTxnBatch.handle_l2_reorg(block_number_to_fetch) + OptimismTransactionBatch.handle_l2_reorg(block_number_to_fetch) OptimismWithdrawal.remove(block_number_to_fetch) end end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/da/celestia.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/da/celestia.ex index 57c6c523ff92..e20db6ecd979 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/da/celestia.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/da/celestia.ex @@ -11,7 +11,7 @@ defmodule Indexer.Fetcher.Arbitrum.DA.Celestia do alias Explorer.Chain.Arbitrum - @enforce_keys [:batch_number, :height, :tx_commitment, :raw] + @enforce_keys [:batch_number, :height, :transaction_commitment, :raw] defstruct @enforce_keys @typedoc """ @@ -19,25 +19,25 @@ defmodule Indexer.Fetcher.Arbitrum.DA.Celestia do * `batch_number` - The batch number in Arbitrum rollup associated with the Celestia data. * `height` - The height of the block in Celestia. - * `tx_commitment` - Data commitment in Celestia. + * `transaction_commitment` - Data commitment in Celestia. * `raw` - Unparsed blob pointer data containing data root, proof, etc. """ @type t :: %__MODULE__{ batch_number: non_neg_integer(), height: non_neg_integer(), - tx_commitment: binary(), + transaction_commitment: binary(), raw: binary() } @typedoc """ Celestia Blob Descriptor struct: * `height` - The height of the block in Celestia. - * `tx_commitment` - Data commitment in Celestia. + * `transaction_commitment` - Data commitment in Celestia. * `raw` - Unparsed blob pointer data containing data root, proof, etc. """ @type blob_descriptor :: %{ :height => non_neg_integer(), - :tx_commitment => String.t(), + :transaction_commitment => String.t(), :raw => String.t() } @@ -67,14 +67,15 @@ defmodule Indexer.Fetcher.Arbitrum.DA.Celestia do _key::big-unsigned-integer-size(64), _num_leaves::big-unsigned-integer-size(64), _tuple_root_nonce::big-unsigned-integer-size(64), - tx_commitment::binary-size(32), + transaction_commitment::binary-size(32), _data_root::binary-size(32), _side_nodes_length::big-unsigned-integer-size(64), _rest::binary >> = raw ) do # https://github.com/celestiaorg/nitro-contracts/blob/celestia/blobstream/src/bridge/SequencerInbox.sol#L334-L360 - {:ok, :in_celestia, %__MODULE__{batch_number: batch_number, height: height, tx_commitment: tx_commitment, raw: raw}} + {:ok, :in_celestia, + %__MODULE__{batch_number: batch_number, height: height, transaction_commitment: transaction_commitment, raw: raw}} end def parse_batch_accompanying_data(_, _) do @@ -96,14 +97,14 @@ defmodule Indexer.Fetcher.Arbitrum.DA.Celestia do def prepare_for_import(source, %__MODULE__{} = da_info) do data = %{ height: da_info.height, - tx_commitment: ArbitrumHelper.bytes_to_hex_str(da_info.tx_commitment), + transaction_commitment: ArbitrumHelper.bytes_to_hex_str(da_info.transaction_commitment), raw: ArbitrumHelper.bytes_to_hex_str(da_info.raw) } [ %{ data_type: 0, - data_key: calculate_celestia_data_key(da_info.height, da_info.tx_commitment), + data_key: calculate_celestia_data_key(da_info.height, da_info.transaction_commitment), data: data, batch_number: da_info.batch_number } diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/messages_to_l2_matcher.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/messages_to_l2_matcher.ex index 9dab37bf16a4..8b855784b9ef 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/messages_to_l2_matcher.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/messages_to_l2_matcher.ex @@ -117,7 +117,7 @@ defmodule Indexer.Fetcher.Arbitrum.MessagesToL2Matcher do by the value of the recheck interval. ## Parameters - - `txs_with_timeouts`: A list of tuples, each containing a timeout and a + - `transactions_with_timeouts`: A list of tuples, each containing a timeout and a transaction with a potentially hashed request ID. - `state`: The current state of the task, including cached IDs of uncompleted messages and the recheck interval. @@ -125,7 +125,7 @@ defmodule Indexer.Fetcher.Arbitrum.MessagesToL2Matcher do ## Returns - `{:ok, updated_state}` if all transactions were processed successfully and no retries are needed. - - `{:retry, txs_to_retry, updated_state}` if some transactions need to be + - `{:retry, transactions_to_retry, updated_state}` if some transactions need to be retried, either due to unmatched request IDs or unexpired timeouts. The returned state always includes an updated cache of IDs of uncompleted @@ -140,18 +140,21 @@ defmodule Indexer.Fetcher.Arbitrum.MessagesToL2Matcher do {:ok, %{:uncompleted_messages => %{binary() => binary()}, optional(any()) => any()}} | {:retry, [{non_neg_integer(), min_transaction()}], %{:uncompleted_messages => %{binary() => binary()}, optional(any()) => any()}} - def run(txs_with_timeouts, %{uncompleted_messages: cached_uncompleted_messages_ids, recheck_interval: _} = state) - when is_list(txs_with_timeouts) do + def run( + transactions_with_timeouts, + %{uncompleted_messages: cached_uncompleted_messages_ids, recheck_interval: _} = state + ) + when is_list(transactions_with_timeouts) do # For next handling only the transactions with expired timeouts are needed. now = DateTime.to_unix(DateTime.utc_now(), :millisecond) - {txs, delayed_txs} = - txs_with_timeouts - |> Enum.reduce({[], []}, fn {timeout, tx}, {txs, delayed_txs} -> + {transactions, delayed_transactions} = + transactions_with_timeouts + |> Enum.reduce({[], []}, fn {timeout, transaction}, {transactions, delayed_transactions} -> if timeout > now do - {txs, [{timeout, tx} | delayed_txs]} + {transactions, [{timeout, transaction} | delayed_transactions]} else - {[tx | txs], delayed_txs} + {[transaction | transactions], delayed_transactions} end end) @@ -159,45 +162,47 @@ defmodule Indexer.Fetcher.Arbitrum.MessagesToL2Matcher do # ids of the uncompleted messages and update the transactions with the decoded # request ids. If it required, the cache is updated. # Possible outcomes: - # - no transactions were updated, because the txs list is empty, the cache is updated + # - no transactions were updated, because the transactions list is empty, the cache is updated # - no transactions were updated, because no matches in both cache and DB were found, the cache is updated # - all matches were found in the cache, the cache is not updated # - all matches were found in the DB, the cache is updated # - some matches were found in the cache, but not all, the cache is not updated - {updated?, handled_txs, updated_cache} = update_txs_with_hashed_ids(txs, cached_uncompleted_messages_ids) + {updated?, handled_transactions, updated_cache} = + update_transactions_with_hashed_ids(transactions, cached_uncompleted_messages_ids) + updated_state = %{state | uncompleted_messages: updated_cache} - case {updated?, txs == []} do + case {updated?, transactions == []} do {false, true} -> # There were no transactions with expired timeouts, so counters of the transactions # updated and the transactions are scheduled for retry. - {:retry, delayed_txs, updated_state} + {:retry, delayed_transactions, updated_state} {false, false} -> # Some of the transactions were with expired timeouts, but no matches were found # for these transaction in the cache or the DB. Timeouts for such transactions # are re-initialized and they are added to the list with transactions with # updated counters. - txs_to_retry = - delayed_txs ++ initialize_timeouts(handled_txs, now + state.recheck_interval) + transactions_to_retry = + delayed_transactions ++ initialize_timeouts(handled_transactions, now + state.recheck_interval) - {:retry, txs_to_retry, updated_state} + {:retry, transactions_to_retry, updated_state} {true, _} -> - {messages, txs_to_retry_wo_timeouts} = MessagingUtils.filter_l1_to_l2_messages(handled_txs) + {messages, transactions_to_retry_wo_timeouts} = MessagingUtils.filter_l1_to_l2_messages(handled_transactions) MessagingUtils.import_to_db(messages) - if txs_to_retry_wo_timeouts == [] and delayed_txs == [] do + if transactions_to_retry_wo_timeouts == [] and delayed_transactions == [] do {:ok, updated_state} else # Either some of the transactions with expired timeouts don't have a matching # request id in the cache or the DB, or there are transactions with non-expired # timeouts. All these transactions are needed to be scheduled for retry. - txs_to_retry = - delayed_txs ++ initialize_timeouts(txs_to_retry_wo_timeouts, now + state.recheck_interval) + transactions_to_retry = + delayed_transactions ++ initialize_timeouts(transactions_to_retry_wo_timeouts, now + state.recheck_interval) - {:retry, txs_to_retry, updated_state} + {:retry, transactions_to_retry, updated_state} end end end @@ -209,19 +214,19 @@ defmodule Indexer.Fetcher.Arbitrum.MessagesToL2Matcher do require further matching. ## Parameters - - `txs_with_messages_from_l1`: A list of transactions containing L1-to-L2 + - `transactions_with_messages_from_l1`: A list of transactions containing L1-to-L2 messages with hashed message IDs. ## Returns - `:ok` """ @spec async_discover_match([min_transaction()]) :: :ok - def async_discover_match(txs_with_messages_from_l1) do + def async_discover_match(transactions_with_messages_from_l1) do # Do nothing in case if the indexing chain is not Arbitrum or the feature is disabled. if MessagesToL2MatcherSupervisor.disabled?() do :ok else - BufferedTask.buffer(__MODULE__, Enum.map(txs_with_messages_from_l1, &{0, &1}), false) + BufferedTask.buffer(__MODULE__, Enum.map(transactions_with_messages_from_l1, &{0, &1}), false) end end @@ -254,7 +259,7 @@ defmodule Indexer.Fetcher.Arbitrum.MessagesToL2Matcher do # no matches are found in the cache, it fetches fresh data from the database. # # ## Parameters - # - `txs`: A list of transactions with potentially hashed request IDs. + # - `transactions`: A list of transactions with potentially hashed request IDs. # - `cached_uncompleted_messages_ids`: A map of cached hashed message IDs to their # original forms. # @@ -269,28 +274,29 @@ defmodule Indexer.Fetcher.Arbitrum.MessagesToL2Matcher do # - If the cache is used successfully, it's returned as-is, even if potentially # outdated. # - If the cache fails, fresh data is fetched and returned, updating the cache. - @spec update_txs_with_hashed_ids([min_transaction()], %{binary() => binary()}) :: + @spec update_transactions_with_hashed_ids([min_transaction()], %{binary() => binary()}) :: {boolean(), [min_transaction()], %{binary() => binary()}} - defp update_txs_with_hashed_ids([], cache), do: {false, [], cache} + defp update_transactions_with_hashed_ids([], cache), do: {false, [], cache} - defp update_txs_with_hashed_ids(txs, cached_uncompleted_messages_ids) do + defp update_transactions_with_hashed_ids(transactions, cached_uncompleted_messages_ids) do # Try to use the cached DB response first. That makes sense if historical # messages are being processed (by catchup block fetcher or by the missing - # messages handler). Since amount of txs provided to this function is limited + # messages handler). Since amount of transactions provided to this function is limited # it OK to inspect the cache before making a DB request. - case revise_txs_with_hashed_ids(txs, cached_uncompleted_messages_ids, true) do + case revise_transactions_with_hashed_ids(transactions, cached_uncompleted_messages_ids, true) do {_, false} -> # If no matches were found in the cache, try to fetch uncompleted messages from the DB. uncompleted_messages = get_hashed_ids_for_uncompleted_messages() - {updated_txs, updated?} = revise_txs_with_hashed_ids(txs, uncompleted_messages, false) + {updated_transactions, updated?} = + revise_transactions_with_hashed_ids(transactions, uncompleted_messages, false) - {updated?, updated_txs, uncompleted_messages} + {updated?, updated_transactions, uncompleted_messages} - {updated_txs, _} -> + {updated_transactions, _} -> # There could be a case when some hashed ids were not found since the cache is outdated - # such txs will be scheduled for retry and the cache will be updated then. - {true, updated_txs, cached_uncompleted_messages_ids} + # such transactions will be scheduled for retry and the cache will be updated then. + {true, updated_transactions, cached_uncompleted_messages_ids} end end @@ -302,7 +308,7 @@ defmodule Indexer.Fetcher.Arbitrum.MessagesToL2Matcher do # (non-hashed) form. # # ## Parameters - # - `txs`: A list of transactions with potentially hashed request IDs. + # - `transactions`: A list of transactions with potentially hashed request IDs. # - `uncompleted_messages`: A map of hashed message IDs to their original forms. # - `report?`: A boolean flag indicating whether to log decoding attempts. # @@ -310,27 +316,31 @@ defmodule Indexer.Fetcher.Arbitrum.MessagesToL2Matcher do # A tuple containing: # - An updated list of transactions, with some request IDs potentially replaced. # - A boolean indicating whether any transactions were updated. - @spec revise_txs_with_hashed_ids([min_transaction()], %{binary() => binary()}, boolean()) :: + @spec revise_transactions_with_hashed_ids([min_transaction()], %{binary() => binary()}, boolean()) :: {[min_transaction()], boolean()} - defp revise_txs_with_hashed_ids(txs, uncompleted_messages, report?) do - txs - |> Enum.reduce({[], false}, fn tx, {updated_txs, updated?} -> - if report?, do: log_info("Attempting to decode the request id #{tx.request_id} in the tx #{tx.hash}") - - case Map.get(uncompleted_messages, tx.request_id) do + defp revise_transactions_with_hashed_ids(transactions, uncompleted_messages, report?) do + transactions + |> Enum.reduce({[], false}, fn transaction, {updated_transactions, updated?} -> + if report?, + do: + log_info( + "Attempting to decode the request id #{transaction.request_id} in the transaction #{transaction.hash}" + ) + + case Map.get(uncompleted_messages, transaction.request_id) do nil -> - {[tx | updated_txs], updated?} + {[transaction | updated_transactions], updated?} id -> - {[%{tx | request_id: id} | updated_txs], true} + {[%{transaction | request_id: id} | updated_transactions], true} end end) end # Assigns a uniform timeout to each transaction in the given list. @spec initialize_timeouts([min_transaction()], non_neg_integer()) :: [{non_neg_integer(), min_transaction()}] - defp initialize_timeouts(txs_to_retry, timeout) do - txs_to_retry + defp initialize_timeouts(transactions_to_retry, timeout) do + transactions_to_retry |> Enum.map(&{timeout, &1}) end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/messaging.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/messaging.ex index bbed0955b80f..04f4268cf950 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/messaging.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/messaging.ex @@ -79,11 +79,11 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do when is_list(transactions) and is_boolean(report) do {transactions_with_proper_message_id, transactions_with_hashed_message_id} = transactions - |> Enum.filter(fn tx -> - tx[:request_id] != nil + |> Enum.filter(fn transaction -> + transaction[:request_id] != nil end) - |> Enum.split_with(fn tx -> - plain_message_id?(tx[:request_id]) + |> Enum.split_with(fn transaction -> + plain_message_id?(transaction[:request_id]) end) # Transform transactions with the plain message ID into messages @@ -137,7 +137,7 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do Processes a list of filtered rollup transactions representing L1-to-L2 messages, constructing a detailed message structure for each. ## Parameters - - `filtered_txs`: A list of rollup transaction entries, each representing an L1-to-L2 + - `filtered_transactions`: A list of rollup transaction entries, each representing an L1-to-L2 message transaction. ## Returns @@ -150,15 +150,15 @@ defmodule Indexer.Fetcher.Arbitrum.Messaging do [] end - def handle_filtered_l1_to_l2_messages(filtered_txs) when is_list(filtered_txs) do - filtered_txs - |> Enum.map(fn tx -> - log_debug("L1 to L2 message #{tx.hash} found with the type #{tx.type}") + def handle_filtered_l1_to_l2_messages(filtered_transactions) when is_list(filtered_transactions) do + filtered_transactions + |> Enum.map(fn transaction -> + log_debug("L1 to L2 message #{transaction.hash} found with the type #{transaction.type}") %{ direction: :to_l2, - message_id: quantity_to_integer(tx.request_id), - completion_transaction_hash: tx.hash, + message_id: quantity_to_integer(transaction.request_id), + completion_transaction_hash: transaction.hash, status: :relayed } |> complete_to_params() diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/tracking_batches_statuses.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/tracking_batches_statuses.ex index af44220f0d02..8e42afd52242 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/tracking_batches_statuses.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/tracking_batches_statuses.ex @@ -26,7 +26,7 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do rollup blocks. - `:check_historical_executions`: Manages historical executions of L2-to-L1 messages. - - `:check_lifecycle_txs_finalization`: Finalizes the status of lifecycle + - `:check_lifecycle_transactions_finalization`: Finalizes the status of lifecycle transactions, confirming the blocks and messages involved. Discovery of rollup transaction batches is executed by requesting logs on L1 @@ -88,7 +88,7 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do config_tracker = Application.get_all_env(:indexer)[__MODULE__] recheck_interval = config_tracker[:recheck_interval] messages_to_blocks_shift = config_tracker[:messages_to_blocks_shift] - track_l1_tx_finalization = config_tracker[:track_l1_tx_finalization] + track_l1_transaction_finalization = config_tracker[:track_l1_transaction_finalization] finalized_confirmations = config_tracker[:finalized_confirmations] confirmation_batches_depth = config_tracker[:confirmation_batches_depth] new_batches_limit = config_tracker[:new_batches_limit] @@ -104,7 +104,7 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do json_rpc_named_arguments: IndexerHelper.json_rpc_named_arguments(l1_rpc), logs_block_range: l1_rpc_block_range, chunk_size: l1_rpc_chunk_size, - track_finalization: track_l1_tx_finalization, + track_finalization: track_l1_transaction_finalization, finalized_confirmations: finalized_confirmations }, rollup_rpc: %{ @@ -417,7 +417,7 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do # are executed. # # After processing, it immediately transitions to finalizing lifecycle transactions - # by sending the `:check_lifecycle_txs_finalization` message. + # by sending the `:check_lifecycle_transactions_finalization` message. # # ## Parameters # - `:check_historical_executions`: The message that triggers the function. @@ -432,7 +432,7 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do {handle_duration, {:ok, start_block}} = :timer.tc(&NewL1Executions.discover_historical_l1_messages_executions/1, [state]) - Process.send(self(), :check_lifecycle_txs_finalization, []) + Process.send(self(), :check_lifecycle_transactions_finalization, []) new_data = Map.merge(state.data, %{ @@ -454,17 +454,17 @@ defmodule Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses do # message is delayed to account for the time spent on the previous handlers' execution. # # ## Parameters - # - `:check_lifecycle_txs_finalization`: The message that triggers the function. + # - `:check_lifecycle_transactions_finalization`: The message that triggers the function. # - `state`: The current state of the fetcher, containing the configuration needed for # the lifecycle transactions status update. # # ## Returns # - `{:noreply, new_state}` where `new_state` is the updated state with the reset duration. @impl GenServer - def handle_info(:check_lifecycle_txs_finalization, state) do + def handle_info(:check_lifecycle_transactions_finalization, state) do {handle_duration, _} = if state.config.l1_rpc.track_finalization do - :timer.tc(&L1Finalization.monitor_lifecycle_txs/1, [state]) + :timer.tc(&L1Finalization.monitor_lifecycle_transactions/1, [state]) else {0, nil} end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex index 0b2dcd75df6b..849e2f60c951 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex @@ -24,11 +24,11 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do the next available indices are assigned. ## Parameters - - `new_l1_txs`: A map of L1 transaction descriptions. The keys of the map are + - `new_l1_transactions`: A map of L1 transaction descriptions. The keys of the map are transaction hashes. ## Returns - - `l1_txs`: A map of L1 transaction descriptions. Each element is extended with + - `l1_transactions`: A map of L1 transaction descriptions. Each element is extended with the key `:id`, representing the index of the L1 transaction in the `arbitrum_lifecycle_l1_transactions` table. """ @@ -42,46 +42,46 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do } }) :: %{binary() => Arbitrum.LifecycleTransaction.to_import()} # TODO: consider a way to remove duplicate with ZkSync.Utils.Db - def get_indices_for_l1_transactions(new_l1_txs) - when is_map(new_l1_txs) do + def get_indices_for_l1_transactions(new_l1_transactions) + when is_map(new_l1_transactions) do # Get indices for l1 transactions previously handled - l1_txs = - new_l1_txs + l1_transactions = + new_l1_transactions |> Map.keys() |> Reader.lifecycle_transaction_ids() - |> Enum.reduce(new_l1_txs, fn {hash, id}, txs -> - {_, txs} = - Map.get_and_update!(txs, hash.bytes, fn l1_tx -> - {l1_tx, Map.put(l1_tx, :id, id)} + |> Enum.reduce(new_l1_transactions, fn {hash, id}, transactions -> + {_, transactions} = + Map.get_and_update!(transactions, hash.bytes, fn l1_transaction -> + {l1_transaction, Map.put(l1_transaction, :id, id)} end) - txs + transactions end) # Get the next index for the first new transaction based # on the indices existing in DB - l1_tx_next_id = Reader.next_lifecycle_transaction_id() + l1_transaction_next_id = Reader.next_lifecycle_transaction_id() # Assign new indices for the transactions which are not in # the l1 transactions table yet - {updated_l1_txs, _} = - l1_txs + {updated_l1_transactions, _} = + l1_transactions |> Map.keys() |> Enum.reduce( - {l1_txs, l1_tx_next_id}, - fn hash, {txs, next_id} -> - tx = txs[hash] - id = Map.get(tx, :id) + {l1_transactions, l1_transaction_next_id}, + fn hash, {transactions, next_id} -> + transaction = transactions[hash] + id = Map.get(transaction, :id) if is_nil(id) do - {Map.put(txs, hash, Map.put(tx, :id, next_id)), next_id + 1} + {Map.put(transactions, hash, Map.put(transaction, :id, next_id)), next_id + 1} else - {txs, next_id} + {transactions, next_id} end end ) - updated_l1_txs + updated_l1_transactions end @doc """ @@ -89,7 +89,7 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do `arbitrum_lifecycle_l1_transactions` table and converts them to maps. ## Parameters - - `l1_tx_hashes`: A list of hashes to retrieve L1 transactions for. + - `l1_transaction_hashes`: A list of hashes to retrieve L1 transactions for. ## Returns - A list of maps representing the `Explorer.Chain.Arbitrum.LifecycleTransaction` @@ -97,8 +97,8 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do compatible with the database import operation. """ @spec lifecycle_transactions([binary()]) :: [Arbitrum.LifecycleTransaction.to_import()] - def lifecycle_transactions(l1_tx_hashes) do - l1_tx_hashes + def lifecycle_transactions(l1_transaction_hashes) do + l1_transaction_hashes |> Reader.lifecycle_transactions() |> Enum.map(&lifecycle_transaction_to_map/1) end @@ -904,9 +904,9 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do end @spec lifecycle_transaction_to_map(Arbitrum.LifecycleTransaction.t()) :: Arbitrum.LifecycleTransaction.to_import() - defp lifecycle_transaction_to_map(tx) do + defp lifecycle_transaction_to_map(transaction) do [:id, :hash, :block_number, :timestamp, :status] - |> db_record_to_map(tx) + |> db_record_to_map(transaction) end @spec rollup_block_to_map(Arbitrum.BatchBlock.t()) :: Arbitrum.BatchBlock.to_import() diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/helper.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/helper.ex index 7dcd56b76578..7c0dc56624fd 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/helper.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/helper.ex @@ -45,32 +45,32 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Helper do and a status. The status is determined based on whether finalization tracking is enabled. ## Parameters - - `lifecycle_txs`: A map where each key is a transaction identifier, and the value is + - `lifecycle_transactions`: A map where each key is a transaction identifier, and the value is a map containing at least the block number (`:block`). - `blocks_to_ts`: A map linking block numbers to their corresponding timestamps. - `track_finalization?`: A boolean flag indicating whether to mark transactions as unfinalized or finalized. ## Returns - - An updated map of the same structure as `lifecycle_txs` but with each transaction extended to include: + - An updated map of the same structure as `lifecycle_transactions` but with each transaction extended to include: - `timestamp`: The timestamp of the block in which the transaction is included. - `status`: Either `:unfinalized` if `track_finalization?` is `true`, or `:finalized` otherwise. """ - @spec extend_lifecycle_txs_with_ts_and_status( + @spec extend_lifecycle_transactions_with_ts_and_status( %{binary() => %{:block => non_neg_integer(), optional(any()) => any()}}, %{non_neg_integer() => DateTime.t()}, boolean() ) :: %{binary() => LifecycleTransaction.to_import()} - def extend_lifecycle_txs_with_ts_and_status(lifecycle_txs, blocks_to_ts, track_finalization?) - when is_map(lifecycle_txs) and is_map(blocks_to_ts) and is_boolean(track_finalization?) do - lifecycle_txs + def extend_lifecycle_transactions_with_ts_and_status(lifecycle_transactions, blocks_to_ts, track_finalization?) + when is_map(lifecycle_transactions) and is_map(blocks_to_ts) and is_boolean(track_finalization?) do + lifecycle_transactions |> Map.keys() - |> Enum.reduce(%{}, fn tx_key, updated_txs -> + |> Enum.reduce(%{}, fn transaction_key, updated_transactions -> Map.put( - updated_txs, - tx_key, - Map.merge(lifecycle_txs[tx_key], %{ - timestamp: blocks_to_ts[lifecycle_txs[tx_key].block_number], + updated_transactions, + transaction_key, + Map.merge(lifecycle_transactions[transaction_key], %{ + timestamp: blocks_to_ts[lifecycle_transactions[transaction_key].block_number], status: if track_finalization? do :unfinalized @@ -88,32 +88,32 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Helper do This function checks if the given lifecycle transaction has the same block number and timestamp as the provided values. If they are the same, it returns `{:same, nil}`. If they differ, it updates the transaction with the new block number and timestamp, - logs the update, and returns `{:updated, updated_tx}`. + logs the update, and returns `{:updated, updated_transaction}`. ## Parameters - - `tx`: The lifecycle transaction to compare and potentially update. + - `transaction`: The lifecycle transaction to compare and potentially update. - `{new_block_num, new_ts}`: A tuple containing the new block number and timestamp. - - `tx_type_str`: A string describing the type of the transaction for logging purposes. + - `transaction_type_str`: A string describing the type of the transaction for logging purposes. ## Returns - `{:same, nil}` if the transaction block number and timestamp are the same as the provided values. - - `{:updated, updated_tx}` if the transaction was updated with the new block number and timestamp. + - `{:updated, updated_transaction}` if the transaction was updated with the new block number and timestamp. """ - @spec compare_lifecycle_tx_and_update( + @spec compare_lifecycle_transaction_and_update( LifecycleTransaction.to_import(), {non_neg_integer(), DateTime.t()}, String.t() ) :: {:same, nil} | {:updated, LifecycleTransaction.to_import()} - def compare_lifecycle_tx_and_update(tx, {new_block_num, new_ts}, tx_type_str) do - if tx.block_number == new_block_num and DateTime.compare(tx.timestamp, new_ts) == :eq do + def compare_lifecycle_transaction_and_update(transaction, {new_block_num, new_ts}, transaction_type_str) do + if transaction.block_number == new_block_num and DateTime.compare(transaction.timestamp, new_ts) == :eq do {:same, nil} else log_info( - "The #{tx_type_str} transaction 0x#{tx.hash |> Base.encode16(case: :lower)} will be updated with the new block number and timestamp" + "The #{transaction_type_str} transaction 0x#{transaction.hash |> Base.encode16(case: :lower)} will be updated with the new block number and timestamp" ) {:updated, - Map.merge(tx, %{ + Map.merge(transaction, %{ block_number: new_block_num, timestamp: new_ts })} diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex index 5f4c688dcd4b..b831022bbcf2 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/utils/rpc.ex @@ -102,7 +102,7 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Rpc do Constructs a JSON RPC request to retrieve a transaction by its hash. ## Parameters - - `%{hash: tx_hash, id: id}`: A map containing the transaction hash (`tx_hash`) and + - `%{hash: transaction_hash, id: id}`: A map containing the transaction hash (`transaction_hash`) and an identifier (`id`) for the request, which can be used later to establish correspondence between requests and responses. @@ -111,9 +111,9 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Rpc do the transaction details associated with the given hash. """ @spec transaction_by_hash_request(%{hash: EthereumJSONRPC.hash(), id: non_neg_integer()}) :: Transport.request() - def transaction_by_hash_request(%{id: id, hash: tx_hash}) - when is_binary(tx_hash) and is_integer(id) do - EthereumJSONRPC.request(%{id: id, method: "eth_getTransactionByHash", params: [tx_hash]}) + def transaction_by_hash_request(%{id: id, hash: transaction_hash}) + when is_binary(transaction_hash) and is_integer(id) do + EthereumJSONRPC.request(%{id: id, method: "eth_getTransactionByHash", params: [transaction_hash]}) end @doc """ @@ -325,7 +325,7 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Rpc do Executes a list of transaction requests and retrieves the sender (from) addresses for each. ## Parameters - - `txs_requests`: A list of `Transport.request()` instances representing the transaction requests. + - `transactions_requests`: A list of `Transport.request()` instances representing the transaction requests. - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. - `chunk_size`: The number of requests to be processed in each batch, defining the size of the chunks. @@ -337,9 +337,9 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Rpc do EthereumJSONRPC.json_rpc_named_arguments(), non_neg_integer() ) :: [%{EthereumJSONRPC.hash() => EthereumJSONRPC.address()}] - def execute_transactions_requests_and_get_from(txs_requests, json_rpc_named_arguments, chunk_size) - when is_list(txs_requests) and is_integer(chunk_size) do - txs_requests + def execute_transactions_requests_and_get_from(transactions_requests, json_rpc_named_arguments, chunk_size) + when is_list(transactions_requests) and is_integer(chunk_size) do + transactions_requests |> Enum.chunk_every(chunk_size) |> Enum.reduce(%{}, fn chunk, result -> chunk @@ -765,13 +765,13 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Rpc do @spec string_hash_to_bytes_hash(EthereumJSONRPC.hash() | nil) :: binary() def string_hash_to_bytes_hash(hash) do hash - |> json_tx_id_to_hash() + |> json_transaction_id_to_hash() |> Base.decode16!(case: :mixed) end - defp json_tx_id_to_hash(hash) do + defp json_transaction_id_to_hash(hash) do case hash do - "0x" <> tx_hash -> tx_hash + "0x" <> transaction_hash -> transaction_hash nil -> @zero_hash end end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex index e1421ff08454..f3b3386f9e93 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/historical_messages_on_l2.ex @@ -265,10 +265,10 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do if transactions_length > 0 do log_debug("#{transactions_length} historical messages to L2 discovered") - {messages, txs_for_further_handling} = + {messages, transactions_for_further_handling} = transactions |> Enum.chunk_every(chunk_size) - |> Enum.reduce({[], []}, fn chunk, {messages_acc, txs_acc} -> + |> Enum.reduce({[], []}, fn chunk, {messages_acc, transactions_acc} -> # Since DB does not contain the field RequestId specific to Arbitrum # all transactions will be requested from the rollup RPC endpoint. # The catchup process intended to be run once and only for the BS instance @@ -276,28 +276,28 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do # the new field in DB requests = build_transaction_requests(chunk) - {messages, txs_with_hashed_message_id} = + {messages, transactions_with_hashed_message_id} = requests |> Rpc.make_chunked_request(json_rpc_named_arguments, "eth_getTransactionByHash") |> Enum.map(&transaction_json_to_map/1) |> Messaging.filter_l1_to_l2_messages(false) - {messages ++ messages_acc, txs_with_hashed_message_id ++ txs_acc} + {messages ++ messages_acc, transactions_with_hashed_message_id ++ transactions_acc} end) handle_messages(messages) - handle_txs_with_hashed_message_id(txs_for_further_handling) + handle_transactions_with_hashed_message_id(transactions_for_further_handling) end {:ok, start_block} end # Constructs a list of `eth_getTransactionByHash` requests for a given list of transaction hashes. - defp build_transaction_requests(tx_hashes) do - tx_hashes - |> Enum.reduce([], fn tx_hash, requests_list -> + defp build_transaction_requests(transaction_hashes) do + transaction_hashes + |> Enum.reduce([], fn transaction_hash, requests_list -> [ - Rpc.transaction_by_hash_request(%{id: 0, hash: tx_hash}) + Rpc.transaction_by_hash_request(%{id: 0, hash: transaction_hash}) | requests_list ] end) @@ -352,19 +352,19 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.HistoricalMessagesOnL2 do # with the corresponding L1 message again. # # ## Parameters - # - `txs_with_hashed_message_id`: A list of transactions containing L1-to-L2 + # - `transactions_with_hashed_message_id`: A list of transactions containing L1-to-L2 # messages with hashed message IDs. # # ## Returns # - `:ok` - @spec handle_txs_with_hashed_message_id([map()]) :: :ok - defp handle_txs_with_hashed_message_id([]), do: :ok + @spec handle_transactions_with_hashed_message_id([map()]) :: :ok + defp handle_transactions_with_hashed_message_id([]), do: :ok - defp handle_txs_with_hashed_message_id(txs_with_hashed_message_id) do + defp handle_transactions_with_hashed_message_id(transactions_with_hashed_message_id) do log_info( - "#{length(txs_with_hashed_message_id)} completions of L1-to-L2 messages require message ID matching discovery" + "#{length(transactions_with_hashed_message_id)} completions of L1-to-L2 messages require message ID matching discovery" ) - ArbitrumMessagesToL2Matcher.async_discover_match(txs_with_hashed_message_id) + ArbitrumMessagesToL2Matcher.async_discover_match(transactions_with_hashed_message_id) end end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/l1_finalization.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/l1_finalization.ex index 9a5c457f3571..3230664d6a76 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/l1_finalization.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/l1_finalization.ex @@ -33,7 +33,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.L1Finalization do ## Returns - `:ok` """ - @spec monitor_lifecycle_txs(%{ + @spec monitor_lifecycle_transactions(%{ :config => %{ :l1_rpc => %{ :json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), @@ -43,7 +43,9 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.L1Finalization do }, optional(any()) => any() }) :: :ok - def monitor_lifecycle_txs(%{config: %{l1_rpc: %{json_rpc_named_arguments: json_rpc_named_arguments}}} = _state) do + def monitor_lifecycle_transactions( + %{config: %{l1_rpc: %{json_rpc_named_arguments: json_rpc_named_arguments}}} = _state + ) do {:ok, safe_block} = IndexerHelper.get_block_number_by_tag( "safe", @@ -51,20 +53,20 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.L1Finalization do Rpc.get_resend_attempts() ) - lifecycle_txs = Db.lifecycle_unfinalized_transactions(safe_block) + lifecycle_transactions = Db.lifecycle_unfinalized_transactions(safe_block) - if length(lifecycle_txs) > 0 do - log_info("Discovered #{length(lifecycle_txs)} lifecycle transaction to be finalized") + if length(lifecycle_transactions) > 0 do + log_info("Discovered #{length(lifecycle_transactions)} lifecycle transaction to be finalized") - updated_lifecycle_txs = - lifecycle_txs - |> Enum.map(fn tx -> - Map.put(tx, :status, :finalized) + updated_lifecycle_transactions = + lifecycle_transactions + |> Enum.map(fn transaction -> + Map.put(transaction, :status, :finalized) end) {:ok, _} = Chain.import(%{ - arbitrum_lifecycle_transactions: %{params: updated_lifecycle_txs}, + arbitrum_lifecycle_transactions: %{params: updated_lifecycle_transactions}, timeout: :infinity }) end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex index fc58788f5e2b..42eb3367df0d 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_batches.ex @@ -607,7 +607,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do logs |> Enum.chunk_every(new_batches_limit) |> Enum.each(fn chunked_logs -> - {batches, lifecycle_txs, rollup_blocks, rollup_txs, committed_txs, da_records} = + {batches, lifecycle_transactions, rollup_blocks, rollup_transactions, committed_transactions, da_records} = handle_batches_from_logs( chunked_logs, messages_to_blocks_shift, @@ -619,18 +619,18 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do {:ok, _} = Chain.import(%{ - arbitrum_lifecycle_transactions: %{params: lifecycle_txs}, + arbitrum_lifecycle_transactions: %{params: lifecycle_transactions}, arbitrum_l1_batches: %{params: batches}, arbitrum_batch_blocks: %{params: rollup_blocks}, - arbitrum_batch_transactions: %{params: rollup_txs}, - arbitrum_messages: %{params: committed_txs}, + arbitrum_batch_transactions: %{params: rollup_transactions}, + arbitrum_messages: %{params: committed_transactions}, arbitrum_da_multi_purpose_records: %{params: da_records}, timeout: :infinity }) if not Enum.empty?(batches) and new_batches_discovery? do Publisher.broadcast( - [{:new_arbitrum_batches, extend_batches_with_commitment_transactions(batches, lifecycle_txs)}], + [{:new_arbitrum_batches, extend_batches_with_commitment_transactions(batches, lifecycle_transactions)}], :realtime ) end @@ -746,14 +746,14 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do |> parse_logs_to_get_batch_numbers() |> Db.batches_exist() - {batches, txs_requests, blocks_requests, existing_commitment_txs} = + {batches, transactions_requests, blocks_requests, existing_commitment_transactions} = parse_logs_for_new_batches(logs, existing_batches) blocks_to_ts = Rpc.execute_blocks_requests_and_get_ts(blocks_requests, json_rpc_named_arguments, chunk_size) - {initial_lifecycle_txs, batches_to_import, da_info} = - execute_tx_requests_parse_txs_calldata( - txs_requests, + {initial_lifecycle_transactions, batches_to_import, da_info} = + execute_transaction_requests_parse_transactions_calldata( + transactions_requests, msg_to_block_shift, blocks_to_ts, batches, @@ -766,17 +766,18 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # Check if the commitment transactions for the batches which are already in the database # needs to be updated in case of reorgs - lifecycle_txs_wo_indices = - initial_lifecycle_txs - |> Map.merge(update_lifecycle_txs_for_new_blocks(existing_commitment_txs, blocks_to_ts)) + lifecycle_transactions_wo_indices = + initial_lifecycle_transactions + |> Map.merge(update_lifecycle_transactions_for_new_blocks(existing_commitment_transactions, blocks_to_ts)) - {blocks_to_import, rollup_txs_to_import} = get_rollup_blocks_and_transactions(batches_to_import, rollup_rpc_config) + {blocks_to_import, rollup_transactions_to_import} = + get_rollup_blocks_and_transactions(batches_to_import, rollup_rpc_config) - lifecycle_txs = - lifecycle_txs_wo_indices + lifecycle_transactions = + lifecycle_transactions_wo_indices |> Db.get_indices_for_l1_transactions() - tx_counts_per_batch = batches_to_rollup_txs_amounts(rollup_txs_to_import) + transaction_counts_per_batch = batches_to_rollup_transactions_amounts(rollup_transactions_to_import) batches_list_to_import = batches_to_import @@ -784,15 +785,15 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do |> Enum.reduce([], fn batch, updated_batches_list -> [ batch - |> Map.put(:commitment_id, get_l1_tx_id_by_hash(lifecycle_txs, batch.tx_hash)) + |> Map.put(:commitment_id, get_l1_transaction_id_by_hash(lifecycle_transactions, batch.transaction_hash)) |> Map.put( :transactions_count, - case tx_counts_per_batch[batch.number] do + case transaction_counts_per_batch[batch.number] do nil -> 0 value -> value end ) - |> Map.drop([:tx_hash]) + |> Map.drop([:transaction_hash]) | updated_batches_list ] end) @@ -815,8 +816,8 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do |> get_committed_l2_to_l1_messages() end - {batches_list_to_import, Map.values(lifecycle_txs), Map.values(blocks_to_import), rollup_txs_to_import, - committed_messages, da_records} + {batches_list_to_import, Map.values(lifecycle_transactions), Map.values(blocks_to_import), + rollup_transactions_to_import, committed_messages, da_records} end # Extracts batch numbers from logs of SequencerBatchDelivered events. @@ -864,7 +865,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do :number => non_neg_integer(), :before_acc => binary(), :after_acc => binary(), - :tx_hash => binary() + :transaction_hash => binary() } }, [EthereumJSONRPC.Transport.request()], @@ -872,20 +873,20 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do %{binary() => non_neg_integer()} } defp parse_logs_for_new_batches(logs, existing_batches) do - {batches, txs_requests, blocks_requests, existing_commitment_txs} = + {batches, transactions_requests, blocks_requests, existing_commitment_transactions} = logs |> Enum.reduce({%{}, [], %{}, %{}}, fn event, acc -> - tx_hash_raw = event["transactionHash"] + transaction_hash_raw = event["transactionHash"] blk_num = quantity_to_integer(event["blockNumber"]) handle_new_batch_data( - {sequencer_batch_delivered_event_parse(event), tx_hash_raw, blk_num}, + {sequencer_batch_delivered_event_parse(event), transaction_hash_raw, blk_num}, existing_batches, acc ) end) - {batches, txs_requests, Map.values(blocks_requests), existing_commitment_txs} + {batches, transactions_requests, Map.values(blocks_requests), existing_commitment_transactions} end # Parses SequencerBatchDelivered event to get batch sequence number and associated accumulators @@ -932,7 +933,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do :number => non_neg_integer(), :before_acc => binary(), :after_acc => binary(), - :tx_hash => binary() + :transaction_hash => binary() } }, [EthereumJSONRPC.Transport.request()], @@ -948,17 +949,17 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do defp handle_new_batch_data({{batch_num, _, _}, _, _}, _, acc) when batch_num == 0, do: acc defp handle_new_batch_data( - {{batch_num, before_acc, after_acc}, tx_hash_raw, blk_num}, + {{batch_num, before_acc, after_acc}, transaction_hash_raw, blk_num}, existing_batches, - {batches, txs_requests, blocks_requests, existing_commitment_txs} + {batches, transactions_requests, blocks_requests, existing_commitment_transactions} ) do - tx_hash = Rpc.string_hash_to_bytes_hash(tx_hash_raw) + transaction_hash = Rpc.string_hash_to_bytes_hash(transaction_hash_raw) - {updated_batches, updated_txs_requests, updated_existing_commitment_txs} = + {updated_batches, updated_transactions_requests, updated_existing_commitment_transactions} = if batch_num in existing_batches do - {batches, txs_requests, Map.put(existing_commitment_txs, tx_hash, blk_num)} + {batches, transactions_requests, Map.put(existing_commitment_transactions, transaction_hash, blk_num)} else - log_info("New batch #{batch_num} found in #{tx_hash_raw}") + log_info("New batch #{batch_num} found in #{transaction_hash_raw}") updated_batches = Map.put( @@ -968,16 +969,16 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do number: batch_num, before_acc: before_acc, after_acc: after_acc, - tx_hash: tx_hash + transaction_hash: transaction_hash } ) - updated_txs_requests = [ - Rpc.transaction_by_hash_request(%{id: 0, hash: tx_hash_raw}) - | txs_requests + updated_transactions_requests = [ + Rpc.transaction_by_hash_request(%{id: 0, hash: transaction_hash_raw}) + | transactions_requests ] - {updated_batches, updated_txs_requests, existing_commitment_txs} + {updated_batches, updated_transactions_requests, existing_commitment_transactions} end # In order to have an ability to update commitment transaction for the existing batches @@ -989,7 +990,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do BlockByNumber.request(%{id: 0, number: blk_num}, false, true) ) - {updated_batches, updated_txs_requests, updated_blocks_requests, updated_existing_commitment_txs} + {updated_batches, updated_transactions_requests, updated_blocks_requests, updated_existing_commitment_transactions} end # Executes transaction requests and parses the calldata to extract batch data. @@ -1002,7 +1003,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # constructs a list of DA info structs. # # ## Parameters - # - `txs_requests`: The list of RPC requests to fetch transaction data. + # - `transactions_requests`: The list of RPC requests to fetch transaction data. # - `msg_to_block_shift`: The shift value to adjust the message count to the correct # rollup block numbers. # - `blocks_to_ts`: A map of block numbers to their timestamps, required to complete @@ -1020,7 +1021,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # - An updated map of batch descriptions with block ranges and data availability # information. # - A list of data availability information structs for Anytrust or Celestia. - @spec execute_tx_requests_parse_txs_calldata( + @spec execute_transaction_requests_parse_transactions_calldata( [EthereumJSONRPC.Transport.request()], non_neg_integer(), %{EthereumJSONRPC.block_number() => DateTime.t()}, @@ -1053,8 +1054,8 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do optional(any()) => any() } }, [Anytrust.t() | Celestia.t()]} - defp execute_tx_requests_parse_txs_calldata( - txs_requests, + defp execute_transaction_requests_parse_transactions_calldata( + transactions_requests, msg_to_block_shift, blocks_to_ts, batches, @@ -1065,16 +1066,17 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do }, rollup_config ) do - txs_requests + transactions_requests |> Enum.chunk_every(chunk_size) - |> Enum.reduce({%{}, batches, []}, fn chunk, {l1_txs, updated_batches, da_info} -> + |> Enum.reduce({%{}, batches, []}, fn chunk, {l1_transactions, updated_batches, da_info} -> chunk # each eth_getTransactionByHash will take time since it returns entire batch # in `input` which is heavy because contains dozens of rollup blocks |> Rpc.make_chunked_request(json_rpc_named_arguments, "eth_getTransactionByHash") - |> Enum.reduce({l1_txs, updated_batches, da_info}, fn resp, {txs_map, batches_map, da_info_list} -> - block_num = quantity_to_integer(resp["blockNumber"]) - tx_hash = Rpc.string_hash_to_bytes_hash(resp["hash"]) + |> Enum.reduce({l1_transactions, updated_batches, da_info}, fn resp, + {transactions_map, batches_map, da_info_list} -> + block_number = quantity_to_integer(resp["blockNumber"]) + transaction_hash = Rpc.string_hash_to_bytes_hash(resp["hash"]) # Although they are called messages in the functions' ABI, in fact they are # rollup blocks @@ -1111,11 +1113,11 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do }) ) - updated_txs_map = - Map.put(txs_map, tx_hash, %{ - hash: tx_hash, - block_number: block_num, - timestamp: blocks_to_ts[block_num], + updated_transactions_map = + Map.put(transactions_map, transaction_hash, %{ + hash: transaction_hash, + block_number: block_number, + timestamp: blocks_to_ts[block_number], status: if track_finalization? do :unfinalized @@ -1132,7 +1134,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do da_info_list end - {updated_txs_map, updated_batches_map, updated_da_info_list} + {updated_transactions_map, updated_batches_map, updated_da_info_list} end) end) end @@ -1320,7 +1322,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # number and timestamp. # # Parameters: - # - `existing_commitment_txs`: A map where keys are transaction hashes and + # - `existing_commitment_transactions`: A map where keys are transaction hashes and # values are block numbers. # - `block_to_ts`: A map where keys are block numbers and values are timestamps. # @@ -1328,22 +1330,24 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # - A map where keys are transaction hashes and values are updated lifecycle # transactions with the block number and timestamp set, compatible with the # database import operation. - @spec update_lifecycle_txs_for_new_blocks(%{binary() => non_neg_integer()}, %{non_neg_integer() => non_neg_integer()}) :: + @spec update_lifecycle_transactions_for_new_blocks(%{binary() => non_neg_integer()}, %{ + non_neg_integer() => non_neg_integer() + }) :: %{binary() => Arbitrum.LifecycleTransaction.to_import()} - defp update_lifecycle_txs_for_new_blocks(existing_commitment_txs, block_to_ts) do - existing_commitment_txs + defp update_lifecycle_transactions_for_new_blocks(existing_commitment_transactions, block_to_ts) do + existing_commitment_transactions |> Map.keys() |> Db.lifecycle_transactions() - |> Enum.reduce(%{}, fn tx, txs -> - block_num = existing_commitment_txs[tx.hash] - ts = block_to_ts[block_num] + |> Enum.reduce(%{}, fn transaction, transactions -> + block_number = existing_commitment_transactions[transaction.hash] + ts = block_to_ts[block_number] - case ArbitrumHelper.compare_lifecycle_tx_and_update(tx, {block_num, ts}, "commitment") do - {:updated, updated_tx} -> - Map.put(txs, tx.hash, updated_tx) + case ArbitrumHelper.compare_lifecycle_transaction_and_update(transaction, {block_number, ts}, "commitment") do + {:updated, updated_transaction} -> + Map.put(transactions, transaction.hash, updated_transaction) _ -> - txs + transactions end end) end @@ -1392,8 +1396,8 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do else log_debug("Identified #{length(required_blocks_numbers)} rollup blocks") - {blocks_to_import_map, txs_to_import_list} = - get_rollup_blocks_and_txs_from_db(required_blocks_numbers, blocks_to_batches) + {blocks_to_import_map, transactions_to_import_list} = + get_rollup_blocks_and_transactions_from_db(required_blocks_numbers, blocks_to_batches) # While it's not entirely aligned with data integrity principles to recover # rollup blocks and transactions from RPC that are not yet indexed, it's @@ -1401,20 +1405,20 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # the potential high frequency of new batch appearances and the substantial # volume of blocks and transactions, prioritizing discovery process advancement # is deemed reasonable. - {blocks_to_import, txs_to_import} = + {blocks_to_import, transactions_to_import} = recover_data_if_necessary( blocks_to_import_map, - txs_to_import_list, + transactions_to_import_list, required_blocks_numbers, blocks_to_batches, rollup_rpc_config ) log_info( - "Found #{length(Map.keys(blocks_to_import))} rollup blocks and #{length(txs_to_import)} rollup transactions in DB" + "Found #{length(Map.keys(blocks_to_import))} rollup blocks and #{length(transactions_to_import)} rollup transactions in DB" ) - {blocks_to_import, txs_to_import} + {blocks_to_import, transactions_to_import} end end @@ -1440,8 +1444,8 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do |> Map.values() |> Enum.reduce(%{}, fn batch, b_2_b -> batch.start_block..batch.end_block - |> Enum.reduce(b_2_b, fn block_num, b_2_b_inner -> - Map.put(b_2_b_inner, block_num, batch.number) + |> Enum.reduce(b_2_b, fn block_number, b_2_b_inner -> + Map.put(b_2_b_inner, block_number, batch.number) end) end) end @@ -1464,16 +1468,16 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # database import. # - A list of transactions, each associated with its respective rollup block # and batch number, ready for database import. - defp get_rollup_blocks_and_txs_from_db(rollup_blocks_numbers, blocks_to_batches) do + defp get_rollup_blocks_and_transactions_from_db(rollup_blocks_numbers, blocks_to_batches) do rollup_blocks_numbers |> Db.rollup_blocks() - |> Enum.reduce({%{}, []}, fn block, {blocks_map, txs_list} -> + |> Enum.reduce({%{}, []}, fn block, {blocks_map, transactions_list} -> batch_num = blocks_to_batches[block.number] - updated_txs_list = + updated_transactions_list = block.transactions - |> Enum.reduce(txs_list, fn tx, acc -> - [%{tx_hash: tx.hash.bytes, batch_number: batch_num} | acc] + |> Enum.reduce(transactions_list, fn transaction, acc -> + [%{transaction_hash: transaction.hash.bytes, batch_number: batch_num} | acc] end) updated_blocks_map = @@ -1484,7 +1488,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do confirmation_id: nil }) - {updated_blocks_map, updated_txs_list} + {updated_blocks_map, updated_transactions_list} end) end @@ -1498,7 +1502,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # # ## Parameters # - `current_rollup_blocks`: The map of rollup blocks currently held. - # - `current_rollup_txs`: The list of transactions currently held. + # - `current_rollup_transactions`: The list of transactions currently held. # - `required_blocks_numbers`: A list of block numbers that are required for # processing. # - `blocks_to_batches`: A map associating rollup block numbers with batch numbers. @@ -1521,7 +1525,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do {%{non_neg_integer() => Arbitrum.BatchBlock.to_import()}, [Arbitrum.BatchTransaction.to_import()]} defp recover_data_if_necessary( current_rollup_blocks, - current_rollup_txs, + current_rollup_transactions, required_blocks_numbers, blocks_to_batches, rollup_rpc_config @@ -1534,17 +1538,18 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do if found_blocks_numbers_length != required_blocks_amount do log_info("Only #{found_blocks_numbers_length} of #{required_blocks_amount} rollup blocks found in DB") - {recovered_blocks_map, recovered_txs_list, _} = - recover_rollup_blocks_and_txs_from_rpc( + {recovered_blocks_map, recovered_transactions_list, _} = + recover_rollup_blocks_and_transactions_from_rpc( required_blocks_numbers, found_blocks_numbers, blocks_to_batches, rollup_rpc_config ) - {Map.merge(current_rollup_blocks, recovered_blocks_map), current_rollup_txs ++ recovered_txs_list} + {Map.merge(current_rollup_blocks, recovered_blocks_map), + current_rollup_transactions ++ recovered_transactions_list} else - {current_rollup_blocks, current_rollup_txs} + {current_rollup_blocks, current_rollup_transactions} end end @@ -1574,7 +1579,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # - A list of transactions, each associated with its respective rollup block # and batch number, ready for database import. # - The updated counter of processed chunks (usually ignored). - @spec recover_rollup_blocks_and_txs_from_rpc( + @spec recover_rollup_blocks_and_transactions_from_rpc( [non_neg_integer()], [non_neg_integer()], %{non_neg_integer() => non_neg_integer()}, @@ -1586,7 +1591,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do ) :: {%{non_neg_integer() => Arbitrum.BatchBlock.to_import()}, [Arbitrum.BatchTransaction.to_import()], non_neg_integer()} - defp recover_rollup_blocks_and_txs_from_rpc( + defp recover_rollup_blocks_and_transactions_from_rpc( required_blocks_numbers, found_blocks_numbers, blocks_to_batches, @@ -1601,7 +1606,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do missed_blocks |> Enum.sort() |> Enum.chunk_every(rollup_chunk_size) - |> Enum.reduce({%{}, [], 0}, fn chunk, {blocks_map, txs_list, chunks_counter} -> + |> Enum.reduce({%{}, [], 0}, fn chunk, {blocks_map, transactions_list, chunks_counter} -> Logging.log_details_chunk_handling( "Collecting rollup data", {"block", "blocks"}, @@ -1612,12 +1617,12 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do requests = chunk - |> Enum.reduce([], fn block_num, requests_list -> + |> Enum.reduce([], fn block_number, requests_list -> [ BlockByNumber.request( %{ - id: blocks_to_batches[block_num], - number: block_num + id: blocks_to_batches[block_number], + number: block_number }, false ) @@ -1625,12 +1630,12 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do ] end) - {blocks_map_updated, txs_list_updated} = + {blocks_map_updated, transactions_list_updated} = requests |> Rpc.make_chunked_request_keep_id(rollup_json_rpc_named_arguments, "eth_getBlockByNumber") - |> prepare_rollup_block_map_and_transactions_list(blocks_map, txs_list) + |> prepare_rollup_block_map_and_transactions_list(blocks_map, transactions_list) - {blocks_map_updated, txs_list_updated, chunks_counter + length(chunk)} + {blocks_map_updated, transactions_list_updated, chunks_counter + length(chunk)} end) end @@ -1644,7 +1649,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do # ## Parameters # - `json_responses`: A list of JSON RPC responses containing rollup block data. # - `rollup_blocks`: The initial map of rollup block information. - # - `rollup_txs`: The initial list of rollup transactions. + # - `rollup_transactions`: The initial list of rollup transactions. # # ## Returns # - A tuple containing: @@ -1657,9 +1662,9 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do %{non_neg_integer() => Arbitrum.BatchBlock.to_import()}, [Arbitrum.BatchTransaction.to_import()] ) :: {%{non_neg_integer() => Arbitrum.BatchBlock.to_import()}, [Arbitrum.BatchTransaction.to_import()]} - defp prepare_rollup_block_map_and_transactions_list(json_responses, rollup_blocks, rollup_txs) do + defp prepare_rollup_block_map_and_transactions_list(json_responses, rollup_blocks, rollup_transactions) do json_responses - |> Enum.reduce({rollup_blocks, rollup_txs}, fn resp, {blocks_map, txs_list} -> + |> Enum.reduce({rollup_blocks, rollup_transactions}, fn resp, {blocks_map, transactions_list} -> batch_num = resp.id blk_num = quantity_to_integer(resp.result["number"]) @@ -1670,35 +1675,35 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do %{block_number: blk_num, batch_number: batch_num, confirmation_id: nil} ) - updated_txs_list = + updated_transactions_list = case resp.result["transactions"] do nil -> - txs_list + transactions_list - new_txs -> - Enum.reduce(new_txs, txs_list, fn l2_tx_hash, txs_list -> - [%{tx_hash: l2_tx_hash, batch_number: batch_num} | txs_list] + new_transactions -> + Enum.reduce(new_transactions, transactions_list, fn l2_transaction_hash, transactions_list -> + [%{transaction_hash: l2_transaction_hash, batch_number: batch_num} | transactions_list] end) end - {updated_blocks_map, updated_txs_list} + {updated_blocks_map, updated_transactions_list} end) end # Retrieves the unique identifier of an L1 transaction by its hash from the given # map. `nil` if there is no such transaction in the map. - defp get_l1_tx_id_by_hash(l1_txs, hash) do - l1_txs + defp get_l1_transaction_id_by_hash(l1_transactions, hash) do + l1_transactions |> Map.get(hash) |> Kernel.||(%{id: nil}) |> Map.get(:id) end # Aggregates rollup transactions by batch number, counting the number of transactions in each batch. - defp batches_to_rollup_txs_amounts(rollup_txs) do - rollup_txs - |> Enum.reduce(%{}, fn tx, acc -> - Map.put(acc, tx.batch_number, Map.get(acc, tx.batch_number, 0) + 1) + defp batches_to_rollup_transactions_amounts(rollup_transactions) do + rollup_transactions + |> Enum.reduce(%{}, fn transaction, acc -> + Map.put(acc, transaction.batch_number, Map.get(acc, transaction.batch_number, 0) + 1) end) end @@ -1707,8 +1712,8 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do defp get_committed_l2_to_l1_messages(block_number) do block_number |> Db.initiated_l2_to_l1_messages() - |> Enum.map(fn tx -> - Map.put(tx, :status, :sent) + |> Enum.map(fn transaction -> + Map.put(transaction, :status, :sent) end) end @@ -1723,10 +1728,12 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do optional(any()) => any() } ] - defp extend_batches_with_commitment_transactions(batches, lifecycle_txs) do + defp extend_batches_with_commitment_transactions(batches, lifecycle_transactions) do Enum.map(batches, fn batch -> - lifecycle_tx = Enum.find(lifecycle_txs, fn tx -> tx.id == batch.commitment_id end) - Map.put(batch, :commitment_transaction, lifecycle_tx) + lifecycle_transaction = + Enum.find(lifecycle_transactions, fn transaction -> transaction.id == batch.commitment_id end) + + Map.put(batch, :commitment_transaction, lifecycle_transaction) end) end end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_confirmations.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_confirmations.ex index 9631ccf78816..d91c6259bd56 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_confirmations.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_confirmations.ex @@ -492,7 +492,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do l1_rpc_config.json_rpc_named_arguments ) - {retcode, {lifecycle_txs, rollup_blocks, confirmed_txs}} = + {retcode, {lifecycle_transactions, rollup_blocks, confirmed_transactions}} = handle_confirmations_from_logs( logs, l1_rpc_config, @@ -501,9 +501,9 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do {:ok, _} = Chain.import(%{ - arbitrum_lifecycle_transactions: %{params: lifecycle_txs}, + arbitrum_lifecycle_transactions: %{params: lifecycle_transactions}, arbitrum_batch_blocks: %{params: rollup_blocks}, - arbitrum_messages: %{params: confirmed_txs}, + arbitrum_messages: %{params: confirmed_transactions}, timeout: :infinity }) @@ -529,9 +529,9 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # - `outbox_address`: The address of the Arbitrum outbox contract. # # ## Returns - # - `{retcode, {lifecycle_txs, rollup_blocks, confirmed_txs}}` where + # - `{retcode, {lifecycle_transactions, rollup_blocks, confirmed_transactions}}` where # - `retcode` is either `:ok` or `:confirmation_missed` - # - `lifecycle_txs` is a list of lifecycle transactions confirming blocks in the + # - `lifecycle_transactions` is a list of lifecycle transactions confirming blocks in the # rollup # - `rollup_blocks` is a list of rollup blocks associated with the corresponding # lifecycle transactions @@ -566,24 +566,24 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # On this step there could be lifecycle transactions for the rollup blocks which are # already confirmed. It is only possible in the scenario when the confirmation # discovery process does not wait for the safe L1 block. In this case: - # - rollup_blocks_to_l1_txs will not contain the corresponding block hash associated + # - rollup_blocks_to_l1_transactions will not contain the corresponding block hash associated # with the L1 transaction hash - # - lifecycle_txs_basic will contain all discovered lifecycle transactions + # - lifecycle_transactions_basic will contain all discovered lifecycle transactions # - blocks_requests will contain all requests to fetch block data for the lifecycle # transactions - # - existing_lifecycle_txs will contain lifecycle transactions which was found in the + # - existing_lifecycle_transactions will contain lifecycle transactions which was found in the # logs and already imported into the database. - {rollup_blocks_to_l1_txs, lifecycle_txs_basic, blocks_requests, existing_lifecycle_txs} = + {rollup_blocks_to_l1_transactions, lifecycle_transactions_basic, blocks_requests, existing_lifecycle_transactions} = parse_logs_for_new_confirmations(logs) # This step must be run only if there are hashes of the confirmed rollup blocks - # in rollup_blocks_to_l1_txs - when there are newly discovered confirmations. + # in rollup_blocks_to_l1_transactions - when there are newly discovered confirmations. rollup_blocks = - if Enum.empty?(rollup_blocks_to_l1_txs) do + if Enum.empty?(rollup_blocks_to_l1_transactions) do [] else discover_rollup_blocks( - rollup_blocks_to_l1_txs, + rollup_blocks_to_l1_transactions, %{ json_rpc_named_arguments: l1_rpc_config.json_rpc_named_arguments, logs_block_range: l1_rpc_config.logs_block_range, @@ -593,17 +593,19 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do end # Will return %{} if there are no new confirmations - applicable_lifecycle_txs = take_lifecycle_txs_for_confirmed_blocks(rollup_blocks, lifecycle_txs_basic) + applicable_lifecycle_transactions = + take_lifecycle_transactions_for_confirmed_blocks(rollup_blocks, lifecycle_transactions_basic) # Will contain :ok if no new confirmations are found retcode = - if Enum.count(lifecycle_txs_basic) != Enum.count(applicable_lifecycle_txs) + length(existing_lifecycle_txs) do + if Enum.count(lifecycle_transactions_basic) != + Enum.count(applicable_lifecycle_transactions) + length(existing_lifecycle_transactions) do :confirmation_missed else :ok end - if Enum.empty?(applicable_lifecycle_txs) and existing_lifecycle_txs == [] do + if Enum.empty?(applicable_lifecycle_transactions) and existing_lifecycle_transactions == [] do # Only if both new confirmations and already existing confirmations are empty {retcode, {[], [], []}} else @@ -615,9 +617,9 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do ) # The lifecycle transactions for the new confirmations are finalized here. - {lifecycle_txs_for_new_confirmations, rollup_blocks, highest_confirmed_block_number} = - finalize_lifecycle_txs_and_confirmed_blocks( - applicable_lifecycle_txs, + {lifecycle_transactions_for_new_confirmations, rollup_blocks, highest_confirmed_block_number} = + finalize_lifecycle_transactions_and_confirmed_blocks( + applicable_lifecycle_transactions, rollup_blocks, l1_blocks_to_ts, l1_rpc_config.track_finalization @@ -625,16 +627,20 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # The lifecycle transactions for the already existing confirmations are updated here # to ensure correct L1 block number and timestamp that could appear due to re-orgs. - lifecycle_txs = - lifecycle_txs_for_new_confirmations ++ - update_lifecycle_txs_for_new_blocks(existing_lifecycle_txs, lifecycle_txs_basic, l1_blocks_to_ts) + lifecycle_transactions = + lifecycle_transactions_for_new_confirmations ++ + update_lifecycle_transactions_for_new_blocks( + existing_lifecycle_transactions, + lifecycle_transactions_basic, + l1_blocks_to_ts + ) # Drawback of marking messages as confirmed during a new confirmation handling # is that the status change could become stuck if confirmations are not handled. # For example, due to DB inconsistency: some blocks/batches are missed. confirmed_messages = get_confirmed_l2_to_l1_messages(highest_confirmed_block_number) - {retcode, {lifecycle_txs, rollup_blocks, confirmed_messages}} + {retcode, {lifecycle_transactions, rollup_blocks, confirmed_messages}} end end @@ -660,7 +666,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # database. Each transaction is compatible with the database import operation. @spec parse_logs_for_new_confirmations([%{String.t() => any()}]) :: { - %{binary() => %{l1_tx_hash: binary(), l1_block_num: non_neg_integer()}}, + %{binary() => %{l1_transaction_hash: binary(), l1_block_num: non_neg_integer()}}, %{binary() => %{hash: binary(), block_number: non_neg_integer()}}, [EthereumJSONRPC.Transport.request()], [Arbitrum.LifecycleTransaction.to_import()] @@ -669,46 +675,46 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do transaction_hashes = logs |> Enum.reduce(%{}, fn event, acc -> - l1_tx_hash_raw = event["transactionHash"] - Map.put_new(acc, l1_tx_hash_raw, Rpc.string_hash_to_bytes_hash(l1_tx_hash_raw)) + l1_transaction_hash_raw = event["transactionHash"] + Map.put_new(acc, l1_transaction_hash_raw, Rpc.string_hash_to_bytes_hash(l1_transaction_hash_raw)) end) - existing_lifecycle_txs = + existing_lifecycle_transactions = transaction_hashes |> Map.values() |> Db.lifecycle_transactions() - |> Enum.reduce(%{}, fn tx, acc -> - Map.put(acc, tx.hash, tx) + |> Enum.reduce(%{}, fn transaction, acc -> + Map.put(acc, transaction.hash, transaction) end) - {rollup_block_to_l1_txs, lifecycle_txs, blocks_requests} = + {rollup_block_to_l1_transactions, lifecycle_transactions, blocks_requests} = logs - |> Enum.reduce({%{}, %{}, %{}}, fn event, {block_to_txs, lifecycle_txs, blocks_requests} -> + |> Enum.reduce({%{}, %{}, %{}}, fn event, {block_to_transactions, lifecycle_transactions, blocks_requests} -> rollup_block_hash = send_root_updated_event_parse(event) - l1_tx_hash_raw = event["transactionHash"] - l1_tx_hash = transaction_hashes[l1_tx_hash_raw] + l1_transaction_hash_raw = event["transactionHash"] + l1_transaction_hash = transaction_hashes[l1_transaction_hash_raw] l1_blk_num = quantity_to_integer(event["blockNumber"]) # There is no need to include the found block hash for the consequent confirmed # blocks discovery step since it is assumed that already existing lifecycle # transactions are already linked with the corresponding rollup blocks. - updated_block_to_txs = - if Map.has_key?(existing_lifecycle_txs, l1_tx_hash) do - block_to_txs + updated_block_to_transactions = + if Map.has_key?(existing_lifecycle_transactions, l1_transaction_hash) do + block_to_transactions else Map.put( - block_to_txs, + block_to_transactions, rollup_block_hash, - %{l1_tx_hash: l1_tx_hash, l1_block_num: l1_blk_num} + %{l1_transaction_hash: l1_transaction_hash, l1_block_num: l1_blk_num} ) end - updated_lifecycle_txs = + updated_lifecycle_transactions = Map.put( - lifecycle_txs, - l1_tx_hash, - %{hash: l1_tx_hash, block_number: l1_blk_num} + lifecycle_transactions, + l1_transaction_hash, + %{hash: l1_transaction_hash, block_number: l1_blk_num} ) updated_blocks_requests = @@ -718,12 +724,13 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do BlockByNumber.request(%{id: 0, number: l1_blk_num}, false, true) ) - log_info("New confirmation for the rollup block #{rollup_block_hash} found in #{l1_tx_hash_raw}") + log_info("New confirmation for the rollup block #{rollup_block_hash} found in #{l1_transaction_hash_raw}") - {updated_block_to_txs, updated_lifecycle_txs, updated_blocks_requests} + {updated_block_to_transactions, updated_lifecycle_transactions, updated_blocks_requests} end) - {rollup_block_to_l1_txs, lifecycle_txs, Map.values(blocks_requests), Map.values(existing_lifecycle_txs)} + {rollup_block_to_l1_transactions, lifecycle_transactions, Map.values(blocks_requests), + Map.values(existing_lifecycle_transactions)} end # Transforms rollup block hashes to numbers and associates them with their confirmation descriptions. @@ -737,14 +744,14 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # confirmations. # # ## Parameters - # - `rollup_blocks_to_l1_txs`: A map of rollup block hashes to confirmation descriptions. + # - `rollup_blocks_to_l1_transactions`: A map of rollup block hashes to confirmation descriptions. # - `outbox_config`: Configuration for the Arbitrum outbox contract. # # ## Returns # - A list of rollup blocks each associated with the transaction's hash that # confirms the block. @spec discover_rollup_blocks( - %{binary() => %{l1_tx_hash: binary(), l1_block_num: non_neg_integer()}}, + %{binary() => %{l1_transaction_hash: binary(), l1_block_num: non_neg_integer()}}, %{ :logs_block_range => non_neg_integer(), :outbox_address => binary(), @@ -752,9 +759,9 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do optional(any()) => any() } ) :: [Arbitrum.BatchBlock.to_import()] - defp discover_rollup_blocks(rollup_blocks_to_l1_txs, outbox_config) do - block_to_l1_txs = - rollup_blocks_to_l1_txs + defp discover_rollup_blocks(rollup_blocks_to_l1_transactions, outbox_config) do + block_to_l1_transactions = + rollup_blocks_to_l1_transactions |> Map.keys() |> Enum.reduce(%{}, fn block_hash, transformed -> rollup_block_num = Db.rollup_block_hash_to_num(block_hash) @@ -767,24 +774,24 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do transformed value -> - Map.put(transformed, value, rollup_blocks_to_l1_txs[block_hash]) + Map.put(transformed, value, rollup_blocks_to_l1_transactions[block_hash]) end end) - if Enum.empty?(block_to_l1_txs) do + if Enum.empty?(block_to_l1_transactions) do [] else # Oldest (with the lowest number) block is first - rollup_block_numbers = Enum.sort(Map.keys(block_to_l1_txs), :asc) + rollup_block_numbers = Enum.sort(Map.keys(block_to_l1_transactions), :asc) rollup_block_numbers - |> Enum.reduce([], fn block_num, updated_rollup_blocks -> - log_info("Attempting to mark all rollup blocks including ##{block_num} and lower as confirmed") + |> Enum.reduce([], fn block_number, updated_rollup_blocks -> + log_info("Attempting to mark all rollup blocks including ##{block_number} and lower as confirmed") {_, confirmed_blocks} = discover_rollup_blocks_belonging_to_one_confirmation( - block_num, - block_to_l1_txs[block_num], + block_number, + block_to_l1_transactions[block_number], outbox_config ) @@ -792,7 +799,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do if length(confirmed_blocks) > 0 do log_info("Found #{length(confirmed_blocks)} confirmed blocks") - add_confirmation_transaction(confirmed_blocks, block_to_l1_txs[block_num].l1_tx_hash) ++ + add_confirmation_transaction(confirmed_blocks, block_to_l1_transactions[block_number].l1_transaction_hash) ++ updated_rollup_blocks else log_info("Either no unconfirmed blocks found or DB inconsistency error discovered") @@ -1413,28 +1420,32 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # Adds the confirmation transaction hash to each rollup block description in the list. @spec add_confirmation_transaction([Arbitrum.BatchBlock.to_import()], binary()) :: [Arbitrum.BatchBlock.to_import()] - defp add_confirmation_transaction(block_descriptions_list, confirm_tx_hash) do + defp add_confirmation_transaction(block_descriptions_list, confirm_transaction_hash) do block_descriptions_list |> Enum.reduce([], fn block_descr, updated -> new_block_descr = block_descr - |> Map.put(:confirmation_transaction, confirm_tx_hash) + |> Map.put(:confirmation_transaction, confirm_transaction_hash) [new_block_descr | updated] end) end # Selects lifecycle transaction descriptions used for confirming a given list of rollup blocks. - @spec take_lifecycle_txs_for_confirmed_blocks( + @spec take_lifecycle_transactions_for_confirmed_blocks( [Arbitrum.BatchBlock.to_import()], %{binary() => %{hash: binary(), block_number: non_neg_integer()}} ) :: %{binary() => %{hash: binary(), block_number: non_neg_integer()}} - defp take_lifecycle_txs_for_confirmed_blocks(confirmed_rollup_blocks, lifecycle_txs) do + defp take_lifecycle_transactions_for_confirmed_blocks(confirmed_rollup_blocks, lifecycle_transactions) do confirmed_rollup_blocks - |> Enum.reduce(%{}, fn block_descr, updated_txs -> - confirmation_tx_hash = block_descr.confirmation_transaction + |> Enum.reduce(%{}, fn block_descr, updated_transactions -> + confirmation_transaction_hash = block_descr.confirmation_transaction - Map.put_new(updated_txs, confirmation_tx_hash, lifecycle_txs[confirmation_tx_hash]) + Map.put_new( + updated_transactions, + confirmation_transaction_hash, + lifecycle_transactions[confirmation_transaction_hash] + ) end) end @@ -1448,7 +1459,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # for import. # # ## Parameters - # - `basic_lifecycle_txs`: The initial list of partially filled lifecycle transaction + # - `basic_lifecycle_transactions`: The initial list of partially filled lifecycle transaction # descriptions. # - `confirmed_rollup_blocks`: Rollup blocks to be considered as confirmed. # - `l1_blocks_requests`: RPC requests of `eth_getBlockByNumber` to fetch L1 block data @@ -1461,7 +1472,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do # - The map of lifecycle transactions where each transaction is ready for import. # - The list of confirmed rollup blocks, ready for import. # - The highest confirmed block number processed during this run. - @spec finalize_lifecycle_txs_and_confirmed_blocks( + @spec finalize_lifecycle_transactions_and_confirmed_blocks( %{binary() => %{hash: binary(), block_number: non_neg_integer()}}, [Arbitrum.BatchBlock.to_import()], %{required(EthereumJSONRPC.block_number()) => DateTime.t()}, @@ -1471,27 +1482,27 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do [Arbitrum.BatchBlock.to_import()], integer() } - defp finalize_lifecycle_txs_and_confirmed_blocks( - basic_lifecycle_txs, + defp finalize_lifecycle_transactions_and_confirmed_blocks( + basic_lifecycle_transactions, confirmed_rollup_blocks, l1_blocks_to_ts, track_finalization? ) - defp finalize_lifecycle_txs_and_confirmed_blocks(basic_lifecycle_txs, _, _, _) - when map_size(basic_lifecycle_txs) == 0 do + defp finalize_lifecycle_transactions_and_confirmed_blocks(basic_lifecycle_transactions, _, _, _) + when map_size(basic_lifecycle_transactions) == 0 do {[], [], -1} end - defp finalize_lifecycle_txs_and_confirmed_blocks( - basic_lifecycle_txs, + defp finalize_lifecycle_transactions_and_confirmed_blocks( + basic_lifecycle_transactions, confirmed_rollup_blocks, l1_blocks_to_ts, track_finalization? ) do - lifecycle_txs = - basic_lifecycle_txs - |> ArbitrumHelper.extend_lifecycle_txs_with_ts_and_status(l1_blocks_to_ts, track_finalization?) + lifecycle_transactions = + basic_lifecycle_transactions + |> ArbitrumHelper.extend_lifecycle_transactions_with_ts_and_status(l1_blocks_to_ts, track_finalization?) |> Db.get_indices_for_l1_transactions() {updated_rollup_blocks, highest_confirmed_block_number} = @@ -1501,41 +1512,45 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do updated_block = block - |> Map.put(:confirmation_id, lifecycle_txs[block.confirmation_transaction].id) + |> Map.put(:confirmation_id, lifecycle_transactions[block.confirmation_transaction].id) |> Map.drop([:confirmation_transaction]) {[updated_block | updated_list], chosen_highest_confirmed} end) - {Map.values(lifecycle_txs), updated_rollup_blocks, highest_confirmed_block_number} + {Map.values(lifecycle_transactions), updated_rollup_blocks, highest_confirmed_block_number} end # Updates lifecycle transactions with new L1 block numbers and timestamps which could appear due to re-orgs. # # ## Parameters - # - `existing_commitment_txs`: A list of existing confirmation transactions to be checked and updated. - # - `tx_to_l1_block`: A map from transaction hashes to their corresponding new L1 block numbers. + # - `existing_commitment_transactions`: A list of existing confirmation transactions to be checked and updated. + # - `transaction_to_l1_block`: A map from transaction hashes to their corresponding new L1 block numbers. # - `l1_block_to_ts`: A map from L1 block numbers to their corresponding new timestamps. # # ## Returns # - A list of updated confirmation transactions with new block numbers and timestamps. - @spec update_lifecycle_txs_for_new_blocks( + @spec update_lifecycle_transactions_for_new_blocks( [Arbitrum.LifecycleTransaction.to_import()], %{binary() => non_neg_integer()}, %{non_neg_integer() => DateTime.t()} ) :: [Arbitrum.LifecycleTransaction.to_import()] - defp update_lifecycle_txs_for_new_blocks(existing_commitment_txs, tx_to_l1_block, l1_block_to_ts) do - existing_commitment_txs - |> Enum.reduce([], fn tx, updated_txs -> - new_block_num = tx_to_l1_block[tx.hash].block_number + defp update_lifecycle_transactions_for_new_blocks( + existing_commitment_transactions, + transaction_to_l1_block, + l1_block_to_ts + ) do + existing_commitment_transactions + |> Enum.reduce([], fn transaction, updated_transactions -> + new_block_num = transaction_to_l1_block[transaction.hash].block_number new_ts = l1_block_to_ts[new_block_num] - case ArbitrumHelper.compare_lifecycle_tx_and_update(tx, {new_block_num, new_ts}, "confirmation") do - {:updated, updated_tx} -> - [updated_tx | updated_txs] + case ArbitrumHelper.compare_lifecycle_transaction_and_update(transaction, {new_block_num, new_ts}, "confirmation") do + {:updated, updated_transaction} -> + [updated_transaction | updated_transactions] _ -> - updated_txs + updated_transactions end end) end @@ -1551,8 +1566,8 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewConfirmations do defp get_confirmed_l2_to_l1_messages(block_number) do block_number |> Db.sent_l2_to_l1_messages() - |> Enum.map(fn tx -> - Map.put(tx, :status, :confirmed) + |> Enum.map(fn transaction -> + Map.put(transaction, :status, :confirmed) end) end end diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_l1_executions.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_l1_executions.ex index 7aedf324a87e..ba572c060e4f 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_l1_executions.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_l1_executions.ex @@ -199,14 +199,14 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewL1Executions do l1_rpc_config.json_rpc_named_arguments ) - {lifecycle_txs, executions} = get_executions_from_logs(logs, l1_rpc_config) + {lifecycle_transactions, executions} = get_executions_from_logs(logs, l1_rpc_config) unless executions == [] do log_info("Executions for #{length(executions)} L2 messages will be imported") {:ok, _} = Chain.import(%{ - arbitrum_lifecycle_transactions: %{params: lifecycle_txs}, + arbitrum_lifecycle_transactions: %{params: lifecycle_transactions}, arbitrum_l1_executions: %{params: executions}, timeout: :infinity }) @@ -291,13 +291,13 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewL1Executions do track_finalization: track_finalization? } = _l1_rpc_config ) do - {basics_executions, basic_lifecycle_txs, blocks_requests} = parse_logs_for_new_executions(logs) + {basics_executions, basic_lifecycle_transactions, blocks_requests} = parse_logs_for_new_executions(logs) blocks_to_ts = Rpc.execute_blocks_requests_and_get_ts(blocks_requests, json_rpc_named_arguments, chunk_size) - lifecycle_txs = - basic_lifecycle_txs - |> ArbitrumHelper.extend_lifecycle_txs_with_ts_and_status(blocks_to_ts, track_finalization?) + lifecycle_transactions = + basic_lifecycle_transactions + |> ArbitrumHelper.extend_lifecycle_transactions_with_ts_and_status(blocks_to_ts, track_finalization?) |> Db.get_indices_for_l1_transactions() executions = @@ -305,13 +305,13 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewL1Executions do |> Enum.reduce([], fn execution, updated_executions -> updated = execution - |> Map.put(:execution_id, lifecycle_txs[execution.execution_tx_hash].id) - |> Map.drop([:execution_tx_hash]) + |> Map.put(:execution_id, lifecycle_transactions[execution.execution_transaction_hash].id) + |> Map.drop([:execution_transaction_hash]) [updated | updated_executions] end) - {Map.values(lifecycle_txs), executions} + {Map.values(lifecycle_transactions), executions} end # Parses logs to extract new execution transactions for L2-to-L1 messages. @@ -330,33 +330,33 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewL1Executions do # - A tuple containing: # - `executions`: A list of details for execution transactions related to # L2-to-L1 messages. - # - `lifecycle_txs`: A map of lifecycle transaction details, keyed by L1 + # - `lifecycle_transactions`: A map of lifecycle transaction details, keyed by L1 # transaction hash. # - `blocks_requests`: A list of RPC requests for fetching block data where # the executions occurred. defp parse_logs_for_new_executions(logs) do - {executions, lifecycle_txs, blocks_requests} = + {executions, lifecycle_transactions, blocks_requests} = logs - |> Enum.reduce({[], %{}, %{}}, fn event, {executions, lifecycle_txs, blocks_requests} -> + |> Enum.reduce({[], %{}, %{}}, fn event, {executions, lifecycle_transactions, blocks_requests} -> msg_id = outbox_transaction_executed_event_parse(event) - l1_tx_hash_raw = event["transactionHash"] - l1_tx_hash = Rpc.string_hash_to_bytes_hash(l1_tx_hash_raw) + l1_transaction_hash_raw = event["transactionHash"] + l1_transaction_hash = Rpc.string_hash_to_bytes_hash(l1_transaction_hash_raw) l1_blk_num = quantity_to_integer(event["blockNumber"]) updated_executions = [ %{ message_id: msg_id, - execution_tx_hash: l1_tx_hash + execution_transaction_hash: l1_transaction_hash } | executions ] - updated_lifecycle_txs = + updated_lifecycle_transactions = Map.put( - lifecycle_txs, - l1_tx_hash, - %{hash: l1_tx_hash, block_number: l1_blk_num} + lifecycle_transactions, + l1_transaction_hash, + %{hash: l1_transaction_hash, block_number: l1_blk_num} ) updated_blocks_requests = @@ -366,12 +366,12 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewL1Executions do BlockByNumber.request(%{id: 0, number: l1_blk_num}, false, true) ) - log_debug("Execution for L2 message ##{msg_id} found in #{l1_tx_hash_raw}") + log_debug("Execution for L2 message ##{msg_id} found in #{l1_transaction_hash_raw}") - {updated_executions, updated_lifecycle_txs, updated_blocks_requests} + {updated_executions, updated_lifecycle_transactions, updated_blocks_requests} end) - {executions, lifecycle_txs, Map.values(blocks_requests)} + {executions, lifecycle_transactions, Map.values(blocks_requests)} end # Parses `OutBoxTransactionExecuted` event data to extract the transaction index parameter diff --git a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_messages_to_l2.ex b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_messages_to_l2.ex index ab030735bebd..ad586ab4f0ee 100644 --- a/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_messages_to_l2.ex +++ b/apps/indexer/lib/indexer/fetcher/arbitrum/workers/new_messages_to_l2.ex @@ -281,13 +281,14 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewMessagesToL2 do defp get_messages_from_logs([], _, _), do: [] defp get_messages_from_logs(logs, json_rpc_named_arguments, chunk_size) do - {messages, txs_requests} = parse_logs_for_l1_to_l2_messages(logs) + {messages, transactions_requests} = parse_logs_for_l1_to_l2_messages(logs) - txs_to_from = Rpc.execute_transactions_requests_and_get_from(txs_requests, json_rpc_named_arguments, chunk_size) + transactions_to_from = + Rpc.execute_transactions_requests_and_get_from(transactions_requests, json_rpc_named_arguments, chunk_size) Enum.map(messages, fn msg -> Map.merge(msg, %{ - originator_address: txs_to_from[msg.originating_transaction_hash], + originator_address: transactions_to_from[msg.originating_transaction_hash], status: :initiated }) end) @@ -307,45 +308,45 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewMessagesToL2 do # ## Returns # - A tuple comprising: # - `messages`: A list of maps, each containing an incomplete representation of a message. - # - `txs_requests`: A list of RPC request `eth_getTransactionByHash` structured to fetch + # - `transactions_requests`: A list of RPC request `eth_getTransactionByHash` structured to fetch # additional data needed to finalize the message descriptions. defp parse_logs_for_l1_to_l2_messages(logs) do - {messages, txs_requests} = + {messages, transactions_requests} = logs - |> Enum.reduce({[], %{}}, fn event, {messages, txs_requests} -> + |> Enum.reduce({[], %{}}, fn event, {messages, transactions_requests} -> {msg_id, type, ts} = message_delivered_event_parse(event) if type in @types_of_l1_messages_forwarded_to_l2 do - tx_hash = event["transactionHash"] + transaction_hash = event["transactionHash"] blk_num = quantity_to_integer(event["blockNumber"]) updated_messages = [ %{ direction: :to_l2, message_id: msg_id, - originating_transaction_hash: tx_hash, + originating_transaction_hash: transaction_hash, origination_timestamp: ts, originating_transaction_block_number: blk_num } | messages ] - updated_txs_requests = + updated_transactions_requests = Map.put( - txs_requests, - tx_hash, - Rpc.transaction_by_hash_request(%{id: 0, hash: tx_hash}) + transactions_requests, + transaction_hash, + Rpc.transaction_by_hash_request(%{id: 0, hash: transaction_hash}) ) - log_debug("L1 to L2 message #{tx_hash} found with the type #{type}") + log_debug("L1 to L2 message #{transaction_hash} found with the type #{type}") - {updated_messages, updated_txs_requests} + {updated_messages, updated_transactions_requests} else - {messages, txs_requests} + {messages, transactions_requests} end end) - {messages, Map.values(txs_requests)} + {messages, Map.values(transactions_requests)} end # Parses the `MessageDelivered` event to extract relevant message details. diff --git a/apps/indexer/lib/indexer/fetcher/internal_transaction.ex b/apps/indexer/lib/indexer/fetcher/internal_transaction.ex index cb3df8a15550..4e3aaad4e0f4 100644 --- a/apps/indexer/lib/indexer/fetcher/internal_transaction.ex +++ b/apps/indexer/lib/indexer/fetcher/internal_transaction.ex @@ -211,7 +211,7 @@ defmodule Indexer.Fetcher.InternalTransaction do Logger.error( fn -> [ - "failed to import first trace for tx: ", + "failed to import first trace for transaction: ", inspect(reason) ] end, diff --git a/apps/indexer/lib/indexer/fetcher/on_demand/first_trace.ex b/apps/indexer/lib/indexer/fetcher/on_demand/first_trace.ex index 4a5bf1187a3f..13ea834560c4 100644 --- a/apps/indexer/lib/indexer/fetcher/on_demand/first_trace.ex +++ b/apps/indexer/lib/indexer/fetcher/on_demand/first_trace.ex @@ -53,7 +53,7 @@ defmodule Indexer.Fetcher.OnDemand.FirstTrace do {:error, reason} -> Logger.error(fn -> - ["Error while fetching first trace for tx: #{hash_string} error reason: ", reason] + ["Error while fetching first trace for transaction: #{hash_string} error reason: ", reason] end) :ignore -> diff --git a/apps/indexer/lib/indexer/fetcher/optimism.ex b/apps/indexer/lib/indexer/fetcher/optimism.ex index 3a63cf01dfa1..e9f1bee03ed7 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism.ex @@ -244,8 +244,9 @@ defmodule Indexer.Fetcher.Optimism do {last_l1_block_number, last_l1_transaction_hash} <- caller.get_last_l1_item(), {:start_block_l1_valid, true} <- {:start_block_l1_valid, start_block_l1 <= last_l1_block_number || last_l1_block_number == 0}, - {:ok, last_l1_tx} <- get_transaction_by_hash(last_l1_transaction_hash, json_rpc_named_arguments), - {:l1_tx_not_found, false} <- {:l1_tx_not_found, !is_nil(last_l1_transaction_hash) && is_nil(last_l1_tx)}, + {:ok, last_l1_transaction} <- get_transaction_by_hash(last_l1_transaction_hash, json_rpc_named_arguments), + {:l1_transaction_not_found, false} <- + {:l1_transaction_not_found, !is_nil(last_l1_transaction_hash) && is_nil(last_l1_transaction)}, {:ok, block_check_interval, last_safe_block} <- get_block_check_interval(json_rpc_named_arguments) do contract_address = if caller == Indexer.Fetcher.Optimism.WithdrawalEvent do @@ -298,7 +299,7 @@ defmodule Indexer.Fetcher.Optimism do {:stop, :normal, %{}} - {:l1_tx_not_found, true} -> + {:l1_transaction_not_found, true} -> Logger.error( "Cannot find last L1 transaction from RPC by its hash. Probably, there was a reorg on L1 chain. Please, check #{table_name} table." ) diff --git a/apps/indexer/lib/indexer/fetcher/optimism/deposit.ex b/apps/indexer/lib/indexer/fetcher/optimism/deposit.ex index 0f1d1e3af22f..8e7155d5a0b6 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/deposit.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/deposit.ex @@ -76,9 +76,11 @@ defmodule Indexer.Fetcher.Optimism.Deposit do json_rpc_named_arguments = Optimism.json_rpc_named_arguments(optimism_l1_rpc), {optimism_portal, start_block_l1} <- Optimism.read_system_config(system_config, json_rpc_named_arguments), true <- start_block_l1 > 0, - {last_l1_block_number, last_l1_tx_hash} <- get_last_l1_item(), - {:ok, last_l1_tx} <- Optimism.get_transaction_by_hash(last_l1_tx_hash, json_rpc_named_arguments), - {:l1_tx_not_found, false} <- {:l1_tx_not_found, !is_nil(last_l1_tx_hash) && is_nil(last_l1_tx)}, + {last_l1_block_number, last_l1_transaction_hash} <- get_last_l1_item(), + {:ok, last_l1_transaction} <- + Optimism.get_transaction_by_hash(last_l1_transaction_hash, json_rpc_named_arguments), + {:l1_transaction_not_found, false} <- + {:l1_transaction_not_found, !is_nil(last_l1_transaction_hash) && is_nil(last_l1_transaction)}, {safe_block, _} = Helper.get_safe_block(json_rpc_named_arguments), {:start_block_l1_valid, true} <- {:start_block_l1_valid, @@ -119,7 +121,7 @@ defmodule Indexer.Fetcher.Optimism.Deposit do {:stop, :normal, state} - {:l1_tx_not_found, true} -> + {:l1_transaction_not_found, true} -> Logger.error( "Cannot find last L1 transaction from RPC by its hash. Probably, there was a reorg on L1 chain. Please, check op_deposits table." ) @@ -463,7 +465,7 @@ defmodule Indexer.Fetcher.Optimism.Deposit do |> Integer.to_string(16) |> String.downcase() - l2_tx_hash = + l2_transaction_hash = "0x" <> ((transaction_type <> "#{rlp_encoded}") |> Base.decode16!(case: :mixed) @@ -477,7 +479,7 @@ defmodule Indexer.Fetcher.Optimism.Deposit do l1_block_timestamp: Map.get(timestamps, block_number), l1_transaction_hash: transaction_hash, l1_transaction_origin: "0x" <> from_stripped, - l2_transaction_hash: l2_tx_hash + l2_transaction_hash: l2_transaction_hash } end diff --git a/apps/indexer/lib/indexer/fetcher/optimism/txn_batch.ex b/apps/indexer/lib/indexer/fetcher/optimism/transaction_batch.ex similarity index 92% rename from apps/indexer/lib/indexer/fetcher/optimism/txn_batch.ex rename to apps/indexer/lib/indexer/fetcher/optimism/transaction_batch.ex index f3373ad57961..cb5b3b148892 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/txn_batch.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/transaction_batch.ex @@ -1,4 +1,4 @@ -defmodule Indexer.Fetcher.Optimism.TxnBatch do +defmodule Indexer.Fetcher.Optimism.TransactionBatch do @moduledoc """ Fills op_transaction_batches, op_frame_sequence, and op_frame_sequence_blobs DB tables. @@ -34,7 +34,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do alias Explorer.Chain.Beacon.Blob, as: BeaconBlob alias Explorer.Chain.{Block, Hash} alias Explorer.Chain.Optimism.{FrameSequence, FrameSequenceBlob} - alias Explorer.Chain.Optimism.TxnBatch, as: OptimismTxnBatch + alias Explorer.Chain.Optimism.TransactionBatch, as: OptimismTransactionBatch alias HTTPoison.Response alias Indexer.Fetcher.Beacon.Blob alias Indexer.Fetcher.Beacon.Client, as: BeaconClient @@ -42,7 +42,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do alias Indexer.Helper alias Varint.LEB128 - @fetcher_name :optimism_txn_batches + @fetcher_name :optimism_transaction_batches # Optimism chain block time is a constant (2 seconds) @op_chain_block_time 2 @@ -105,11 +105,12 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do true <- start_block_l1 > 0, chunk_size = parse_integer(env[:blocks_chunk_size]), {:chunk_size_valid, true} <- {:chunk_size_valid, !is_nil(chunk_size) && chunk_size > 0}, - {last_l1_block_number, last_l1_transaction_hash, last_l1_tx} = get_last_l1_item(json_rpc_named_arguments), + {last_l1_block_number, last_l1_transaction_hash, last_l1_transaction} = + get_last_l1_item(json_rpc_named_arguments), {:start_block_l1_valid, true} <- {:start_block_l1_valid, start_block_l1 <= last_l1_block_number || last_l1_block_number == 0}, - {:l1_tx_not_found, false} <- - {:l1_tx_not_found, !is_nil(last_l1_transaction_hash) && is_nil(last_l1_tx)}, + {:l1_transaction_not_found, false} <- + {:l1_transaction_not_found, !is_nil(last_l1_transaction_hash) && is_nil(last_l1_transaction)}, {:ok, block_check_interval, last_safe_block} <- Optimism.get_block_check_interval(json_rpc_named_arguments) do start_block = max(start_block_l1, last_l1_block_number) @@ -173,7 +174,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do {:stop, :normal, state} - {:l1_tx_not_found, true} -> + {:l1_transaction_not_found, true} -> Logger.error( "Cannot find last L1 transaction from RPC by its hash. Probably, there was a reorg on L1 chain. Please, check op_transaction_batches table." ) @@ -255,7 +256,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do ) {:ok, new_incomplete_channels, batches, sequences, blobs} = - get_txn_batches( + get_transaction_batches( Range.new(chunk_start, chunk_end), batch_inbox, batch_submitter, @@ -277,7 +278,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do {:ok, inserted} = Chain.import(%{ optimism_frame_sequence_blobs: %{params: blobs}, - optimism_txn_batches: %{params: batches}, + optimism_transaction_batches: %{params: batches}, timeout: :infinity }) @@ -405,7 +406,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do l1_transaction_hashes = Repo.one( from( - tb in OptimismTxnBatch, + tb in OptimismTransactionBatch, inner_join: fs in FrameSequence, on: fs.id == tb.frame_sequence_id, select: fs.l1_transaction_hashes, @@ -419,14 +420,14 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do else last_l1_transaction_hash = List.last(l1_transaction_hashes) - {:ok, last_l1_tx} = Optimism.get_transaction_by_hash(last_l1_transaction_hash, json_rpc_named_arguments) + {:ok, last_l1_transaction} = Optimism.get_transaction_by_hash(last_l1_transaction_hash, json_rpc_named_arguments) - last_l1_block_number = quantity_to_integer(Map.get(last_l1_tx || %{}, "blockNumber", 0)) - {last_l1_block_number, last_l1_transaction_hash, last_l1_tx} + last_l1_block_number = quantity_to_integer(Map.get(last_l1_transaction || %{}, "blockNumber", 0)) + {last_l1_block_number, last_l1_transaction_hash, last_l1_transaction} end end - defp get_txn_batches( + defp get_transaction_batches( block_range, batch_inbox, batch_submitter, @@ -439,8 +440,8 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do case fetch_blocks_by_range(block_range, json_rpc_named_arguments) do {:ok, %Blocks{transactions_params: transactions_params, blocks_params: blocks_params, errors: []}} -> transactions_params - |> txs_filter(batch_submitter, batch_inbox) - |> get_txn_batches_inner( + |> transactions_filter(batch_submitter, batch_inbox) + |> get_transaction_batches_inner( blocks_params, genesis_block_l2, incomplete_channels, @@ -466,7 +467,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do Logger.error("#{error_message} Retrying...") :timer.sleep(3000) - get_txn_batches( + get_transaction_batches( block_range, batch_inbox, batch_submitter, @@ -504,7 +505,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do blob_data |> String.trim_leading("0x") |> Base.decode16!(case: :lower) - |> OptimismTxnBatch.decode_eip4844_blob() + |> OptimismTransactionBatch.decode_eip4844_blob() if is_nil(decoded) do Logger.warning("Cannot decode the blob #{blob_hash} taken from the Blockscout Blobs API.") @@ -574,7 +575,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do |> Map.get("blob") |> String.trim_leading("0x") |> Base.decode16!(case: :lower) - |> OptimismTxnBatch.decode_eip4844_blob() + |> OptimismTransactionBatch.decode_eip4844_blob() if is_nil(decoded_blob_data) do raise "Invalid blob" @@ -597,24 +598,24 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do inputs_acc end - defp celestia_blob_to_input("0x" <> tx_input, tx_hash, blobs_api_url) do - tx_input + defp celestia_blob_to_input("0x" <> transaction_input, transaction_hash, blobs_api_url) do + transaction_input |> Base.decode16!(case: :mixed) - |> celestia_blob_to_input(tx_hash, blobs_api_url) + |> celestia_blob_to_input(transaction_hash, blobs_api_url) end - defp celestia_blob_to_input(tx_input, _tx_hash, blobs_api_url) - when byte_size(tx_input) == 1 + 8 + 32 and blobs_api_url != "" do + defp celestia_blob_to_input(transaction_input, _transaction_hash, blobs_api_url) + when byte_size(transaction_input) == 1 + 8 + 32 and blobs_api_url != "" do # the first byte encodes Celestia sign 0xCE # the next 8 bytes encode little-endian Celestia blob height height = - tx_input + transaction_input |> binary_part(1, 8) |> :binary.decode_unsigned(:little) # the next 32 bytes contain the commitment - commitment = binary_part(tx_input, 1 + 8, 32) + commitment = binary_part(transaction_input, 1 + 8, 32) commitment_string = Base.encode16(commitment, case: :lower) url = blobs_api_url <> "?height=#{height}&commitment=" <> commitment_string @@ -647,13 +648,13 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do end end - defp celestia_blob_to_input(_tx_input, tx_hash, blobs_api_url) when blobs_api_url != "" do - Logger.error("L1 transaction with Celestia commitment has incorrect input length. Tx hash: #{tx_hash}") + defp celestia_blob_to_input(_transaction_input, transaction_hash, blobs_api_url) when blobs_api_url != "" do + Logger.error("L1 transaction with Celestia commitment has incorrect input length. Tx hash: #{transaction_hash}") [] end - defp celestia_blob_to_input(_tx_input, _tx_hash, "") do + defp celestia_blob_to_input(_transaction_input, _transaction_hash, "") do Logger.error( "Cannot read Celestia blobs from the server as the API URL is not defined. Please, check INDEXER_OPTIMISM_L1_BATCH_CELESTIA_BLOBS_API_URL env variable." ) @@ -661,7 +662,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do [] end - defp get_txn_batches_inner( + defp get_transaction_batches_inner( transactions_filtered, blocks_params, genesis_block_l2, @@ -670,29 +671,29 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do {eip4844_blobs_api_url, celestia_blobs_api_url} ) do transactions_filtered - |> Enum.reduce({:ok, incomplete_channels, [], [], []}, fn tx, + |> Enum.reduce({:ok, incomplete_channels, [], [], []}, fn transaction, {_, incomplete_channels_acc, batches_acc, sequences_acc, blobs_acc} -> inputs = cond do - tx.type == 3 -> + transaction.type == 3 -> # this is EIP-4844 transaction, so we get the inputs from the blobs - block_timestamp = get_block_timestamp_by_number(tx.block_number, blocks_params) + block_timestamp = get_block_timestamp_by_number(transaction.block_number, blocks_params) eip4844_blobs_to_inputs( - tx.hash, - tx.blob_versioned_hashes, + transaction.hash, + transaction.blob_versioned_hashes, block_timestamp, eip4844_blobs_api_url ) - first_byte(tx.input) == 0xCE -> + first_byte(transaction.input) == 0xCE -> # this is Celestia DA transaction, so we get the data from Celestia blob - celestia_blob_to_input(tx.input, tx.hash, celestia_blobs_api_url) + celestia_blob_to_input(transaction.input, transaction.hash, celestia_blobs_api_url) true -> # this is calldata transaction, so the data is in the transaction input - [%{bytes: tx.input}] + [%{bytes: transaction.input}] end Enum.reduce( @@ -701,7 +702,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do fn input, {_, new_incomplete_channels_acc, new_batches_acc, new_sequences_acc, new_blobs_acc} -> handle_input( input, - tx, + transaction, blocks_params, new_incomplete_channels_acc, {new_batches_acc, new_sequences_acc, new_blobs_acc}, @@ -715,7 +716,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do defp handle_input( input, - tx, + transaction, blocks_params, incomplete_channels_acc, {batches_acc, sequences_acc, blobs_acc}, @@ -725,11 +726,11 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do frame = input_to_frame(input.bytes) if frame == :invalid_frame do - Logger.warning("The frame in transaction #{tx.hash} is invalid.") + Logger.warning("The frame in transaction #{transaction.hash} is invalid.") raise "Invalid frame" end - block_timestamp = get_block_timestamp_by_number(tx.block_number, blocks_params) + block_timestamp = get_block_timestamp_by_number(transaction.block_number, blocks_params) channel = Map.get(incomplete_channels_acc, frame.channel_id, %{frames: %{}}) @@ -737,9 +738,9 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do Map.put(channel.frames, frame.number, %{ data: frame.data, is_last: frame.is_last, - block_number: tx.block_number, + block_number: transaction.block_number, block_timestamp: block_timestamp, - tx_hash: tx.hash, + transaction_hash: transaction.hash, eip4844_blob_hash: Map.get(input, :eip4844_blob_hash), celestia_blob_metadata: Map.get(input, :celestia_blob_metadata) }) @@ -789,7 +790,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do {bytes, l1_transaction_hashes, new_blobs_acc} = 0..(Enum.count(channel.frames) - 1) - |> Enum.reduce({<<>>, [], blobs_acc}, fn frame_number, {bytes_acc, tx_hashes_acc, new_blobs_acc} -> + |> Enum.reduce({<<>>, [], blobs_acc}, fn frame_number, {bytes_acc, transaction_hashes_acc, new_blobs_acc} -> frame = Map.get(channel.frames, frame_number) next_blob_id = next_blob_id(List.last(new_blobs_acc)) @@ -808,7 +809,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do ), type: :eip4844, metadata: %{hash: frame.eip4844_blob_hash}, - l1_transaction_hash: frame.tx_hash, + l1_transaction_hash: frame.transaction_hash, l1_timestamp: frame.block_timestamp, frame_sequence_id: frame_sequence_id } @@ -830,7 +831,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do key: :crypto.hash(:sha256, height <> commitment), type: :celestia, metadata: frame.celestia_blob_metadata, - l1_transaction_hash: frame.tx_hash, + l1_transaction_hash: frame.transaction_hash, l1_timestamp: frame.block_timestamp, frame_sequence_id: frame_sequence_id } @@ -840,7 +841,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do new_blobs_acc end - {bytes_acc <> frame.data, [frame.tx_hash | tx_hashes_acc], new_blobs_acc} + {bytes_acc <> frame.data, [frame.transaction_hash | transaction_hashes_acc], new_blobs_acc} end) batches_parsed = @@ -900,14 +901,15 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do frame_sequence_ids = Repo.all( from( - tb in OptimismTxnBatch, + tb in OptimismTransactionBatch, select: tb.frame_sequence_id, where: tb.l2_block_number >= ^reorg_block ), timeout: :infinity ) - {deleted_count, _} = Repo.delete_all(from(tb in OptimismTxnBatch, where: tb.l2_block_number >= ^reorg_block)) + {deleted_count, _} = + Repo.delete_all(from(tb in OptimismTransactionBatch, where: tb.l2_block_number >= ^reorg_block)) Repo.delete_all(from(fs in FrameSequence, where: fs.id in ^frame_sequence_ids)) @@ -1261,7 +1263,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do defp remove_prev_frame_sequences(inserted) do ids = inserted - |> Map.get(:insert_txn_batches, []) + |> Map.get(:insert_transaction_batches, []) |> Enum.map(fn tb -> tb.frame_sequence_id_prev end) |> Enum.uniq() |> Enum.filter(fn id -> id > 0 end) @@ -1286,7 +1288,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do ) end - defp txs_filter(transactions_params, batch_submitter, batch_inbox) do + defp transactions_filter(transactions_params, batch_submitter, batch_inbox) do transactions_params |> Enum.filter(fn t -> from_address_hash = Map.get(t, :from_address_hash) @@ -1333,8 +1335,8 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do end end - defp first_byte("0x" <> tx_input) do - tx_input + defp first_byte("0x" <> transaction_input) do + transaction_input |> Base.decode16!(case: :mixed) |> first_byte() end @@ -1343,7 +1345,7 @@ defmodule Indexer.Fetcher.Optimism.TxnBatch do version_byte end - defp first_byte(_tx_input) do + defp first_byte(_transaction_input) do nil end diff --git a/apps/indexer/lib/indexer/fetcher/optimism/withdrawal.ex b/apps/indexer/lib/indexer/fetcher/optimism/withdrawal.ex index 5756ad60064c..113a56fbce1f 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/withdrawal.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/withdrawal.ex @@ -61,8 +61,10 @@ defmodule Indexer.Fetcher.Optimism.Withdrawal do {:start_block_l2_valid, true} <- {:start_block_l2_valid, (start_block_l2 <= last_l2_block_number || last_l2_block_number == 0) && start_block_l2 <= safe_block}, - {:ok, last_l2_tx} <- Optimism.get_transaction_by_hash(last_l2_transaction_hash, json_rpc_named_arguments), - {:l2_tx_not_found, false} <- {:l2_tx_not_found, !is_nil(last_l2_transaction_hash) && is_nil(last_l2_tx)} do + {:ok, last_l2_transaction} <- + Optimism.get_transaction_by_hash(last_l2_transaction_hash, json_rpc_named_arguments), + {:l2_transaction_not_found, false} <- + {:l2_transaction_not_found, !is_nil(last_l2_transaction_hash) && is_nil(last_l2_transaction)} do Process.send(self(), :continue, []) {:noreply, @@ -92,7 +94,7 @@ defmodule Indexer.Fetcher.Optimism.Withdrawal do {:stop, :normal, state} - {:l2_tx_not_found, true} -> + {:l2_transaction_not_found, true} -> Logger.error( "Cannot find last L2 transaction from RPC by its hash. Probably, there was a reorg on L2 chain. Please, check op_withdrawals table." ) diff --git a/apps/indexer/lib/indexer/fetcher/optimism/withdrawal_event.ex b/apps/indexer/lib/indexer/fetcher/optimism/withdrawal_event.ex index f2472a5c2716..735db06d42a7 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/withdrawal_event.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/withdrawal_event.ex @@ -164,15 +164,15 @@ defmodule Indexer.Fetcher.Optimism.WithdrawalEvent do end end - defp get_transaction_input_by_hash(blocks, tx_hashes) do + defp get_transaction_input_by_hash(blocks, transaction_hashes) do Enum.reduce(blocks, %{}, fn block, acc -> block |> Map.get("transactions", []) - |> Enum.filter(fn tx -> - Enum.member?(tx_hashes, tx["hash"]) + |> Enum.filter(fn transaction -> + Enum.member?(transaction_hashes, transaction["hash"]) end) - |> Enum.map(fn tx -> - {tx["hash"], tx["input"]} + |> Enum.map(fn transaction -> + {transaction["hash"], transaction["input"]} end) |> Enum.into(%{}) |> Map.merge(acc) @@ -184,7 +184,7 @@ defmodule Indexer.Fetcher.Optimism.WithdrawalEvent do events |> get_blocks_by_events(json_rpc_named_arguments, Helper.infinite_retries_number()) - tx_hashes = + transaction_hashes = events |> Enum.reduce([], fn event, acc -> if Enum.member?([@withdrawal_proven_event, @withdrawal_proven_event_blast], Enum.at(event["topics"], 0)) do @@ -194,7 +194,7 @@ defmodule Indexer.Fetcher.Optimism.WithdrawalEvent do end end) - input_by_hash = get_transaction_input_by_hash(blocks, tx_hashes) + input_by_hash = get_transaction_input_by_hash(blocks, transaction_hashes) timestamps = blocks @@ -206,13 +206,13 @@ defmodule Indexer.Fetcher.Optimism.WithdrawalEvent do events |> Enum.map(fn event -> - tx_hash = event["transactionHash"] + transaction_hash = event["transactionHash"] {l1_event_type, game_index} = if Enum.member?([@withdrawal_proven_event, @withdrawal_proven_event_blast], Enum.at(event["topics"], 0)) do game_index = input_by_hash - |> Map.get(tx_hash) + |> Map.get(transaction_hash) |> input_to_game_index() {"WithdrawalProven", game_index} @@ -226,7 +226,7 @@ defmodule Indexer.Fetcher.Optimism.WithdrawalEvent do withdrawal_hash: Enum.at(event["topics"], 1), l1_event_type: l1_event_type, l1_timestamp: Map.get(timestamps, l1_block_number), - l1_transaction_hash: tx_hash, + l1_transaction_hash: transaction_hash, l1_block_number: l1_block_number, game_index: game_index } @@ -280,10 +280,10 @@ defmodule Indexer.Fetcher.Optimism.WithdrawalEvent do method_signature = String.slice(input, 0..9) if method_signature == "0x4870496f" do - # the signature of `proveWithdrawalTransaction(tuple _tx, uint256 _disputeGameIndex, tuple _outputRootProof, bytes[] _withdrawalProof)` method + # the signature of `proveWithdrawalTransaction(tuple _transaction, uint256 _disputeGameIndex, tuple _outputRootProof, bytes[] _withdrawalProof)` method - # to get (slice) `_disputeGameIndex` from the tx input, we need to know its offset in the input string (represented as 0x...): - # offset = 10 symbols of signature (incl. `0x` prefix) + 64 symbols (representing 32 bytes) of the `_tx` tuple offset, totally is 74 + # to get (slice) `_disputeGameIndex` from the transaction input, we need to know its offset in the input string (represented as 0x...): + # offset = 10 symbols of signature (incl. `0x` prefix) + 64 symbols (representing 32 bytes) of the `_transaction` tuple offset, totally is 74 game_index_offset = String.length(method_signature) + 32 * 2 game_index_length = 32 * 2 diff --git a/apps/indexer/lib/indexer/fetcher/polygon_edge.ex b/apps/indexer/lib/indexer/fetcher/polygon_edge.ex index 73a42f83ac9e..cb343f9c71d8 100644 --- a/apps/indexer/lib/indexer/fetcher/polygon_edge.ex +++ b/apps/indexer/lib/indexer/fetcher/polygon_edge.ex @@ -66,13 +66,14 @@ defmodule Indexer.Fetcher.PolygonEdge do {:start_block_l1_valid, true} <- {:start_block_l1_valid, start_block_l1 <= last_l1_block_number || last_l1_block_number == 0}, json_rpc_named_arguments = json_rpc_named_arguments(polygon_edge_l1_rpc), - {:ok, last_l1_tx} <- + {:ok, last_l1_transaction} <- Helper.get_transaction_by_hash( last_l1_transaction_hash, json_rpc_named_arguments, Helper.infinite_retries_number() ), - {:l1_tx_not_found, false} <- {:l1_tx_not_found, !is_nil(last_l1_transaction_hash) && is_nil(last_l1_tx)}, + {:l1_transaction_not_found, false} <- + {:l1_transaction_not_found, !is_nil(last_l1_transaction_hash) && is_nil(last_l1_transaction)}, {:ok, block_check_interval, last_safe_block} <- Helper.get_block_check_interval(json_rpc_named_arguments) do start_block = max(start_block_l1, last_l1_block_number) @@ -112,7 +113,7 @@ defmodule Indexer.Fetcher.PolygonEdge do :ignore - {:l1_tx_not_found, true} -> + {:l1_transaction_not_found, true} -> Logger.error( "Cannot find last L1 transaction from RPC by its hash. Probably, there was a reorg on L1 chain. Please, check #{table_name} table." ) @@ -147,13 +148,14 @@ defmodule Indexer.Fetcher.PolygonEdge do {:start_block_l2_valid, true} <- {:start_block_l2_valid, (start_block_l2 <= last_l2_block_number || last_l2_block_number == 0) && start_block_l2 <= safe_block}, - {:ok, last_l2_tx} <- + {:ok, last_l2_transaction} <- Helper.get_transaction_by_hash( last_l2_transaction_hash, json_rpc_named_arguments, Helper.infinite_retries_number() ), - {:l2_tx_not_found, false} <- {:l2_tx_not_found, !is_nil(last_l2_transaction_hash) && is_nil(last_l2_tx)} do + {:l2_transaction_not_found, false} <- + {:l2_transaction_not_found, !is_nil(last_l2_transaction_hash) && is_nil(last_l2_transaction)} do Process.send(pid, :continue, []) {:ok, @@ -184,7 +186,7 @@ defmodule Indexer.Fetcher.PolygonEdge do :ignore - {:l2_tx_not_found, true} -> + {:l2_transaction_not_found, true} -> Logger.error( "Cannot find last L2 transaction from RPC by its hash. Probably, there was a reorg on L2 chain. Please, check #{table_name} table." ) diff --git a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex index 54a6d02b62e2..d6b77b89e0c8 100644 --- a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex +++ b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex @@ -77,8 +77,10 @@ defmodule Indexer.Fetcher.PolygonZkevm.BridgeL1 do {:start_block_valid, (start_block <= last_l1_block_number || last_l1_block_number == 0) && start_block <= safe_block, last_l1_block_number, safe_block}, - {:ok, last_l1_tx} <- Helper.get_transaction_by_hash(last_l1_transaction_hash, json_rpc_named_arguments), - {:l1_tx_not_found, false} <- {:l1_tx_not_found, !is_nil(last_l1_transaction_hash) && is_nil(last_l1_tx)} do + {:ok, last_l1_transaction} <- + Helper.get_transaction_by_hash(last_l1_transaction_hash, json_rpc_named_arguments), + {:l1_transaction_not_found, false} <- + {:l1_transaction_not_found, !is_nil(last_l1_transaction_hash) && is_nil(last_l1_transaction)} do Process.send(self(), :continue, []) {:noreply, @@ -144,7 +146,7 @@ defmodule Indexer.Fetcher.PolygonZkevm.BridgeL1 do {:stop, :normal, %{}} - {:l1_tx_not_found, true} -> + {:l1_transaction_not_found, true} -> Logger.error( "Cannot find last L1 transaction from RPC by its hash. Probably, there was a reorg on L1 chain. Please, check polygon_zkevm_bridge table." ) diff --git a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l2.ex b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l2.ex index b5011d6de7a5..983f69a39cc1 100644 --- a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l2.ex +++ b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l2.ex @@ -76,8 +76,10 @@ defmodule Indexer.Fetcher.PolygonZkevm.BridgeL2 do {:start_block_valid, true} <- {:start_block_valid, (start_block <= last_l2_block_number || last_l2_block_number == 0) && start_block <= latest_block}, - {:ok, last_l2_tx} <- Helper.get_transaction_by_hash(last_l2_transaction_hash, json_rpc_named_arguments), - {:l2_tx_not_found, false} <- {:l2_tx_not_found, !is_nil(last_l2_transaction_hash) && is_nil(last_l2_tx)} do + {:ok, last_l2_transaction} <- + Helper.get_transaction_by_hash(last_l2_transaction_hash, json_rpc_named_arguments), + {:l2_transaction_not_found, false} <- + {:l2_transaction_not_found, !is_nil(last_l2_transaction_hash) && is_nil(last_l2_transaction)} do Process.send(self(), :continue, []) {:noreply, @@ -137,7 +139,7 @@ defmodule Indexer.Fetcher.PolygonZkevm.BridgeL2 do {:stop, :normal, state} - {:l2_tx_not_found, true} -> + {:l2_transaction_not_found, true} -> Logger.error( "Cannot find last L2 transaction from RPC by its hash. Probably, there was a reorg on L2 chain. Please, check polygon_zkevm_bridge table." ) diff --git a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/transaction_batch.ex b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/transaction_batch.ex index 379d19db02af..63325ac99b52 100644 --- a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/transaction_batch.ex +++ b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/transaction_batch.ex @@ -173,23 +173,23 @@ defmodule Indexer.Fetcher.PolygonZkevm.TransactionBatch do {:ok, responses} = Helper.repeated_call(&json_rpc/2, [requests, json_rpc_named_arguments], error_message, 3) - # For every batch info extract batches' L1 sequence tx and L1 verify tx + # For every batch info extract batches' L1 sequence transaction and L1 verify transaction {sequence_hashes, verify_hashes} = responses |> Enum.reduce({[], []}, fn res, {sequences, verifies} = _acc -> - send_sequences_tx_hash = get_tx_hash(res.result, "sendSequencesTxHash") - verify_batch_tx_hash = get_tx_hash(res.result, "verifyBatchTxHash") + send_sequences_transaction_hash = get_transaction_hash(res.result, "sendSequencesTxHash") + verify_batch_transaction_hash = get_transaction_hash(res.result, "verifyBatchTxHash") sequences = - if send_sequences_tx_hash != @zero_hash do - [Base.decode16!(send_sequences_tx_hash, case: :mixed) | sequences] + if send_sequences_transaction_hash != @zero_hash do + [Base.decode16!(send_sequences_transaction_hash, case: :mixed) | sequences] else sequences end verifies = - if verify_batch_tx_hash != @zero_hash do - [Base.decode16!(verify_batch_tx_hash, case: :mixed) | verifies] + if verify_batch_transaction_hash != @zero_hash do + [Base.decode16!(verify_batch_transaction_hash, case: :mixed) | verifies] else verifies end @@ -198,21 +198,22 @@ defmodule Indexer.Fetcher.PolygonZkevm.TransactionBatch do end) # All L1 transactions in one list without repetition - l1_tx_hashes = Enum.uniq(sequence_hashes ++ verify_hashes) + l1_transaction_hashes = Enum.uniq(sequence_hashes ++ verify_hashes) - # Receive all IDs for L1 txs + # Receive all IDs for L1 transactions hash_to_id = - l1_tx_hashes + l1_transaction_hashes |> Reader.lifecycle_transactions() |> Enum.reduce(%{}, fn {hash, id}, acc -> Map.put(acc, hash.bytes, id) end) # For every batch build batch representation, collect associated L1 and L2 transactions - {batches_to_import, l2_txs_to_import, l1_txs_to_import, _, _} = + {batches_to_import, l2_transactions_to_import, l1_transactions_to_import, _, _} = responses |> Enum.reduce({[], [], [], Reader.next_id(), hash_to_id}, fn res, - {batches, l2_txs, l1_txs, next_id, hash_to_id} = + {batches, l2_transactions, l1_transactions, next_id, + hash_to_id} = _acc -> number = quantity_to_integer(Map.get(res.result, "number")) @@ -229,32 +230,32 @@ defmodule Indexer.Fetcher.PolygonZkevm.TransactionBatch do state_root = Map.get(res.result, "stateRoot") # Get ID for sequence transaction (new ID if the batch is just sequenced) - {sequence_id, l1_txs, next_id, hash_to_id} = + {sequence_id, l1_transactions, next_id, hash_to_id} = res.result - |> get_tx_hash("sendSequencesTxHash") - |> handle_tx_hash(hash_to_id, next_id, l1_txs, false) + |> get_transaction_hash("sendSequencesTxHash") + |> handle_transaction_hash(hash_to_id, next_id, l1_transactions, false) # Get ID for verify transaction (new ID if the batch is just verified) - {verify_id, l1_txs, next_id, hash_to_id} = + {verify_id, l1_transactions, next_id, hash_to_id} = res.result - |> get_tx_hash("verifyBatchTxHash") - |> handle_tx_hash(hash_to_id, next_id, l1_txs, true) + |> get_transaction_hash("verifyBatchTxHash") + |> handle_transaction_hash(hash_to_id, next_id, l1_transactions, true) # Associate every transaction from batch with the batch number - l2_txs_append = + l2_transactions_append = l2_transaction_hashes |> Kernel.||([]) - |> Enum.map(fn l2_tx_hash -> + |> Enum.map(fn l2_transaction_hash -> %{ batch_number: number, - hash: l2_tx_hash + hash: l2_transaction_hash } end) batch = %{ number: number, timestamp: timestamp, - l2_transactions_count: Enum.count(l2_txs_append), + l2_transactions_count: Enum.count(l2_transactions_append), global_exit_root: global_exit_root, acc_input_hash: acc_input_hash, state_root: state_root, @@ -262,15 +263,15 @@ defmodule Indexer.Fetcher.PolygonZkevm.TransactionBatch do verify_id: verify_id } - {[batch | batches], l2_txs ++ l2_txs_append, l1_txs, next_id, hash_to_id} + {[batch | batches], l2_transactions ++ l2_transactions_append, l1_transactions, next_id, hash_to_id} end) # Update batches list, L1 transactions list and L2 transaction list {:ok, _} = Chain.import(%{ - polygon_zkevm_lifecycle_transactions: %{params: l1_txs_to_import}, + polygon_zkevm_lifecycle_transactions: %{params: l1_transactions_to_import}, polygon_zkevm_transaction_batches: %{params: batches_to_import}, - polygon_zkevm_batch_transactions: %{params: l2_txs_to_import}, + polygon_zkevm_batch_transactions: %{params: l2_transactions_to_import}, timeout: :infinity }) @@ -306,27 +307,27 @@ defmodule Indexer.Fetcher.PolygonZkevm.TransactionBatch do {latest_batch_number, virtual_batch_number, verified_batch_number} end - defp get_tx_hash(result, type) do + defp get_transaction_hash(result, type) do case Map.get(result, type) do - "0x" <> tx_hash -> tx_hash + "0x" <> transaction_hash -> transaction_hash nil -> @zero_hash end end - defp handle_tx_hash(encoded_tx_hash, hash_to_id, next_id, l1_txs, is_verify) do - if encoded_tx_hash != @zero_hash do - tx_hash = Base.decode16!(encoded_tx_hash, case: :mixed) + defp handle_transaction_hash(encoded_transaction_hash, hash_to_id, next_id, l1_transactions, is_verify) do + if encoded_transaction_hash != @zero_hash do + transaction_hash = Base.decode16!(encoded_transaction_hash, case: :mixed) - id = Map.get(hash_to_id, tx_hash) + id = Map.get(hash_to_id, transaction_hash) if is_nil(id) do - {next_id, [%{id: next_id, hash: tx_hash, is_verify: is_verify} | l1_txs], next_id + 1, - Map.put(hash_to_id, tx_hash, next_id)} + {next_id, [%{id: next_id, hash: transaction_hash, is_verify: is_verify} | l1_transactions], next_id + 1, + Map.put(hash_to_id, transaction_hash, next_id)} else - {id, l1_txs, next_id, hash_to_id} + {id, l1_transactions, next_id, hash_to_id} end else - {nil, l1_txs, next_id, hash_to_id} + {nil, l1_transactions, next_id, hash_to_id} end end end diff --git a/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex b/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex index 3c810cd7e730..fa7c4723c312 100644 --- a/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex +++ b/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex @@ -33,7 +33,7 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do optimism_modules = [ Indexer.Fetcher.Optimism.OutputRoot, - Indexer.Fetcher.Optimism.TxnBatch, + Indexer.Fetcher.Optimism.TransactionBatch, Indexer.Fetcher.Optimism.WithdrawalEvent ] diff --git a/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex b/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex index 4a3889dd2a4d..4c29d2b5d1fb 100644 --- a/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex +++ b/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex @@ -135,8 +135,10 @@ defmodule Indexer.Fetcher.Shibarium.L1 do {:start_block_valid, true} <- {:start_block_valid, start_block <= last_l1_block_number || last_l1_block_number == 0}, json_rpc_named_arguments = json_rpc_named_arguments(rpc), - {:ok, last_l1_tx} <- Helper.get_transaction_by_hash(last_l1_transaction_hash, json_rpc_named_arguments), - {:l1_tx_not_found, false} <- {:l1_tx_not_found, !is_nil(last_l1_transaction_hash) && is_nil(last_l1_tx)}, + {:ok, last_l1_transaction} <- + Helper.get_transaction_by_hash(last_l1_transaction_hash, json_rpc_named_arguments), + {:l1_transaction_not_found, false} <- + {:l1_transaction_not_found, !is_nil(last_l1_transaction_hash) && is_nil(last_l1_transaction)}, {:ok, block_check_interval, latest_block} <- get_block_check_interval(json_rpc_named_arguments), {:start_block_valid, true} <- {:start_block_valid, start_block <= latest_block} do recalculate_cached_count() @@ -204,7 +206,7 @@ defmodule Indexer.Fetcher.Shibarium.L1 do {:stop, :normal, %{}} - {:l1_tx_not_found, true} -> + {:l1_transaction_not_found, true} -> Logger.error( "Cannot find last L1 transaction from RPC by its hash. Probably, there was a reorg on L1 chain. Please, check shibarium_bridge table." ) diff --git a/apps/indexer/lib/indexer/fetcher/shibarium/l2.ex b/apps/indexer/lib/indexer/fetcher/shibarium/l2.ex index b85e4b558694..4007ba2211c5 100644 --- a/apps/indexer/lib/indexer/fetcher/shibarium/l2.ex +++ b/apps/indexer/lib/indexer/fetcher/shibarium/l2.ex @@ -100,8 +100,10 @@ defmodule Indexer.Fetcher.Shibarium.L2 do {:start_block_valid, true} <- {:start_block_valid, (start_block <= last_l2_block_number || last_l2_block_number == 0) && start_block <= latest_block}, - {:ok, last_l2_tx} <- Helper.get_transaction_by_hash(last_l2_transaction_hash, json_rpc_named_arguments), - {:l2_tx_not_found, false} <- {:l2_tx_not_found, !is_nil(last_l2_transaction_hash) && is_nil(last_l2_tx)} do + {:ok, last_l2_transaction} <- + Helper.get_transaction_by_hash(last_l2_transaction_hash, json_rpc_named_arguments), + {:l2_transaction_not_found, false} <- + {:l2_transaction_not_found, !is_nil(last_l2_transaction_hash) && is_nil(last_l2_transaction)} do recalculate_cached_count() Process.send(self(), :continue, []) @@ -143,7 +145,7 @@ defmodule Indexer.Fetcher.Shibarium.L2 do {:stop, :normal, state} - {:l2_tx_not_found, true} -> + {:l2_transaction_not_found, true} -> Logger.error( "Cannot find last L2 transaction from RPC by its hash. Probably, there was a reorg on L2 chain. Please, check shibarium_bridge table." ) @@ -445,9 +447,9 @@ defmodule Indexer.Fetcher.Shibarium.L2 do end end - defp get_receipt_logs(tx_hashes, json_rpc_named_arguments, retries) do + defp get_receipt_logs(transaction_hashes, json_rpc_named_arguments, retries) do reqs = - tx_hashes + transaction_hashes |> Enum.with_index() |> Enum.map(fn {hash, id} -> request(%{ diff --git a/apps/indexer/lib/indexer/fetcher/transaction_action.ex b/apps/indexer/lib/indexer/fetcher/transaction_action.ex index 0e1a8a1756ca..cb20400ce074 100644 --- a/apps/indexer/lib/indexer/fetcher/transaction_action.ex +++ b/apps/indexer/lib/indexer/fetcher/transaction_action.ex @@ -19,9 +19,9 @@ defmodule Indexer.Fetcher.TransactionAction do alias Explorer.Chain.{Block, BlockNumberHelper, Log, TransactionAction} alias Indexer.Transform.{Addresses, TransactionActions} - @stage_first_block "tx_action_first_block" - @stage_next_block "tx_action_next_block" - @stage_last_block "tx_action_last_block" + @stage_first_block "transaction_action_first_block" + @stage_next_block "transaction_action_next_block" + @stage_last_block "transaction_action_last_block" defstruct first_block: nil, next_block: nil, last_block: nil, protocols: [], task: nil, pid: nil @@ -178,7 +178,7 @@ defmodule Indexer.Fetcher.TransactionAction do transaction_actions: transaction_actions }) - tx_actions = + transaction_actions_with_data = Enum.map(transaction_actions, fn action -> Map.put(action, :data, Map.delete(action.data, :block_number)) end) @@ -186,7 +186,7 @@ defmodule Indexer.Fetcher.TransactionAction do {:ok, _} = Chain.import(%{ addresses: %{params: addresses, on_conflict: :nothing}, - transaction_actions: %{params: tx_actions}, + transaction_actions: %{params: transaction_actions_with_data}, timeout: :infinity }) @@ -203,7 +203,7 @@ defmodule Indexer.Fetcher.TransactionAction do Logger.info( "Block #{block_number} handled successfully. Progress: #{progress_percentage}%. Initial block range: #{first_block}..#{last_block}." <> - " Actions found: #{Enum.count(tx_actions)}." <> + " Actions found: #{Enum.count(transaction_actions_with_data)}." <> if(next_block_new >= first_block, do: " Remaining block range: #{first_block}..#{next_block_new}", else: "") ) diff --git a/apps/indexer/lib/indexer/fetcher/zksync/discovery/batches_data.ex b/apps/indexer/lib/indexer/fetcher/zksync/discovery/batches_data.ex index 3c68a0ff4caf..36a816674beb 100644 --- a/apps/indexer/lib/indexer/fetcher/zksync/discovery/batches_data.ex +++ b/apps/indexer/lib/indexer/fetcher/zksync/discovery/batches_data.ex @@ -21,11 +21,11 @@ defmodule Indexer.Fetcher.ZkSync.Discovery.BatchesData do and `json_rpc_named_arguments` defining parameters for the RPC connection. ## Returns - - `{batches_to_import, l2_blocks_to_import, l2_txs_to_import}` + - `{batches_to_import, l2_blocks_to_import, l2_transactions_to_import}` where - `batches_to_import` is a map of batches data - `l2_blocks_to_import` is a list of blocks associated with batches by batch numbers - - `l2_txs_to_import` is a list of transactions associated with batches by batch numbers + - `l2_transactions_to_import` is a list of transactions associated with batches by batch numbers """ @spec extract_data_from_batches([integer()] | {integer(), integer()}, %{ :chunk_size => pos_integer(), @@ -55,10 +55,10 @@ defmodule Indexer.Fetcher.ZkSync.Discovery.BatchesData do batches_to_import = get_block_ranges(initial_batches_to_import, config) - {l2_blocks_to_import, l2_txs_to_import} = get_l2_blocks_and_transactions(batches_to_import, config) - log_info("Linked #{length(l2_blocks_to_import)} L2 blocks and #{length(l2_txs_to_import)} L2 transactions") + {l2_blocks_to_import, l2_transactions_to_import} = get_l2_blocks_and_transactions(batches_to_import, config) + log_info("Linked #{length(l2_blocks_to_import)} L2 blocks and #{length(l2_transactions_to_import)} L2 transactions") - {batches_to_import, l2_blocks_to_import, l2_txs_to_import} + {batches_to_import, l2_blocks_to_import, l2_transactions_to_import} end @doc """ @@ -70,40 +70,40 @@ defmodule Indexer.Fetcher.ZkSync.Discovery.BatchesData do ## Parameters - `batches`: A list of maps describing batches. Each map is expected to define the following - elements: `commit_tx_hash`, `commit_timestamp`, `prove_tx_hash`, `prove_timestamp`, - `executed_tx_hash`, `executed_timestamp`. + elements: `commit_transaction_hash`, `commit_timestamp`, `prove_transaction_hash`, `prove_timestamp`, + `executed_transaction_hash`, `executed_timestamp`. ## Returns - - `l1_txs`: A map where keys are L1 transaction hashes, and values are maps containing + - `l1_transactions`: A map where keys are L1 transaction hashes, and values are maps containing transaction hashes and timestamps. """ @spec collect_l1_transactions(list()) :: map() def collect_l1_transactions(batches) when is_list(batches) do - l1_txs = + l1_transactions = batches - |> Enum.reduce(%{}, fn batch, l1_txs -> + |> Enum.reduce(%{}, fn batch, l1_transactions -> [ - %{hash: batch.commit_tx_hash, timestamp: batch.commit_timestamp}, - %{hash: batch.prove_tx_hash, timestamp: batch.prove_timestamp}, - %{hash: batch.executed_tx_hash, timestamp: batch.executed_timestamp} + %{hash: batch.commit_transaction_hash, timestamp: batch.commit_timestamp}, + %{hash: batch.prove_transaction_hash, timestamp: batch.prove_timestamp}, + %{hash: batch.executed_transaction_hash, timestamp: batch.executed_timestamp} ] - |> Enum.reduce(l1_txs, fn l1_tx, acc -> - # checks if l1_tx is not empty and adds to acc - add_l1_tx_to_list(acc, l1_tx) + |> Enum.reduce(l1_transactions, fn l1_transaction, acc -> + # checks if l1_transaction is not empty and adds to acc + add_l1_transaction_to_list(acc, l1_transaction) end) end) - log_info("Collected #{length(Map.keys(l1_txs))} L1 hashes") + log_info("Collected #{length(Map.keys(l1_transactions))} L1 hashes") - l1_txs + l1_transactions end - defp add_l1_tx_to_list(l1_txs, l1_tx) do - if l1_tx.hash != Rpc.get_binary_zero_hash() do - Map.put(l1_txs, l1_tx.hash, l1_tx) + defp add_l1_transaction_to_list(l1_transactions, l1_transaction) do + if l1_transaction.hash != Rpc.get_binary_zero_hash() do + Map.put(l1_transactions, l1_transaction.hash, l1_transaction) else - l1_txs + l1_transactions end end @@ -260,10 +260,10 @@ defmodule Indexer.Fetcher.ZkSync.Discovery.BatchesData do # RPC connection. # # ## Returns - # - {l2_blocks_to_import, l2_txs_to_import}, where + # - {l2_blocks_to_import, l2_transactions_to_import}, where # - `l2_blocks_to_import` contains a list of all rollup blocks with their associations with # the provided batches. The association is a map with the block hash and the batch number. - # - `l2_txs_to_import` contains a list of all rollup transactions with their associations + # - `l2_transactions_to_import` contains a list of all rollup transactions with their associations # with the provided batches. The association is a map with the transaction hash and # the batch number. defp get_l2_blocks_and_transactions( @@ -297,12 +297,12 @@ defmodule Indexer.Fetcher.ZkSync.Discovery.BatchesData do # The chunks requests are sent to the RPC node and parsed to # extract rollup block hashes and rollup transactions. - {blocks_associations, l2_txs_to_import} = + {blocks_associations, l2_transactions_to_import} = finalized_chunked_requests - |> Enum.reduce({blocks_to_batches, []}, fn requests, {blocks, l2_txs} -> + |> Enum.reduce({blocks_to_batches, []}, fn requests, {blocks, l2_transactions} -> requests |> Rpc.fetch_blocks_details(json_rpc_named_arguments) - |> extract_block_hash_and_transactions_list(blocks, l2_txs) + |> extract_block_hash_and_transactions_list(blocks, l2_transactions) end) # Check that amount of received transactions for a batch is correct @@ -310,15 +310,15 @@ defmodule Indexer.Fetcher.ZkSync.Discovery.BatchesData do |> Map.keys() |> Enum.each(fn batch_number -> batch = Map.get(batches, batch_number) - txs_in_batch = batch.l1_tx_count + batch.l2_tx_count + transactions_in_batch = batch.l1_transaction_count + batch.l2_transaction_count - ^txs_in_batch = - Enum.count(l2_txs_to_import, fn tx -> - tx.batch_number == batch_number + ^transactions_in_batch = + Enum.count(l2_transactions_to_import, fn transaction -> + transaction.batch_number == batch_number end) end) - {Map.values(blocks_associations), l2_txs_to_import} + {Map.values(blocks_associations), l2_transactions_to_import} end # For a given list of rollup block numbers, this function extends: @@ -376,38 +376,38 @@ defmodule Indexer.Fetcher.ZkSync.Discovery.BatchesData do # ## Parameters # - `json_responses`: A list of responses to `eth_getBlockByNumber` calls. # - `l2_blocks`: A map of accumulated associations between rollup blocks and batches. - # - `l2_txs`: A list of accumulated associations between rollup transactions and batches. + # - `l2_transactions`: A list of accumulated associations between rollup transactions and batches. # # ## Returns - # - {l2_blocks, l2_txs}, where + # - {l2_blocks, l2_transactions}, where # - `l2_blocks`: Updated map of accumulated associations between rollup blocks and batches. - # - `l2_txs`: Updated list of accumulated associations between rollup transactions and batches. - defp extract_block_hash_and_transactions_list(json_responses, l2_blocks, l2_txs) do + # - `l2_transactions`: Updated list of accumulated associations between rollup transactions and batches. + defp extract_block_hash_and_transactions_list(json_responses, l2_blocks, l2_transactions) do json_responses - |> Enum.reduce({l2_blocks, l2_txs}, fn resp, {l2_blocks, l2_txs} -> + |> Enum.reduce({l2_blocks, l2_transactions}, fn resp, {l2_blocks, l2_transactions} -> {block, l2_blocks} = Map.get_and_update(l2_blocks, resp.id, fn block -> {block, Map.put(block, :hash, Map.get(resp.result, "hash"))} end) - l2_txs = + l2_transactions = case Map.get(resp.result, "transactions") do nil -> - l2_txs + l2_transactions - new_txs -> - Enum.reduce(new_txs, l2_txs, fn l2_tx_hash, l2_txs -> + new_transactions -> + Enum.reduce(new_transactions, l2_transactions, fn l2_transaction_hash, l2_transactions -> [ %{ batch_number: block.batch_number, - tx_hash: l2_tx_hash + transaction_hash: l2_transaction_hash } - | l2_txs + | l2_transactions ] end) end - {l2_blocks, l2_txs} + {l2_blocks, l2_transactions} end) end end diff --git a/apps/indexer/lib/indexer/fetcher/zksync/discovery/workers.ex b/apps/indexer/lib/indexer/fetcher/zksync/discovery/workers.ex index 43ad89b7f124..327c9e5a56d5 100644 --- a/apps/indexer/lib/indexer/fetcher/zksync/discovery/workers.ex +++ b/apps/indexer/lib/indexer/fetcher/zksync/discovery/workers.ex @@ -40,7 +40,7 @@ defmodule Indexer.Fetcher.ZkSync.Discovery.Workers do is_integer(end_batch_number) and (is_map(config) and is_map_key(config, :json_rpc_named_arguments) and is_map_key(config, :chunk_size)) do - {batches_to_import, l2_blocks_to_import, l2_txs_to_import} = + {batches_to_import, l2_blocks_to_import, l2_transactions_to_import} = extract_data_from_batches({start_batch_number, end_batch_number}, config) batches_list_to_import = @@ -53,7 +53,7 @@ defmodule Indexer.Fetcher.ZkSync.Discovery.Workers do Db.import_to_db( batches_list_to_import, [], - l2_txs_to_import, + l2_transactions_to_import, l2_blocks_to_import ) @@ -82,10 +82,11 @@ defmodule Indexer.Fetcher.ZkSync.Discovery.Workers do (is_map(config) and is_map_key(config, :json_rpc_named_arguments) and is_map_key(config, :chunk_size)) do # Collect batches and linked L2 blocks and transaction - {batches_to_import, l2_blocks_to_import, l2_txs_to_import} = extract_data_from_batches(batches_numbers_list, config) + {batches_to_import, l2_blocks_to_import, l2_transactions_to_import} = + extract_data_from_batches(batches_numbers_list, config) # Collect L1 transactions associated with batches - l1_txs = + l1_transactions = batches_to_import |> Map.values() |> collect_l1_transactions() @@ -98,9 +99,9 @@ defmodule Indexer.Fetcher.ZkSync.Discovery.Workers do |> Enum.reduce([], fn batch, batches -> [ batch - |> Map.put(:commit_id, get_l1_tx_id_by_hash(l1_txs, batch.commit_tx_hash)) - |> Map.put(:prove_id, get_l1_tx_id_by_hash(l1_txs, batch.prove_tx_hash)) - |> Map.put(:execute_id, get_l1_tx_id_by_hash(l1_txs, batch.executed_tx_hash)) + |> Map.put(:commit_id, get_l1_transaction_id_by_hash(l1_transactions, batch.commit_transaction_hash)) + |> Map.put(:prove_id, get_l1_transaction_id_by_hash(l1_transactions, batch.prove_transaction_hash)) + |> Map.put(:execute_id, get_l1_transaction_id_by_hash(l1_transactions, batch.executed_transaction_hash)) |> Db.prune_json_batch() | batches ] @@ -108,8 +109,8 @@ defmodule Indexer.Fetcher.ZkSync.Discovery.Workers do Db.import_to_db( batches_list_to_import, - Map.values(l1_txs), - l2_txs_to_import, + Map.values(l1_transactions), + l2_transactions_to_import, l2_blocks_to_import ) @@ -154,8 +155,8 @@ defmodule Indexer.Fetcher.ZkSync.Discovery.Workers do :ok end - defp get_l1_tx_id_by_hash(l1_txs, hash) do - l1_txs + defp get_l1_transaction_id_by_hash(l1_transactions, hash) do + l1_transactions |> Map.get(hash) |> Kernel.||(%{id: nil}) |> Map.get(:id) diff --git a/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/committed.ex b/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/committed.ex index ed1a0464b63c..85ed1f9cbd45 100644 --- a/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/committed.ex +++ b/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/committed.ex @@ -52,8 +52,8 @@ defmodule Indexer.Fetcher.ZkSync.StatusTracking.Committed do expected_batch_number -> log_info("Checking if the batch #{expected_batch_number} was committed") - {next_action, tx_hash, l1_txs} = - check_if_batch_status_changed(expected_batch_number, :commit_tx, json_l2_rpc_named_arguments) + {next_action, transaction_hash, l1_transactions} = + check_if_batch_status_changed(expected_batch_number, :commit_transaction, json_l2_rpc_named_arguments) case next_action do :skip -> @@ -61,17 +61,25 @@ defmodule Indexer.Fetcher.ZkSync.StatusTracking.Committed do :look_for_batches -> log_info("The batch #{expected_batch_number} looks like committed") - commit_tx_receipt = Rpc.fetch_tx_receipt_by_hash(tx_hash, json_l1_rpc_named_arguments) - batches_numbers_from_rpc = get_committed_batches_from_logs(commit_tx_receipt["logs"]) - associate_and_import_or_prepare_for_recovery(batches_numbers_from_rpc, l1_txs, tx_hash, :commit_id) + commit_transaction_receipt = + Rpc.fetch_transaction_receipt_by_hash(transaction_hash, json_l1_rpc_named_arguments) + + batches_numbers_from_rpc = get_committed_batches_from_logs(commit_transaction_receipt["logs"]) + + associate_and_import_or_prepare_for_recovery( + batches_numbers_from_rpc, + l1_transactions, + transaction_hash, + :commit_id + ) end end end defp get_committed_batches_from_logs(logs) do committed_batches = Rpc.filter_logs_and_extract_topic_at(logs, @block_commit_event, 1) - log_info("Discovered #{length(committed_batches)} committed batches in the commitment tx") + log_info("Discovered #{length(committed_batches)} committed batches in the commitment transaction") committed_batches end diff --git a/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/common.ex b/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/common.ex index 0c8cccffc30d..a5133d97bc2f 100644 --- a/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/common.ex +++ b/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/common.ex @@ -10,18 +10,18 @@ defmodule Indexer.Fetcher.ZkSync.StatusTracking.CommonUtils do @doc """ Fetches the details of the batch with the given number and checks if the representation of the same batch in the database refers to the same commitment, proving, or executing transaction - depending on `tx_type`. If the transaction state changes, the new transaction is prepared for + depending on `transaction_type`. If the transaction state changes, the new transaction is prepared for import to the database. ## Parameters - `batch_number`: the number of the batch to check L1 transaction state. - - `tx_type`: a type of the transaction to check, one of :commit_tx, :execute_tx, or :prove_tx. + - `transaction_type`: a type of the transaction to check, one of :commit_transaction, :execute_transaction, or :prove_transaction. - `json_l2_rpc_named_arguments`: parameters for the RPC connections. ## Returns - - `{:look_for_batches, l1_tx_hash, l1_txs}` where - - `l1_tx_hash` is the hash of the L1 transaction. - - `l1_txs` is a map containing the transaction hash as a key, and values are maps + - `{:look_for_batches, l1_transaction_hash, l1_transactions}` where + - `l1_transaction_hash` is the hash of the L1 transaction. + - `l1_transactions` is a map containing the transaction hash as a key, and values are maps with transaction hashes and transaction timestamps. - `{:skip, "", %{}}` means the batch is not found in the database or the state of the transaction in the batch representation is the same as the state of the transaction for the batch @@ -29,12 +29,12 @@ defmodule Indexer.Fetcher.ZkSync.StatusTracking.CommonUtils do """ @spec check_if_batch_status_changed( binary() | non_neg_integer(), - :commit_tx | :execute_tx | :prove_tx, + :commit_transaction | :execute_transaction | :prove_transaction, EthereumJSONRPC.json_rpc_named_arguments() ) :: {:look_for_batches, any(), any()} | {:skip, <<>>, %{}} - def check_if_batch_status_changed(batch_number, tx_type, json_l2_rpc_named_arguments) + def check_if_batch_status_changed(batch_number, transaction_type, json_l2_rpc_named_arguments) when (is_binary(batch_number) or is_integer(batch_number)) and - tx_type in [:commit_tx, :prove_tx, :execute_tx] and + transaction_type in [:commit_transaction, :prove_transaction, :execute_transaction] and is_list(json_l2_rpc_named_arguments) do batch_from_rpc = Rpc.fetch_batch_details_by_batch_number(batch_number, json_l2_rpc_named_arguments) @@ -42,62 +42,67 @@ defmodule Indexer.Fetcher.ZkSync.StatusTracking.CommonUtils do case Reader.batch( batch_number, necessity_by_association: %{ - get_association(tx_type) => :optional + get_association(transaction_type) => :optional } ) do - {:ok, batch_from_db} -> transactions_of_batch_changed?(batch_from_db, batch_from_rpc, tx_type) + {:ok, batch_from_db} -> transactions_of_batch_changed?(batch_from_db, batch_from_rpc, transaction_type) {:error, :not_found} -> :error end - l1_tx = get_l1_tx_from_batch(batch_from_rpc, tx_type) + l1_transaction = get_l1_transaction_from_batch(batch_from_rpc, transaction_type) - if l1_tx.hash != Rpc.get_binary_zero_hash() and status_changed_or_error in [true, :error] do - l1_txs = Db.get_indices_for_l1_transactions(%{l1_tx.hash => l1_tx}) + if l1_transaction.hash != Rpc.get_binary_zero_hash() and status_changed_or_error in [true, :error] do + l1_transactions = Db.get_indices_for_l1_transactions(%{l1_transaction.hash => l1_transaction}) - {:look_for_batches, l1_tx.hash, l1_txs} + {:look_for_batches, l1_transaction.hash, l1_transactions} else {:skip, "", %{}} end end - defp get_association(tx_type) do - case tx_type do - :commit_tx -> :commit_transaction - :prove_tx -> :prove_transaction - :execute_tx -> :execute_transaction + defp get_association(transaction_type) do + case transaction_type do + :commit_transaction -> :commit_transaction + :prove_transaction -> :prove_transaction + :execute_transaction -> :execute_transaction end end - defp transactions_of_batch_changed?(batch_db, batch_json, tx_type) do - tx_hash_json = - case tx_type do - :commit_tx -> batch_json.commit_tx_hash - :prove_tx -> batch_json.prove_tx_hash - :execute_tx -> batch_json.executed_tx_hash + defp transactions_of_batch_changed?(batch_db, batch_json, transaction_type) do + transaction_hash_json = + case transaction_type do + :commit_transaction -> batch_json.commit_transaction_hash + :prove_transaction -> batch_json.prove_transaction_hash + :execute_transaction -> batch_json.executed_transaction_hash end - tx_hash_db = - case tx_type do - :commit_tx -> batch_db.commit_transaction - :prove_tx -> batch_db.prove_transaction - :execute_tx -> batch_db.execute_transaction + transaction_hash_db = + case transaction_type do + :commit_transaction -> batch_db.commit_transaction + :prove_transaction -> batch_db.prove_transaction + :execute_transaction -> batch_db.execute_transaction end - tx_hash_db_bytes = - if is_nil(tx_hash_db) do + transaction_hash_db_bytes = + if is_nil(transaction_hash_db) do Rpc.get_binary_zero_hash() else - tx_hash_db.hash.bytes + transaction_hash_db.hash.bytes end - tx_hash_json != tx_hash_db_bytes + transaction_hash_json != transaction_hash_db_bytes end - defp get_l1_tx_from_batch(batch_from_rpc, tx_type) do - case tx_type do - :commit_tx -> %{hash: batch_from_rpc.commit_tx_hash, timestamp: batch_from_rpc.commit_timestamp} - :prove_tx -> %{hash: batch_from_rpc.prove_tx_hash, timestamp: batch_from_rpc.prove_timestamp} - :execute_tx -> %{hash: batch_from_rpc.executed_tx_hash, timestamp: batch_from_rpc.executed_timestamp} + defp get_l1_transaction_from_batch(batch_from_rpc, transaction_type) do + case transaction_type do + :commit_transaction -> + %{hash: batch_from_rpc.commit_transaction_hash, timestamp: batch_from_rpc.commit_timestamp} + + :prove_transaction -> + %{hash: batch_from_rpc.prove_transaction_hash, timestamp: batch_from_rpc.prove_timestamp} + + :execute_transaction -> + %{hash: batch_from_rpc.executed_transaction_hash, timestamp: batch_from_rpc.executed_timestamp} end end @@ -110,9 +115,9 @@ defmodule Indexer.Fetcher.ZkSync.StatusTracking.CommonUtils do ## Parameters - `batches_numbers`: the list of batch numbers that must be updated. - - `l1_txs`: a map containing transaction hashes as keys, and values are maps + - `l1_transactions`: a map containing transaction hashes as keys, and values are maps with transaction hashes and transaction timestamps of L1 transactions to import to the database. - - `tx_hash`: the hash of the L1 transaction to build an association with. + - `transaction_hash`: the hash of the L1 transaction to build an association with. - `association_key`: the field in the batch description to build an association with L1 transactions. @@ -123,15 +128,15 @@ defmodule Indexer.Fetcher.ZkSync.StatusTracking.CommonUtils do """ @spec associate_and_import_or_prepare_for_recovery([integer()], map(), binary(), :commit_id | :execute_id | :prove_id) :: :ok | {:recovery_required, [integer()]} - def associate_and_import_or_prepare_for_recovery(batches_numbers, l1_txs, tx_hash, association_key) - when is_list(batches_numbers) and is_map(l1_txs) and is_binary(tx_hash) and + def associate_and_import_or_prepare_for_recovery(batches_numbers, l1_transactions, transaction_hash, association_key) + when is_list(batches_numbers) and is_map(l1_transactions) and is_binary(transaction_hash) and association_key in [:commit_id, :prove_id, :execute_id] do - case prepare_batches_to_import(batches_numbers, %{association_key => l1_txs[tx_hash][:id]}) do + case prepare_batches_to_import(batches_numbers, %{association_key => l1_transactions[transaction_hash][:id]}) do {:error, batches_to_recover} -> {:recovery_required, batches_to_recover} {:ok, batches_to_import} -> - Db.import_to_db(batches_to_import, Map.values(l1_txs)) + Db.import_to_db(batches_to_import, Map.values(l1_transactions)) :ok end end diff --git a/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/executed.ex b/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/executed.ex index 38d7db9d81a1..03538bd82e9a 100644 --- a/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/executed.ex +++ b/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/executed.ex @@ -52,8 +52,8 @@ defmodule Indexer.Fetcher.ZkSync.StatusTracking.Executed do expected_batch_number -> log_info("Checking if the batch #{expected_batch_number} was executed") - {next_action, tx_hash, l1_txs} = - check_if_batch_status_changed(expected_batch_number, :execute_tx, json_l2_rpc_named_arguments) + {next_action, transaction_hash, l1_transactions} = + check_if_batch_status_changed(expected_batch_number, :execute_transaction, json_l2_rpc_named_arguments) case next_action do :skip -> @@ -61,17 +61,25 @@ defmodule Indexer.Fetcher.ZkSync.StatusTracking.Executed do :look_for_batches -> log_info("The batch #{expected_batch_number} looks like executed") - execute_tx_receipt = Rpc.fetch_tx_receipt_by_hash(tx_hash, json_l1_rpc_named_arguments) - batches_numbers_from_rpc = get_executed_batches_from_logs(execute_tx_receipt["logs"]) - associate_and_import_or_prepare_for_recovery(batches_numbers_from_rpc, l1_txs, tx_hash, :execute_id) + execute_transaction_receipt = + Rpc.fetch_transaction_receipt_by_hash(transaction_hash, json_l1_rpc_named_arguments) + + batches_numbers_from_rpc = get_executed_batches_from_logs(execute_transaction_receipt["logs"]) + + associate_and_import_or_prepare_for_recovery( + batches_numbers_from_rpc, + l1_transactions, + transaction_hash, + :execute_id + ) end end end defp get_executed_batches_from_logs(logs) do executed_batches = Rpc.filter_logs_and_extract_topic_at(logs, @block_execution_event, 1) - log_info("Discovered #{length(executed_batches)} executed batches in the executing tx") + log_info("Discovered #{length(executed_batches)} executed batches in the executing transaction") executed_batches end diff --git a/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/proven.ex b/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/proven.ex index ad2bb986c8d2..0fb3e21a8c17 100644 --- a/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/proven.ex +++ b/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/proven.ex @@ -50,8 +50,8 @@ defmodule Indexer.Fetcher.ZkSync.StatusTracking.Proven do expected_batch_number -> log_info("Checking if the batch #{expected_batch_number} was proven") - {next_action, tx_hash, l1_txs} = - check_if_batch_status_changed(expected_batch_number, :prove_tx, json_l2_rpc_named_arguments) + {next_action, transaction_hash, l1_transactions} = + check_if_batch_status_changed(expected_batch_number, :prove_transaction, json_l2_rpc_named_arguments) case next_action do :skip -> @@ -59,10 +59,15 @@ defmodule Indexer.Fetcher.ZkSync.StatusTracking.Proven do :look_for_batches -> log_info("The batch #{expected_batch_number} looks like proven") - prove_tx = Rpc.fetch_tx_by_hash(tx_hash, json_l1_rpc_named_arguments) - batches_numbers_from_rpc = get_proven_batches_from_calldata(prove_tx["input"]) - - associate_and_import_or_prepare_for_recovery(batches_numbers_from_rpc, l1_txs, tx_hash, :prove_id) + prove_transaction = Rpc.fetch_transaction_by_hash(transaction_hash, json_l1_rpc_named_arguments) + batches_numbers_from_rpc = get_proven_batches_from_calldata(prove_transaction["input"]) + + associate_and_import_or_prepare_for_recovery( + batches_numbers_from_rpc, + l1_transactions, + transaction_hash, + :prove_id + ) end end end @@ -176,7 +181,7 @@ defmodule Indexer.Fetcher.ZkSync.StatusTracking.Proven do [] end - log_info("Discovered #{length(proven_batches)} proven batches in the prove tx") + log_info("Discovered #{length(proven_batches)} proven batches in the prove transaction") proven_batches |> Enum.map(fn batch_info -> elem(batch_info, 0) end) diff --git a/apps/indexer/lib/indexer/fetcher/zksync/utils/db.ex b/apps/indexer/lib/indexer/fetcher/zksync/utils/db.ex index 64eedeea9671..eee7f470b442 100644 --- a/apps/indexer/lib/indexer/fetcher/zksync/utils/db.ex +++ b/apps/indexer/lib/indexer/fetcher/zksync/utils/db.ex @@ -8,11 +8,11 @@ defmodule Indexer.Fetcher.ZkSync.Utils.Db do import Indexer.Fetcher.ZkSync.Utils.Logging, only: [log_warning: 1, log_info: 1] @json_batch_fields_absent_in_db_batch [ - :commit_tx_hash, + :commit_transaction_hash, :commit_timestamp, - :prove_tx_hash, + :prove_transaction_hash, :prove_timestamp, - :executed_tx_hash, + :executed_transaction_hash, :executed_timestamp ] @@ -126,57 +126,57 @@ defmodule Indexer.Fetcher.ZkSync.Utils.Db do the next available indices are assigned. ## Parameters - - `new_l1_txs`: A map of L1 transaction descriptions. The keys of the map are + - `new_l1_transactions`: A map of L1 transaction descriptions. The keys of the map are transaction hashes. ## Returns - - `l1_txs`: A map of L1 transaction descriptions. Each element is extended with + - `l1_transactions`: A map of L1 transaction descriptions. Each element is extended with the key `:id`, representing the index of the L1 transaction in the `zksync_lifecycle_l1_transactions` table. """ @spec get_indices_for_l1_transactions(map()) :: any() # TODO: consider a way to remove duplicate with Arbitrum.Utils.Db # credo:disable-for-next-line Credo.Check.Design.DuplicatedCode - def get_indices_for_l1_transactions(new_l1_txs) - when is_map(new_l1_txs) do + def get_indices_for_l1_transactions(new_l1_transactions) + when is_map(new_l1_transactions) do # Get indices for l1 transactions previously handled - l1_txs = - new_l1_txs + l1_transactions = + new_l1_transactions |> Map.keys() |> Reader.lifecycle_transactions() - |> Enum.reduce(new_l1_txs, fn {hash, id}, txs -> - {_, txs} = - Map.get_and_update!(txs, hash.bytes, fn l1_tx -> - {l1_tx, Map.put(l1_tx, :id, id)} + |> Enum.reduce(new_l1_transactions, fn {hash, id}, transactions -> + {_, transactions} = + Map.get_and_update!(transactions, hash.bytes, fn l1_transaction -> + {l1_transaction, Map.put(l1_transaction, :id, id)} end) - txs + transactions end) # Get the next index for the first new transaction based # on the indices existing in DB - l1_tx_next_id = Reader.next_id() + l1_transaction_next_id = Reader.next_id() # Assign new indices for the transactions which are not in # the l1 transactions table yet - {updated_l1_txs, _} = - l1_txs + {updated_l1_transactions, _} = + l1_transactions |> Map.keys() |> Enum.reduce( - {l1_txs, l1_tx_next_id}, - fn hash, {txs, next_id} -> - tx = txs[hash] - id = Map.get(tx, :id) + {l1_transactions, l1_transaction_next_id}, + fn hash, {transactions, next_id} -> + transaction = transactions[hash] + id = Map.get(transaction, :id) if is_nil(id) do - {Map.put(txs, hash, Map.put(tx, :id, next_id)), next_id + 1} + {Map.put(transactions, hash, Map.put(transaction, :id, next_id)), next_id + 1} else - {txs, next_id} + {transactions, next_id} end end ) - updated_l1_txs + updated_l1_transactions end @doc """ @@ -185,20 +185,20 @@ defmodule Indexer.Fetcher.ZkSync.Utils.Db do ## Parameters - `batches`: A list of maps with batch descriptions. - - `l1_txs`: A list of maps with L1 transaction descriptions. Optional. - - `l2_txs`: A list of maps with rollup transaction associations. Optional. + - `l1_transactions`: A list of maps with L1 transaction descriptions. Optional. + - `l2_transactions`: A list of maps with rollup transaction associations. Optional. - `l2_blocks`: A list of maps with rollup block associations. Optional. ## Returns n/a """ - def import_to_db(batches, l1_txs \\ [], l2_txs \\ [], l2_blocks \\ []) - when is_list(batches) and is_list(l1_txs) and is_list(l2_txs) and is_list(l2_blocks) do + def import_to_db(batches, l1_transactions \\ [], l2_transactions \\ [], l2_blocks \\ []) + when is_list(batches) and is_list(l1_transactions) and is_list(l2_transactions) and is_list(l2_blocks) do {:ok, _} = Chain.import(%{ - zksync_lifecycle_transactions: %{params: l1_txs}, + zksync_lifecycle_transactions: %{params: l1_transactions}, zksync_transaction_batches: %{params: batches}, - zksync_batch_transactions: %{params: l2_txs}, + zksync_batch_transactions: %{params: l2_transactions}, zksync_batch_blocks: %{params: l2_blocks}, timeout: :infinity }) diff --git a/apps/indexer/lib/indexer/fetcher/zksync/utils/rpc.ex b/apps/indexer/lib/indexer/fetcher/zksync/utils/rpc.ex index 71e594d51358..2323888c9086 100644 --- a/apps/indexer/lib/indexer/fetcher/zksync/utils/rpc.ex +++ b/apps/indexer/lib/indexer/fetcher/zksync/utils/rpc.ex @@ -67,16 +67,16 @@ defmodule Indexer.Fetcher.ZkSync.Utils.Rpc do end end - defp json_tx_id_to_hash(hash) do + defp json_transaction_id_to_hash(hash) do case hash do - "0x" <> tx_hash -> tx_hash + "0x" <> transaction_hash -> transaction_hash nil -> @zero_hash end end defp string_hash_to_bytes_hash(hash) do hash - |> json_tx_id_to_hash() + |> json_transaction_id_to_hash() |> Base.decode16!(case: :mixed) end @@ -99,14 +99,14 @@ defmodule Indexer.Fetcher.ZkSync.Utils.Rpc do %{ "number" => {:number, :ok}, "timestamp" => {:timestamp, :ts_to_datetime}, - "l1TxCount" => {:l1_tx_count, :ok}, - "l2TxCount" => {:l2_tx_count, :ok}, + "l1TxCount" => {:l1_transaction_count, :ok}, + "l2TxCount" => {:l2_transaction_count, :ok}, "rootHash" => {:root_hash, :str_to_byteshash}, - "commitTxHash" => {:commit_tx_hash, :str_to_byteshash}, + "commitTxHash" => {:commit_transaction_hash, :str_to_byteshash}, "committedAt" => {:commit_timestamp, :iso8601_to_datetime}, - "proveTxHash" => {:prove_tx_hash, :str_to_byteshash}, + "proveTxHash" => {:prove_transaction_hash, :str_to_byteshash}, "provenAt" => {:prove_timestamp, :iso8601_to_datetime}, - "executeTxHash" => {:executed_tx_hash, :str_to_byteshash}, + "executeTxHash" => {:executed_transaction_hash, :str_to_byteshash}, "executedAt" => {:executed_timestamp, :iso8601_to_datetime}, "l1GasPrice" => {:l1_gas_price, :ok}, "l2FairGasPrice" => {:l2_fair_gas_price, :ok} @@ -122,7 +122,7 @@ defmodule Indexer.Fetcher.ZkSync.Utils.Rpc do case transform_type do :iso8601_to_datetime -> from_iso8601_to_datetime(value_in_json_response) :ts_to_datetime -> IndexerHelper.timestamp_to_datetime(value_in_json_response) - :str_to_txhash -> json_tx_id_to_hash(value_in_json_response) + :str_to_txhash -> json_transaction_id_to_hash(value_in_json_response) :str_to_byteshash -> string_hash_to_bytes_hash(value_in_json_response) _ -> value_in_json_response end @@ -146,8 +146,8 @@ defmodule Indexer.Fetcher.ZkSync.Utils.Rpc do %{ number: batch.number, timestamp: batch.timestamp, - l1_tx_count: batch.l1_tx_count, - l2_tx_count: batch.l2_tx_count, + l1_transaction_count: batch.l1_transaction_count, + l2_transaction_count: batch.l2_transaction_count, root_hash: batch.root_hash.bytes, l1_gas_price: batch.l1_gas_price, l2_fair_gas_price: batch.l2_fair_gas_price, @@ -200,10 +200,10 @@ defmodule Indexer.Fetcher.ZkSync.Utils.Rpc do ## Returns - A map containing details of the transaction. """ - @spec fetch_tx_by_hash(binary(), EthereumJSONRPC.json_rpc_named_arguments()) :: map() - def fetch_tx_by_hash(raw_hash, json_rpc_named_arguments) + @spec fetch_transaction_by_hash(binary(), EthereumJSONRPC.json_rpc_named_arguments()) :: map() + def fetch_transaction_by_hash(raw_hash, json_rpc_named_arguments) when is_binary(raw_hash) and is_list(json_rpc_named_arguments) do - hash = prepare_tx_hash(raw_hash) + hash = prepare_transaction_hash(raw_hash) req = EthereumJSONRPC.request(%{ @@ -231,10 +231,10 @@ defmodule Indexer.Fetcher.ZkSync.Utils.Rpc do ## Returns - A map containing the receipt details of the transaction. """ - @spec fetch_tx_receipt_by_hash(binary(), EthereumJSONRPC.json_rpc_named_arguments()) :: map() - def fetch_tx_receipt_by_hash(raw_hash, json_rpc_named_arguments) + @spec fetch_transaction_receipt_by_hash(binary(), EthereumJSONRPC.json_rpc_named_arguments()) :: map() + def fetch_transaction_receipt_by_hash(raw_hash, json_rpc_named_arguments) when is_binary(raw_hash) and is_list(json_rpc_named_arguments) do - hash = prepare_tx_hash(raw_hash) + hash = prepare_transaction_hash(raw_hash) req = EthereumJSONRPC.request(%{ @@ -377,8 +377,8 @@ defmodule Indexer.Fetcher.ZkSync.Utils.Rpc do end # Converts a transaction hash represented as binary to a hexadecimal string - @spec prepare_tx_hash(binary()) :: binary() - defp prepare_tx_hash(raw_hash) do + @spec prepare_transaction_hash(binary()) :: binary() + defp prepare_transaction_hash(raw_hash) do case raw_hash do "0x" <> <<_::binary-size(64)>> -> raw_hash _ -> "0x" <> Base.encode16(raw_hash, case: :lower) diff --git a/apps/indexer/lib/indexer/helper.ex b/apps/indexer/lib/indexer/helper.ex index b7e110100cd3..854cb187e811 100644 --- a/apps/indexer/lib/indexer/helper.ex +++ b/apps/indexer/lib/indexer/helper.ex @@ -129,9 +129,9 @@ defmodule Indexer.Helper do - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. ## Returns - `{block_num, latest}`: A tuple where - - `block_num` is the safe or latest block number. - - `latest` is a boolean, where `true` indicates that `block_num` is the latest block number fetched using the tag `latest`. + `{block_number, latest}`: A tuple where + - `block_number` is the safe or latest block number. + - `latest` is a boolean, where `true` indicates that `block_number` is the latest block number fetched using the tag `latest`. """ @spec get_safe_block(EthereumJSONRPC.json_rpc_named_arguments()) :: {non_neg_integer(), boolean()} def get_safe_block(json_rpc_named_arguments) do diff --git a/apps/indexer/lib/indexer/pending_transactions_sanitizer.ex b/apps/indexer/lib/indexer/pending_transactions_sanitizer.ex index e1ea48996d18..11c0c260a571 100644 --- a/apps/indexer/lib/indexer/pending_transactions_sanitizer.ex +++ b/apps/indexer/lib/indexer/pending_transactions_sanitizer.ex @@ -75,17 +75,17 @@ defmodule Indexer.PendingTransactionsSanitizer do |> json_rpc(json_rpc_named_arguments) do Enum.each(responses, fn %{id: id, result: result} -> - pending_tx = Map.fetch!(id_to_params, id) + pending_transaction = Map.fetch!(id_to_params, id) if result do - fetch_block_and_invalidate_wrapper(pending_tx, to_string(pending_tx.hash), result) + fetch_block_and_invalidate_wrapper(pending_transaction, to_string(pending_transaction.hash), result) else Logger.debug( - "Transaction with hash #{pending_tx.hash} doesn't exist in the node anymore. We should remove it from Blockscout DB.", + "Transaction with hash #{pending_transaction.hash} doesn't exist in the node anymore. We should remove it from Blockscout DB.", fetcher: :pending_transactions_to_refetch ) - fetch_pending_transaction_and_delete(pending_tx) + fetch_pending_transaction_and_delete(pending_transaction) end error -> @@ -104,40 +104,40 @@ defmodule Indexer.PendingTransactionsSanitizer do end) end - defp fetch_block_and_invalidate_wrapper(pending_tx, pending_tx_hash_str, result) do + defp fetch_block_and_invalidate_wrapper(pending_transaction, pending_transaction_hash_string, result) do block_hash = Map.get(result, "blockHash") if block_hash do Logger.debug( - "Transaction with hash #{pending_tx_hash_str} already included into the block #{block_hash}. We should invalidate consensus for it in order to re-fetch transactions", + "Transaction with hash #{pending_transaction_hash_string} already included into the block #{block_hash}. We should invalidate consensus for it in order to re-fetch transactions", fetcher: :pending_transactions_to_refetch ) - fetch_block_and_invalidate(block_hash, pending_tx, result) + fetch_block_and_invalidate(block_hash, pending_transaction, result) else Logger.debug( - "Transaction with hash #{pending_tx_hash_str} is still pending. Do nothing.", + "Transaction with hash #{pending_transaction_hash_string} is still pending. Do nothing.", fetcher: :pending_transactions_to_refetch ) end end defp fetch_pending_transaction_and_delete(transaction) do - pending_tx_hash_str = "0x" <> Base.encode16(transaction.hash.bytes, case: :lower) + pending_transaction_hash_string = "0x" <> Base.encode16(transaction.hash.bytes, case: :lower) case transaction |> Changeset.change() |> Repo.delete() do {:ok, _transaction} -> Logger.debug( - "Transaction with hash #{pending_tx_hash_str} successfully deleted from Blockscout DB because it doesn't exist in the archive node anymore", + "Transaction with hash #{pending_transaction_hash_string} successfully deleted from Blockscout DB because it doesn't exist in the archive node anymore", fetcher: :pending_transactions_to_refetch ) {:error, changeset} -> Logger.debug( [ - "Deletion of pending transaction with hash #{pending_tx_hash_str} from Blockscout DB failed", + "Deletion of pending transaction with hash #{pending_transaction_hash_string} from Blockscout DB failed", inspect(changeset) ], fetcher: :pending_transactions_to_refetch @@ -145,7 +145,7 @@ defmodule Indexer.PendingTransactionsSanitizer do end end - defp fetch_block_and_invalidate(block_hash, pending_tx, tx) do + defp fetch_block_and_invalidate(block_hash, pending_transaction, transaction) do case Chain.fetch_block_by_hash(block_hash) do %{number: number, consensus: consensus} = block -> Logger.debug( @@ -153,7 +153,7 @@ defmodule Indexer.PendingTransactionsSanitizer do fetcher: :pending_transactions_to_refetch ) - invalidate_block(block, pending_tx, tx) + invalidate_block(block, pending_transaction, transaction) _ -> Logger.debug( @@ -163,18 +163,18 @@ defmodule Indexer.PendingTransactionsSanitizer do end end - defp invalidate_block(block, pending_tx, tx) do + defp invalidate_block(block, pending_transaction, transaction) do if block.consensus do Block.set_refetch_needed(block.number) else - tx_info = to_elixir(tx) + transaction_info = to_elixir(transaction) changeset = - pending_tx + pending_transaction |> Transaction.changeset() - |> Changeset.put_change(:cumulative_gas_used, tx_info["cumulativeGasUsed"]) - |> Changeset.put_change(:gas_used, tx_info["gasUsed"]) - |> Changeset.put_change(:index, tx_info["transactionIndex"]) + |> Changeset.put_change(:cumulative_gas_used, transaction_info["cumulativeGasUsed"]) + |> Changeset.put_change(:gas_used, transaction_info["gasUsed"]) + |> Changeset.put_change(:index, transaction_info["transactionIndex"]) |> Changeset.put_change(:block_number, block.number) |> Changeset.put_change(:block_hash, block.hash) |> Changeset.put_change(:block_timestamp, block.timestamp) @@ -183,7 +183,7 @@ defmodule Indexer.PendingTransactionsSanitizer do Repo.update(changeset) Logger.debug( - "Pending tx with hash #{"0x" <> Base.encode16(pending_tx.hash.bytes, case: :lower)} assigned to block ##{block.number} with hash #{block.hash}" + "Pending transaction with hash #{"0x" <> Base.encode16(pending_transaction.hash.bytes, case: :lower)} assigned to block ##{block.number} with hash #{block.hash}" ) end end diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex index a75c196ea42d..389b77532476 100644 --- a/apps/indexer/lib/indexer/supervisor.ex +++ b/apps/indexer/lib/indexer/supervisor.ex @@ -145,7 +145,7 @@ defmodule Indexer.Supervisor do {ReplacedTransaction.Supervisor, [[memory_monitor: memory_monitor]]}, {Indexer.Fetcher.RollupL1ReorgMonitor.Supervisor, [[memory_monitor: memory_monitor]]}, configure( - Indexer.Fetcher.Optimism.TxnBatch.Supervisor, + Indexer.Fetcher.Optimism.TransactionBatch.Supervisor, [[memory_monitor: memory_monitor, json_rpc_named_arguments: json_rpc_named_arguments]] ), configure(Indexer.Fetcher.Optimism.OutputRoot.Supervisor, [[memory_monitor: memory_monitor]]), diff --git a/apps/indexer/lib/indexer/transform/addresses.ex b/apps/indexer/lib/indexer/transform/addresses.ex index 73c48375f879..bec6ee4c7318 100644 --- a/apps/indexer/lib/indexer/transform/addresses.ex +++ b/apps/indexer/lib/indexer/transform/addresses.ex @@ -501,18 +501,18 @@ defmodule Indexer.Transform.Addresses do (entity_items = Map.get(fetched_data, entity_key)) != nil, do: extract_addresses_from_collection(entity_items, entity_fields, state) - tx_actions_addresses = + transaction_actions_addresses = fetched_data |> Map.get(:transaction_actions, []) - |> Enum.map(fn tx_action -> - tx_action.data + |> Enum.map(fn transaction_action -> + transaction_action.data |> Map.get(:block_number) - |> find_tx_action_addresses(tx_action.data) + |> find_transaction_action_addresses(transaction_action.data) end) |> List.flatten() addresses - |> Enum.concat(tx_actions_addresses) + |> Enum.concat(transaction_actions_addresses) |> List.flatten() |> merge_addresses() end @@ -522,16 +522,16 @@ defmodule Indexer.Transform.Addresses do def extract_addresses_from_item(item, fields, state), do: Enum.flat_map(fields, &extract_fields(&1, item, state)) - defp find_tx_action_addresses(block_number, data, accumulator \\ []) + defp find_transaction_action_addresses(block_number, data, accumulator \\ []) - defp find_tx_action_addresses(block_number, data, accumulator) when is_map(data) or is_list(data) do + defp find_transaction_action_addresses(block_number, data, accumulator) when is_map(data) or is_list(data) do Enum.reduce(data, accumulator, fn - {_, value}, acc -> find_tx_action_addresses(block_number, value, acc) - value, acc -> find_tx_action_addresses(block_number, value, acc) + {_, value}, acc -> find_transaction_action_addresses(block_number, value, acc) + value, acc -> find_transaction_action_addresses(block_number, value, acc) end) end - defp find_tx_action_addresses(block_number, value, accumulator) when is_binary(value) do + defp find_transaction_action_addresses(block_number, value, accumulator) when is_binary(value) do if Helper.address_correct?(value) do [%{:fetched_coin_balance_block_number => block_number, :hash => value} | accumulator] else @@ -539,7 +539,7 @@ defmodule Indexer.Transform.Addresses do end end - defp find_tx_action_addresses(_block_number, _value, accumulator), do: accumulator + defp find_transaction_action_addresses(_block_number, _value, accumulator), do: accumulator def merge_addresses(addresses) when is_list(addresses) do addresses diff --git a/apps/indexer/lib/indexer/transform/celo/transaction_token_transfers.ex b/apps/indexer/lib/indexer/transform/celo/transaction_token_transfers.ex index 2efa6b74de26..659a9c8143f2 100644 --- a/apps/indexer/lib/indexer/transform/celo/transaction_token_transfers.ex +++ b/apps/indexer/lib/indexer/transform/celo/transaction_token_transfers.ex @@ -49,23 +49,25 @@ defmodule Indexer.Transform.Celo.TransactionTokenTransfers do token_transfers = if Application.get_env(:explorer, :chain_type) == :celo do transactions - |> Enum.filter(fn tx -> tx.value > 0 end) - |> Enum.map(fn tx -> - to_address_hash = Map.get(tx, :to_address_hash) || Map.get(tx, :created_contract_address_hash) - log_index = -1 * (tx.index + 1) * @transaction_buffer_size - {:ok, celo_token_address} = CeloCoreContracts.get_address(:celo_token, tx.block_number) + |> Enum.filter(fn transaction -> transaction.value > 0 end) + |> Enum.map(fn transaction -> + to_address_hash = + Map.get(transaction, :to_address_hash) || Map.get(transaction, :created_contract_address_hash) + + log_index = -1 * (transaction.index + 1) * @transaction_buffer_size + {:ok, celo_token_address} = CeloCoreContracts.get_address(:celo_token, transaction.block_number) %{ - amount: Decimal.new(tx.value), - block_hash: tx.block_hash, - block_number: tx.block_number, - from_address_hash: tx.from_address_hash, + amount: Decimal.new(transaction.value), + block_hash: transaction.block_hash, + block_number: transaction.block_number, + from_address_hash: transaction.from_address_hash, log_index: log_index, to_address_hash: to_address_hash, token_contract_address_hash: celo_token_address, token_ids: nil, token_type: @token_type, - transaction_hash: tx.hash + transaction_hash: transaction.hash } end) |> tap(&Logger.debug("Found #{length(&1)} Celo token transfers.")) @@ -86,28 +88,33 @@ defmodule Indexer.Transform.Celo.TransactionTokenTransfers do def parse_internal_transactions(internal_transactions, block_number_to_block_hash) do token_transfers = internal_transactions - |> Enum.filter(fn itx -> - itx.value > 0 && - itx.index > 0 && - not Map.has_key?(itx, :error) && - (not Map.has_key?(itx, :call_type) || itx.call_type != "delegatecall") + |> Enum.filter(fn internal_transaction -> + internal_transaction.value > 0 && + internal_transaction.index > 0 && + not Map.has_key?(internal_transaction, :error) && + (not Map.has_key?(internal_transaction, :call_type) || internal_transaction.call_type != "delegatecall") end) - |> Enum.map(fn itx -> - to_address_hash = Map.get(itx, :to_address_hash) || Map.get(itx, :created_contract_address_hash) - log_index = -1 * (itx.transaction_index * @transaction_buffer_size + itx.index) - {:ok, celo_token_address} = CeloCoreContracts.get_address(:celo_token, itx.block_number) + |> Enum.map(fn internal_transaction -> + to_address_hash = + Map.get(internal_transaction, :to_address_hash) || + Map.get(internal_transaction, :created_contract_address_hash) + + log_index = + -1 * (internal_transaction.transaction_index * @transaction_buffer_size + internal_transaction.index) + + {:ok, celo_token_address} = CeloCoreContracts.get_address(:celo_token, internal_transaction.block_number) %{ - amount: Decimal.new(itx.value), - block_hash: block_number_to_block_hash[itx.block_number], - block_number: itx.block_number, - from_address_hash: itx.from_address_hash, + amount: Decimal.new(internal_transaction.value), + block_hash: block_number_to_block_hash[internal_transaction.block_number], + block_number: internal_transaction.block_number, + from_address_hash: internal_transaction.from_address_hash, log_index: log_index, to_address_hash: to_address_hash, token_contract_address_hash: celo_token_address, token_ids: nil, token_type: @token_type, - transaction_hash: itx.transaction_hash + transaction_hash: internal_transaction.transaction_hash } end) diff --git a/apps/indexer/lib/indexer/transform/shibarium/bridge.ex b/apps/indexer/lib/indexer/transform/shibarium/bridge.ex index dc348815c105..6d867c5f67b5 100644 --- a/apps/indexer/lib/indexer/transform/shibarium/bridge.ex +++ b/apps/indexer/lib/indexer/transform/shibarium/bridge.ex @@ -43,8 +43,8 @@ defmodule Indexer.Transform.Shibarium.Bridge do deposit_transaction_hashes = transactions_with_receipts - |> Enum.filter(fn tx -> tx.from_address_hash == burn_address_hash_string() end) - |> Enum.map(fn tx -> tx.hash end) + |> Enum.filter(fn transaction -> transaction.from_address_hash == burn_address_hash_string() end) + |> Enum.map(fn transaction -> transaction.hash end) deposit_events = logs @@ -53,11 +53,11 @@ defmodule Indexer.Transform.Shibarium.Bridge do withdrawal_transaction_hashes = transactions_with_receipts - |> Enum.filter(fn tx -> + |> Enum.filter(fn transaction -> # filter by `withdraw(uint256 amount)` signature - String.downcase(String.slice(tx.input, 0..9)) == withdraw_method_signature() + String.downcase(String.slice(transaction.input, 0..9)) == withdraw_method_signature() end) - |> Enum.map(fn tx -> tx.hash end) + |> Enum.map(fn transaction -> transaction.hash end) withdrawal_events = logs diff --git a/apps/indexer/lib/indexer/transform/transaction_actions.ex b/apps/indexer/lib/indexer/transform/transaction_actions.ex index e2b951ffae8d..be9c16d0d3e5 100644 --- a/apps/indexer/lib/indexer/transform/transaction_actions.ex +++ b/apps/indexer/lib/indexer/transform/transaction_actions.ex @@ -133,7 +133,7 @@ defmodule Indexer.Transform.TransactionActions do if not is_nil(protocols_to_rewrite) do logs - |> logs_group_by_txs() + |> logs_group_by_transactions() |> clear_actions(protocols_to_rewrite) end @@ -157,7 +157,7 @@ defmodule Indexer.Transform.TransactionActions do Enum.member?(protocols_to_rewrite, "aave_v3")) do logs |> aave_filter_logs(String.downcase(aave_v3_pool)) - |> logs_group_by_txs() + |> logs_group_by_transactions() |> aave(actions, chain_id) else actions @@ -175,7 +175,7 @@ defmodule Indexer.Transform.TransactionActions do logs |> uniswap_filter_logs(uniswap_v3_positions_nft) - |> logs_group_by_txs() + |> logs_group_by_transactions() |> uniswap(actions, chain_id, uniswap_v3_positions_nft) else actions @@ -203,9 +203,9 @@ defmodule Indexer.Transform.TransactionActions do defp aave(logs_grouped, actions, chain_id) do # iterate for each transaction - Enum.reduce(logs_grouped, actions, fn {_tx_hash, tx_logs}, actions_acc -> + Enum.reduce(logs_grouped, actions, fn {_transaction_hash, transaction_logs}, actions_acc -> # go through actions - Enum.reduce(tx_logs, actions_acc, fn log, acc -> + Enum.reduce(transaction_logs, actions_acc, fn log, acc -> acc ++ aave_handle_action(log, chain_id) end) end) @@ -388,12 +388,13 @@ defmodule Indexer.Transform.TransactionActions do legitimate = uniswap_legitimate_pools(logs_grouped) # iterate for each transaction - Enum.reduce(logs_grouped, actions, fn {tx_hash, tx_logs}, actions_acc -> + Enum.reduce(logs_grouped, actions, fn {transaction_hash, transaction_logs}, actions_acc -> # trying to find `mint_nft` actions - actions_acc = uniswap_handle_mint_nft_actions(tx_hash, tx_logs, actions_acc, uniswap_v3_positions_nft) + actions_acc = + uniswap_handle_mint_nft_actions(transaction_hash, transaction_logs, actions_acc, uniswap_v3_positions_nft) # go through other actions - Enum.reduce(tx_logs, actions_acc, fn log, acc -> + Enum.reduce(transaction_logs, actions_acc, fn log, acc -> acc ++ uniswap_handle_action(log, legitimate, chain_id) end) end) @@ -455,11 +456,11 @@ defmodule Indexer.Transform.TransactionActions do end end - defp uniswap_handle_mint_nft_actions(tx_hash, tx_logs, actions_acc, uniswap_v3_positions_nft) do - first_log = Enum.at(tx_logs, 0) + defp uniswap_handle_mint_nft_actions(transaction_hash, transaction_logs, actions_acc, uniswap_v3_positions_nft) do + first_log = Enum.at(transaction_logs, 0) local_acc = - tx_logs + transaction_logs |> Enum.reduce(%{}, fn log, acc -> if sanitize_first_topic(log.first_topic) == @uniswap_v3_transfer_nft_event do # This is Transfer event for NFT @@ -495,7 +496,7 @@ defmodule Indexer.Transform.TransactionActions do end) |> Enum.reduce([], fn {to, %{ids: ids, log_index: log_index}}, acc -> action = %{ - hash: tx_hash, + hash: transaction_hash, protocol: "uniswap_v3", data: %{ name: "Uniswap V3: Positions NFT", @@ -554,7 +555,7 @@ defmodule Indexer.Transform.TransactionActions do true -> Logger.error( - "TransactionActions: Invalid Swap event in tx #{log.transaction_hash}. Log index: #{log.index}. amount0 = #{amount0}, amount1 = #{amount1}" + "TransactionActions: Invalid Swap event in transaction #{log.transaction_hash}. Log index: #{log.index}. amount0 = #{amount0}, amount1 = #{amount1}" ) {amount0, symbol0, address0, amount1, symbol1, address1, true} @@ -607,8 +608,8 @@ defmodule Indexer.Transform.TransactionActions do {pools_to_request, pools_cached} = logs_grouped - |> Enum.reduce(%{}, fn {_tx_hash, tx_logs}, addresses_acc -> - tx_logs + |> Enum.reduce(%{}, fn {_transaction_hash, transaction_logs}, addresses_acc -> + transaction_logs |> Enum.filter(fn log -> sanitize_first_topic(log.first_topic) != @uniswap_v3_transfer_nft_event end) @@ -778,12 +779,12 @@ defmodule Indexer.Transform.TransactionActions do defp clear_actions(logs_grouped, protocols_to_clear) do logs_grouped - |> Enum.each(fn {tx_hash, _} -> + |> Enum.each(fn {transaction_hash, _} -> query = if Enum.empty?(protocols_to_clear) do - from(ta in TransactionAction, where: ta.hash == ^tx_hash) + from(ta in TransactionAction, where: ta.hash == ^transaction_hash) else - from(ta in TransactionAction, where: ta.hash == ^tx_hash and ta.protocol in ^protocols_to_clear) + from(ta in TransactionAction, where: ta.hash == ^transaction_hash and ta.protocol in ^protocols_to_clear) end Repo.delete_all(query) @@ -968,7 +969,7 @@ defmodule Indexer.Transform.TransactionActions do {requests, responses} end - defp logs_group_by_txs(logs) do + defp logs_group_by_transactions(logs) do logs |> Enum.group_by(& &1.transaction_hash) end diff --git a/apps/indexer/mix.exs b/apps/indexer/mix.exs index 3e2456724dc7..6b075cf25331 100644 --- a/apps/indexer/mix.exs +++ b/apps/indexer/mix.exs @@ -20,7 +20,7 @@ defmodule Indexer.MixProject do Explorer.Chain.Optimism.Deposit, Explorer.Chain.Optimism.FrameSequence, Explorer.Chain.Optimism.OutputRoot, - Explorer.Chain.Optimism.TxnBatch, + Explorer.Chain.Optimism.TransactionBatch, Explorer.Chain.Optimism.Withdrawal, Explorer.Chain.Optimism.WithdrawalEvent ] diff --git a/apps/indexer/test/indexer/fetcher/internal_transaction_test.exs b/apps/indexer/test/indexer/fetcher/internal_transaction_test.exs index 66de4d813a21..1a87a0a61637 100644 --- a/apps/indexer/test/indexer/fetcher/internal_transaction_test.exs +++ b/apps/indexer/test/indexer/fetcher/internal_transaction_test.exs @@ -604,14 +604,14 @@ defmodule Indexer.Fetcher.InternalTransactionTest do assert nil == Repo.get(PendingBlockOperation, block_hash) - int_txs = Repo.all(from(i in Chain.InternalTransaction, where: i.block_hash == ^block_hash)) + internal_transactions = Repo.all(from(i in Chain.InternalTransaction, where: i.block_hash == ^block_hash)) - assert Enum.count(int_txs) > 0 + assert Enum.count(internal_transactions) > 0 - last_int_tx = List.last(int_txs) + last_internal_transaction = List.last(internal_transactions) - assert last_int_tx.type == :call - assert last_int_tx.call_type == :invalid + assert last_internal_transaction.type == :call + assert last_internal_transaction.call_type == :invalid end end diff --git a/apps/indexer/test/indexer/fetcher/token_instance/helper_test.exs b/apps/indexer/test/indexer/fetcher/token_instance/helper_test.exs index 552ea312090b..03e6f27c1f2f 100644 --- a/apps/indexer/test/indexer/fetcher/token_instance/helper_test.exs +++ b/apps/indexer/test/indexer/fetcher/token_instance/helper_test.exs @@ -25,7 +25,7 @@ defmodule Indexer.Fetcher.TokenInstance.HelperTest do Application.put_env(:explorer, :http_adapter, Explorer.Mox.HTTPoison) result = - "{\"id\":100500,\"name\":\"KittyBlue_2_Lemonade\",\"generation\":20,\"genes\":\"623509754227292470437941473598751240781530569131665917719736997423495595\",\"created_at\":\"2017-12-06T01:56:27.000Z\",\"birthday\":\"2017-12-06T00:00:00.000Z\",\"image_url\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/100500.svg\",\"image_url_cdn\":\"https://img.cn.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/100500.svg\",\"color\":\"strawberry\",\"background_color\":\"#ffe0e5\",\"bio\":\"Shalom! I'm KittyBlue_2_Lemonade. I'm a professional Foreign Film Director and I love cantaloupe. I'm convinced that the world is flat. One day I'll prove it. It's pawesome to meet you!\",\"kitty_type\":null,\"is_fancy\":false,\"is_exclusive\":false,\"is_special_edition\":false,\"fancy_type\":null,\"language\":\"en\",\"is_prestige\":false,\"prestige_type\":null,\"prestige_ranking\":null,\"prestige_time_limit\":null,\"status\":{\"is_ready\":true,\"is_gestating\":false,\"cooldown\":1410310201506,\"dynamic_cooldown\":1475064986478,\"cooldown_index\":10,\"cooldown_end_block\":0,\"pending_tx_type\":null,\"pending_tx_since\":null},\"purrs\":{\"count\":1,\"is_purred\":false},\"watchlist\":{\"count\":0,\"is_watchlisted\":false},\"hatcher\":{\"address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\",\"image\":\"14\",\"nickname\":\"KittyBlu\",\"hasDapper\":false,\"twitter_id\":null,\"twitter_image_url\":null,\"twitter_handle\":null},\"auction\":{},\"offer\":{},\"owner\":{\"address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\",\"hasDapper\":false,\"twitter_id\":null,\"twitter_image_url\":null,\"twitter_handle\":null,\"image\":\"14\",\"nickname\":\"KittyBlu\"},\"matron\":{\"id\":46234,\"name\":\"KittyBlue_1_Limegreen\",\"generation\":10,\"enhanced_cattributes\":[{\"type\":\"body\",\"kittyId\":19631,\"position\":105,\"description\":\"cymric\"},{\"type\":\"coloreyes\",\"kittyId\":40356,\"position\":263,\"description\":\"limegreen\"},{\"type\":\"eyes\",\"kittyId\":3185,\"position\":16,\"description\":\"raisedbrow\"},{\"type\":\"pattern\",\"kittyId\":46234,\"position\":-1,\"description\":\"totesbasic\"},{\"type\":\"mouth\",\"kittyId\":46234,\"position\":-1,\"description\":\"happygokitty\"},{\"type\":\"colorprimary\",\"kittyId\":46234,\"position\":-1,\"description\":\"greymatter\"},{\"type\":\"colorsecondary\",\"kittyId\":46234,\"position\":-1,\"description\":\"lemonade\"},{\"type\":\"colortertiary\",\"kittyId\":46234,\"position\":-1,\"description\":\"granitegrey\"}],\"owner_wallet_address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\",\"owner\":{\"address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\"},\"created_at\":\"2017-12-03T21:29:17.000Z\",\"image_url\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/46234.svg\",\"image_url_cdn\":\"https://img.cn.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/46234.svg\",\"color\":\"limegreen\",\"is_fancy\":false,\"kitty_type\":null,\"is_exclusive\":false,\"is_special_edition\":false,\"fancy_type\":null,\"status\":{\"is_ready\":true,\"is_gestating\":false,\"cooldown\":1486487069384},\"hatched\":true,\"wrapped\":false,\"image_url_png\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/46234.png\"},\"sire\":{\"id\":82090,\"name\":null,\"generation\":19,\"enhanced_cattributes\":[{\"type\":\"body\",\"kittyId\":82090,\"position\":-1,\"description\":\"himalayan\"},{\"type\":\"coloreyes\",\"kittyId\":82090,\"position\":-1,\"description\":\"strawberry\"},{\"type\":\"eyes\",\"kittyId\":82090,\"position\":-1,\"description\":\"thicccbrowz\"},{\"type\":\"pattern\",\"kittyId\":82090,\"position\":-1,\"description\":\"totesbasic\"},{\"type\":\"mouth\",\"kittyId\":82090,\"position\":-1,\"description\":\"pouty\"},{\"type\":\"colorprimary\",\"kittyId\":82090,\"position\":-1,\"description\":\"aquamarine\"},{\"type\":\"colorsecondary\",\"kittyId\":82090,\"position\":-1,\"description\":\"chocolate\"},{\"type\":\"colortertiary\",\"kittyId\":82090,\"position\":-1,\"description\":\"granitegrey\"}],\"owner_wallet_address\":\"0x798fdad0cedc4b298fc7d53a982fa0c5f447eaa5\",\"owner\":{\"address\":\"0x798fdad0cedc4b298fc7d53a982fa0c5f447eaa5\"},\"created_at\":\"2017-12-05T06:30:05.000Z\",\"image_url\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/82090.svg\",\"image_url_cdn\":\"https://img.cn.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/82090.svg\",\"color\":\"strawberry\",\"is_fancy\":false,\"is_exclusive\":false,\"is_special_edition\":false,\"fancy_type\":null,\"status\":{\"is_ready\":true,\"is_gestating\":false,\"cooldown\":1486619010030},\"kitty_type\":null,\"hatched\":true,\"wrapped\":false,\"image_url_png\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/82090.png\"},\"children\":[],\"hatched\":true,\"wrapped\":false,\"enhanced_cattributes\":[{\"type\":\"colorprimary\",\"description\":\"greymatter\",\"position\":null,\"kittyId\":100500},{\"type\":\"coloreyes\",\"description\":\"strawberry\",\"position\":null,\"kittyId\":100500},{\"type\":\"body\",\"description\":\"himalayan\",\"position\":null,\"kittyId\":100500},{\"type\":\"colorsecondary\",\"description\":\"lemonade\",\"position\":null,\"kittyId\":100500},{\"type\":\"mouth\",\"description\":\"pouty\",\"position\":null,\"kittyId\":100500},{\"type\":\"pattern\",\"description\":\"totesbasic\",\"position\":null,\"kittyId\":100500},{\"type\":\"eyes\",\"description\":\"thicccbrowz\",\"position\":null,\"kittyId\":100500},{\"type\":\"colortertiary\",\"description\":\"kittencream\",\"position\":null,\"kittyId\":100500},{\"type\":\"secret\",\"description\":\"se5\",\"position\":-1,\"kittyId\":100500},{\"type\":\"purrstige\",\"description\":\"pu20\",\"position\":-1,\"kittyId\":100500}],\"variation\":null,\"variation_ranking\":null,\"image_url_png\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/100500.png\",\"items\":[]}" + "{\"id\":100500,\"name\":\"KittyBlue_2_Lemonade\",\"generation\":20,\"genes\":\"623509754227292470437941473598751240781530569131665917719736997423495595\",\"created_at\":\"2017-12-06T01:56:27.000Z\",\"birthday\":\"2017-12-06T00:00:00.000Z\",\"image_url\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/100500.svg\",\"image_url_cdn\":\"https://img.cn.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/100500.svg\",\"color\":\"strawberry\",\"background_color\":\"#ffe0e5\",\"bio\":\"Shalom! I'm KittyBlue_2_Lemonade. I'm a professional Foreign Film Director and I love cantaloupe. I'm convinced that the world is flat. One day I'll prove it. It's pawesome to meet you!\",\"kitty_type\":null,\"is_fancy\":false,\"is_exclusive\":false,\"is_special_edition\":false,\"fancy_type\":null,\"language\":\"en\",\"is_prestige\":false,\"prestige_type\":null,\"prestige_ranking\":null,\"prestige_time_limit\":null,\"status\":{\"is_ready\":true,\"is_gestating\":false,\"cooldown\":1410310201506,\"dynamic_cooldown\":1475064986478,\"cooldown_index\":10,\"cooldown_end_block\":0,\"pending_transaction_type\":null,\"pending_tx_since\":null},\"purrs\":{\"count\":1,\"is_purred\":false},\"watchlist\":{\"count\":0,\"is_watchlisted\":false},\"hatcher\":{\"address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\",\"image\":\"14\",\"nickname\":\"KittyBlu\",\"hasDapper\":false,\"twitter_id\":null,\"twitter_image_url\":null,\"twitter_handle\":null},\"auction\":{},\"offer\":{},\"owner\":{\"address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\",\"hasDapper\":false,\"twitter_id\":null,\"twitter_image_url\":null,\"twitter_handle\":null,\"image\":\"14\",\"nickname\":\"KittyBlu\"},\"matron\":{\"id\":46234,\"name\":\"KittyBlue_1_Limegreen\",\"generation\":10,\"enhanced_cattributes\":[{\"type\":\"body\",\"kittyId\":19631,\"position\":105,\"description\":\"cymric\"},{\"type\":\"coloreyes\",\"kittyId\":40356,\"position\":263,\"description\":\"limegreen\"},{\"type\":\"eyes\",\"kittyId\":3185,\"position\":16,\"description\":\"raisedbrow\"},{\"type\":\"pattern\",\"kittyId\":46234,\"position\":-1,\"description\":\"totesbasic\"},{\"type\":\"mouth\",\"kittyId\":46234,\"position\":-1,\"description\":\"happygokitty\"},{\"type\":\"colorprimary\",\"kittyId\":46234,\"position\":-1,\"description\":\"greymatter\"},{\"type\":\"colorsecondary\",\"kittyId\":46234,\"position\":-1,\"description\":\"lemonade\"},{\"type\":\"colortertiary\",\"kittyId\":46234,\"position\":-1,\"description\":\"granitegrey\"}],\"owner_wallet_address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\",\"owner\":{\"address\":\"0x7b9ea9ac69b8fde875554321472c732eeff06ca0\"},\"created_at\":\"2017-12-03T21:29:17.000Z\",\"image_url\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/46234.svg\",\"image_url_cdn\":\"https://img.cn.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/46234.svg\",\"color\":\"limegreen\",\"is_fancy\":false,\"kitty_type\":null,\"is_exclusive\":false,\"is_special_edition\":false,\"fancy_type\":null,\"status\":{\"is_ready\":true,\"is_gestating\":false,\"cooldown\":1486487069384},\"hatched\":true,\"wrapped\":false,\"image_url_png\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/46234.png\"},\"sire\":{\"id\":82090,\"name\":null,\"generation\":19,\"enhanced_cattributes\":[{\"type\":\"body\",\"kittyId\":82090,\"position\":-1,\"description\":\"himalayan\"},{\"type\":\"coloreyes\",\"kittyId\":82090,\"position\":-1,\"description\":\"strawberry\"},{\"type\":\"eyes\",\"kittyId\":82090,\"position\":-1,\"description\":\"thicccbrowz\"},{\"type\":\"pattern\",\"kittyId\":82090,\"position\":-1,\"description\":\"totesbasic\"},{\"type\":\"mouth\",\"kittyId\":82090,\"position\":-1,\"description\":\"pouty\"},{\"type\":\"colorprimary\",\"kittyId\":82090,\"position\":-1,\"description\":\"aquamarine\"},{\"type\":\"colorsecondary\",\"kittyId\":82090,\"position\":-1,\"description\":\"chocolate\"},{\"type\":\"colortertiary\",\"kittyId\":82090,\"position\":-1,\"description\":\"granitegrey\"}],\"owner_wallet_address\":\"0x798fdad0cedc4b298fc7d53a982fa0c5f447eaa5\",\"owner\":{\"address\":\"0x798fdad0cedc4b298fc7d53a982fa0c5f447eaa5\"},\"created_at\":\"2017-12-05T06:30:05.000Z\",\"image_url\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/82090.svg\",\"image_url_cdn\":\"https://img.cn.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/82090.svg\",\"color\":\"strawberry\",\"is_fancy\":false,\"is_exclusive\":false,\"is_special_edition\":false,\"fancy_type\":null,\"status\":{\"is_ready\":true,\"is_gestating\":false,\"cooldown\":1486619010030},\"kitty_type\":null,\"hatched\":true,\"wrapped\":false,\"image_url_png\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/82090.png\"},\"children\":[],\"hatched\":true,\"wrapped\":false,\"enhanced_cattributes\":[{\"type\":\"colorprimary\",\"description\":\"greymatter\",\"position\":null,\"kittyId\":100500},{\"type\":\"coloreyes\",\"description\":\"strawberry\",\"position\":null,\"kittyId\":100500},{\"type\":\"body\",\"description\":\"himalayan\",\"position\":null,\"kittyId\":100500},{\"type\":\"colorsecondary\",\"description\":\"lemonade\",\"position\":null,\"kittyId\":100500},{\"type\":\"mouth\",\"description\":\"pouty\",\"position\":null,\"kittyId\":100500},{\"type\":\"pattern\",\"description\":\"totesbasic\",\"position\":null,\"kittyId\":100500},{\"type\":\"eyes\",\"description\":\"thicccbrowz\",\"position\":null,\"kittyId\":100500},{\"type\":\"colortertiary\",\"description\":\"kittencream\",\"position\":null,\"kittyId\":100500},{\"type\":\"secret\",\"description\":\"se5\",\"position\":-1,\"kittyId\":100500},{\"type\":\"purrstige\",\"description\":\"pu20\",\"position\":-1,\"kittyId\":100500}],\"variation\":null,\"variation_ranking\":null,\"image_url_png\":\"https://img.cryptokitties.co/0x06012c8cf97bead5deae237070f9587f8e7a266d/100500.png\",\"items\":[]}" Explorer.Mox.HTTPoison |> expect(:get, fn "https://api.cryptokitties.co/kitties/100500", _headers, _options -> diff --git a/apps/indexer/test/indexer/fetcher/token_instance/sanitize_erc721_test.exs b/apps/indexer/test/indexer/fetcher/token_instance/sanitize_erc721_test.exs index 5568b8da3dcc..79b27ad1ee45 100644 --- a/apps/indexer/test/indexer/fetcher/token_instance/sanitize_erc721_test.exs +++ b/apps/indexer/test/indexer/fetcher/token_instance/sanitize_erc721_test.exs @@ -10,14 +10,14 @@ defmodule Indexer.Fetcher.TokenInstance.SanitizeERC721Test do for x <- 0..3 do erc_721_token = insert(:token, type: "ERC-721") - tx = insert(:transaction, input: "0xabcd010203040506") |> with_block() + transaction = insert(:transaction, input: "0xabcd010203040506") |> with_block() address = insert(:address) insert(:token_transfer, - transaction: tx, - block: tx.block, - block_number: tx.block_number, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, from_address: address, token_contract_address: erc_721_token.contract_address, token_ids: [x] diff --git a/config/runtime.exs b/config/runtime.exs index caa9b9438e0b..8f92fec804d8 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -141,7 +141,7 @@ price_chart_config = price_chart_legend_enabled? = ConfigHelper.parse_bool_env_var("SHOW_PRICE_CHART") || ConfigHelper.parse_bool_env_var("SHOW_PRICE_CHART_LEGEND") -tx_chart_config = +transaction_chart_config = if ConfigHelper.parse_bool_env_var("SHOW_TXS_CHART", "true") do %{transactions: [:transactions_per_day]} else @@ -149,7 +149,7 @@ tx_chart_config = end config :block_scout_web, :chart, - chart_config: Map.merge(price_chart_config, tx_chart_config), + chart_config: Map.merge(price_chart_config, transaction_chart_config), price_chart_legend_enabled?: price_chart_legend_enabled? config :block_scout_web, BlockScoutWeb.Chain.Address.CoinBalance, @@ -853,7 +853,7 @@ config :indexer, Indexer.Fetcher.CoinBalance.Realtime, batch_size: coin_balances_batch_size, concurrency: coin_balances_concurrency -config :indexer, Indexer.Fetcher.Optimism.TxnBatch.Supervisor, enabled: ConfigHelper.chain_type() == :optimism +config :indexer, Indexer.Fetcher.Optimism.TransactionBatch.Supervisor, enabled: ConfigHelper.chain_type() == :optimism config :indexer, Indexer.Fetcher.Optimism.OutputRoot.Supervisor, enabled: ConfigHelper.chain_type() == :optimism config :indexer, Indexer.Fetcher.Optimism.DisputeGame.Supervisor, enabled: ConfigHelper.chain_type() == :optimism config :indexer, Indexer.Fetcher.Optimism.Deposit.Supervisor, enabled: ConfigHelper.chain_type() == :optimism @@ -876,7 +876,7 @@ config :indexer, Indexer.Fetcher.Optimism.Withdrawal, message_passer: System.get_env("INDEXER_OPTIMISM_L2_MESSAGE_PASSER_CONTRACT", "0x4200000000000000000000000000000000000016") -config :indexer, Indexer.Fetcher.Optimism.TxnBatch, +config :indexer, Indexer.Fetcher.Optimism.TransactionBatch, blocks_chunk_size: System.get_env("INDEXER_OPTIMISM_L1_BATCH_BLOCKS_CHUNK_SIZE", "4"), eip4844_blobs_api_url: System.get_env("INDEXER_OPTIMISM_L1_BATCH_BLOCKSCOUT_BLOBS_API_URL", ""), celestia_blobs_api_url: System.get_env("INDEXER_OPTIMISM_L1_BATCH_CELESTIA_BLOBS_API_URL", ""), @@ -955,7 +955,7 @@ config :indexer, Indexer.Fetcher.Arbitrum.TrackingMessagesOnL1.Supervisor, config :indexer, Indexer.Fetcher.Arbitrum.TrackingBatchesStatuses, recheck_interval: ConfigHelper.parse_time_env_var("INDEXER_ARBITRUM_BATCHES_TRACKING_RECHECK_INTERVAL", "20s"), - track_l1_tx_finalization: + track_l1_transaction_finalization: ConfigHelper.parse_bool_env_var("INDEXER_ARBITRUM_BATCHES_TRACKING_L1_FINALIZATION_CHECK_ENABLED", "false"), messages_to_blocks_shift: ConfigHelper.parse_integer_env_var("INDEXER_ARBITRUM_BATCHES_TRACKING_MESSAGES_TO_BLOCKS_SHIFT", 0), From f6c40652d7fbbb38ca9209602081afa509b93fa8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 10:45:34 +0300 Subject: [PATCH 236/363] chore(deps): bump tesla from 1.12.1 to 1.12.2 (#10987) Bumps [tesla](https://github.com/elixir-tesla/tesla) from 1.12.1 to 1.12.2. - [Release notes](https://github.com/elixir-tesla/tesla/releases) - [Commits](https://github.com/elixir-tesla/tesla/compare/v1.12.1...v1.12.2) --- updated-dependencies: - dependency-name: tesla dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 312670caf86b..275a41c666b6 100644 --- a/mix.lock +++ b/mix.lock @@ -135,7 +135,7 @@ "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, - "tesla": {:hex, :tesla, "1.12.1", "fe2bf4250868ee72e5d8b8dfa408d13a00747c41b7237b6aa3b9a24057346681", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2391efc6243d37ead43afd0327b520314c7b38232091d4a440c1212626fdd6e7"}, + "tesla": {:hex, :tesla, "1.12.2", "1399c2dada208e381fec752e8fc21d6aa646c0a1f7cec8276484324bcba11b1e", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "36bbea8b6e94fa9d089118f31afc622cf4139e3bcd035773b85ca0c6e96e94c1"}, "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, "toml": {:hex, :toml, "0.6.2", "38f445df384a17e5d382befe30e3489112a48d3ba4c459e543f748c2f25dd4d1", [:mix], [], "hexpm", "d013e45126d74c0c26a38d31f5e8e9b83ea19fc752470feb9a86071ca5a672fa"}, "typed_ecto_schema": {:hex, :typed_ecto_schema, "0.4.1", "a373ca6f693f4de84cde474a67467a9cb9051a8a7f3f615f1e23dc74b75237fa", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm", "85c6962f79d35bf543dd5659c6adc340fd2480cacc6f25d2cc2933ea6e8fcb3b"}, From 1bb721865b942510c8412634323c8562038e69a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 10:47:10 +0300 Subject: [PATCH 237/363] chore(deps): bump secp256k1 in /apps/block_scout_web/assets (#10985) Bumps [secp256k1](https://github.com/cryptocoinjs/secp256k1-node) from 4.0.2 to 4.0.4. - [Release notes](https://github.com/cryptocoinjs/secp256k1-node/releases) - [Commits](https://github.com/cryptocoinjs/secp256k1-node/compare/v4.0.2...v4.0.4) --- updated-dependencies: - dependency-name: secp256k1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 11dac710fa3d..6b8734412c9c 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -13834,19 +13834,24 @@ "integrity": "sha512-8CYNl/bjkEhXWbDTU/K7c2jQtrnqEffIPyOLMqygW/7/b+ym8UtQumcAZjOfMLjZKR6AxK5tOr9fChbQZCzPqg==" }, "node_modules/secp256k1": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz", - "integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.4.tgz", + "integrity": "sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==", "hasInstallScript": true, "dependencies": { - "elliptic": "^6.5.2", - "node-addon-api": "^2.0.0", + "elliptic": "^6.5.7", + "node-addon-api": "^5.0.0", "node-gyp-build": "^4.2.0" }, "engines": { - "node": ">=10.0.0" + "node": ">=18.0.0" } }, + "node_modules/secp256k1/node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, "node_modules/select": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", @@ -26289,13 +26294,20 @@ "integrity": "sha512-8CYNl/bjkEhXWbDTU/K7c2jQtrnqEffIPyOLMqygW/7/b+ym8UtQumcAZjOfMLjZKR6AxK5tOr9fChbQZCzPqg==" }, "secp256k1": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz", - "integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.4.tgz", + "integrity": "sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==", "requires": { - "elliptic": "^6.5.2", - "node-addon-api": "^2.0.0", + "elliptic": "^6.5.7", + "node-addon-api": "^5.0.0", "node-gyp-build": "^4.2.0" + }, + "dependencies": { + "node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + } } }, "select": { From 213a3247ae6d6567734ce036d6db394acb7e818a Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Tue, 22 Oct 2024 11:41:21 +0300 Subject: [PATCH 238/363] fix: Workaround for repeating logIndex (#10880) * fix: Workaround for repeating logIndex * Fix tests * Done migration, but need to rewrite tuples usage * Migration rewritten * Fix tests * Fix test * Process review comments * Update cspell ignore * Refactoring --- .../lib/ethereum_jsonrpc/log.ex | 29 +- apps/explorer/config/test.exs | 2 +- apps/explorer/lib/explorer/application.ex | 13 + .../chain/cache/background_migrations.ex | 12 +- .../explorer/chain/import/runner/blocks.ex | 70 ++--- .../explorer/migrator/filling_migration.ex | 14 + .../sanitize_duplicated_log_index_logs.ex | 271 ++++++++++++++++++ .../chain/import/runner/blocks_test.exs | 117 -------- ...sanitize_duplicated_log_index_logs_test.ex | 144 ++++++++++ apps/explorer/test/support/factory.ex | 5 +- apps/indexer/lib/indexer/block/fetcher.ex | 20 +- cspell.json | 5 +- 12 files changed, 514 insertions(+), 188 deletions(-) create mode 100644 apps/explorer/lib/explorer/migrator/sanitize_duplicated_log_index_logs.ex create mode 100644 apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.ex diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/log.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/log.ex index e1b55f384251..7e7ace5b2a57 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/log.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/log.ex @@ -46,7 +46,8 @@ defmodule EthereumJSONRPC.Log do index: 0, second_topic: nil, third_topic: nil, - transaction_hash: "0x53bd884872de3e488692881baeec262e7b95234d3965248c39fe992fffd433e5" + transaction_hash: "0x53bd884872de3e488692881baeec262e7b95234d3965248c39fe992fffd433e5", + transaction_index: 0 } iex> EthereumJSONRPC.Log.elixir_to_params( @@ -74,26 +75,30 @@ defmodule EthereumJSONRPC.Log do index: 0, second_topic: "0x000000000000000000000000c15bf627accd3b054075c7880425f903106be72a", third_topic: "0x000000000000000000000000a59eb37750f9c8f2e11aac6700e62ef89187e4ed", - transaction_hash: "0xf9b663b4e9b1fdc94eb27b5cfba04eb03d2f7b3fa0b24eb2e1af34f823f2b89e" + transaction_hash: "0xf9b663b4e9b1fdc94eb27b5cfba04eb03d2f7b3fa0b24eb2e1af34f823f2b89e", + transaction_index: 0 } """ - def elixir_to_params(%{ - "address" => address_hash, - "blockNumber" => block_number, - "blockHash" => block_hash, - "data" => data, - "logIndex" => index, - "topics" => topics, - "transactionHash" => transaction_hash - }) do + def elixir_to_params( + %{ + "address" => address_hash, + "blockNumber" => block_number, + "blockHash" => block_hash, + "data" => data, + "logIndex" => index, + "topics" => topics, + "transactionHash" => transaction_hash + } = log + ) do %{ address_hash: address_hash, block_number: block_number, block_hash: block_hash, data: data, index: index, - transaction_hash: transaction_hash + transaction_hash: transaction_hash, + transaction_index: log["transactionIndex"] } |> put_topics(topics) end diff --git a/apps/explorer/config/test.exs b/apps/explorer/config/test.exs index 0352d8bc4fad..1e7e5be3aef8 100644 --- a/apps/explorer/config/test.exs +++ b/apps/explorer/config/test.exs @@ -14,7 +14,7 @@ config :explorer, Explorer.Repo, url: database_url, pool: Ecto.Adapters.SQL.Sandbox, # Default of `5_000` was too low for `BlockFetcher` test - ownership_timeout: :timer.minutes(7), + ownership_timeout: :timer.minutes(1), timeout: :timer.seconds(60), queue_target: 1000, migration_lock: nil, diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index c5ed03e9292b..b2645f4c30d8 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -146,6 +146,11 @@ defmodule Explorer.Application do configure_mode_dependent_process(Explorer.Migrator.ShrinkInternalTransactions, :indexer), configure_chain_type_dependent_process(Explorer.Chain.Cache.BlackfortValidatorsCounters, :blackfort), configure_chain_type_dependent_process(Explorer.Chain.Cache.StabilityValidatorsCounters, :stability), + configure_chain_type_dependent_process(Explorer.Migrator.SanitizeDuplicatedLogIndexLogs, [ + :polygon_zkevm, + :rsk, + :filecoin + ]), configure_mode_dependent_process(Explorer.Migrator.SanitizeMissingTokenBalances, :indexer), configure_mode_dependent_process(Explorer.Migrator.SanitizeReplacedTransactions, :indexer), configure_mode_dependent_process(Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, :indexer) @@ -207,6 +212,14 @@ defmodule Explorer.Application do end end + defp configure_chain_type_dependent_process(process, chain_types) when is_list(chain_types) do + if Application.get_env(:explorer, :chain_type) in chain_types do + process + else + [] + end + end + defp configure_chain_type_dependent_process(process, chain_type) do if Application.get_env(:explorer, :chain_type) == chain_type do process diff --git a/apps/explorer/lib/explorer/chain/cache/background_migrations.ex b/apps/explorer/lib/explorer/chain/cache/background_migrations.ex index 3d716b7da808..ebe0f199b2da 100644 --- a/apps/explorer/lib/explorer/chain/cache/background_migrations.ex +++ b/apps/explorer/lib/explorer/chain/cache/background_migrations.ex @@ -10,13 +10,15 @@ defmodule Explorer.Chain.Cache.BackgroundMigrations do key: :transactions_denormalization_finished, key: :tb_token_type_finished, key: :ctb_token_type_finished, - key: :tt_denormalization_finished + key: :tt_denormalization_finished, + key: :sanitize_duplicated_log_index_logs_finished @dialyzer :no_match alias Explorer.Migrator.{ AddressCurrentTokenBalanceTokenType, AddressTokenBalanceTokenType, + SanitizeDuplicatedLogIndexLogs, TokenTransferTokenType, TransactionsDenormalization } @@ -52,4 +54,12 @@ defmodule Explorer.Chain.Cache.BackgroundMigrations do {:return, false} end + + defp handle_fallback(:sanitize_duplicated_log_index_logs_finished) do + Task.start_link(fn -> + set_sanitize_duplicated_log_index_logs_finished(SanitizeDuplicatedLogIndexLogs.migration_finished?()) + end) + + {:return, false} + end end diff --git a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex index e25b41386927..f6f435a85d9b 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex @@ -828,61 +828,27 @@ defmodule Explorer.Chain.Import.Runner.Blocks do end defp refs_to_token_transfers_query(historical_token_transfers_query, filtered_query) do - if Application.get_env(:explorer, :chain_type) in [:polygon_zkevm, :rsk] do - 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), - inner_join: t in Transaction, - on: tt.transaction_hash == t.hash, - select: %{ - token_contract_address_hash: tt.token_contract_address_hash, - token_id: historical_tt.token_id, - block_number: tt.block_number, - transaction_hash: t.hash, - log_index: tt.log_index, - position: - over(row_number(), - partition_by: [tt.token_contract_address_hash, historical_tt.token_id, tt.block_number], - order_by: [desc: t.index, desc: tt.log_index] - ) - } - ) - else - 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] - ) - end + 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] + ) end defp derived_token_transfers_query(refs_to_token_transfers, filtered_query) do - if Application.get_env(:explorer, :chain_type) in [:polygon_zkevm, :rsk] do - 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 and - tt_1.transaction_hash == tt.transaction_hash, - where: tt_1.position == 1 - ) - else - 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 - ) - end + 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 + ) end defp token_instances_on_conflict do diff --git a/apps/explorer/lib/explorer/migrator/filling_migration.ex b/apps/explorer/lib/explorer/migrator/filling_migration.ex index f1afea795de8..5d46272ca0c6 100644 --- a/apps/explorer/lib/explorer/migrator/filling_migration.ex +++ b/apps/explorer/lib/explorer/migrator/filling_migration.ex @@ -8,6 +8,8 @@ defmodule Explorer.Migrator.FillingMigration do @callback last_unprocessed_identifiers(map()) :: {[any()], map()} @callback update_batch([any()]) :: any() @callback update_cache :: any() + @callback on_finish :: any() + @callback before_start :: any() defmacro __using__(_opts) do quote do @@ -44,6 +46,7 @@ defmodule Explorer.Migrator.FillingMigration do migration_status -> MigrationStatus.set_status(migration_name(), "started") + before_start() schedule_batch_migration() {:noreply, (migration_status && migration_status.meta) || %{}} end @@ -53,6 +56,7 @@ defmodule Explorer.Migrator.FillingMigration do def handle_info(:migrate_batch, state) do case last_unprocessed_identifiers(state) do {[], new_state} -> + on_finish() update_cache() MigrationStatus.set_status(migration_name(), "completed") {:stop, :normal, new_state} @@ -86,6 +90,16 @@ defmodule Explorer.Migrator.FillingMigration do Application.get_env(:explorer, __MODULE__)[:concurrency] || default end + + def on_finish do + :ignore + end + + def before_start do + :ignore + end + + defoverridable on_finish: 0, before_start: 0 end end end diff --git a/apps/explorer/lib/explorer/migrator/sanitize_duplicated_log_index_logs.ex b/apps/explorer/lib/explorer/migrator/sanitize_duplicated_log_index_logs.ex new file mode 100644 index 000000000000..2ab9203799e4 --- /dev/null +++ b/apps/explorer/lib/explorer/migrator/sanitize_duplicated_log_index_logs.ex @@ -0,0 +1,271 @@ +defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogs do + @moduledoc """ + This module is responsible for sanitizing duplicate log index entries in the database. + The migration process includes identifying duplicate log indexes and updating the related token transfers and token instances accordingly. + """ + + use Explorer.Migrator.FillingMigration + + import Ecto.Query + + alias Explorer.Chain.Cache.BackgroundMigrations + alias Explorer.Chain.{Log, TokenTransfer} + alias Explorer.Chain.Token.Instance + alias Explorer.Migrator.FillingMigration + alias Explorer.Repo + + require Logger + + @migration_name "sanitize_duplicated_log_index_logs" + + @impl FillingMigration + def migration_name, do: @migration_name + + @impl FillingMigration + def last_unprocessed_identifiers(state) do + block_number = state[:block_number_to_process] || 0 + + limit = batch_size() * concurrency() + + ids = + block_number + |> unprocessed_data_query(block_number + limit) + |> Repo.all(timeout: :infinity) + |> Enum.group_by(& &1.block_hash) + |> Map.to_list() + + {ids, Map.put(state, :block_number_to_process, block_number + limit)} + end + + @doc """ + Stub implementation to satisfy FillingMigration behaviour + """ + @impl FillingMigration + @spec unprocessed_data_query() :: nil + def unprocessed_data_query do + nil + end + + def unprocessed_data_query(block_number_start, block_number_end) do + Log + |> where([l], l.block_number >= ^block_number_start and l.block_number < ^block_number_end) + end + + @impl FillingMigration + @doc """ + Updates a batch of logs grouped by block. + + ## Parameters + + - logs_by_block: A map where the keys are block identifiers and the values are lists of logs associated with those blocks. + + ## Returns + + :ok + """ + def update_batch(logs_by_block) do + logs_to_update = + logs_by_block + |> Enum.map(&process_block/1) + |> Enum.reject(&(&1 == :ignore)) + |> List.flatten() + + {ids, logs, ids_to_new_index} = + logs_to_update + |> Enum.reduce({[], [], %{}}, fn {log, new_index}, {ids, logs, ids_to_new_index} -> + id = {log.transaction_hash, log.block_hash, log.index} + + {[id | ids], + [ + %Log{log | index: new_index} |> Map.from_struct() |> Map.drop([:block, :address, :transaction, :__meta__]) + | logs + ], Map.put(ids_to_new_index, id, new_index)} + end) + + prepared_ids = + Enum.map(ids, fn {transaction_hash, block_hash, log_index} -> + {transaction_hash.bytes, block_hash.bytes, log_index} + end) + + Repo.transaction(fn -> + Log + |> where( + [log], + fragment( + "(?, ?, ?) = ANY(?::log_id[])", + log.transaction_hash, + log.block_hash, + log.index, + ^prepared_ids + ) + ) + |> Repo.delete_all(timeout: :infinity) + + {_, token_transfers} = + TokenTransfer + |> where( + [token_transfer], + fragment( + "(?, ?, ?) = ANY(?::log_id[])", + token_transfer.transaction_hash, + token_transfer.block_hash, + token_transfer.log_index, + ^prepared_ids + ) + ) + |> select([token_transfer], token_transfer) + |> Repo.delete_all(timeout: :infinity) + + Repo.insert_all(Log, logs, timeout: :infinity) + + token_transfers + |> Enum.map(fn token_transfer -> + id = token_transfer_to_index(token_transfer) + + %TokenTransfer{token_transfer | log_index: ids_to_new_index[id]} + |> Map.from_struct() + |> Map.drop([ + :token_id, + :index_in_batch, + :reverse_index_in_batch, + :token_decimals, + :from_address, + :to_address, + :token_contract_address, + :block, + :instances, + :token, + :transaction, + :__meta__ + ]) + end) + |> (&Repo.insert_all(TokenTransfer, &1, timeout: :infinity)).() + + nft_instances_params = + token_transfers + |> Enum.filter(&(&1.token_type == "ERC-721")) + |> Enum.map(fn token_transfer -> {token_transfer.block_number, token_transfer.log_index} end) + + nft_updates_map = + token_transfers + |> Enum.filter(&(&1.token_type == "ERC-721" && &1.block_consensus)) + |> Enum.reduce(%{}, fn token_transfer, acc -> + id = token_transfer_to_index(token_transfer) + Map.put(acc, {token_transfer.block_number, token_transfer.log_index}, ids_to_new_index[id]) + end) + + Instance + |> where( + [nft], + fragment( + "(?, ?) = ANY(?::nft_id[])", + nft.owner_updated_at_block, + nft.owner_updated_at_log_index, + ^nft_instances_params + ) + ) + |> Repo.all(timeout: :infinity) + |> Enum.map(fn nft -> + %Instance{ + nft + | owner_updated_at_log_index: nft_updates_map[{nft.owner_updated_at_block, nft.owner_updated_at_log_index}] + } + |> Map.from_struct() + |> Map.drop([ + :current_token_balance, + :is_unique, + :owner, + :token, + :__meta__ + ]) + end) + |> (&Repo.insert_all(Instance, &1, + conflict_target: [:token_contract_address_hash, :token_id], + on_conflict: {:replace, [:owner_updated_at_log_index]}, + timeout: :infinity + )).() + end) + + :ok + end + + defp process_block({block_hash, logs}) do + if logs |> Enum.frequencies_by(& &1.index) |> Map.values() |> Enum.max() == 1 do + :ignore + else + Logger.error("Found logs with same index within one block: #{block_hash} in DB") + + logs = Repo.preload(logs, :transaction) + + logs + |> Enum.sort_by(&{&1.transaction.index, &1.index, &1.transaction_hash}) + # credo:disable-for-next-line Credo.Check.Refactor.Nesting + |> Enum.map_reduce(0, fn log, index -> + {{log, index}, index + 1} + end) + |> elem(0) + end + end + + @impl FillingMigration + def update_cache do + BackgroundMigrations.set_sanitize_duplicated_log_index_logs_finished(true) + end + + defp token_transfer_to_index(token_transfer) do + {token_transfer.transaction_hash, token_transfer.block_hash, token_transfer.log_index} + end + + @doc """ + Callback function that is executed before the migration process starts. + """ + @impl FillingMigration + def before_start do + """ + DO $$ + BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'log_id') THEN + CREATE TYPE log_id AS ( + transaction_hash bytea, + block_hash bytea, + log_index integer + ); + END IF; + END$$; + """ + |> Repo.query!() + + """ + DO $$ + BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'nft_id') THEN + CREATE TYPE nft_id AS ( + block_number bigint, + log_index integer + ); + END IF; + END$$; + """ + |> Repo.query!() + + :ok + end + + @doc """ + Callback function that is executed when the migration process finishes. + """ + @impl FillingMigration + def on_finish do + """ + DROP TYPE log_id; + """ + |> Repo.query!([], timeout: :infinity) + + """ + DROP TYPE nft_id; + """ + |> Repo.query!([], timeout: :infinity) + + :ok + 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 278034f2fd31..61ce2aa45c4d 100644 --- a/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs +++ b/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs @@ -86,123 +86,6 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do "Tuple was written even though it is not distinct" end - test "update_token_instances_owner inserts correct token instances in cases when log_index is not unique within block", - %{ - consensus_block: %{hash: previous_block_hash, miner_hash: miner_hash, number: previous_block_number}, - options: options - } do - old_env = Application.get_env(:explorer, :chain_type) - - Application.put_env(:explorer, :chain_type, :polygon_zkevm) - - previous_consensus_block = insert(:block, hash: previous_block_hash, number: previous_block_number) - %{hash: block_hash, number: block_number} = consensus_block = insert(:block) - - transaction = - :transaction - |> insert() - |> with_block(consensus_block) - - transaction_with_previous_transfer = - :transaction - |> insert() - |> with_block(previous_consensus_block, index: 1) - - older_transaction_with_previous_transfer = - :transaction - |> insert() - |> with_block(previous_consensus_block, index: 0) - - transaction_of_other_instance = - :transaction - |> insert() - |> with_block(previous_consensus_block) - - token = insert(:token, type: "ERC-721") - correct_token_id = Decimal.new(1) - - forked_token_transfer = - insert(:token_transfer, - token_type: "ERC-721", - token_contract_address: token.contract_address, - transaction: transaction, - token_ids: [correct_token_id], - block_number: block_number - ) - - _token_instance = - insert(:token_instance, - token_id: correct_token_id, - token_contract_address_hash: token.contract_address_hash, - owner_updated_at_block: block_number, - owner_updated_at_log_index: forked_token_transfer.log_index - ) - - _previous_token_transfer = - insert(:token_transfer, - token_type: "ERC-721", - token_contract_address: token.contract_address, - transaction: transaction_with_previous_transfer, - token_ids: [correct_token_id], - block_number: previous_block_number, - log_index: 10 - ) - - _older_previous_token_transfer = - insert(:token_transfer, - token_type: "ERC-721", - token_contract_address: token.contract_address, - transaction: older_transaction_with_previous_transfer, - token_ids: [correct_token_id], - block_number: previous_block_number, - log_index: 11 - ) - - _unsuitable_token_instance = - insert(:token_instance, - token_id: 2, - token_contract_address_hash: token.contract_address_hash, - owner_updated_at_block: previous_block_number, - owner_updated_at_log_index: forked_token_transfer.log_index - ) - - _unsuitable_token_transfer = - insert(:token_transfer, - token_type: "ERC-721", - token_contract_address: token.contract_address, - transaction: transaction_of_other_instance, - token_ids: [2], - block_number: previous_block_number, - log_index: forked_token_transfer.log_index - ) - - 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] - - assert {:ok, %{}} - - assert {:ok, - %{ - update_token_instances_owner: [ - %{ - token_id: ^correct_token_id, - owner_updated_at_block: ^previous_block_number, - owner_updated_at_log_index: 10 - } - ] - }} = - Multi.new() - |> Blocks.run(changes_list, options) - |> Repo.transaction() - - on_exit(fn -> - Application.put_env(:explorer, :chain_type, old_env) - end) - end - test "coin balances are deleted and new balances are derived if some blocks lost consensus", %{consensus_block: %{number: block_number} = block, options: options} do %{hash: address_hash} = address = insert(:address) diff --git a/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.ex b/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.ex new file mode 100644 index 000000000000..df5fe83d08c5 --- /dev/null +++ b/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.ex @@ -0,0 +1,144 @@ +defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogsTest do + use Explorer.DataCase, async: false + + alias Explorer.Chain.Cache.BackgroundMigrations + alias Explorer.Chain.Log + alias Explorer.Chain.TokenTransfer + alias Explorer.Chain.Token.Instance + alias Explorer.Migrator.{SanitizeDuplicatedLogIndexLogs, MigrationStatus} + + describe "Sanitize duplicated log index logs" do + test "correctly identifies and updates duplicated log index logs" do + block = insert(:block) + + tx1 = :transaction |> insert() |> with_block(block, index: 0) + tx2 = :transaction |> insert() |> with_block(block, index: 1) + + _log1 = insert(:log, transaction: tx1, index: 3, data: "0x01", block: block, block_number: block.number) + _log2 = insert(:log, transaction: tx1, index: 0, data: "0x02", block: block, block_number: block.number) + _log3 = insert(:log, transaction: tx2, index: 3, data: "0x03", block: block, block_number: block.number) + + log4 = insert(:log) + + assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil + + SanitizeDuplicatedLogIndexLogs.start_link([]) + Process.sleep(300) + + assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" + assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true + + updated_logs = Repo.all(Log |> where([log], log.block_number == ^block.number) |> order_by([log], asc: log.index)) + + assert match?( + [ + %{index: 0, data: %Explorer.Chain.Data{bytes: <<2>>}}, + %{index: 1, data: %Explorer.Chain.Data{bytes: <<1>>}}, + %{index: 2, data: %Explorer.Chain.Data{bytes: <<3>>}} + ], + updated_logs + ) + + assert %Log{log4 | address: nil, block: nil, transaction: nil} == %Log{ + Repo.one(Log |> where([log], log.block_number != ^block.number)) + | address: nil, + block: nil, + transaction: nil + } + end + + test "correctly identifies and updates duplicated log index logs & updates corresponding token transfers and token instances" do + block = insert(:block) + 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) + + tx1 = :transaction |> insert() |> with_block(block, index: 0) + tx2 = :transaction |> insert() |> with_block(block, index: 1) + + log1 = insert(:log, transaction: tx1, index: 3, data: "0x01", block: block, block_number: block.number) + log2 = insert(:log, transaction: tx1, index: 0, data: "0x02", block: block, block_number: block.number) + log3 = insert(:log, transaction: tx2, index: 3, data: "0x03", block: block, block_number: block.number) + + log4 = insert(:log) + + _tt1 = + insert(:token_transfer, + token_type: "ERC-721", + block: block, + block_number: block.number, + log_index: log1.index, + token_ids: [instance.token_id], + token_contract_address: token_address, + token_contract_address_hash: token_address.hash, + transaction: tx1, + transaction_hash: tx1.hash, + block_hash: block.hash + ) + + _tt2 = + insert(:token_transfer, + block: block, + block_number: block.number, + log_index: log2.index, + transaction: tx1, + transaction_hash: tx1.hash + ) + + _tt3 = + insert(:token_transfer, + block: block, + block_number: block.number, + log_index: log3.index, + transaction: tx2, + transaction_hash: tx2.hash + ) + + Instance.changeset(instance, %{owner_updated_at_block: block.number, owner_updated_at_log_index: log1.index}) + |> Repo.update!() + + assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil + + SanitizeDuplicatedLogIndexLogs.start_link([]) + Process.sleep(300) + + assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" + assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true + + updated_logs = Repo.all(Log |> where([log], log.block_number == ^block.number) |> order_by([log], asc: log.index)) + + assert match?( + [ + %{index: 0, data: %Explorer.Chain.Data{bytes: <<2>>}}, + %{index: 1, data: %Explorer.Chain.Data{bytes: <<1>>}}, + %{index: 2, data: %Explorer.Chain.Data{bytes: <<3>>}} + ], + updated_logs + ) + + block_number = block.number + assert [%{owner_updated_at_block: ^block_number, owner_updated_at_log_index: 1}] = Repo.all(Instance) + + assert [%{log_index: 1, block_number: ^block_number}] = + Repo.all(TokenTransfer |> where([tt], tt.token_type == "ERC-721")) + + assert %Log{log4 | address: nil, block: nil, transaction: nil} == %Log{ + Repo.one(Log |> where([log], log.block_number != ^block.number)) + | address: nil, + block: nil, + transaction: nil + } + end + + test "correctly handles cases where there are no duplicated log index logs" do + assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil + + SanitizeDuplicatedLogIndexLogs.start_link([]) + Process.sleep(100) + + assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" + assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true + end + end +end diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index 31754fff3885..d1e855e23a70 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -825,11 +825,12 @@ defmodule Explorer.Factory do token_address = insert(:contract_address, contract_code: contract_code) token = insert(:token, contract_address: token_address) + block = build(:block) %TokenTransfer{ - block: build(:block), + block: block, amount: Decimal.new(1), - block_number: block_number(), + block_number: block.number, from_address: from_address, to_address: to_address, token_contract_address: token_address, diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 1d11987a66bd..8a28d507a07f 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -160,7 +160,7 @@ defmodule Indexer.Block.Fetcher do %{logs: receipt_logs, receipts: receipts} = receipt_params, transactions_with_receipts = Receipts.put(transactions_params_without_receipts, receipts), celo_epoch_logs = CeloEpochLogs.fetch(blocks, json_rpc_named_arguments), - logs = receipt_logs ++ celo_epoch_logs, + logs = maybe_set_new_log_index(receipt_logs) ++ celo_epoch_logs, %{token_transfers: token_transfers, tokens: tokens} = TokenTransfers.parse(logs), %{token_transfers: celo_native_token_transfers, tokens: celo_tokens} = CeloTransactionTokenTransfers.parse_transactions(transactions_with_receipts), @@ -754,4 +754,22 @@ defmodule Indexer.Block.Fetcher do defp async_match_arbitrum_messages_to_l2(transactions_with_messages_from_l1) do ArbitrumMessagesToL2Matcher.async_discover_match(transactions_with_messages_from_l1) end + + # workaround for cases when RPC send logs with same index within one block + defp maybe_set_new_log_index(logs) do + logs + |> Enum.group_by(& &1.block_hash) + |> Enum.map(fn {block_hash, logs_per_block} -> + if logs_per_block |> Enum.frequencies_by(& &1.index) |> Map.values() |> Enum.max() == 1 do + logs_per_block + else + Logger.error("Found logs with same index within one block: #{block_hash}") + + logs_per_block + |> Enum.sort_by(&{&1.transaction_index, &1.index, &1.transaction_hash}) + |> Enum.with_index(&%{&1 | index: &2}) + end + end) + |> List.flatten() + end end diff --git a/cspell.json b/cspell.json index fe3444f1df18..30818229bc8f 100644 --- a/cspell.json +++ b/cspell.json @@ -181,8 +181,8 @@ "errora", "errorb", "erts", - "Ethash", "ethaccount", + "Ethash", "etherchain", "ethprice", "ethsupply", @@ -561,14 +561,15 @@ "Txns", "txpool", "txreceipt", + "typname", "ueberauth", "ufixed", "uncatalog", "unclosable", "unfetched", "unfinalized", - "unindexed", "Unichain", + "unindexed", "Unitarion", "Unitorius", "Unitorus", From 8a826f11285cf495c39094a621d4726711189cad Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Tue, 22 Oct 2024 13:42:28 +0400 Subject: [PATCH 239/363] fix: Handle import exceptions in MassiveBlocksFetcher (#10993) --- .../lib/indexer/block/catchup/massive_blocks_fetcher.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/indexer/lib/indexer/block/catchup/massive_blocks_fetcher.ex b/apps/indexer/lib/indexer/block/catchup/massive_blocks_fetcher.ex index fa1c91ed5ad7..ea3de2e8d538 100644 --- a/apps/indexer/lib/indexer/block/catchup/massive_blocks_fetcher.ex +++ b/apps/indexer/lib/indexer/block/catchup/massive_blocks_fetcher.ex @@ -58,7 +58,7 @@ defmodule Indexer.Block.Catchup.MassiveBlocksFetcher do defp process_block(block_fetcher, number) do case Fetcher.fetch_and_import_range(block_fetcher, number..number, %{timeout: :infinity}) do {:ok, _result} -> - Logger.info("MassiveBlockFetcher successfully proceed block #{inspect(number)}") + Logger.info("MassiveBlockFetcher successfully processed block #{inspect(number)}") MassiveBlock.delete_block_number(number) [] @@ -66,6 +66,10 @@ defmodule Indexer.Block.Catchup.MassiveBlocksFetcher do Logger.error("MassiveBlockFetcher failed: #{inspect(error)}") [number] end + rescue + error -> + Logger.error("MassiveBlockFetcher failed: #{inspect(error)}") + [number] end defp generate_block_fetcher do From 1b1d3402876595b15968edeced3d73d84e3fc8d8 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 22 Oct 2024 13:04:30 +0300 Subject: [PATCH 240/363] fix: Process foreign key violation in scam addresses assigning functionality (#10977) --- apps/explorer/lib/explorer/chain.ex | 39 ++------------ apps/explorer/lib/explorer/chain/address.ex | 52 ++++++++++++++++++- .../chain/address/scam_badge_to_address.ex | 22 ++++++++ 3 files changed, 76 insertions(+), 37 deletions(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 385791447a12..39e100380dc4 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -746,37 +746,6 @@ defmodule Explorer.Chain do def confirmations(nil, _), do: {:error, :pending} - @doc """ - Creates an address. - - iex> {:ok, %Explorer.Chain.Address{hash: hash}} = Explorer.Chain.create_address( - ...> %{hash: "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"} - ...> ) - ...> to_string(hash) - "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" - - A `String.t/0` value for `Explorer.Chain.Address.t/0` `hash` must have 40 hexadecimal characters after the `0x` prefix - to prevent short- and long-hash transcription errors. - - iex> {:error, %Ecto.Changeset{errors: errors}} = Explorer.Chain.create_address( - ...> %{hash: "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0"} - ...> ) - ...> errors - [hash: {"is invalid", [type: Explorer.Chain.Hash.Address, validation: :cast]}] - iex> {:error, %Ecto.Changeset{errors: errors}} = Explorer.Chain.create_address( - ...> %{hash: "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0ba"} - ...> ) - ...> errors - [hash: {"is invalid", [type: Explorer.Chain.Hash.Address, validation: :cast]}] - - """ - @spec create_address(map()) :: {:ok, Address.t()} | {:error, Ecto.Changeset.t()} - def create_address(attrs \\ %{}) do - %Address{} - |> Address.changeset(attrs) - |> Repo.insert() - end - @doc """ Creates a decompiled smart contract. """ @@ -943,7 +912,7 @@ defmodule Explorer.Chain do Returns `{:ok, %Explorer.Chain.Address{}}` if found - iex> {:ok, %Explorer.Chain.Address{hash: hash}} = Explorer.Chain.create_address( + iex> {:ok, %Explorer.Chain.Address{hash: hash}} = Explorer.Chain.Address.create( ...> %{hash: "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed"} ...> ) iex> {:ok, %Explorer.Chain.Address{hash: found_hash}} = Explorer.Chain.hash_to_address(hash) @@ -1042,7 +1011,7 @@ defmodule Explorer.Chain do Returns `{:ok, %Explorer.Chain.Address{}}` if found - iex> {:ok, %Explorer.Chain.Address{hash: hash}} = Explorer.Chain.create_address( + iex> {:ok, %Explorer.Chain.Address{hash: hash}} = Explorer.Chain.Address.create( ...> %{hash: "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed"} ...> ) iex> {:ok, %Explorer.Chain.Address{hash: found_hash}} = Explorer.Chain.hash_to_address(hash) @@ -1051,7 +1020,7 @@ defmodule Explorer.Chain do Returns `{:error, address}` if not found but created an address - iex> {:ok, %Explorer.Chain.Address{hash: hash}} = Explorer.Chain.create_address( + iex> {:ok, %Explorer.Chain.Address{hash: hash}} = Explorer.Chain.Address.create( ...> %{hash: "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed"} ...> ) iex> {:ok, %Explorer.Chain.Address{hash: found_hash}} = Explorer.Chain.hash_to_address(hash) @@ -1088,7 +1057,7 @@ defmodule Explorer.Chain do {:ok, address} {:error, :not_found} -> - create_address(%{hash: to_string(hash)}) + Address.create(%{hash: to_string(hash)}) hash_to_address(hash, options, query_decompiled_code_flag) end end diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index 72baeb7babb7..f2c4a4ed2a53 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -205,6 +205,54 @@ defmodule Explorer.Chain.Address do @balance_changeset_required_attrs @required_attrs ++ ~w(fetched_coin_balance fetched_coin_balance_block_number)a + @doc """ + Creates an address. + + iex> {:ok, %Explorer.Chain.Address{hash: hash}} = Explorer.Chain.Address.create( + ...> %{hash: "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"} + ...> ) + ...> to_string(hash) + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + + A `String.t/0` value for `Explorer.Chain.Address.t/0` `hash` must have 40 hexadecimal characters after the `0x` prefix + to prevent short- and long-hash transcription errors. + + iex> {:error, %Ecto.Changeset{errors: errors}} = Explorer.Chain.Address.create( + ...> %{hash: "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0"} + ...> ) + ...> errors + [hash: {"is invalid", [type: Explorer.Chain.Hash.Address, validation: :cast]}] + iex> {:error, %Ecto.Changeset{errors: errors}} = Explorer.Chain.Address.create( + ...> %{hash: "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0ba"} + ...> ) + ...> errors + [hash: {"is invalid", [type: Explorer.Chain.Hash.Address, validation: :cast]}] + + """ + @spec create(map()) :: {:ok, __MODULE__.t()} | {:error, Ecto.Changeset.t()} + def create(attrs \\ %{}) do + %__MODULE__{} + |> changeset(attrs) + |> Repo.insert() + end + + @doc """ + Creates multiple instances of a `Explorer.Chain.Address`. + + ## Parameters + + - address_insert_params: List of address changesets to create. + + ## Returns + + - A list of created resource instances. + + """ + @spec create_multiple(list()) :: {non_neg_integer(), nil | [term()]} + def create_multiple(address_insert_params) do + Repo.insert_all(Address, address_insert_params, on_conflict: :nothing, returning: [:hash]) + end + def balance_changeset(%__MODULE__{} = address, attrs) do address |> cast(attrs, @allowed_attrs) @@ -518,7 +566,7 @@ defmodule Explorer.Chain.Address do Returns `:ok` if found - iex> {:ok, %Explorer.Chain.Address{hash: hash}} = Explorer.Chain.create_address( + iex> {:ok, %Explorer.Chain.Address{hash: hash}} = Explorer.Chain.Address.create( ...> %{hash: "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed"} ...> ) iex> Explorer.Address.check_address_exists(hash) @@ -543,7 +591,7 @@ defmodule Explorer.Chain.Address do Returns `true` if found - iex> {:ok, %Explorer.Chain.Address{hash: hash}} = Explorer.Chain.create_address( + iex> {:ok, %Explorer.Chain.Address{hash: hash}} = Explorer.Chain.Address.create( ...> %{hash: "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed"} ...> ) iex> Explorer.Chain.Address.address_exists?(hash) diff --git a/apps/explorer/lib/explorer/chain/address/scam_badge_to_address.ex b/apps/explorer/lib/explorer/chain/address/scam_badge_to_address.ex index 085de60e44c7..e911cfbc9286 100644 --- a/apps/explorer/lib/explorer/chain/address/scam_badge_to_address.ex +++ b/apps/explorer/lib/explorer/chain/address/scam_badge_to_address.ex @@ -50,7 +50,29 @@ defmodule Explorer.Chain.Address.ScamBadgeToAddress do end) |> Enum.filter(&(!is_nil(&1))) + safe_add(insert_params) + end + + defp safe_add(insert_params) do Repo.insert_all(__MODULE__, insert_params, on_conflict: :nothing, returning: [:address_hash]) + rescue + # if at least one of the address hashes didn't yet exist in the `addresses` table by the moment of scam badge assignment, we'll receive foreign key violation error. + # In this case, we'll try to insert the missing addresses and then try to insert the scam badge mappings again. + error -> + address_insert_params = + insert_params + |> Enum.map(fn scam_address -> + scam_address + |> Map.put(:hash, scam_address.address_hash) + |> Map.drop([:address_hash]) + end) + + with %Postgrex.Error{postgres: %{code: :foreign_key_violation}} <- error, + {_number_of_inserted_addresses, [_term]} <- Address.create_multiple(address_insert_params) do + safe_add(insert_params) + else + _ -> {0, []} + end end @doc """ From 26d906a5cc239de956790451758537989eb8b9cf Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:59:15 +0300 Subject: [PATCH 241/363] feat: Account V2 (#10706) * feat: Account V2 Implement merging Add captcha to send_otp Refactor Handle old sessions Add address hash to session info Finish core functionality * Some fixes Fix captcha related tests Add recaptcha v3 support Add error for wrong otp Add uri encoding for user id Fix recaptcha error * Return session info in auth responses * hostname check for recaptcha; merge addresses Merge accounts with the same address as well Add hostname check for recaptcha Remove debug code * Fix address accounts merging * Handle email linking to siwe account * Allow hostname check disabling * Fix @vbaranov review * Fix RE_CAPTCHA_CHECK_HOSTNAME logic * Restrict users with email to link email * Fix @nikitosing review * Format --- .../assets/js/lib/csv_download.js | 2 +- apps/block_scout_web/config/config.exs | 3 +- apps/block_scout_web/config/dev.exs | 2 - apps/block_scout_web/config/prod.exs | 2 - apps/block_scout_web/config/test.exs | 2 - .../lib/block_scout_web/captcha_helper.ex | 68 +- .../account/api/v2/address_controller.ex | 41 + .../account/api/v2/authenticate_controller.ex | 247 +++++- .../account/api/v2/email_controller.ex | 72 +- .../account/api/v2/fallback_controller.ex | 32 +- .../account/api/v2/tags_controller.ex | 10 +- .../account/api/v2/user_controller.ex | 73 +- .../controllers/account/auth_controller.ex | 4 +- .../address_transaction_controller.ex | 47 +- .../api/v2/advanced_filter_controller.ex | 6 +- .../controllers/api/v2/api_key_controller.ex | 7 +- .../api/v2/csv_export_controller.ex | 7 +- .../controllers/api/v2/fallback_controller.ex | 2 +- .../api/v2/smart_contract_controller.ex | 6 +- .../controllers/api/v2/token_controller.ex | 6 +- .../models/get_transaction_tags.ex | 6 +- .../block_scout_web/models/user_from_auth.ex | 143 ---- .../block_scout_web/routers/account_router.ex | 23 +- .../views/account/api/v2/account_view.ex | 2 +- .../views/account/api/v2/tags_view.ex | 2 +- .../views/account/api/v2/user_view.ex | 12 +- .../account/api/v2/user_controller_test.exs | 7 +- .../account/custom_abi_controller_test.exs | 4 +- .../address_transaction_controller_test.exs | 142 +++- .../api/v2/address_controller_test.exs | 5 +- .../api/v2/main_page_controller_test.exs | 5 +- .../api/v2/smart_contract_controller_test.exs | 4 +- .../api/v2/token_controller_test.exs | 81 +- .../api/v2/transaction_controller_test.exs | 7 +- apps/block_scout_web/test/test_helper.exs | 1 - apps/explorer/lib/explorer/account.ex | 79 ++ apps/explorer/lib/explorer/account/api/key.ex | 27 + .../lib/explorer/account/custom_abi.ex | 65 +- .../explorer/lib/explorer/account/identity.ex | 295 ++++++- .../explorer/account/public_tags_request.ex | 34 +- .../lib/explorer/account/tag_address.ex | 31 +- .../lib/explorer/account/tag_transaction.ex | 59 +- .../lib/explorer/account/watchlist.ex | 36 + .../lib/explorer/account/watchlist_address.ex | 53 +- .../account/watchlist_notification.ex | 53 ++ apps/explorer/lib/explorer/chain.ex | 30 +- .../lib/explorer/chain/csv_export/helper.ex | 6 - .../chain/smart_contract/audit_report.ex | 2 +- apps/explorer/lib/explorer/helper.ex | 48 ++ .../third_party_integrations/auth0.ex | 758 +++++++++++++++++- apps/explorer/mix.exs | 6 +- .../migrations/20240913194307_account_v2.exs | 50 ++ .../test/explorer/account/identity_test.exs} | 12 +- apps/explorer/test/support/factory.ex | 44 +- config/runtime.exs | 7 +- cspell.json | 4 + docker-compose/envs/common-blockscout.env | 2 + mix.lock | 9 +- 58 files changed, 2381 insertions(+), 412 deletions(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/address_controller.ex delete mode 100644 apps/block_scout_web/lib/block_scout_web/models/user_from_auth.ex create mode 100644 apps/explorer/priv/account/migrations/20240913194307_account_v2.exs rename apps/{block_scout_web/test/block_scout_web/models/user_from_auth_test.exs => explorer/test/explorer/account/identity_test.exs} (89%) diff --git a/apps/block_scout_web/assets/js/lib/csv_download.js b/apps/block_scout_web/assets/js/lib/csv_download.js index 7f36a773e64b..eb00e5e77b82 100644 --- a/apps/block_scout_web/assets/js/lib/csv_download.js +++ b/apps/block_scout_web/assets/js/lib/csv_download.js @@ -53,7 +53,7 @@ $button.on('click', () => { // eslint-disable-next-line grecaptcha.execute(reCaptchaV3ClientKey, { action: 'login' }) .then(function (token) { - const url = `${baseURL}&recaptcha_response=${token}` + const url = `${baseURL}&recaptcha_v3_response=${token}` download(url) }) diff --git a/apps/block_scout_web/config/config.exs b/apps/block_scout_web/config/config.exs index 7467a51f3c1a..1c0c83bd72a3 100644 --- a/apps/block_scout_web/config/config.exs +++ b/apps/block_scout_web/config/config.exs @@ -17,7 +17,8 @@ config :block_scout_web, # 604800 seconds, 1 week session_cookie_ttl: 60 * 60 * 24 * 7, invalid_session_key: "invalid_session", - api_v2_temp_token_key: "api_v2_temp_token" + api_v2_temp_token_key: "api_v2_temp_token", + http_adapter: HTTPoison config :block_scout_web, admin_panel_enabled: ConfigHelper.parse_bool_env_var("ADMIN_PANEL_ENABLED") diff --git a/apps/block_scout_web/config/dev.exs b/apps/block_scout_web/config/dev.exs index 3f2bb8aa70e7..d69502e2d5bd 100644 --- a/apps/block_scout_web/config/dev.exs +++ b/apps/block_scout_web/config/dev.exs @@ -67,5 +67,3 @@ config :logger, :api_v2, # Set a higher stacktrace during development. Avoid configuring such # in production as building large stacktraces may be expensive. config :phoenix, :stacktrace_depth, 20 - -config :block_scout_web, :captcha_helper, BlockScoutWeb.CaptchaHelper diff --git a/apps/block_scout_web/config/prod.exs b/apps/block_scout_web/config/prod.exs index d94e56e534d8..2efcfd72068f 100644 --- a/apps/block_scout_web/config/prod.exs +++ b/apps/block_scout_web/config/prod.exs @@ -35,5 +35,3 @@ config :logger, :api_v2, path: Path.absname("logs/prod/api_v2.log"), metadata_filter: [application: :api_v2], rotate: %{max_bytes: 52_428_800, keep: 19} - -config :block_scout_web, :captcha_helper, BlockScoutWeb.CaptchaHelper diff --git a/apps/block_scout_web/config/test.exs b/apps/block_scout_web/config/test.exs index 9c3414fa4f48..033fc74d0444 100644 --- a/apps/block_scout_web/config/test.exs +++ b/apps/block_scout_web/config/test.exs @@ -24,8 +24,6 @@ config :block_scout_web, BlockScoutWeb.Counters.BlocksIndexedCounter, enabled: f config :block_scout_web, BlockScoutWeb.Counters.InternalTransactionsIndexedCounter, enabled: false -config :block_scout_web, :captcha_helper, BlockScoutWeb.TestCaptchaHelper - config :ueberauth, Ueberauth, providers: [ auth0: { diff --git a/apps/block_scout_web/lib/block_scout_web/captcha_helper.ex b/apps/block_scout_web/lib/block_scout_web/captcha_helper.ex index 78194bd8df7c..b05f39d696c0 100644 --- a/apps/block_scout_web/lib/block_scout_web/captcha_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/captcha_helper.ex @@ -3,35 +3,69 @@ defmodule BlockScoutWeb.CaptchaHelper do A helper for CAPTCHA """ - @callback recaptcha_passed?(String.t() | nil) :: bool - @spec recaptcha_passed?(String.t() | nil) :: bool - def recaptcha_passed?(nil), do: false + alias Explorer.Helper - def recaptcha_passed?(recaptcha_response) do - re_captcha_v2_secret_key = Application.get_env(:block_scout_web, :recaptcha)[:v2_secret_key] + @doc """ + Verifies if the CAPTCHA challenge has been passed based on the provided parameters. + + This function handles both reCAPTCHA v3 and v2 responses, as well as cases where + CAPTCHA is disabled. + + ## Parameters + - `params`: A map containing CAPTCHA response parameters or nil. + + ## Returns + - `true` if the CAPTCHA challenge is passed or disabled. + - `false` if the CAPTCHA challenge fails or an error occurs during verification. + """ + @spec recaptcha_passed?(%{String.t() => String.t()} | nil) :: bool + def recaptcha_passed?(%{"recaptcha_v3_response" => recaptcha_response}) do re_captcha_v3_secret_key = Application.get_env(:block_scout_web, :recaptcha)[:v3_secret_key] - re_captcha_secret_key = re_captcha_v2_secret_key || re_captcha_v3_secret_key - body = "secret=#{re_captcha_secret_key}&response=#{recaptcha_response}" + do_recaptcha_passed?(re_captcha_v3_secret_key, recaptcha_response) + end + + def recaptcha_passed?(%{"recaptcha_response" => recaptcha_response}) do + re_captcha_v2_secret_key = Application.get_env(:block_scout_web, :recaptcha)[:v2_secret_key] + do_recaptcha_passed?(re_captcha_v2_secret_key, recaptcha_response) + end + + def recaptcha_passed?(_), do: Application.get_env(:block_scout_web, :recaptcha)[:is_disabled] + + defp do_recaptcha_passed?(recaptcha_secret_key, recaptcha_response) do + body = "secret=#{recaptcha_secret_key}&response=#{recaptcha_response}" headers = [{"Content-type", "application/x-www-form-urlencoded"}] - case HTTPoison.post("https://www.google.com/recaptcha/api/siteverify", body, headers, []) do + case !Application.get_env(:block_scout_web, :recaptcha)[:is_disabled] && + Application.get_env(:block_scout_web, :http_adapter).post( + "https://www.google.com/recaptcha/api/siteverify", + body, + headers, + [] + ) do {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> - case Jason.decode!(body) do - %{"success" => true} = resp -> success?(resp) - _ -> false - end + body |> Jason.decode!() |> success?() + + false -> + true _ -> false end end - defp success?(%{"score" => score}) do - check_recaptcha_v3_score(score) + # v3 case + defp success?(%{"success" => true, "score" => score, "hostname" => hostname}) do + (!check_hostname?() || Helper.get_app_host() == hostname) && + check_recaptcha_v3_score(score) end - defp success?(_resp), do: true + # v2 case + defp success?(%{"success" => true, "hostname" => hostname}) do + !check_hostname?() || Helper.get_app_host() == hostname + end + + defp success?(_resp), do: false defp check_recaptcha_v3_score(score) do if score >= 0.5 do @@ -40,4 +74,8 @@ defmodule BlockScoutWeb.CaptchaHelper do false end end + + defp check_hostname? do + Application.get_env(:block_scout_web, :recaptcha)[:check_hostname?] + end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/address_controller.ex new file mode 100644 index 000000000000..2034b8a10652 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/address_controller.ex @@ -0,0 +1,41 @@ +defmodule BlockScoutWeb.Account.API.V2.AddressController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + + alias BlockScoutWeb.Account.API.V2.AuthenticateController + alias Explorer.ThirdPartyIntegrations.Auth0 + alias Plug.Conn + + action_fallback(BlockScoutWeb.Account.API.V2.FallbackController) + + @doc """ + Links an Ethereum address to the current user's account. + + This function attempts to link a provided Ethereum address to the currently + authenticated user's account. It verifies the provided message and signature, + then uses the Auth0 service to associate the address with the user's account. + + ## Parameters + - `conn`: The `Plug.Conn` struct representing the current connection. + - `params`: A map containing: + - `"message"`: The message that was signed. + - `"signature"`: The signature of the message. + + ## Returns + - `{:error, any()}`: Error and a description of the error. + - `:error`: In case of unexpected error. + - `Conn.t()`: A modified connection struct if the address is successfully + linked. The connection will have updated session information. + + ## Notes + - Errors are handled later in `BlockScoutWeb.Account.API.V2.FallbackController`. + """ + @spec link_address(Plug.Conn.t(), map()) :: :error | {:error, any()} | Conn.t() + def link_address(conn, %{"message" => message, "signature" => signature}) do + with %{uid: id} <- conn |> current_user(), + {:ok, auth} <- Auth0.link_address(id, message, signature) do + AuthenticateController.put_auth_to_session(conn, auth) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/authenticate_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/authenticate_controller.ex index b0b9454a8387..5fd57e7da6d3 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/authenticate_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/authenticate_controller.ex @@ -1,12 +1,18 @@ -defmodule BlockScoutWeb.Account.Api.V2.AuthenticateController do +defmodule BlockScoutWeb.Account.API.V2.AuthenticateController do use BlockScoutWeb, :controller import BlockScoutWeb.Account.AuthController, only: [current_user: 1] - alias BlockScoutWeb.Models.UserFromAuth + alias BlockScoutWeb.{AccessHelper, CaptchaHelper} + alias BlockScoutWeb.Account.API.V2.UserView + alias BlockScoutWeb.API.V2.ApiView alias Explorer.Account.Identity + alias Explorer.Chain + alias Explorer.Chain.Address + alias Explorer.ThirdPartyIntegrations.Auth0 + alias Plug.Conn - action_fallback(BlockScoutWeb.Account.Api.V2.FallbackController) + action_fallback(BlockScoutWeb.Account.API.V2.FallbackController) def authenticate_get(conn, params) do authenticate(conn, params) @@ -21,10 +27,243 @@ defmodule BlockScoutWeb.Account.Api.V2.AuthenticateController do {:sensitive_endpoints_api_key, Application.get_env(:block_scout_web, :sensitive_endpoints_api_key)}, {:api_key, ^api_key} <- {:api_key, params["api_key"]}, {:auth, %{id: uid} = current_user} <- {:auth, current_user(conn)}, - {:identity, %Identity{}} <- {:identity, UserFromAuth.find_identity(uid)} do + {:identity, %Identity{}} <- {:identity, Identity.find_identity(uid)} do conn |> put_status(200) |> json(current_user) end end + + @doc """ + Sends a one-time password (OTP) to the specified email address. + + This function handles the process of sending an OTP to a given email address, + with different behaviors based on the current user's authentication status + and the relationship between the provided email and existing accounts. + + The function first verifies the reCAPTCHA response to prevent abuse. Then, + it checks the current user's status and proceeds accordingly: + + 1. If no user is logged in, it sends an OTP for a new account. + 2. If a user is logged in and the email matches their account, it returns an error. + 3. If a user is logged in but the email doesn't match, it checks if there is already + a user with such email and sends an OTP for linking if there is no such user. + + ## Parameters + - `conn`: The `Plug.Conn` struct representing the current connection. + - `params`: A map containing: + - `"email"`: The email address to which the OTP should be sent. + - `"recaptcha_v3_response"` or `"recaptcha_response"`: The reCAPTCHA response token. + + ## Returns + - `:error`: If there's an unexpected error during the process. + - `{:error, String.t()}`: If there's a specific error (e.g., email already linked). + - `{:interval, integer()}`: If an OTP was recently sent and the cooldown period hasn't elapsed. + - `{:recaptcha, false}`: If the reCAPTCHA verification fails. + - `Plug.Conn.t()`: A modified connection struct with a 200 status and success message + if the OTP is successfully sent. + + ## Notes + - Errors are handled later in `BlockScoutWeb.Account.API.V2.FallbackController`. + - The function uses the client's IP address for rate limiting and abuse prevention. + - It handles both logged-in and non-logged-in user scenarios. + """ + @spec send_otp(Conn.t(), map()) :: + :error + | {:error, String.t()} + | {:interval, integer()} + | {:recaptcha, false} + | Conn.t() + def send_otp(conn, %{"email" => email} = params) do + with {:recaptcha, true} <- {:recaptcha, CaptchaHelper.recaptcha_passed?(params)} do + case conn |> Conn.fetch_session() |> current_user() do + nil -> + with :ok <- Auth0.send_otp(email, AccessHelper.conn_to_ip_string(conn)) do + conn |> put_status(200) |> json(%{message: "Success"}) + end + + %{email: nil} -> + with :ok <- Auth0.send_otp_for_linking(email, AccessHelper.conn_to_ip_string(conn)) do + conn |> put_status(200) |> json(%{message: "Success"}) + end + + %{} -> + conn + |> put_status(500) + |> put_view(ApiView) + |> render(:message, %{message: "This account already has an email"}) + end + end + end + + @doc """ + Confirms a one-time password (OTP) for a given email and updates the session. + + This function verifies the OTP provided for a specific email address. If the + OTP is valid, it retrieves the authentication information and updates the + user's session accordingly. + + The function performs the following steps: + 1. Confirms the OTP with Auth0 and retrieves the authentication information. + 2. If successful, updates the session with the new authentication data. + + ## Parameters + - `conn`: The `Plug.Conn` struct representing the current connection. + - `params`: A map containing: + - `"email"`: The email address associated with the OTP. + - `"otp"`: The one-time password to be confirmed. + + ## Returns + - `:error`: If there's an unexpected error during the process. + - `{:error, any()}`: If there's a specific error during OTP confirmation or + session update. The error details are included. + - `Conn.t()`: A modified connection struct with updated session information + if the OTP is successfully confirmed. + + ## Notes + - Errors are handled later in `BlockScoutWeb.Account.API.V2.FallbackController`. + - This function relies on the Auth0 service to confirm the OTP and retrieve + the authentication information. + - The function handles both existing and newly created users. + - For newly created users, it may create a new authentication record if the + user is not immediately found in the search after OTP confirmation. + - The session update is handled by the `put_auth_to_session/2` function, which + perform additional operations such as setting cookies or rendering user + information. + """ + @spec confirm_otp(Conn.t(), map()) :: :error | {:error, any()} | Conn.t() + def confirm_otp(conn, %{"email" => email, "otp" => otp}) do + with {:ok, auth} <- Auth0.confirm_otp_and_get_auth(email, otp) do + put_auth_to_session(conn, auth) + end + end + + @doc """ + Generates a Sign-In with Ethereum (SIWE) message for a given Ethereum address. + + This function takes an Ethereum address, validates its format, converts it to + its checksum representation, and then generates a SIWE message. The generated + message is returned as part of a JSON response. + + The function performs the following steps: + 1. Validates and converts the input address string to an address hash. + 2. Converts the address hash to its checksum representation. + 3. Generates a SIWE message using the checksum address. + 4. Returns the generated message in a JSON response. + + ## Parameters + - `conn`: The `Plug.Conn` struct representing the current connection. + - `params`: A map containing: + - `"address"`: The Ethereum address as a string, starting with "0x". + + ## Returns + - `{:error, String.t()}`: If there's an error during the SIWE message generation process. + - `{:format, :error}`: If the provided address string is not in a valid format. + - `Conn.t()`: A modified connection struct with a 200 status and a JSON body + containing the generated SIWE message if successful. + + ## Notes + - Errors are handled later in `BlockScoutWeb.Account.API.V2.FallbackController`. + - The address is converted to its checksum format before generating the SIWE message. + - The generated SIWE message includes: + - The domain and URI of the application. + - A statement for signing in. + - The chain ID of the current network. + - A nonce for security. + - Issuance and expiration timestamps. + - The nonce is cached for the address to prevent replay attacks. + - The SIWE message expires after 300 seconds from generation. + """ + @spec siwe_message(Conn.t(), map()) :: {:error, String.t()} | {:format, :error} | Conn.t() + def siwe_message(conn, %{"address" => address}) do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address)}, + {:ok, message} <- Auth0.generate_siwe_message(Address.checksum(address_hash)) do + conn |> put_status(200) |> json(%{siwe_message: message}) + end + end + + @doc """ + Authenticates a user via their Ethereum wallet using a signed message. + + This function verifies a signed Ethereum message to authenticate a user. It uses + the Sign-In with Ethereum (SIWE) protocol to validate the signature and retrieve + or create the user's authentication information. + + The function performs the following steps: + 1. Verifies the provided message and signature. + 2. If successful, updates the session with the new authentication data. + + ## Parameters + - `conn`: The `Plug.Conn` struct representing the current connection. + - `params`: A map containing: + - `"message"`: The SIWE message that was signed. + - `"signature"`: The signature of the message. + + ## Returns + - `:error`: If there's an unexpected error during the process. + - `{:error, any()}`: If there's a specific error during authentication or + session update. The error details are included. + - `Conn.t()`: A modified connection struct with updated session information + if the authentication is successful. + + ## Notes + - Errors are handled later in `BlockScoutWeb.Account.API.V2.FallbackController`. + - The function verifies the nonce in the message to prevent replay attacks. + - If the user doesn't exist, a new Web3 user is created based on the Ethereum address. + - The nonce is deleted after successful verification to prevent reuse. + - The session update is handled by the `put_auth_to_session/2` function, which + perform additional operations such as setting cookies or rendering user + information. + """ + @spec authenticate_via_wallet(Conn.t(), map()) :: :error | {:error, any()} | Conn.t() + def authenticate_via_wallet(conn, %{"message" => message, "signature" => signature}) do + with {:ok, auth} <- Auth0.get_auth_with_web3(message, signature) do + put_auth_to_session(conn, auth) + end + end + + @doc """ + Updates the session with authentication information and renders user info. + + This function takes the authentication data, creates or retrieves the user's + identity, updates the session, and renders the user information. It performs + the following steps: + + 1. Finds or creates a user session based on the authentication data. + 2. Retrieves the user's identity using the session ID. + 3. Updates the connection's session with the current user information. + 4. Renders the user information view. + + ## Parameters + - `conn`: The `Plug.Conn` struct representing the current connection. + - `auth`: A `Ueberauth.Auth.t()` struct containing the authentication information. + + ## Returns + - `{:error, any()}`: If there's an error during the process of finding/creating + the user session or retrieving the user's identity. + - `Conn.t()`: A modified connection struct with updated session information + and rendered user info if successful. + + ## Notes + - Errors are handled later in `BlockScoutWeb.Account.API.V2.FallbackController`. + - This function relies on the `Identity` module to handle user identity operations. + - It updates the session with the current user information. + - The function sets the HTTP status to 200 on successful authentication. + - It uses the `UserView` to render the user information. + - The rendered user information includes session data (name, nickname, and + optionally address_hash) merged with the identity data. + """ + @spec put_auth_to_session(Conn.t(), Ueberauth.Auth.t()) :: {:error, any()} | Conn.t() + def put_auth_to_session(conn, auth) do + with {:ok, %{id: uid} = session} <- Identity.find_or_create(auth), + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)} do + conn + |> Conn.fetch_session() + |> put_session(:current_user, session) + |> delete_resp_cookie(Application.get_env(:block_scout_web, :invalid_session_key)) + |> put_status(200) + |> put_view(UserView) + |> render(:user_info, %{identity: identity |> Identity.put_session_info(session)}) + end + end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/email_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/email_controller.ex index c9b7f321e1cc..88c8e1fd72c9 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/email_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/email_controller.ex @@ -1,16 +1,18 @@ -defmodule BlockScoutWeb.Account.Api.V2.EmailController do +defmodule BlockScoutWeb.Account.API.V2.EmailController do use BlockScoutWeb, :controller - alias BlockScoutWeb.Models.UserFromAuth + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + + alias BlockScoutWeb.Account.API.V2.AuthenticateController alias Explorer.Account.Identity - alias Explorer.Repo + alias Explorer.{Helper, Repo} alias Explorer.ThirdPartyIntegrations.Auth0 require Logger @invalid_session_key Application.compile_env(:block_scout_web, :invalid_session_key) - action_fallback(BlockScoutWeb.Account.Api.V2.FallbackController) + action_fallback(BlockScoutWeb.Account.API.V2.FallbackController) plug(:fetch_cookies, signed: [@invalid_session_key]) @@ -18,8 +20,13 @@ defmodule BlockScoutWeb.Account.Api.V2.EmailController do with user <- conn.cookies[@invalid_session_key], {:auth, false} <- {:auth, is_nil(user)}, {:email_verified, false} <- {:email_verified, user[:email_verified]}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(user[:id])}, - {:interval, true} <- {:interval, check_time_interval(identity.verification_email_sent_at)} do + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(user[:id])}, + {:interval, true} <- + {:interval, + Helper.check_time_interval( + identity.verification_email_sent_at, + Application.get_env(:explorer, Explorer.Account)[:verification_email_resend_interval] + )} do domain = Application.get_env(:ueberauth, Ueberauth.Strategy.Auth0.OAuth)[:domain] api_key = Auth0.get_m2m_jwt() headers = [{"Authorization", "Bearer #{api_key}"}, {"Content-Type", "application/json"}] @@ -49,20 +56,49 @@ defmodule BlockScoutWeb.Account.Api.V2.EmailController do end end - def check_time_interval(nil), do: true + @doc """ + Links an email address to the current user's account using OTP verification. + + This function attempts to link a provided email address to the currently + authenticated user's account. It verifies the provided one-time password (OTP) + and uses the Auth0 service to associate the email with the user's account. + + The function performs the following steps: + 1. Retrieves the current user's information from the session. + 2. Attempts to link the email to the user's account using the Auth0 service. + 3. If successful, updates the session with the new authentication data. + + ## Parameters + - `conn`: The `Plug.Conn` struct representing the current connection. + - `params`: A map containing: + - `"email"`: The email address to be linked. + - `"otp"`: The one-time password for verification. - def check_time_interval(sent_at) do - interval = Application.get_env(:explorer, Explorer.Account)[:resend_interval] - now = DateTime.utc_now() + ## Returns + - `:error`: If there's an unexpected error during the process. + - `{:error, any()}`: If there's a specific error during email linking or + session update. The error details are included. + - `Conn.t()`: A modified connection struct with updated session information + if the email is successfully linked. - if sent_at - |> DateTime.add(interval, :millisecond) - |> DateTime.compare(now) != :gt do - true - else - sent_at - |> DateTime.add(interval, :millisecond) - |> DateTime.diff(now, :second) + ## Notes + - Errors are handled later in `BlockScoutWeb.Account.API.V2.FallbackController`. + - This function requires the user to be already authenticated (current user in session). + - The function will fail if the email is already associated with another account. + - The OTP must be valid and match the one sent to the provided email. + - If successful, the function updates the user's Auth0 profile and local session. + - The session update is handled by the `AuthenticateController.put_auth_to_session/2` + function, which perform additional operations such as setting cookies or + rendering user information. + """ + @spec link_email(Plug.Conn.t(), map()) :: + :error + | {:error, any()} + | Plug.Conn.t() + def link_email(conn, %{"email" => email, "otp" => otp}) do + with {:auth, %{} = user} <- {:auth, current_user(conn)}, + {:ok, auth} <- Auth0.link_email(user, email, otp) do + AuthenticateController.put_auth_to_session(conn, auth) end end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/fallback_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/fallback_controller.ex index a4821b23e41f..bf4bd39f9d6d 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/fallback_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/fallback_controller.ex @@ -1,7 +1,7 @@ -defmodule BlockScoutWeb.Account.Api.V2.FallbackController do +defmodule BlockScoutWeb.Account.API.V2.FallbackController do use Phoenix.Controller - alias BlockScoutWeb.Account.Api.V2.UserView + alias BlockScoutWeb.Account.API.V2.UserView alias Ecto.Changeset def call(conn, {:identity, _}) do @@ -32,6 +32,20 @@ defmodule BlockScoutWeb.Account.Api.V2.FallbackController do |> render(:changeset_errors, changeset: changeset) end + def call(conn, {:error, message}) do + conn + |> put_status(500) + |> put_view(UserView) + |> render(:message, %{message: message}) + end + + def call(conn, :error) do + conn + |> put_status(500) + |> put_view(UserView) + |> render(:message, %{message: "Unexpected error"}) + end + def call(conn, {:create_tag, {:error, message}}) do conn |> put_status(:unprocessable_entity) @@ -111,6 +125,20 @@ defmodule BlockScoutWeb.Account.Api.V2.FallbackController do |> json(%{message: "Email resend is available in #{remain} seconds.", seconds_before_next_resend: remain}) end + def call(conn, {:format, _params}) do + conn + |> put_status(:unprocessable_entity) + |> put_view(UserView) + |> render(:message, %{message: "Invalid parameter(s)"}) + end + + def call(conn, {:recaptcha, _}) do + conn + |> put_status(:forbidden) + |> put_view(UserView) + |> render(:message, %{message: "Invalid reCAPTCHA response"}) + end + defp unauthorized_error(%{email_verified: false, email: email}) do %{message: "Unverified email", email: email} end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/tags_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/tags_controller.ex index b76004ea97ee..47b967192ab0 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/tags_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/tags_controller.ex @@ -1,14 +1,14 @@ -defmodule BlockScoutWeb.Account.Api.V2.TagsController do +defmodule BlockScoutWeb.Account.API.V2.TagsController do use BlockScoutWeb, :controller import BlockScoutWeb.Account.AuthController, only: [current_user: 1] - alias BlockScoutWeb.Models.{GetAddressTags, GetTransactionTags, UserFromAuth} + alias BlockScoutWeb.Models.{GetAddressTags, GetTransactionTags} alias Explorer.Account.Identity alias Explorer.{Chain, Repo} alias Explorer.Chain.Hash.{Address, Full} - action_fallback(BlockScoutWeb.Account.Api.V2.FallbackController) + action_fallback(BlockScoutWeb.Account.API.V2.FallbackController) def tags_address(conn, %{"address_hash" => address_hash}) do personal_tags = @@ -17,7 +17,7 @@ defmodule BlockScoutWeb.Account.Api.V2.TagsController do else uid = current_user(conn).id - with {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + with {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:watchlist, %{watchlists: [watchlist | _]}} <- {:watchlist, Repo.account_repo().preload(identity, :watchlists)}, {:address_hash, {:ok, address_hash}} <- {:address_hash, Address.cast(address_hash)} do @@ -58,7 +58,7 @@ defmodule BlockScoutWeb.Account.Api.V2.TagsController do else uid = current_user(conn).id - with {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + with {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:watchlist, %{watchlists: [watchlist | _]}} <- {:watchlist, Repo.account_repo().preload(identity, :watchlists)}, false <- is_nil(transaction) do diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/user_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/user_controller.ex index 461d4afcb442..982265f42a36 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/user_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/user_controller.ex @@ -1,4 +1,5 @@ -defmodule BlockScoutWeb.Account.Api.V2.UserController do +defmodule BlockScoutWeb.Account.API.V2.UserController do + alias Explorer.ThirdPartyIntegrations.Auth0 use BlockScoutWeb, :controller import BlockScoutWeb.Account.AuthController, only: [current_user: 1] @@ -12,30 +13,38 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do import BlockScoutWeb.PagingHelper, only: [delete_parameters_from_next_page_params: 1] - alias BlockScoutWeb.Models.UserFromAuth alias Explorer.Account.Api.Key, as: ApiKey alias Explorer.Account.CustomABI alias Explorer.Account.{Identity, PublicTagsRequest, TagAddress, TagTransaction, WatchlistAddress} alias Explorer.{Chain, Market, PagingOptions, Repo} alias Plug.CSRFProtection - action_fallback(BlockScoutWeb.Account.Api.V2.FallbackController) + action_fallback(BlockScoutWeb.Account.API.V2.FallbackController) @ok_message "OK" @token_balances_amount 150 def info(conn, _params) do - with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)} do - conn - |> put_status(200) - |> render(:user_info, %{identity: identity}) + with {:auth, %{id: uid} = session} <- {:auth, current_user(conn)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)} do + case Auth0.update_session_with_address_hash(session) do + {:old, session} -> + conn + |> put_status(200) + |> render(:user_info, %{identity: identity |> Identity.put_session_info(session)}) + + {:new, session} -> + conn + |> put_session(:current_user, session) + |> put_status(200) + |> render(:user_info, %{identity: identity |> Identity.put_session_info(session)}) + end end end def watchlist(conn, params) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:watchlist, %{watchlists: [watchlist | _]}} <- {:watchlist, Repo.account_repo().preload(identity, :watchlists)} do results_plus_one = WatchlistAddress.get_watchlist_addresses_by_watchlist_id(watchlist.id, paging_options(params)) @@ -80,7 +89,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def delete_watchlist(conn, %{"id" => watchlist_address_id}) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:watchlist, %{watchlists: [watchlist | _]}} <- {:watchlist, Repo.account_repo().preload(identity, :watchlists)}, {count, _} <- WatchlistAddress.delete(watchlist_address_id, watchlist.id), @@ -137,7 +146,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do } with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:watchlist, %{watchlists: [watchlist | _]}} <- {:watchlist, Repo.account_repo().preload(identity, :watchlists)}, {:ok, watchlist_address} <- @@ -199,7 +208,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do } with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:watchlist, %{watchlists: [watchlist | _]}} <- {:watchlist, Repo.account_repo().preload(identity, :watchlists)}, {:ok, watchlist_address} <- @@ -215,7 +224,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def tags_address(conn, params) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)} do + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)} do results_plus_one = TagAddress.get_tags_address_by_identity_id(identity.id, paging_options(params)) {tags, next_page} = split_list_by_page(results_plus_one) @@ -230,7 +239,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def delete_tag_address(conn, %{"id" => tag_id}) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {count, _} <- TagAddress.delete(tag_id, identity.id), {:tag_delete, true} <- {:tag_delete, count > 0} do conn @@ -241,7 +250,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def create_tag_address(conn, %{"address_hash" => address_hash, "name" => name}) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:ok, address_tag} <- TagAddress.create(%{ name: name, @@ -256,7 +265,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def update_tag_address(conn, %{"id" => tag_id} = attrs) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:ok, address_tag} <- TagAddress.update( reject_nil_map_values(%{ @@ -274,7 +283,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def tags_transaction(conn, params) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)} do + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)} do results_plus_one = TagTransaction.get_tags_transaction_by_identity_id(identity.id, paging_options(params)) {tags, next_page} = split_list_by_page(results_plus_one) @@ -289,7 +298,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def delete_tag_transaction(conn, %{"id" => tag_id}) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {count, _} <- TagTransaction.delete(tag_id, identity.id), {:tag_delete, true} <- {:tag_delete, count > 0} do conn @@ -300,7 +309,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def create_tag_transaction(conn, %{"transaction_hash" => transaction_hash, "name" => name}) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:ok, transaction_tag} <- TagTransaction.create(%{ name: name, @@ -315,7 +324,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def update_tag_transaction(conn, %{"id" => tag_id} = attrs) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:ok, transaction_tag} <- TagTransaction.update( reject_nil_map_values(%{ @@ -333,7 +342,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def api_keys(conn, _params) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, api_keys <- ApiKey.get_api_keys_by_identity_id(identity.id) do conn |> put_status(200) @@ -343,7 +352,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def delete_api_key(conn, %{"api_key" => api_key_uuid}) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {count, _} <- ApiKey.delete(api_key_uuid, identity.id), {:api_key_delete, true} <- {:api_key_delete, count > 0} do conn @@ -354,7 +363,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def create_api_key(conn, %{"name" => api_key_name}) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:ok, api_key} <- ApiKey.create(%{name: api_key_name, identity_id: identity.id}) do conn @@ -365,7 +374,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def update_api_key(conn, %{"name" => api_key_name, "api_key" => api_key_value}) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:ok, api_key} <- ApiKey.update(%{value: api_key_value, name: api_key_name, identity_id: identity.id}) do conn @@ -376,7 +385,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def custom_abis(conn, _params) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, custom_abis <- CustomABI.get_custom_abis_by_identity_id(identity.id) do conn |> put_status(200) @@ -386,7 +395,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def delete_custom_abi(conn, %{"id" => id}) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {count, _} <- CustomABI.delete(id, identity.id), {:custom_abi_delete, true} <- {:custom_abi_delete, count > 0} do conn @@ -397,7 +406,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def create_custom_abi(conn, %{"contract_address_hash" => contract_address_hash, "name" => name, "abi" => abi}) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:ok, custom_abi} <- CustomABI.create(%{ name: name, @@ -418,7 +427,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do } = params ) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:ok, custom_abi} <- CustomABI.update( reject_nil_map_values(%{ @@ -437,7 +446,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def public_tags_requests(conn, _params) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, public_tags_requests <- PublicTagsRequest.get_public_tags_requests_by_identity_id(identity.id) do conn |> put_status(200) @@ -447,7 +456,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def delete_public_tags_request(conn, %{"id" => id, "remove_reason" => remove_reason}) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:public_tag_delete, true} <- {:public_tag_delete, PublicTagsRequest.mark_as_deleted_public_tags_request(%{ @@ -463,7 +472,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do def create_public_tags_request(conn, params) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:ok, public_tags_request} <- PublicTagsRequest.create(%{ full_name: params["full_name"], @@ -489,7 +498,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserController do } = params ) do with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, - {:identity, %Identity{} = identity} <- {:identity, UserFromAuth.find_identity(uid)}, + {:identity, %Identity{} = identity} <- {:identity, Identity.find_identity(uid)}, {:ok, public_tags_request} <- PublicTagsRequest.update( reject_nil_map_values(%{ diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/auth_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/auth_controller.ex index 65f29163179b..2c4bbc066d6c 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/account/auth_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/auth_controller.ex @@ -1,8 +1,8 @@ defmodule BlockScoutWeb.Account.AuthController do use BlockScoutWeb, :controller - alias BlockScoutWeb.Models.UserFromAuth alias Explorer.Account + alias Explorer.Account.Identity alias Explorer.Repo.ConfigHelper alias Plug.CSRFProtection @@ -34,7 +34,7 @@ defmodule BlockScoutWeb.Account.AuthController do end def callback(%{assigns: %{ueberauth_auth: auth}} = conn, params) do - case UserFromAuth.find_or_create(auth) do + case Identity.find_or_create(auth) do {:ok, %{email_verified: false} = user} -> conn |> put_session(:current_user, user) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex index 9e3710ef1364..7317039553e7 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex @@ -10,11 +10,10 @@ defmodule BlockScoutWeb.AddressTransactionController do import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] - alias BlockScoutWeb.{AccessHelper, Controller, TransactionView} + alias BlockScoutWeb.{AccessHelper, CaptchaHelper, Controller, TransactionView} alias BlockScoutWeb.API.V2.CSVExportController alias Explorer.{Chain, Market} alias Explorer.Chain.Address - alias Explorer.Chain.CSVExport.Helper, as: CSVHelper alias Explorer.Chain.CSVExport.{ AddressInternalTransactionCsvExporter, @@ -168,46 +167,6 @@ defmodule BlockScoutWeb.AddressTransactionController do end end - defp items_csv( - conn, - %{ - "address_id" => address_hash_string, - "from_period" => from_period, - "to_period" => to_period, - "recaptcha_response" => recaptcha_response - } = params, - csv_export_module - ) - when is_binary(address_hash_string) do - with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), - {:address_exists, true} <- {:address_exists, Address.address_exists?(address_hash)}, - {:recaptcha, true} <- {:recaptcha, CSVHelper.captcha_helper().recaptcha_passed?(recaptcha_response)} do - filter_type = Map.get(params, "filter_type") - filter_value = Map.get(params, "filter_value") - - address_hash - |> csv_export_module.export(from_period, to_period, filter_type, filter_value) - |> Enum.reduce_while(CSVExportController.put_resp_params(conn), fn chunk, conn -> - case Conn.chunk(conn, chunk) do - {:ok, conn} -> - {:cont, conn} - - {:error, :closed} -> - {:halt, conn} - end - end) - else - :error -> - unprocessable_entity(conn) - - {:address_exists, false} -> - not_found(conn) - - {:recaptcha, false} -> - not_found(conn) - end - end - defp items_csv( conn, %{ @@ -220,7 +179,7 @@ defmodule BlockScoutWeb.AddressTransactionController do when is_binary(address_hash_string) do with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), {:address_exists, true} <- {:address_exists, Address.address_exists?(address_hash)}, - true <- Application.get_env(:block_scout_web, :recaptcha)[:is_disabled] do + {:recaptcha, true} <- {:recaptcha, CaptchaHelper.recaptcha_passed?(params)} do filter_type = Map.get(params, "filter_type") filter_value = Map.get(params, "filter_value") @@ -242,7 +201,7 @@ defmodule BlockScoutWeb.AddressTransactionController do {:address_exists, false} -> not_found(conn) - false -> + {:recaptcha, false} -> not_found(conn) end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex index 14223ccabb2f..922788dde650 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex @@ -4,6 +4,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do import BlockScoutWeb.Chain, only: [split_list_by_page: 1, next_page_params: 4] import Explorer.PagingOptions, only: [default_paging_options: 0] + alias BlockScoutWeb.CaptchaHelper alias BlockScoutWeb.API.V2.{AdvancedFilterView, CSVExportController} alias Explorer.{Chain, PagingOptions} alias Explorer.Chain.{AdvancedFilter, ContractMethod, Data, Token, Transaction} @@ -81,10 +82,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do """ @spec list_csv(Plug.Conn.t(), map()) :: Plug.Conn.t() def list_csv(conn, params) do - with {:recaptcha, true} <- - {:recaptcha, - Application.get_env(:block_scout_web, :recaptcha)[:is_disabled] || - CSVHelper.captcha_helper().recaptcha_passed?(params["recaptcha_response"])} do + with {:recaptcha, true} <- {:recaptcha, CaptchaHelper.recaptcha_passed?(params)} do full_options = params |> extract_filters() diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/api_key_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/api_key_controller.ex index 4135a6bba9fb..2ca19379af2d 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/api_key_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/api_key_controller.ex @@ -1,7 +1,7 @@ defmodule BlockScoutWeb.API.V2.APIKeyController do use BlockScoutWeb, :controller - alias BlockScoutWeb.AccessHelper + alias BlockScoutWeb.{AccessHelper, CaptchaHelper} @api_v2_temp_token_key Application.compile_env(:block_scout_web, :api_v2_temp_token_key) @@ -14,12 +14,9 @@ defmodule BlockScoutWeb.API.V2.APIKeyController do """ @spec get_key(Plug.Conn.t(), nil | map) :: {:recaptcha, any} | Plug.Conn.t() def get_key(conn, params) do - helper = Application.get_env(:block_scout_web, :captcha_helper) ttl = Application.get_env(:block_scout_web, :api_rate_limit)[:api_v2_token_ttl_seconds] - with recaptcha_response <- params["recaptcha_response"], - {:recaptcha, false} <- {:recaptcha, is_nil(recaptcha_response)}, - {:recaptcha, true} <- {:recaptcha, helper.recaptcha_passed?(recaptcha_response)} do + with {:recaptcha, true} <- {:recaptcha, CaptchaHelper.recaptcha_passed?(params)} do conn |> put_resp_cookie(@api_v2_temp_token_key, %{ip: AccessHelper.conn_to_ip_string(conn)}, max_age: ttl, diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/csv_export_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/csv_export_controller.ex index 30e1fa79c215..478812364c80 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/csv_export_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/csv_export_controller.ex @@ -1,7 +1,7 @@ defmodule BlockScoutWeb.API.V2.CSVExportController do use BlockScoutWeb, :controller - alias BlockScoutWeb.AccessHelper + alias BlockScoutWeb.{AccessHelper, CaptchaHelper} alias Explorer.Chain alias Explorer.Chain.Address.CurrentTokenBalance alias Explorer.Chain.CSVExport.Helper, as: CSVHelper @@ -19,10 +19,7 @@ defmodule BlockScoutWeb.API.V2.CSVExportController do def export_token_holders(conn, %{"address_hash_param" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), - {:recaptcha, true} <- - {:recaptcha, - Application.get_env(:block_scout_web, :recaptcha)[:is_disabled] || - CSVHelper.captcha_helper().recaptcha_passed?(params["recaptcha_response"])}, + {:recaptcha, true} <- {:recaptcha, CaptchaHelper.recaptcha_passed?(params)}, {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)} do token_holders = Chain.fetch_token_holders_from_token_hash_for_csv(address_hash, options()) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex index df67fa9627c1..0879bd5adf44 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex @@ -3,7 +3,7 @@ defmodule BlockScoutWeb.API.V2.FallbackController do require Logger - alias BlockScoutWeb.Account.Api.V2.UserView + alias BlockScoutWeb.Account.API.V2.UserView alias BlockScoutWeb.API.V2.ApiView alias Ecto.Changeset diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex index 7d19376428e2..10e176e60d3a 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex @@ -8,7 +8,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do import Explorer.SmartContract.Solidity.Verifier, only: [parse_boolean: 1] - alias BlockScoutWeb.{AccessHelper, AddressView} + alias BlockScoutWeb.{AccessHelper, AddressView, CaptchaHelper} alias Ecto.Association.NotLoaded alias Explorer.Chain alias Explorer.Chain.{Address, SmartContract} @@ -275,11 +275,9 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do | {:restricted_access, true} | Plug.Conn.t() def audit_report_submission(conn, %{"address_hash" => address_hash_string} = params) do - captcha_helper = Application.get_env(:block_scout_web, :captcha_helper) - with {:disabled, true} <- {:disabled, Application.get_env(:explorer, :air_table_audit_reports)[:enabled]}, {:ok, address_hash, _smart_contract} <- validate_smart_contract(params, address_hash_string), - {:recaptcha, _} <- {:recaptcha, captcha_helper.recaptcha_passed?(params["recaptcha_response"])}, + {:recaptcha, _} <- {:recaptcha, CaptchaHelper.recaptcha_passed?(params)}, audit_report_params <- %{ address_hash: address_hash, submitter_name: params["submitter_name"], diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex index 934b4dbaf7bb..fe9bc6777e76 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex @@ -2,11 +2,10 @@ defmodule BlockScoutWeb.API.V2.TokenController do alias Explorer.PagingOptions use BlockScoutWeb, :controller - alias BlockScoutWeb.AccessHelper + alias BlockScoutWeb.{AccessHelper, CaptchaHelper} alias BlockScoutWeb.API.V2.{AddressView, TransactionView} alias Explorer.{Chain, Helper} alias Explorer.Chain.{Address, BridgedToken, Token, Token.Instance} - alias Explorer.Chain.CSVExport.Helper, as: CSVHelper alias Indexer.Fetcher.OnDemand.TokenInstanceMetadataRefetch, as: TokenInstanceMetadataRefetchOnDemand alias Indexer.Fetcher.OnDemand.TokenTotalSupply, as: TokenTotalSupplyOnDemand @@ -339,11 +338,10 @@ defmodule BlockScoutWeb.API.V2.TokenController do ) do address_hash_string = params["address_hash_param"] token_id_string = params["token_id"] - recaptcha_response = params["recaptcha_response"] with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), - {:recaptcha, true} <- {:recaptcha, CSVHelper.captcha_helper().recaptcha_passed?(recaptcha_response)}, + {:recaptcha, true} <- {:recaptcha, CaptchaHelper.recaptcha_passed?(params)}, {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)}, {:not_found, false} <- {:not_found, Chain.erc_20_token?(token)}, {:format, {token_id, ""}} <- {:format, Integer.parse(token_id_string)}, diff --git a/apps/block_scout_web/lib/block_scout_web/models/get_transaction_tags.ex b/apps/block_scout_web/lib/block_scout_web/models/get_transaction_tags.ex index f7ca350515a2..1aeed2d04391 100644 --- a/apps/block_scout_web/lib/block_scout_web/models/get_transaction_tags.ex +++ b/apps/block_scout_web/lib/block_scout_web/models/get_transaction_tags.ex @@ -7,7 +7,6 @@ defmodule BlockScoutWeb.Models.GetTransactionTags do alias Explorer.Account.TagTransaction alias Explorer.Chain.Transaction - alias Explorer.Repo def get_transaction_with_addresses_tags( %Transaction{} = transaction, @@ -27,7 +26,10 @@ defmodule BlockScoutWeb.Models.GetTransactionTags do } def get_transaction_tags(transaction_hash, %{id: identity_id}) do - Repo.account_repo().get_by(TagTransaction, transaction_hash_hash: transaction_hash, identity_id: identity_id) + case TagTransaction.get_tag_transaction_by_transaction_hash_and_identity_id(transaction_hash, identity_id) do + [tag | _] -> tag + _ -> nil + end end def get_transaction_tags(_, _), do: nil diff --git a/apps/block_scout_web/lib/block_scout_web/models/user_from_auth.ex b/apps/block_scout_web/lib/block_scout_web/models/user_from_auth.ex deleted file mode 100644 index 2d09503128f3..000000000000 --- a/apps/block_scout_web/lib/block_scout_web/models/user_from_auth.ex +++ /dev/null @@ -1,143 +0,0 @@ -defmodule BlockScoutWeb.Models.UserFromAuth do - @moduledoc """ - Retrieve the user information from an auth request - """ - require Logger - require Poison - - alias Explorer.Account.Identity - alias Explorer.Repo - alias Ueberauth.Auth - - import Ecto.Query, only: [from: 2] - - def find_or_create(%Auth{} = auth) do - case find_identity(auth) do - nil -> - case create_identity(auth) do - %Identity{} = identity -> - {:ok, session_info(auth, identity)} - - {:error, changeset} -> - {:error, changeset} - end - - %{} = identity -> - update_identity(identity, update_identity_map(auth)) - {:ok, session_info(auth, identity)} - end - end - - defp create_identity(auth) do - with {:ok, %Identity{} = identity} <- Repo.account_repo().insert(new_identity(auth)), - {:ok, _watchlist} <- add_watchlist(identity) do - identity - end - end - - defp update_identity(identity, attrs) do - identity - |> Identity.changeset(attrs) - |> Repo.account_repo().update() - end - - defp new_identity(auth) do - %Identity{ - uid: auth.uid, - uid_hash: auth.uid, - email: email_from_auth(auth), - name: name_from_auth(auth), - nickname: nickname_from_auth(auth), - avatar: avatar_from_auth(auth) - } - end - - defp add_watchlist(identity) do - watchlist = Ecto.build_assoc(identity, :watchlists, %{}) - - with {:ok, _} <- Repo.account_repo().insert(watchlist), - do: {:ok, identity} - end - - def find_identity(auth_or_uid) do - Repo.account_repo().one(query_identity(auth_or_uid)) - end - - def query_identity(%Auth{} = auth) do - from(i in Identity, where: i.uid_hash == ^auth.uid) - end - - def query_identity(id) do - from(i in Identity, where: i.id == ^id) - end - - defp session_info( - %Auth{extra: %Ueberauth.Auth.Extra{raw_info: %{user: %{"email_verified" => false}}}} = auth, - identity - ) do - %{ - id: identity.id, - uid: auth.uid, - email: email_from_auth(auth), - nickname: nickname_from_auth(auth), - avatar: avatar_from_auth(auth), - email_verified: false - } - end - - defp session_info(auth, identity) do - %{watchlists: [watchlist | _]} = Repo.account_repo().preload(identity, :watchlists) - - %{ - id: identity.id, - uid: auth.uid, - email: email_from_auth(auth), - name: name_from_auth(auth), - nickname: nickname_from_auth(auth), - avatar: avatar_from_auth(auth), - watchlist_id: watchlist.id, - email_verified: true - } - end - - defp update_identity_map(auth) do - %{ - email: email_from_auth(auth), - name: name_from_auth(auth), - nickname: nickname_from_auth(auth), - avatar: avatar_from_auth(auth) - } - end - - # github does it this way - defp avatar_from_auth(%{info: %{urls: %{avatar_url: image}}}), do: image - - # facebook does it this way - defp avatar_from_auth(%{info: %{image: image}}), do: image - - # default case if nothing matches - defp avatar_from_auth(auth) do - Logger.warning(auth.provider <> " needs to find an avatar URL!") - Logger.debug(Poison.encode!(auth)) - nil - end - - defp email_from_auth(%{info: %{email: email}}), do: email - - defp nickname_from_auth(%{info: %{nickname: nickname}}), do: nickname - - defp name_from_auth(%{info: %{name: name}}) - when name != "" and not is_nil(name), - do: name - - defp name_from_auth(%{info: info}) do - [info.first_name, info.last_name, info.nickname] - |> Enum.map(&(&1 |> to_string() |> String.trim())) - |> case do - ["", "", nick] -> nick - ["", lastname, _] -> lastname - [name, "", _] -> name - [name, lastname, _] -> name <> " " <> lastname - end - end -end diff --git a/apps/block_scout_web/lib/block_scout_web/routers/account_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/account_router.ex index 6263aa6d7965..2a915b9d6881 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/account_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/account_router.ex @@ -4,7 +4,14 @@ defmodule BlockScoutWeb.Routers.AccountRouter do """ use BlockScoutWeb, :router - alias BlockScoutWeb.Account.Api.V2.{AuthenticateController, EmailController, TagsController, UserController} + alias BlockScoutWeb.Account.API.V2.{ + AddressController, + AuthenticateController, + EmailController, + TagsController, + UserController + } + alias BlockScoutWeb.Plug.{CheckAccountAPI, CheckAccountWeb} @max_query_string_length 5_000 @@ -117,8 +124,13 @@ defmodule BlockScoutWeb.Routers.AccountRouter do get("/get_csrf", UserController, :get_csrf) + scope "/address" do + post("/link", AddressController, :link_address) + end + scope "/email" do get("/resend", EmailController, :resend_email) + post("/link", EmailController, :link_email) end scope "/user" do @@ -170,4 +182,13 @@ defmodule BlockScoutWeb.Routers.AccountRouter do get("/transaction/:transaction_hash", TagsController, :tags_transaction) end end + + scope "/v2" do + pipe_through(:api) + + post("/authenticate_via_wallet", AuthenticateController, :authenticate_via_wallet) + post("/send_otp", AuthenticateController, :send_otp) + post("/confirm_otp", AuthenticateController, :confirm_otp) + get("/siwe_message", AuthenticateController, :siwe_message) + end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/account_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/account_view.ex index 632b3109501f..3a41871dc0c2 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/account_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/account_view.ex @@ -1,4 +1,4 @@ -defmodule BlockScoutWeb.Account.Api.V2.AccountView do +defmodule BlockScoutWeb.Account.API.V2.AccountView do def render("message.json", %{message: message}) do %{ "message" => message diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/tags_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/tags_view.ex index e520fe0bddda..06bb0ce49fcb 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/tags_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/tags_view.ex @@ -1,4 +1,4 @@ -defmodule BlockScoutWeb.Account.Api.V2.TagsView do +defmodule BlockScoutWeb.Account.API.V2.TagsView do def render("address_tags.json", %{tags_map: tags_map}) do tags_map end diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex index 6e7211322dca..f9b6a6a30735 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex @@ -1,5 +1,5 @@ -defmodule BlockScoutWeb.Account.Api.V2.UserView do - alias BlockScoutWeb.Account.Api.V2.AccountView +defmodule BlockScoutWeb.Account.API.V2.UserView do + alias BlockScoutWeb.Account.API.V2.AccountView alias BlockScoutWeb.API.V2.Helper alias Ecto.Changeset alias Explorer.Chain @@ -9,7 +9,13 @@ defmodule BlockScoutWeb.Account.Api.V2.UserView do end def render("user_info.json", %{identity: identity}) do - %{"name" => identity.name, "email" => identity.email, "avatar" => identity.avatar, "nickname" => identity.nickname} + %{ + "name" => identity.name, + "email" => identity.email, + "avatar" => identity.avatar, + "nickname" => identity.nickname, + "address_hash" => identity.address_hash + } end def render("watchlist_addresses.json", %{ diff --git a/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs index ae3e048e178c..68f7047ddfa9 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs @@ -2,6 +2,7 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do use BlockScoutWeb.ConnCase alias Explorer.Account.{ + Identity, TagAddress, TagTransaction, WatchlistAddress @@ -9,12 +10,11 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do alias Explorer.Chain.Address alias Explorer.Repo - alias BlockScoutWeb.Models.UserFromAuth setup %{conn: conn} do auth = build(:auth) - {:ok, user} = UserFromAuth.find_or_create(auth) + {:ok, user} = Identity.find_or_create(auth) {:ok, user: user, conn: Plug.Test.init_test_session(conn, current_user: user)} end @@ -30,7 +30,8 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do "nickname" => user.nickname, "name" => user.name, "email" => user.email, - "avatar" => user.avatar + "avatar" => user.avatar, + "address_hash" => user.address_hash } end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/account/custom_abi_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/account/custom_abi_controller_test.exs index d2b56b163aad..cea4aa418f73 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/account/custom_abi_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/account/custom_abi_controller_test.exs @@ -1,15 +1,15 @@ defmodule BlockScoutWeb.Account.CustomABIControllerTest do use BlockScoutWeb.ConnCase + alias Explorer.Account.Identity alias Explorer.TestHelper - alias BlockScoutWeb.Models.UserFromAuth @custom_abi "[{\"type\":\"function\",\"outputs\":[{\"type\":\"string\",\"name\":\"\"}],\"name\":\"name\",\"inputs\":[],\"constant\":true}]" setup %{conn: conn} do auth = build(:auth) - {:ok, user} = UserFromAuth.find_or_create(auth) + {:ok, user} = Identity.find_or_create(auth) {:ok, conn: Plug.Test.init_test_session(conn, current_user: user)} end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs index 04dd677be94b..873597f0b0c6 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs @@ -161,6 +161,10 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do end describe "GET token-transfers-csv/2" do + setup do + csv_setup() + end + test "do not export token transfers to csv without recaptcha recaptcha_response provided", %{conn: conn} do address = insert(:address) @@ -185,9 +189,16 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do assert conn.status == 404 end - test "do not export token transfers to csv without recaptcha passed", %{conn: conn} do - BlockScoutWeb.TestCaptchaHelper - |> expect(:recaptcha_passed?, fn _captcha_response -> false end) + test "do not export token transfers to csv without recaptcha passed", %{ + conn: conn, + v2_secret_key: recaptcha_secret_key + } do + expected_body = "secret=#{recaptcha_secret_key}&response=123" + + Explorer.Mox.HTTPoison + |> expect(:post, fn _url, ^expected_body, _headers, _options -> + {:ok, %HTTPoison.Response{status_code: 200, body: Jason.encode!(%{"success" => false})}} + end) address = insert(:address) @@ -242,9 +253,21 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do Application.put_env(:block_scout_web, :recaptcha, init_config) end - test "exports token transfers to csv", %{conn: conn} do - BlockScoutWeb.TestCaptchaHelper - |> expect(:recaptcha_passed?, fn _captcha_response -> true end) + test "exports token transfers to csv", %{conn: conn, v2_secret_key: recaptcha_secret_key} do + expected_body = "secret=#{recaptcha_secret_key}&response=123" + + Explorer.Mox.HTTPoison + |> expect(:post, fn _url, ^expected_body, _headers, _options -> + {:ok, + %HTTPoison.Response{ + status_code: 200, + body: + Jason.encode!(%{ + "success" => true, + "hostname" => Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:host] + }) + }} + end) address = insert(:address) @@ -272,9 +295,25 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do end describe "GET transactions_csv/2" do - test "download csv file with transactions", %{conn: conn} do - BlockScoutWeb.TestCaptchaHelper - |> expect(:recaptcha_passed?, fn _captcha_response -> true end) + setup do + csv_setup() + end + + test "download csv file with transactions", %{conn: conn, v2_secret_key: recaptcha_secret_key} do + expected_body = "secret=#{recaptcha_secret_key}&response=123" + + Explorer.Mox.HTTPoison + |> expect(:post, fn _url, ^expected_body, _headers, _options -> + {:ok, + %HTTPoison.Response{ + status_code: 200, + body: + Jason.encode!(%{ + "success" => true, + "hostname" => Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:host] + }) + }} + end) address = insert(:address) @@ -302,9 +341,25 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do end describe "GET internal_transactions_csv/2" do - test "download csv file with internal transactions", %{conn: conn} do - BlockScoutWeb.TestCaptchaHelper - |> expect(:recaptcha_passed?, fn _captcha_response -> true end) + setup do + csv_setup() + end + + test "download csv file with internal transactions", %{conn: conn, v2_secret_key: recaptcha_secret_key} do + expected_body = "secret=#{recaptcha_secret_key}&response=123" + + Explorer.Mox.HTTPoison + |> expect(:post, fn _url, ^expected_body, _headers, _options -> + {:ok, + %HTTPoison.Response{ + status_code: 200, + body: + Jason.encode!(%{ + "success" => true, + "hostname" => Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:host] + }) + }} + end) address = insert(:address) @@ -369,9 +424,25 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do end describe "GET logs_csv/2" do - test "download csv file with logs", %{conn: conn} do - BlockScoutWeb.TestCaptchaHelper - |> expect(:recaptcha_passed?, fn _captcha_response -> true end) + setup do + csv_setup() + end + + test "download csv file with logs", %{conn: conn, v2_secret_key: recaptcha_secret_key} do + expected_body = "secret=#{recaptcha_secret_key}&response=123" + + Explorer.Mox.HTTPoison + |> expect(:post, fn _url, ^expected_body, _headers, _options -> + {:ok, + %HTTPoison.Response{ + status_code: 200, + body: + Jason.encode!(%{ + "success" => true, + "hostname" => Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:host] + }) + }} + end) address = insert(:address) @@ -428,9 +499,21 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do assert conn.resp_body |> String.split("\n") |> Enum.count() == 5 end - test "handles null filter", %{conn: conn} do - BlockScoutWeb.TestCaptchaHelper - |> expect(:recaptcha_passed?, fn _captcha_response -> true end) + test "handles null filter", %{conn: conn, v2_secret_key: recaptcha_secret_key} do + expected_body = "secret=#{recaptcha_secret_key}&response=123" + + Explorer.Mox.HTTPoison + |> expect(:post, fn _url, ^expected_body, _headers, _options -> + {:ok, + %HTTPoison.Response{ + status_code: 200, + body: + Jason.encode!(%{ + "success" => true, + "hostname" => Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:host] + }) + }} + end) address = insert(:address) @@ -463,4 +546,27 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do assert conn.resp_body |> String.split("\n") |> Enum.count() == 3 end end + + defp csv_setup() do + old_recaptcha_env = Application.get_env(:block_scout_web, :recaptcha) + old_http_adapter = Application.get_env(:block_scout_web, :http_adapter) + + v2_secret_key = "v2_secret_key" + v3_secret_key = "v3_secret_key" + + Application.put_env(:block_scout_web, :recaptcha, + v2_secret_key: v2_secret_key, + v3_secret_key: v3_secret_key, + is_disabled: false + ) + + Application.put_env(:block_scout_web, :http_adapter, Explorer.Mox.HTTPoison) + + on_exit(fn -> + Application.put_env(:block_scout_web, :recaptcha, old_recaptcha_env) + Application.put_env(:block_scout_web, :http_adapter, old_http_adapter) + end) + + {:ok, %{v2_secret_key: v2_secret_key, v3_secret_key: v3_secret_key}} + end end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs index 94aef36b245d..ba804e55c6fe 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs @@ -4,7 +4,6 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do use BlockScoutWeb.ChannelCase alias ABI.{TypeDecoder, TypeEncoder} - alias BlockScoutWeb.Models.UserFromAuth alias Explorer.{Chain, Repo, TestHelper} alias Explorer.Chain.Address.Counters alias Explorer.Chain.Events.Subscriber @@ -23,7 +22,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do Withdrawal } - alias Explorer.Account.WatchlistAddress + alias Explorer.Account.{Identity, WatchlistAddress} alias Explorer.Chain.Address.CurrentTokenBalance alias Indexer.Fetcher.OnDemand.ContractCode, as: ContractCodeOnDemand alias Plug.Conn @@ -275,7 +274,7 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do test "get watchlist id", %{conn: conn} do auth = build(:auth) address = insert(:address) - {:ok, user} = UserFromAuth.find_or_create(auth) + {:ok, user} = Identity.find_or_create(auth) conn = Plug.Test.init_test_session(conn, current_user: user) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/main_page_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/main_page_controller_test.exs index 0b56c7411a80..3445aa5699ae 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/main_page_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/main_page_controller_test.exs @@ -1,8 +1,7 @@ defmodule BlockScoutWeb.API.V2.MainPageControllerTest do use BlockScoutWeb.ConnCase - alias BlockScoutWeb.Models.UserFromAuth - alias Explorer.Account.WatchlistAddress + alias Explorer.Account.{Identity, WatchlistAddress} alias Explorer.Chain.{Address, Block, Transaction} alias Explorer.Repo @@ -65,7 +64,7 @@ defmodule BlockScoutWeb.API.V2.MainPageControllerTest do insert_list(10, :transaction) |> with_block() auth = build(:auth) - {:ok, user} = UserFromAuth.find_or_create(auth) + {:ok, user} = Identity.find_or_create(auth) conn = Plug.Test.init_test_session(conn, current_user: user) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs index 6f94f4da8518..5d4c0222de0c 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs @@ -5,8 +5,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do import Mox alias BlockScoutWeb.AddressContractView - alias BlockScoutWeb.Models.UserFromAuth alias Explorer.Chain.{Address, SmartContract} + alias Explorer.Account.Identity alias Explorer.TestHelper alias Plug.Conn @@ -2763,7 +2763,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do setup %{conn: conn} do auth = build(:auth) - {:ok, user} = UserFromAuth.find_or_create(auth) + {:ok, user} = Identity.find_or_create(auth) {:ok, conn: Plug.Test.init_test_session(conn, current_user: user)} end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs index b40da67f8c2d..1e391497e9cb 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs @@ -1455,6 +1455,20 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do setup :verify_on_exit! setup %{json_rpc_named_arguments: json_rpc_named_arguments} do + old_recaptcha_env = Application.get_env(:block_scout_web, :recaptcha) + old_http_adapter = Application.get_env(:block_scout_web, :http_adapter) + + v2_secret_key = "v2_secret_key" + v3_secret_key = "v3_secret_key" + + Application.put_env(:block_scout_web, :recaptcha, + v2_secret_key: v2_secret_key, + v3_secret_key: v3_secret_key, + is_disabled: false + ) + + Application.put_env(:block_scout_web, :http_adapter, Explorer.Mox.HTTPoison) + mocked_json_rpc_named_arguments = Keyword.put(json_rpc_named_arguments, :transport, EthereumJSONRPC.Mox) start_supervised!({Task.Supervisor, name: Indexer.TaskSupervisor}) @@ -1468,12 +1482,29 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do Subscriber.to(:fetched_token_instance_metadata, :on_demand) - :ok + on_exit(fn -> + Application.put_env(:block_scout_web, :recaptcha, old_recaptcha_env) + Application.put_env(:block_scout_web, :http_adapter, old_http_adapter) + end) + + {:ok, %{v2_secret_key: v2_secret_key, v3_secret_key: v3_secret_key}} end - test "token instance metadata on-demand re-fetcher is called", %{conn: conn} do - BlockScoutWeb.TestCaptchaHelper - |> expect(:recaptcha_passed?, fn _captcha_response -> true end) + test "token instance metadata on-demand re-fetcher is called", %{conn: conn, v2_secret_key: v2_secret_key} do + expected_body = "secret=#{v2_secret_key}&response=123" + + Explorer.Mox.HTTPoison + |> expect(:post, fn _url, ^expected_body, _headers, _options -> + {:ok, + %HTTPoison.Response{ + status_code: 200, + body: + Jason.encode!(%{ + "success" => true, + "hostname" => Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:host] + }) + }} + end) token = insert(:token, type: "ERC-721") token_id = 1 @@ -1534,9 +1565,24 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do Application.put_env(:explorer, :http_adapter, HTTPoison) end - test "don't fetch token instance metadata for non-existent token instance", %{conn: conn} do - BlockScoutWeb.TestCaptchaHelper - |> expect(:recaptcha_passed?, fn _captcha_response -> true end) + test "don't fetch token instance metadata for non-existent token instance", %{ + conn: conn, + v2_secret_key: v2_secret_key + } do + expected_body = "secret=#{v2_secret_key}&response=123" + + Explorer.Mox.HTTPoison + |> expect(:post, fn _url, ^expected_body, _headers, _options -> + {:ok, + %HTTPoison.Response{ + status_code: 200, + body: + Jason.encode!(%{ + "success" => true, + "hostname" => Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:host] + }) + }} + end) token = insert(:token, type: "ERC-721") token_id = 0 @@ -1551,9 +1597,24 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do assert %{"message" => "Not found"} = json_response(request, 404) end - test "fetch token instance metadata for existing token instance with no metadata", %{conn: conn} do - BlockScoutWeb.TestCaptchaHelper - |> expect(:recaptcha_passed?, fn _captcha_response -> true end) + test "fetch token instance metadata for existing token instance with no metadata", %{ + conn: conn, + v2_secret_key: v2_secret_key + } do + expected_body = "secret=#{v2_secret_key}&response=123" + + Explorer.Mox.HTTPoison + |> expect(:post, fn _url, ^expected_body, _headers, _options -> + {:ok, + %HTTPoison.Response{ + status_code: 200, + body: + Jason.encode!(%{ + "success" => true, + "hostname" => Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:host] + }) + }} + end) token = insert(:token, type: "ERC-721") token_id = 1 diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs index d83dc7a40a5b..1ecfe52df92f 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs @@ -4,8 +4,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do import Explorer.Chain, only: [hash_to_lower_case_string: 1] import Mox - alias BlockScoutWeb.Models.UserFromAuth - alias Explorer.Account.WatchlistAddress + alias Explorer.Account.{Identity, WatchlistAddress} alias Explorer.Chain.{Address, InternalTransaction, Log, Token, TokenTransfer, Transaction, Wei} alias Explorer.Repo @@ -109,7 +108,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do auth = build(:auth) insert(:address) - {:ok, user} = UserFromAuth.find_or_create(auth) + {:ok, user} = Identity.find_or_create(auth) conn = Plug.Test.init_test_session(conn, current_user: user) @@ -122,7 +121,7 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do test "watchlist transactions can paginate", %{conn: conn} do auth = build(:auth) - {:ok, user} = UserFromAuth.find_or_create(auth) + {:ok, user} = Identity.find_or_create(auth) conn = Plug.Test.init_test_session(conn, current_user: user) diff --git a/apps/block_scout_web/test/test_helper.exs b/apps/block_scout_web/test/test_helper.exs index 1a77c4a155bb..0758a27e2be1 100644 --- a/apps/block_scout_web/test/test_helper.exs +++ b/apps/block_scout_web/test/test_helper.exs @@ -42,5 +42,4 @@ Absinthe.Test.prime(BlockScoutWeb.GraphQL.Schema) Mox.defmock(EthereumJSONRPC.Mox, for: EthereumJSONRPC.Transport) -Mox.defmock(BlockScoutWeb.TestCaptchaHelper, for: BlockScoutWeb.CaptchaHelper) Mox.defmock(Explorer.Mox.HTTPoison, for: HTTPoison.Base) diff --git a/apps/explorer/lib/explorer/account.ex b/apps/explorer/lib/explorer/account.ex index 5f2155b1f4f5..ecbfdda5f42f 100644 --- a/apps/explorer/lib/explorer/account.ex +++ b/apps/explorer/lib/explorer/account.ex @@ -3,7 +3,86 @@ defmodule Explorer.Account do Context for Account module. """ + alias Ecto.Multi + alias Explorer.Account.Api.Key + + alias Explorer.Account.{ + CustomABI, + Identity, + PublicTagsRequest, + TagAddress, + TagTransaction, + Watchlist, + WatchlistAddress, + WatchlistNotification + } + + alias Explorer.Repo + def enabled? do Application.get_env(:explorer, __MODULE__)[:enabled] end + + @doc """ + Merges multiple Identity records into a primary Identity. + + This function consolidates data from multiple Identity records into a single + primary Identity. It performs a series of merge operations for various + associated entities (API keys, custom ABIs, public tags requests, address tags, + transaction tags, watchlists, watchlist addresses, and watchlist notifications) + and then deletes the merged Identity records. + + ## Parameters + - `identities`: A list of Identity structs. The first element is considered + the primary Identity, and the rest are merged into it. + + ## Returns + - A tuple containing two elements: + 1. The result of the transaction: + - `{:ok, result}` if the merge was successful + - `{:error, failed_operation, failed_value, changes_so_far}` if an error occurred + 2. The primary Identity struct or nil if the input list was empty + + ## Process + 1. Extracts IDs from the input Identity structs + 2. Performs the following merge operations in a single database transaction: + - Merges API keys + - Merges custom ABIs + - Merges public tags requests + - Merges address tags + - Merges transaction tags + - Acquires and merges watchlists + - Merges watchlist addresses + - Merges watchlist notifications + 3. Deletes the merged Identity records + 4. Commits the transaction + + ## Notes + - If an empty list is provided, the function returns `{{:ok, 0}, nil}`. + - This function uses Ecto.Multi for transactional integrity. + - All merge operations update the associated records to point to the primary Identity. + - Some merge operations (like custom ABIs and tags) set `user_created: false` to satisfy database constraints. + - The function relies on the account repository specified in the application configuration. + """ + @spec merge([Identity.t()]) :: {{:ok, any()} | {:error, any()} | Multi.failure(), Identity.t() | nil} + def merge([primary_identity | identities_to_merge]) do + primary_identity_id = primary_identity.id + identities_to_merge_ids = Enum.map(identities_to_merge, & &1.id) + + {Multi.new() + |> Key.merge(primary_identity_id, identities_to_merge_ids) + |> CustomABI.merge(primary_identity_id, identities_to_merge_ids) + |> PublicTagsRequest.merge(primary_identity_id, identities_to_merge_ids) + |> TagAddress.merge(primary_identity_id, identities_to_merge_ids) + |> TagTransaction.merge(primary_identity_id, identities_to_merge_ids) + |> Watchlist.acquire_for_merge(primary_identity_id, identities_to_merge_ids) + |> WatchlistAddress.merge() + |> WatchlistNotification.merge() + |> Identity.delete(identities_to_merge_ids) + |> Repo.account_repo().transaction(), primary_identity} + end + + def merge([]) do + {{:ok, 0}, nil} + end end diff --git a/apps/explorer/lib/explorer/account/api/key.ex b/apps/explorer/lib/explorer/account/api/key.ex index 5330ab98a461..7f286ee823a8 100644 --- a/apps/explorer/lib/explorer/account/api/key.ex +++ b/apps/explorer/lib/explorer/account/api/key.ex @@ -4,6 +4,7 @@ defmodule Explorer.Account.Api.Key do """ use Explorer.Schema + alias Ecto.Multi alias Explorer.Account.Identity alias Ecto.{Changeset, UUID} alias Explorer.Repo @@ -126,4 +127,30 @@ defmodule Explorer.Account.Api.Key do def api_key_with_plan_by_value(_), do: nil def get_max_api_keys_count, do: @max_key_per_account + + @doc """ + Merges API keys from multiple identities into a primary identity. + + This function updates the `identity_id` of all API keys belonging to the + identities specified in `ids_to_merge` to the `primary_id`. It's designed to + be used as part of an Ecto.Multi transaction. + + ## Parameters + - `multi`: An Ecto.Multi struct to which this operation will be added. + - `primary_id`: The ID of the primary identity that will own the merged keys. + - `ids_to_merge`: A list of identity IDs whose API keys will be merged. + + ## Returns + - An updated Ecto.Multi struct with the merge operation added. + """ + @spec merge(Multi.t(), integer(), [integer()]) :: Multi.t() + def merge(multi, primary_id, ids_to_merge) do + Multi.run(multi, :merge_keys, fn repo, _ -> + {:ok, + repo.update_all( + from(key in __MODULE__, where: key.identity_id in ^ids_to_merge), + set: [identity_id: primary_id] + )} + end) + end end diff --git a/apps/explorer/lib/explorer/account/custom_abi.ex b/apps/explorer/lib/explorer/account/custom_abi.ex index 6c618a1cc1ec..56f8b4bfad93 100644 --- a/apps/explorer/lib/explorer/account/custom_abi.ex +++ b/apps/explorer/lib/explorer/account/custom_abi.ex @@ -5,9 +5,10 @@ defmodule Explorer.Account.CustomABI do use Explorer.Schema alias ABI.FunctionSelector - alias Ecto.Changeset + alias Ecto.{Changeset, Multi} alias Explorer.Account.Identity alias Explorer.{Chain, Repo} + alias Explorer.Chain.Hash import Explorer.Chain, only: [hash_to_lower_case_string: 1] import Ecto.Changeset @@ -21,6 +22,7 @@ defmodule Explorer.Account.CustomABI do field(:address_hash_hash, Cloak.Ecto.SHA256) :: binary() | nil field(:address_hash, Explorer.Encrypted.AddressHash, null: false) field(:name, Explorer.Encrypted.Binary, null: false) + field(:user_created, :boolean, null: false, default: true) belongs_to(:identity, Identity, null: false) @@ -177,12 +179,37 @@ defmodule Explorer.Account.CustomABI do def custom_abi_by_identity_id_and_address_hash_query(_, _), do: nil + @doc """ + Retrieves a custom ABI for a given address hash and identity ID. + + This function searches for a custom ABI associated with the provided address + hash and identity ID. It returns the first matching ABI if found, or nil if no + matching ABI exists. + + ## Parameters + - `address_hash`: The address hash to search for. Can be a `Hash.Address.t()`, + `String.t()`, or `nil`. + - `identity_id`: The identity ID associated with the custom ABI. Can be an + `integer()` or `nil`. + + ## Returns + - A `Explorer.Account.CustomABI` struct if a matching ABI is found. + - `nil` if no matching ABI is found or if either input is nil. + """ + @spec get_custom_abi_by_identity_id_and_address_hash(Hash.Address.t() | String.t() | nil, integer() | nil) :: + __MODULE__.t() | nil def get_custom_abi_by_identity_id_and_address_hash(address_hash, identity_id) when not is_nil(identity_id) and not is_nil(address_hash) do - address_hash - |> hash_to_lower_case_string() - |> custom_abi_by_identity_id_and_address_hash_query(identity_id) - |> Repo.account_repo().one() + abis = + address_hash + |> hash_to_lower_case_string() + |> custom_abi_by_identity_id_and_address_hash_query(identity_id) + |> Repo.account_repo().all() + + case abis do + [abi | _] -> abi + _ -> nil + end end def get_custom_abi_by_identity_id_and_address_hash(_, _), do: nil @@ -224,4 +251,32 @@ defmodule Explorer.Account.CustomABI do end def get_max_custom_abis_count, do: @max_abis_per_account + + @doc """ + Merges custom ABIs from multiple identities into a primary identity. + + This function updates all custom ABIs associated with the identities specified + in `ids_to_merge` to be associated with the `primary_id`. It also marks these + ABIs as not user-created in order to satisfy database constraint. + + ## Parameters + - `multi`: An `Ecto.Multi` struct to which the merge operation will be added. + - `primary_id`: An integer representing the ID of the primary identity to + which the custom ABIs will be merged. + - `ids_to_merge`: A list of integer IDs representing the identities whose + custom ABIs will be merged into the primary identity. + + ## Returns + - An updated `Ecto.Multi` struct with the merge operation added. + """ + @spec merge(Multi.t(), integer(), [integer()]) :: Multi.t() + def merge(multi, primary_id, ids_to_merge) do + Multi.run(multi, :merge_custom_abis, fn repo, _ -> + {:ok, + repo.update_all( + from(key in __MODULE__, where: key.identity_id in ^ids_to_merge), + set: [identity_id: primary_id, user_created: false] + )} + end) + end end diff --git a/apps/explorer/lib/explorer/account/identity.ex b/apps/explorer/lib/explorer/account/identity.ex index c9c55e6e691b..7f3739dc6557 100644 --- a/apps/explorer/lib/explorer/account/identity.ex +++ b/apps/explorer/lib/explorer/account/identity.ex @@ -2,22 +2,42 @@ defmodule Explorer.Account.Identity do @moduledoc """ Identity of user fetched via Oauth """ - use Explorer.Schema - import Ecto.Changeset + require Logger + require Poison + alias BlockScoutWeb.Chain + alias Ecto.Multi alias Explorer.Account.Api.Plan alias Explorer.Account.{TagAddress, Watchlist} + alias Explorer.{Chain, Repo} + alias Explorer.Chain.{Address, Hash} + alias Ueberauth.Auth + alias Ueberauth.Auth.{Extra, Info} + + @type session :: %{ + optional(:name) => String.t(), + optional(:watchlist_id) => integer(), + id: integer(), + uid: String.t(), + email: String.t(), + nickname: String.t(), + avatar: String.t(), + address_hash: String.t(), + email_verified: boolean() + } typed_schema "account_identities" do field(:uid_hash, Cloak.Ecto.SHA256) :: binary() | nil field(:uid, Explorer.Encrypted.Binary, null: false) field(:email, Explorer.Encrypted.Binary, null: false) - field(:name, Explorer.Encrypted.Binary, null: false) - field(:nickname, Explorer.Encrypted.Binary) + field(:name, :string, virtual: true) + field(:nickname, :string, virtual: true) + field(:address_hash, Hash.Address, virtual: true) field(:avatar, Explorer.Encrypted.Binary) field(:verification_email_sent_at, :utc_datetime_usec) + field(:otp_sent_at, :utc_datetime_usec) has_many(:tag_addresses, TagAddress) has_many(:watchlists, Watchlist) @@ -40,4 +60,271 @@ defmodule Explorer.Account.Identity do changeset |> force_change(:uid_hash, get_field(changeset, :uid)) end + + @doc """ + Populates Identity virtual fields with data from user-stored session. + + This function updates the virtual fields of an Identity struct with + information from the user's session. + + ## Parameters + - `identity`: The Identity struct to be updated. + - `session`: A map containing session information. + + ## Returns + - An updated Identity struct with populated virtual fields. + """ + @spec put_session_info(t(), session()) :: t() + def put_session_info(identity, %{name: name, nickname: nickname, address_hash: address_hash}) do + %__MODULE__{ + identity + | name: name, + nickname: nickname, + address_hash: address_hash + } + end + + def put_session_info(identity, %{name: name, nickname: nickname}) do + %__MODULE__{ + identity + | name: name, + nickname: nickname + } + end + + @doc """ + Finds an existing Identity or creates a new one based on authentication data. + + This function attempts to find an Identity matching the given authentication + data. If not found, it creates a new Identity. + + ## Parameters + - `auth`: An Auth struct containing authentication information. + + ## Returns + - `{:ok, session()}`: A tuple containing the atom `:ok` and a session map if + the Identity is found or successfully created. + - `{:error, Ecto.Changeset.t()}`: A tuple containing the atom `:error` and a + changeset if there was an error creating the Identity. + """ + @spec find_or_create(Auth.t()) :: {:ok, session()} | {:error, Ecto.Changeset.t()} + def find_or_create(%Auth{} = auth) do + case find_identity(auth) do + nil -> + case create_identity(auth) do + %__MODULE__{} = identity -> + {:ok, session_info(auth, identity)} + + {:error, changeset} -> + {:error, changeset} + end + + %{} = identity -> + update_identity(identity, update_identity_map(auth)) + {:ok, session_info(auth, identity)} + end + end + + defp create_identity(auth) do + with {:ok, %__MODULE__{} = identity} <- Repo.account_repo().insert(new_identity(auth)), + {:ok, _watchlist} <- add_watchlist(identity) do + identity + end + end + + defp update_identity(identity, attrs) do + identity + |> changeset(attrs) + |> Repo.account_repo().update() + end + + defp new_identity(auth) do + %__MODULE__{ + uid: auth.uid, + uid_hash: auth.uid, + email: email_from_auth(auth), + name: name_from_auth(auth), + nickname: nickname_from_auth(auth), + avatar: avatar_from_auth(auth), + address_hash: address_hash_from_auth(auth) + } + end + + defp add_watchlist(identity) do + watchlist = Ecto.build_assoc(identity, :watchlists, %{}) + + with {:ok, _} <- Repo.account_repo().insert(watchlist), + do: {:ok, identity} + end + + @doc """ + Finds an Identity based on authentication data or ID. + + This function searches for an Identity using either authentication data or + an ID. + + ## Parameters + - `auth_or_uid`: Either an Auth struct or an integer ID. + + ## Returns + - The found Identity struct or nil if not found. + """ + @spec find_identity(Auth.t() | integer()) :: t() | nil + def find_identity(auth_or_uid) do + Repo.account_repo().one(query_identity(auth_or_uid)) + end + + defp query_identity(%Auth{} = auth) do + from(i in __MODULE__, where: i.uid_hash == ^auth.uid) + end + + defp query_identity(id) do + from(i in __MODULE__, where: i.id == ^id) + end + + @doc """ + Finds multiple Identities based on a list of user IDs. + + This function retrieves multiple Identity structs that match the given list + of user IDs. + + ## Parameters + - `user_ids`: A list of user ID strings. + + ## Returns + - A list of found Identity structs. + """ + @spec find_identities([String.t()]) :: [t()] + def find_identities(user_ids) do + Repo.account_repo().all(query_identities(user_ids)) + end + + defp query_identities(user_ids) do + from(i in __MODULE__, where: i.uid_hash in ^user_ids) + end + + @doc """ + Deletes multiple Identities as part of a Multi transaction. + + This function adds a step to a Multi transaction to delete Identities with + the specified IDs. + + ## Parameters + - `multi`: The Multi struct to which the delete operation will be added. + - `ids_to_merge`: A list of Identity IDs to be deleted. + + ## Returns + - An updated Multi struct with the delete operation added. + """ + @spec delete(Multi.t(), [integer()]) :: Multi.t() + def delete(multi, ids_to_merge) do + Multi.run(multi, :delete_identities, fn repo, _ -> + query = from(identity in __MODULE__, where: identity.id in ^ids_to_merge) + {:ok, repo.delete_all(query)} + end) + end + + defp session_info(auth, identity) do + if email_verified_from_auth(auth) do + %{watchlists: [watchlist | _]} = Repo.account_repo().preload(identity, :watchlists) + + %{ + id: identity.id, + uid: auth.uid, + email: email_from_auth(auth), + name: name_from_auth(auth), + nickname: nickname_from_auth(auth), + avatar: avatar_from_auth(auth), + address_hash: address_hash_from_auth(auth), + watchlist_id: watchlist.id, + email_verified: true + } + else + %{ + id: identity.id, + uid: auth.uid, + email: email_from_auth(auth), + nickname: nickname_from_auth(auth), + avatar: avatar_from_auth(auth), + address_hash: address_hash_from_auth(auth), + email_verified: false + } + end + end + + defp update_identity_map(auth) do + %{ + email: email_from_auth(auth), + name: name_from_auth(auth), + nickname: nickname_from_auth(auth), + avatar: avatar_from_auth(auth), + address_hash: address_hash_from_auth(auth) + } + end + + # github does it this way + defp avatar_from_auth(%{info: %{urls: %{avatar_url: image}}}), do: image + + # facebook does it this way + defp avatar_from_auth(%{info: %{image: image}}), do: image + + defp email_from_auth(%Auth{extra: %Extra{raw_info: %{user: %{"user_metadata" => %{"email" => email}}}}}), + do: email + + defp email_from_auth(%{info: %{email: email}}), do: email + + defp nickname_from_auth(%{info: %{nickname: nickname}}), do: nickname + + defp name_from_auth(%{info: %{name: name}}) + when name != "" and not is_nil(name), + do: name + + defp name_from_auth(%{info: info}) do + [info.first_name, info.last_name, info.nickname] + |> Enum.map(&(&1 |> to_string() |> String.trim())) + |> case do + ["", "", nick] -> nick + ["", lastname, _] -> lastname + [name, "", _] -> name + [name, lastname, _] -> name <> " " <> lastname + end + end + + @doc """ + Extracts the address hash from authentication data. + + This function attempts to extract an Ethereum address hash from the + authentication data, either from user metadata or by parsing the UID. + + ## Parameters + - `auth`: An Auth struct containing authentication information. + + ## Returns + - A string representation of the Ethereum address hash, or nil if not found. + """ + @spec address_hash_from_auth(Auth.t()) :: String.t() | nil + def address_hash_from_auth(%Auth{ + extra: %Extra{raw_info: %{user: %{"user_metadata" => %{"web3_address_hash" => address_hash}}}} + }) do + address_hash + end + + def address_hash_from_auth(%Auth{uid: uid, info: %Info{nickname: nickname}}) do + case uid |> String.slice(-42..-1) |> Chain.string_to_address_hash() do + {:ok, address_hash} -> + address_hash |> Address.checksum() + + _ -> + case String.contains?(uid, "Passkey") && Chain.string_to_address_hash(nickname) do + {:ok, address_hash} -> address_hash |> Address.checksum() + _ -> nil + end + end + end + + defp email_verified_from_auth(%Auth{extra: %Extra{raw_info: %{user: %{"user_metadata" => %{"email" => _email}}}}}), + do: true + + defp email_verified_from_auth(%Auth{extra: %Extra{raw_info: %{user: %{"email_verified" => false}}}}), do: false + defp email_verified_from_auth(_), do: true end diff --git a/apps/explorer/lib/explorer/account/public_tags_request.ex b/apps/explorer/lib/explorer/account/public_tags_request.ex index 8cdf5e56fbda..9d38c07a297c 100644 --- a/apps/explorer/lib/explorer/account/public_tags_request.ex +++ b/apps/explorer/lib/explorer/account/public_tags_request.ex @@ -4,10 +4,10 @@ defmodule Explorer.Account.PublicTagsRequest do """ use Explorer.Schema - alias Ecto.Changeset + alias Ecto.{Changeset, Multi} alias Explorer.Account.Identity alias Explorer.Chain.Hash - alias Explorer.Repo + alias Explorer.{Helper, Repo} alias Explorer.ThirdPartyIntegrations.AirTable import Ecto.Changeset @@ -44,9 +44,7 @@ defmodule Explorer.Account.PublicTagsRequest do association_fields = request.__struct__.__schema__(:associations) waste_fields = association_fields ++ @local_fields - network = - Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:host] <> - Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:path] + network = Helper.get_app_host() <> Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:path] request |> Map.from_struct() |> Map.drop(waste_fields) |> Map.put(:network, network) end @@ -252,4 +250,30 @@ defmodule Explorer.Account.PublicTagsRequest do end def get_max_public_tags_request_count, do: @max_public_tags_request_per_account + + @doc """ + Merges public tags requests from multiple identities into a primary identity. + + This function updates the `identity_id` of all public tags requests belonging to the + identities specified in `ids_to_merge` to the `primary_id`. It's designed to + be used as part of an Ecto.Multi transaction. + + ## Parameters + - `multi`: An Ecto.Multi struct to which this operation will be added. + - `primary_id`: The ID of the primary identity that will own the merged keys. + - `ids_to_merge`: A list of identity IDs whose public tags requests will be merged. + + ## Returns + - An updated Ecto.Multi struct with the merge operation added. + """ + @spec merge(Multi.t(), integer(), [integer()]) :: Multi.t() + def merge(multi, primary_id, ids_to_merge) do + Multi.run(multi, :merge_public_tags_requests, fn repo, _ -> + {:ok, + repo.update_all( + from(key in __MODULE__, where: key.identity_id in ^ids_to_merge), + set: [identity_id: primary_id] + )} + end) + end end diff --git a/apps/explorer/lib/explorer/account/tag_address.ex b/apps/explorer/lib/explorer/account/tag_address.ex index 87d8c403106b..c89bfb97d71e 100644 --- a/apps/explorer/lib/explorer/account/tag_address.ex +++ b/apps/explorer/lib/explorer/account/tag_address.ex @@ -1,13 +1,13 @@ defmodule Explorer.Account.TagAddress do @moduledoc """ - Watchlist is root entity for WatchlistAddresses + User created custom address tags. """ use Explorer.Schema import Ecto.Changeset - alias Ecto.Changeset + alias Ecto.{Changeset, Multi} alias Explorer.Account.Identity alias Explorer.{Chain, PagingOptions, Repo} alias Explorer.Chain.{Address, Hash} @@ -18,6 +18,7 @@ defmodule Explorer.Account.TagAddress do field(:address_hash_hash, Cloak.Ecto.SHA256) :: binary() | nil field(:name, Explorer.Encrypted.Binary, null: false) field(:address_hash, Explorer.Encrypted.AddressHash, null: false) + field(:user_created, :boolean, null: false, default: true) belongs_to(:identity, Identity, null: false) @@ -177,4 +178,30 @@ defmodule Explorer.Account.TagAddress do end def get_max_tags_count, do: Application.get_env(:explorer, Explorer.Account)[:private_tags_limit] + + @doc """ + Merges address tags from multiple identities into a primary identity. + + This function updates the `identity_id` of all address tags belonging to the + identities specified in `ids_to_merge` to the `primary_id`. It's designed to + be used as part of an Ecto.Multi transaction. + + ## Parameters + - `multi`: An Ecto.Multi struct to which this operation will be added. + - `primary_id`: The ID of the primary identity that will own the merged keys. + - `ids_to_merge`: A list of identity IDs whose address tags will be merged. + + ## Returns + - An updated Ecto.Multi struct with the merge operation added. + """ + @spec merge(Multi.t(), integer(), [integer()]) :: Multi.t() + def merge(multi, primary_id, ids_to_merge) do + Multi.run(multi, :merge_tag_addresses, fn repo, _ -> + {:ok, + repo.update_all( + from(key in __MODULE__, where: key.identity_id in ^ids_to_merge), + set: [identity_id: primary_id, user_created: false] + )} + end) + end end diff --git a/apps/explorer/lib/explorer/account/tag_transaction.ex b/apps/explorer/lib/explorer/account/tag_transaction.ex index 510610e59113..1fefb8ea3452 100644 --- a/apps/explorer/lib/explorer/account/tag_transaction.ex +++ b/apps/explorer/lib/explorer/account/tag_transaction.ex @@ -7,15 +7,17 @@ defmodule Explorer.Account.TagTransaction do import Ecto.Changeset - alias Ecto.Changeset + alias Ecto.{Changeset, Multi} alias Explorer.Account.Identity alias Explorer.{Chain, PagingOptions, Repo} + alias Explorer.Chain.Hash import Explorer.Chain, only: [hash_to_lower_case_string: 1] typed_schema "account_tag_transactions" do field(:transaction_hash_hash, Cloak.Ecto.SHA256) :: binary() | nil field(:name, Explorer.Encrypted.Binary, null: false) field(:transaction_hash, Explorer.Encrypted.TransactionHash, null: false) + field(:user_created, :boolean, null: false, default: true) belongs_to(:identity, Identity, null: false) @@ -122,20 +124,29 @@ defmodule Explorer.Account.TagTransaction do defp page_transaction_tags(query, _), do: query - def tag_transaction_by_transaction_hash_and_identity_id_query(transaction_hash, identity_id) - when not is_nil(transaction_hash) and not is_nil(identity_id) do - __MODULE__ - |> where([tag], tag.identity_id == ^identity_id and tag.transaction_hash == ^transaction_hash) - end + @doc """ + Retrieves tag transactions for a given transaction hash and identity ID. - def tag_transaction_by_transaction_hash_and_identity_id_query(_, _), do: nil + This function queries the database for all tag transactions that match both + the provided transaction hash and identity ID. + ## Parameters + - `tx_hash`: The transaction hash to search for. Can be a `String.t()`, + `Explorer.Chain.Hash.Full.t()`, or `nil`. + - `identity_id`: The identity ID to search for. Can be an `integer()` or `nil`. + + ## Returns + - A list of `Explorer.Account.TagTransaction` structs if matching records are found. + - `nil` if either `tx_hash` or `identity_id` is `nil`. + """ + @spec get_tag_transaction_by_transaction_hash_and_identity_id(String.t() | Hash.Full.t() | nil, integer() | nil) :: + [__MODULE__.t()] | nil def get_tag_transaction_by_transaction_hash_and_identity_id(transaction_hash, identity_id) when not is_nil(transaction_hash) and not is_nil(identity_id) do - transaction_hash - |> hash_to_lower_case_string() - |> tag_transaction_by_transaction_hash_and_identity_id_query(identity_id) - |> Repo.account_repo().one() + query = + from(tag in __MODULE__, where: tag.transaction_hash_hash == ^transaction_hash and tag.identity_id == ^identity_id) + + Repo.account_repo().all(query) end def get_tag_transaction_by_transaction_hash_and_identity_id(_, _), do: nil @@ -176,6 +187,32 @@ defmodule Explorer.Account.TagTransaction do end def get_max_tags_count, do: Application.get_env(:explorer, Explorer.Account)[:private_tags_limit] + + @doc """ + Merges transaction tags from multiple identities into a primary identity. + + This function updates the `identity_id` of all transaction tags belonging to the + identities specified in `ids_to_merge` to the `primary_id`. It's designed to + be used as part of an Ecto.Multi transaction. + + ## Parameters + - `multi`: An Ecto.Multi struct to which this operation will be added. + - `primary_id`: The ID of the primary identity that will own the merged keys. + - `ids_to_merge`: A list of identity IDs whose transaction tags will be merged. + + ## Returns + - An updated Ecto.Multi struct with the merge operation added. + """ + @spec merge(Multi.t(), integer(), [integer()]) :: Multi.t() + def merge(multi, primary_id, ids_to_merge) do + Multi.run(multi, :merge_tag_transactions, fn repo, _ -> + {:ok, + repo.update_all( + from(key in __MODULE__, where: key.identity_id in ^ids_to_merge), + set: [identity_id: primary_id, user_created: false] + )} + end) + end end defimpl Jason.Encoder, for: Explorer.Account.TagTransaction do diff --git a/apps/explorer/lib/explorer/account/watchlist.ex b/apps/explorer/lib/explorer/account/watchlist.ex index 0130ac6fcf6d..86bc8141983a 100644 --- a/apps/explorer/lib/explorer/account/watchlist.ex +++ b/apps/explorer/lib/explorer/account/watchlist.ex @@ -7,6 +7,7 @@ defmodule Explorer.Account.Watchlist do import Ecto.Changeset + alias Ecto.Multi alias Explorer.Account.{Identity, WatchlistAddress} @derive {Jason.Encoder, only: [:name, :watchlist_addresses]} @@ -24,4 +25,39 @@ defmodule Explorer.Account.Watchlist do |> cast(attrs, [:name]) |> validate_required([:name]) end + + @doc """ + Acquires data for merging from the database. + + This function is used to fetch data from the database in preparation for a merge operation. + It retrieves both the primary watchlist and the watchlists to be merged. + + ## Parameters + + * `multi` - An `Ecto.Multi` struct representing the current multi-operation transaction. + * `primary_id` - An integer representing the ID of the primary identity. + * `ids_to_merge` - A list of integers representing the IDs of the identities to be merged. + + ## Returns + + Returns an updated `Ecto.Multi` struct with two additional operations: + + * `:acquire_primary_watchlist` - Fetches the watchlists associated with the primary identity. + * `:acquire_watchlists_to_merge` - Fetches the watchlists associated with the identities to be merged. + + ## Notes + + This function is typically used as part of a larger transaction process for merging watchlists. + It prepares the data needed for the merge without actually performing the merge operation. + """ + @spec acquire_for_merge(Multi.t(), integer(), [integer()]) :: Multi.t() + def acquire_for_merge(multi, primary_id, ids_to_merge) do + multi + |> Multi.run(:acquire_primary_watchlist, fn repo, _ -> + {:ok, repo.all(from(watchlist in __MODULE__, where: watchlist.identity_id == ^primary_id))} + end) + |> Multi.run(:acquire_watchlists_to_merge, fn repo, _ -> + {:ok, repo.all(from(watchlist in __MODULE__, where: watchlist.identity_id in ^ids_to_merge))} + end) + end end diff --git a/apps/explorer/lib/explorer/account/watchlist_address.ex b/apps/explorer/lib/explorer/account/watchlist_address.ex index 529186dd36fb..1dddc02b41c3 100644 --- a/apps/explorer/lib/explorer/account/watchlist_address.ex +++ b/apps/explorer/lib/explorer/account/watchlist_address.ex @@ -7,7 +7,7 @@ defmodule Explorer.Account.WatchlistAddress do import Ecto.Changeset - alias Ecto.Changeset + alias Ecto.{Changeset, Multi} alias Explorer.Account.Notifier.ForbiddenAddress alias Explorer.Account.Watchlist alias Explorer.{Chain, PagingOptions, Repo} @@ -19,6 +19,7 @@ defmodule Explorer.Account.WatchlistAddress do field(:address_hash_hash, Cloak.Ecto.SHA256) :: binary() | nil field(:name, Explorer.Encrypted.Binary, null: false) field(:address_hash, Explorer.Encrypted.AddressHash, null: false) + field(:user_created, :boolean, null: false, default: true) belongs_to(:watchlist, Watchlist, null: false) @@ -209,4 +210,54 @@ defmodule Explorer.Account.WatchlistAddress do end def preload_address_fetched_coin_balance(watchlist), do: watchlist + + @doc """ + Merges watchlist addresses into a primary watchlist. + + This function is used to merge multiple watchlists into a single primary watchlist. It updates + the `watchlist_id` of all addresses belonging to the watchlists being merged to point to the + primary watchlist. + + ## Parameters + + * `multi` - An `Ecto.Multi` struct representing the current multi-operation transaction. + + ## Returns + + Returns an updated `Ecto.Multi` struct with an additional `:merge_watchlist_addresses` operation. + + ## Operation Details + + The function adds a `:merge_watchlist_addresses` operation to the `Ecto.Multi` struct. This operation: + + 1. Identifies the primary watchlist and the watchlists to be merged from the results of previous operations. + 2. Updates all watchlist addresses associated with the watchlists being merged: + - Sets their `watchlist_id` to the ID of the primary watchlist. + - Sets their `user_created` flag to `false`. + + ## Notes + + - This function assumes that the `Explorer.Account.Watchlist.acquire_for_merge/3` function has been called previously in the + `Ecto.Multi` chain to provide the necessary data for the merge operation. + - After this operation, all addresses from the merged watchlists will be associated with the + primary watchlist, and their `user_created` status will be set to `false`. + """ + @spec merge(Multi.t()) :: Multi.t() + def merge(multi) do + multi + |> Multi.run(:merge_watchlist_addresses, fn repo, + %{ + acquire_primary_watchlist: [primary_watchlist | _], + acquire_watchlists_to_merge: watchlists_to_merge + } -> + primary_watchlist_id = primary_watchlist.id + watchlists_to_merge_ids = Enum.map(watchlists_to_merge, & &1.id) + + {:ok, + repo.update_all( + from(key in __MODULE__, where: key.watchlist_id in ^watchlists_to_merge_ids), + set: [watchlist_id: primary_watchlist_id, user_created: false] + )} + end) + end end diff --git a/apps/explorer/lib/explorer/account/watchlist_notification.ex b/apps/explorer/lib/explorer/account/watchlist_notification.ex index 2b7d129a0156..9d5c9d4ca74f 100644 --- a/apps/explorer/lib/explorer/account/watchlist_notification.ex +++ b/apps/explorer/lib/explorer/account/watchlist_notification.ex @@ -9,6 +9,7 @@ defmodule Explorer.Account.WatchlistNotification do import Ecto.Changeset import Explorer.Chain, only: [hash_to_lower_case_string: 1] + alias Ecto.Multi alias Explorer.Repo alias Explorer.Account.{Watchlist, WatchlistAddress} @@ -95,4 +96,56 @@ defmodule Explorer.Account.WatchlistNotification do defp watchlist_notification_30_days_limit do Application.get_env(:explorer, Explorer.Account)[:notifications_limit_for_30_days] end + + @doc """ + Merges watchlist notifications into a primary watchlist. + + This function is used to merge notifications from multiple watchlists into a single primary watchlist. + It updates the `watchlist_id` of all notifications belonging to the watchlists being merged to point + to the primary watchlist. + + ## Parameters + + * `multi` - An `Ecto.Multi` struct representing the current multi-operation transaction. + + ## Returns + + Returns an updated `Ecto.Multi` struct with an additional `:merge_watchlist_notifications` operation. + + ## Operation Details + + The function adds a `:merge_watchlist_notifications` operation to the `Ecto.Multi` struct. This operation: + + 1. Identifies the primary watchlist and the watchlists to be merged from the results of previous operations. + 2. Updates all notifications associated with the watchlists being merged: + - Sets their `watchlist_id` to the ID of the primary watchlist. + + + ## Notes + + - This function assumes that the `Explorer.Account.Watchlist.acquire_for_merge/3` function has been called previously in the + `Ecto.Multi` chain to provide the necessary data for the merge operation. + - After this operation, all notifications from the merged watchlists will be associated with the + primary watchlist. + - This function is typically used as part of a larger watchlist merging process, which may include + merging other related data such as watchlist addresses. + """ + @spec merge(Multi.t()) :: Multi.t() + def merge(multi) do + multi + |> Multi.run(:merge_watchlist_notifications, fn repo, + %{ + acquire_primary_watchlist: [primary_watchlist | _], + acquire_watchlists_to_merge: watchlists_to_merge + } -> + primary_watchlist_id = primary_watchlist.id + watchlists_to_merge_ids = Enum.map(watchlists_to_merge, & &1.id) + + {:ok, + repo.update_all( + from(notification in __MODULE__, where: notification.watchlist_id in ^watchlists_to_merge_ids), + set: [watchlist_id: primary_watchlist_id] + )} + end) + end end diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 39e100380dc4..2e4f3525b118 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -5381,12 +5381,34 @@ defmodule Explorer.Chain do end end + @doc """ + Retrieves the ID of a WatchlistAddress entry for a given watchlist and address. + + This function queries the WatchlistAddress table to find an entry that matches + both the provided watchlist ID and address hash. It returns the ID of the first + matching entry, if found. + + ## Parameters + - `watchlist_id`: The ID of the watchlist to search within. + - `address_hash`: The address hash to look for, as a `Hash.Address.t()` struct. + + ## Returns + - An integer representing the ID of the matching WatchlistAddress entry, if found. + - `nil` if no matching entry is found or if either input is `nil`. + """ + @spec select_watchlist_address_id(integer() | nil, Hash.Address.t() | nil) :: integer() | nil def select_watchlist_address_id(watchlist_id, address_hash) when not is_nil(watchlist_id) and not is_nil(address_hash) do - WatchlistAddress - |> where([wa], wa.watchlist_id == ^watchlist_id and wa.address_hash_hash == ^address_hash) - |> select([wa], wa.id) - |> Repo.account_repo().one() + wa_ids = + WatchlistAddress + |> where([wa], wa.watchlist_id == ^watchlist_id and wa.address_hash_hash == ^address_hash) + |> select([wa], wa.id) + |> Repo.account_repo().all() + + case wa_ids do + [wa_id | _] -> wa_id + _ -> nil + end end def select_watchlist_address_id(_watchlist_id, _address_hash), do: nil diff --git a/apps/explorer/lib/explorer/chain/csv_export/helper.ex b/apps/explorer/lib/explorer/chain/csv_export/helper.ex index 973f9f60975f..1b3774dc3b7f 100644 --- a/apps/explorer/lib/explorer/chain/csv_export/helper.ex +++ b/apps/explorer/lib/explorer/chain/csv_export/helper.ex @@ -115,10 +115,4 @@ defmodule Explorer.Chain.CSVExport.Helper do true end end - - @spec captcha_helper() :: module() - def captcha_helper do - :block_scout_web - |> Application.get_env(:captcha_helper) - end end diff --git a/apps/explorer/lib/explorer/chain/smart_contract/audit_report.ex b/apps/explorer/lib/explorer/chain/smart_contract/audit_report.ex index e92a4cf82a0f..124255b920c9 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/audit_report.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/audit_report.ex @@ -39,7 +39,7 @@ defmodule Explorer.Chain.SmartContract.AuditReport do waste_fields = association_fields ++ @local_fields chain = - Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:host] <> + Helper.get_app_host() <> Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:path] request |> Map.from_struct() |> Map.drop(waste_fields) |> Map.put(:chain, chain) diff --git a/apps/explorer/lib/explorer/helper.ex b/apps/explorer/lib/explorer/helper.ex index f31081578edd..226379d89cfb 100644 --- a/apps/explorer/lib/explorer/helper.ex +++ b/apps/explorer/lib/explorer/helper.ex @@ -248,4 +248,52 @@ defmodule Explorer.Helper do query end end + + @doc """ + Checks if a specified time interval has passed since a given datetime. + + This function compares the given datetime plus the interval against the current + time. It returns `true` if the interval has passed, or the number of seconds + remaining if it hasn't. + + ## Parameters + - `sent_at`: The reference datetime, or `nil`. + - `interval`: The time interval in milliseconds. + + ## Returns + - `true` if the interval has passed or if `sent_at` is `nil`. + - An integer representing the number of seconds remaining in the interval if it + hasn't passed yet. + """ + @spec check_time_interval(DateTime.t() | nil, integer()) :: true | integer() + def check_time_interval(nil, _interval), do: true + + def check_time_interval(sent_at, interval) do + now = DateTime.utc_now() + + if sent_at + |> DateTime.add(interval, :millisecond) + |> DateTime.compare(now) != :gt do + true + else + sent_at + |> DateTime.add(interval, :millisecond) + |> DateTime.diff(now, :second) + end + end + + @doc """ + Retrieves the host URL for the BlockScoutWeb application. + + This function fetches the host URL from the application's configuration, + specifically from the `:block_scout_web` application's `BlockScoutWeb.Endpoint` + configuration. + + ## Returns + A string containing the host URL for the BlockScoutWeb application. + """ + @spec get_app_host :: String.t() + def get_app_host do + Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:host] + end end diff --git a/apps/explorer/lib/explorer/third_party_integrations/auth0.ex b/apps/explorer/lib/explorer/third_party_integrations/auth0.ex index 2b5c6c67a6c5..939f14909458 100644 --- a/apps/explorer/lib/explorer/third_party_integrations/auth0.ex +++ b/apps/explorer/lib/explorer/third_party_integrations/auth0.ex @@ -2,11 +2,33 @@ defmodule Explorer.ThirdPartyIntegrations.Auth0 do @moduledoc """ Module for fetching jwt Auth0 Management API (https://auth0.com/docs/api/management/v2) jwt """ + require Logger + + alias Explorer.Account.Identity + alias Explorer.{Account, Helper, Repo} + alias OAuth2.{AccessToken, Client} + alias Ueberauth.Auth + alias Ueberauth.Strategy.Auth0 + alias Ueberauth.Strategy.Auth0.OAuth + @redis_key "auth0" + @request_siwe_message "Request Sign in with Ethereum message via /api/account/v2/siwe_message" + @wrong_nonce "Wrong nonce in message" + @misconfiguration_detected "Misconfiguration detected, please contact support." + @disabled_otp_error_description "Grant type 'http://auth0.com/oauth/grant-type/passwordless/otp' not allowed for the client." + @users_path "/api/v2/users" + @json_content_type [{"Content-type", "application/json"}] + @doc """ - Function responsible for retrieving machine to machine JWT for interacting with Auth0 Management API. - Firstly it tries to access cached token and if there is no cached one, token will be requested from Auth0 + Retrieves a machine-to-machine JWT for interacting with the Auth0 Management API. + + This function first attempts to access a cached token. If no cached token is + found, it requests a new token from Auth0 and caches it for future use. + + ## Returns + - `nil` if token retrieval fails + - `String.t()` containing the JWT if successful """ @spec get_m2m_jwt() :: nil | String.t() def get_m2m_jwt do @@ -16,7 +38,7 @@ defmodule Explorer.ThirdPartyIntegrations.Auth0 do def get_m2m_jwt_inner({:ok, token}) when not is_nil(token), do: token def get_m2m_jwt_inner(_) do - config = Application.get_env(:ueberauth, Ueberauth.Strategy.Auth0.OAuth) + config = Application.get_env(:ueberauth, OAuth) body = %{ "client_id" => config[:client_id], @@ -25,9 +47,7 @@ defmodule Explorer.ThirdPartyIntegrations.Auth0 do "grant_type" => "client_credentials" } - headers = [{"Content-type", "application/json"}] - - case HTTPoison.post("https://#{config[:domain]}/oauth/token", Jason.encode!(body), headers, []) do + case HTTPoison.post("https://#{config[:domain]}/oauth/token", Jason.encode!(body), @json_content_type, []) do {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> case Jason.decode!(body) do %{"access_token" => token, "expires_in" => ttl} -> @@ -43,7 +63,16 @@ defmodule Explorer.ThirdPartyIntegrations.Auth0 do end @doc """ - Generates key from chain_id and cookie hash for storing in Redis + Generates a key from chain_id and cookie hash for storing in Redis. + + This function combines the chain_id (if available) with the provided hash to + create a unique key for Redis storage. + + ## Parameters + - `hash`: The hash to be combined with the chain_id + + ## Returns + - `String.t()` representing the generated key """ @spec cookie_key(binary) :: String.t() def cookie_key(hash) do @@ -60,4 +89,719 @@ defmodule Explorer.ThirdPartyIntegrations.Auth0 do Redix.command(:redix, ["SET", cookie_key(@redis_key), token, "EX", ttl]) token end + + @doc """ + Sends a one-time password (OTP) for linking an email to an existing account. + + This function checks if the email is already associated with an account before + sending the OTP. If the email is already in use, it returns an error. + + ## Parameters + - `email`: The email address to send the OTP to + - `ip`: The IP address of the requester + + ## Returns + - `:ok` if the OTP was sent successfully + - `{:error, String.t()}` error with the description + - `:error` if there was an unexpected error + """ + @spec send_otp_for_linking(String.t(), String.t()) :: :error | :ok | {:error, String.t()} + def send_otp_for_linking(email, ip) do + case find_users_by_email(email) do + {:ok, []} -> + do_send_otp(email, ip) + + {:ok, users} when is_list(users) and length(users) > 0 -> + {:error, "Account with this email already exists"} + + error -> + error + end + end + + @doc """ + Sends a one-time password (OTP) to the specified email address. + + This function checks if the email is associated with an existing user before + sending the OTP. If the user exists, it checks time interval and sends the OTP + or reports when the user can request a new OTP. + + ## Parameters + - `email`: The email address to send the OTP to + - `ip`: The IP address of the requester + + ## Returns + - `:ok` if the OTP was sent successfully + - `:error` if there was an unexpected error + - `{:interval, integer()}` if the user need to wait before sending the OTP + """ + @spec send_otp(String.t(), String.t()) :: :error | :ok | {:interval, integer()} + def send_otp(email, ip) do + case find_users_by_email(email) do + {:ok, []} -> + do_send_otp(email, ip) + + {:ok, [user | _]} -> + handle_existing_user(user, email, ip) + + error -> + error + end + end + + @doc """ + Links an email to an existing user account using a one-time password (OTP). + + This function verifies the OTP, creates a new identity for the email, and links + it to the existing user account. + + ## Parameters + - `primary_user_id`: The ID of the existing user account + - `email`: The email address to be linked + - `otp`: The one-time password for verification + + ## Returns + - `{:ok, Auth.t()}` if the email was successfully linked + - `{:error, String.t()}` error with the description + - `:error` if there was an unexpected error + """ + @spec link_email(Identity.session(), String.t(), String.t()) :: :error | {:ok, Auth.t()} | {:error, String.t()} + def link_email(%{uid: primary_user_id, email: nil}, email, otp) do + case find_users_by_email(email) do + {:ok, []} -> + with {:ok, token} <- confirm_otp(email, otp), + {:ok, %{"sub" => "email|" <> identity_id}} <- get_user_from_token(token), + :ok <- link_users(primary_user_id, identity_id, "email"), + {:ok, user} <- update_user_email(primary_user_id, email) do + {:ok, create_auth(user)} + end + + {:ok, users} when is_list(users) -> + {:error, "Account with this email already exists"} + + error -> + error + end + end + + def link_email(_account_with_email, _, _), do: {:error, "This account already has an email"} + + @doc """ + Confirms a one-time password (OTP) and retrieves authentication information. + + This function verifies the OTP for the given email and returns the + authentication information if successful. + + ## Parameters + - `email`: The email address associated with the OTP + - `otp`: The one-time password to be confirmed + + ## Returns + - `{:ok, Auth.t()}` if the OTP is confirmed and authentication is successful + - `{:error, String.t()}` error with the description + - `:error` if there was an unexpected error + """ + @spec confirm_otp_and_get_auth(String.t(), String.t()) :: :error | {:error, String.t()} | {:ok, Auth.t()} + def confirm_otp_and_get_auth(email, otp) do + with {:ok, token} <- confirm_otp(email, otp), + {:ok, %{"sub" => user_id} = user} <- get_user_from_token(token), + {:search, _user_from_token, {:ok, user}} <- {:search, user, get_user_by_id(user_id)} do + maybe_link_email_and_get_auth(user) + else + # newly created user, sometimes just created user with otp does not appear in the search + {:search, %{"sub" => user_id} = user_from_token, {:error, "User not found"}} -> + {:ok, user_from_token |> Map.put("user_id", user_id) |> create_auth()} + + err -> + err + end + end + + @doc """ + Updates the session with the user's address hash. + + This function checks if the session already has an address hash. If not, it + retrieves the user's information and adds the address hash to the session. + + ## Parameters + - `session`: The current session map (Identity.session()) + + ## Returns + - `{:old, Identity.session()}` if the session already has an address hash + - `{:new, Identity.session()}` if the address hash was added to the session + """ + @spec update_session_with_address_hash(Identity.session()) :: {:old, Identity.session()} | {:new, Identity.session()} + def update_session_with_address_hash(%{address_hash: _} = session), do: {:old, session} + + def update_session_with_address_hash(%{uid: user_id} = session) do + case get_user_by_id(user_id) do + {:ok, user} -> + {:new, Map.put(session, :address_hash, user |> create_auth() |> Identity.address_hash_from_auth())} + + error -> + Logger.error("Error when updating session with address hash: #{inspect(error)}") + {:old, session} + end + end + + @doc """ + Generates a Sign-In with Ethereum (SIWE) message for the given address. + + This function creates a SIWE message with a unique nonce, caches the nonce, + and returns the formatted message string. + + ## Parameters + - `address`: The Ethereum address for which to generate the SIWE message + + ## Returns + - `{:ok, String.t()}` containing the generated SIWE message + - `{:error, String.t()}` error with the description + """ + @spec generate_siwe_message(String.t()) :: {:ok, String.t()} | {:error, String.t()} + def generate_siwe_message(address) do + nonce = Siwe.generate_nonce() + {int_chain_id, _} = Integer.parse(Application.get_env(:block_scout_web, :chain_id)) + + message = %Siwe.Message{ + domain: Helper.get_app_host(), + address: address, + statement: "Sign in to Blockscout Account V2 via Ethereum account", + uri: + Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:scheme] <> + "://" <> Helper.get_app_host(), + version: "1", + chain_id: int_chain_id, + nonce: nonce, + issued_at: DateTime.utc_now() |> DateTime.to_iso8601(), + expiration_time: DateTime.utc_now() |> DateTime.add(300, :second) |> DateTime.to_iso8601() + } + + with {:cache, {:ok, _nonce}} <- {:cache, cache_nonce_for_address(nonce, address)}, + {:message, {:ok, message}} <- {:message, Siwe.to_str(message)} do + {:ok, message} + else + {:cache, {:error, error}} -> + Logger.error("Error while caching nonce: #{inspect(error)}") + {:error, @misconfiguration_detected} + + {:message, {:error, error}} -> + Logger.error("Error while generating Sign in with Ethereum Message: #{inspect(error)}") + {:error, error} + end + end + + @doc """ + Links an Ethereum address to an existing user account. + + This function verifies the SIWE message and signature, checks for existing + users with the same address, and updates the user's account with the new + address. + + ## Parameters + - `user_id`: The ID of the existing user account + - `message`: The SIWE message + - `signature`: The signature of the SIWE message + + ## Returns + - `{:ok, Auth.t()}` if the address was successfully linked + - `{:error, String.t()}` error with the description + - `:error` if there was an unexpected error + """ + @spec link_address(String.t(), String.t(), String.t()) :: :error | {:error, String.t()} | {:ok, Auth.t()} + def link_address(user_id, message, signature) do + with {:signature, {:ok, %{nonce: nonce, address: address}}} <- + {:signature, message |> String.trim() |> Siwe.parse_if_valid(signature)}, + {:nonce, {:ok, ^nonce}} <- + {:nonce, Redix.command(:redix, ["GET", cookie_key(address <> "siwe_nonce")])}, + Redix.command(:redix, ["DEL", cookie_key(address <> "siwe_nonce")]), + {:user, {:ok, []}} <- {:user, find_users_by_web3_address(address)}, + {:ok, user} <- update_user_with_web3_address(user_id, address) do + {:ok, create_auth(user)} + else + {:nonce, {:ok, _}} -> + {:error, @wrong_nonce} + + {:nonce, _} -> + {:error, @request_siwe_message} + + {:signature, error} -> + error + + {:user, {:ok, _users}} -> + {:error, "Account with this address already exists"} + + {:user, error} -> + error + + other -> + other + end + end + + @doc """ + Authenticates a user using a Sign-In with Ethereum (SIWE) message and signature. + + This function verifies the SIWE message and signature, finds or creates a user + associated with the Ethereum address, and returns the authentication information. + + ## Parameters + - `message`: The SIWE message + - `signature`: The signature of the SIWE message + + ## Returns + - `{:ok, Auth.t()}` if authentication is successful + - `{:error, String.t()}` error with the description + - `:error` if there was an unexpected error + """ + @spec get_auth_with_web3(String.t(), String.t()) :: :error | {:error, String.t()} | {:ok, Auth.t()} + def get_auth_with_web3(message, signature) do + with {:signature, {:ok, %{nonce: nonce, address: address}}} <- + {:signature, message |> String.trim() |> Siwe.parse_if_valid(signature)}, + {:nonce, {:ok, ^nonce}} <- + {:nonce, Redix.command(:redix, ["GET", cookie_key(address <> "siwe_nonce")])}, + {:user, {:ok, user}} <- {:user, find_or_create_web3_user(address, signature)} do + Redix.command(:redix, ["DEL", cookie_key(address <> "siwe_nonce")]) + {:ok, create_auth(user)} + else + {:nonce, {:ok, _}} -> + {:error, @wrong_nonce} + + {:nonce, _} -> + {:error, @request_siwe_message} + + {_step, error} -> + error + end + end + + defp handle_existing_user(user, email, ip) do + user + |> create_auth() + |> Identity.find_identity() + |> handle_identity(email, ip) + end + + defp handle_identity(nil, email, ip), do: do_send_otp(email, ip) + + defp handle_identity(%Identity{otp_sent_at: otp_sent_at} = identity, email, ip) do + otp_resend_interval = Application.get_env(:explorer, Account, :otp_resend_interval) + + case Helper.check_time_interval(otp_sent_at, otp_resend_interval) do + true -> + identity + |> Identity.changeset(%{otp_sent_at: DateTime.utc_now()}) + |> Repo.account_repo().update() + + do_send_otp(email, ip) + + interval -> + {:interval, interval} + end + end + + defp do_send_otp(email, ip) do + client = OAuth.client() + + body = + %{ + email: email, + connection: :email, + send: :code + } + |> put_client_id_and_secret() + + headers = [{"auth0-forwarded-for", ip} | @json_content_type] + + case Client.post(client, "/passwordless/start", body, headers) do + {:ok, %OAuth2.Response{status_code: 200}} -> + :ok + + other -> + Logger.error("Error while sending otp: ", inspect(other)) + + :error + end + end + + defp get_user_from_token(%AccessToken{other_params: %{"id_token" => token}}) do + case Joken.peek_claims(token) do + {:ok, %{"sub" => _} = user} -> + {:ok, user} + + error -> + Logger.error("Error while peeking claims from token: #{inspect(error)}") + :error + end + end + + defp get_user_from_token(token) do + Logger.error("No id_token in token: #{inspect(Map.update(token, :access_token, "xxx", fn _ -> "xxx" end))}") + + {:error, @misconfiguration_detected} + end + + defp confirm_otp(email, otp) do + client = OAuth.client() + + body = + %{ + username: email, + otp: otp, + realm: :email, + grant_type: :"http://auth0.com/oauth/grant-type/passwordless/otp" + } + |> put_client_id_and_secret() + + case Client.post(client, "/oauth/token", body, @json_content_type) do + {:ok, %OAuth2.Response{status_code: 200, body: body}} -> + {:ok, AccessToken.new(body)} + + {:error, + %OAuth2.Response{ + status_code: 403, + body: + %{ + "error" => "unauthorized_client", + "error_description" => @disabled_otp_error_description, + "error_uri" => "https://auth0.com/docs/clients/client-grant-types" + } = body + }} -> + Logger.error("Need to enable OTP: #{inspect(body)}") + {:error, @misconfiguration_detected} + + {:error, + %OAuth2.Response{ + status_code: 403, + body: %{"error" => "invalid_grant", "error_description" => "Wrong email or verification code."} + }} -> + {:error, "Wrong verification code."} + + other -> + Logger.error("Error while confirming otp: #{inspect(other)}") + + :error + end + end + + defp get_user_by_id(id) do + case get_m2m_jwt() do + token when is_binary(token) -> + client = OAuth.client(token: token) + + case Client.get(client, "#{@users_path}/#{URI.encode(id)}") do + {:ok, %OAuth2.Response{status_code: 200, body: %{"user_id" => ^id} = user}} -> + {:ok, user} + + {:error, %OAuth2.Response{status_code: 404}} -> + {:error, "User not found"} + + other -> + Logger.error(["Error while getting user by id: ", inspect(other)]) + :error + end + end + end + + defp find_users_by_email(email) do + case get_m2m_jwt() do + token when is_binary(token) -> + client = OAuth.client(token: token) + email = URI.encode(email) + + case Client.get(client, @users_path, [], + params: %{"q" => ~s(email:"#{email}" OR user_metadata.email:"#{email}")} + ) do + {:ok, %OAuth2.Response{status_code: 200, body: users}} when is_list(users) -> + {:ok, users} + + {:error, %OAuth2.Response{status_code: 403, body: %{"errorCode" => "insufficient_scope"} = body}} -> + Logger.error(["Failed to get web3 user. Insufficient scope: ", inspect(body)]) + {:error, @misconfiguration_detected} + + other -> + Logger.error(["Error while getting web3 user: ", inspect(other)]) + :error + end + + nil -> + Logger.error("Failed to get M2M JWT") + {:error, @misconfiguration_detected} + end + end + + defp maybe_link_email_and_get_auth(%{"email" => email, "user_id" => "email|" <> identity_id = user_id} = user) do + case get_m2m_jwt() do + token when is_binary(token) -> + client = OAuth.client(token: token) + + case Client.get(client, @users_path, [], + params: %{"q" => ~s(email:"#{URI.encode(email)}" AND NOT user_id:"#{URI.encode(user_id)}")} + ) do + {:ok, %OAuth2.Response{status_code: 200, body: []}} -> + {:ok, create_auth(user)} + + {:ok, %OAuth2.Response{status_code: 200, body: [%{"user_id" => primary_user_id} = user]}} -> + link_users(primary_user_id, identity_id, "email") + maybe_verify_email(user) + {:ok, create_auth(user)} + + {:ok, %OAuth2.Response{status_code: 200, body: users}} when is_list(users) and length(users) > 1 -> + merge_email_users(users, identity_id, "email") + + {:error, %OAuth2.Response{status_code: 403, body: %{"errorCode" => "insufficient_scope"} = body}} -> + Logger.error(["Failed to get web3 user. Insufficient scope: ", inspect(body)]) + {:error, @misconfiguration_detected} + + other -> + Logger.error(["Error while getting web3 user: ", inspect(other)]) + :error + end + + nil -> + Logger.error("Failed to get M2M JWT") + {:error, @misconfiguration_detected} + end + end + + defp maybe_link_email_and_get_auth(user) do + {:ok, create_auth(user)} + end + + defp merge_web3_users([primary_user | _] = users) do + identity_map = + users + |> Enum.map(& &1["user_id"]) + |> Identity.find_identities() + |> Map.new(&{&1.uid, &1}) + + users_map = users |> Enum.map(&{&1["user_id"], &1}) |> Map.new() + + case users |> Enum.map(&identity_map[&1["user_id"]]) |> Enum.reject(&is_nil(&1)) |> Account.merge() do + {{:ok, 0}, nil} -> + unless match?(%{"user_metadata" => %{"web3_address_hash" => _}}, primary_user) do + update_user_with_web3_address( + primary_user["user_id"], + primary_user |> create_auth() |> Identity.address_hash_from_auth() + ) + end + + {:ok, primary_user} + + {{:ok, _}, primary_identity} -> + primary_user_from_db = users_map[primary_identity.uid] + + unless match?(%{"user_metadata" => %{"web3_address_hash" => _}}, primary_user_from_db) do + update_user_with_web3_address( + primary_user_from_db["user_id"], + primary_user_from_db |> create_auth() |> Identity.address_hash_from_auth() + ) + end + + {:ok, primary_user_from_db} + + error -> + Logger.error(["Error while merging users with the same address: ", inspect(error)]) + :error + end + end + + defp merge_email_users([primary_user | _] = users, identity_id_to_link, provider_for_linking) do + identity_map = + users + |> Enum.map(& &1["user_id"]) + |> Identity.find_identities() + |> Map.new(&{&1.uid, &1}) + + users_map = users |> Enum.map(&{&1["user_id"], &1}) |> Map.new() + + case users |> Enum.map(&identity_map[&1["user_id"]]) |> Enum.reject(&is_nil(&1)) |> Account.merge() do + {{:ok, 0}, nil} -> + link_users(primary_user["user_id"], identity_id_to_link, provider_for_linking) + maybe_verify_email(primary_user) + {:ok, create_auth(primary_user)} + + {{:ok, _}, primary_identity} -> + link_users(primary_identity.uid, identity_id_to_link, provider_for_linking) + maybe_verify_email(users_map[primary_identity.uid]) + {:ok, create_auth(users_map[primary_identity.uid])} + + error -> + Logger.error(["Error while merging users with the same email: ", inspect(error)]) + :error + end + end + + defp maybe_verify_email(%{"email_verified" => false, "user_id" => user_id}) do + with token when is_binary(token) <- get_m2m_jwt(), + client = OAuth.client(token: token), + body = %{"email_verified" => true}, + {:ok, %OAuth2.Response{status_code: 200, body: _user}} <- + Client.patch(client, "#{@users_path}/#{URI.encode(user_id)}", body, @json_content_type) do + :ok + else + error -> handle_common_errors(error, "Failed to patch email_verified to true") + end + end + + defp maybe_verify_email(_), do: :ok + + defp cache_nonce_for_address(nonce, address) do + case Redix.command(:redix, ["SET", cookie_key(address <> "siwe_nonce"), nonce, "EX", 300]) do + {:ok, _} -> {:ok, nonce} + err -> err + end + end + + defp find_or_create_web3_user(address, signature) do + case find_users_by_web3_address(address) do + {:ok, [%{"user_metadata" => %{"web3_address_hash" => ^address}} = user]} -> + {:ok, user} + + {:ok, [%{"user_id" => user_id}]} -> + update_user_with_web3_address(user_id, address) + + {:ok, []} -> + create_web3_user(address, signature) + + {:ok, users} when is_list(users) and length(users) > 1 -> + merge_web3_users(users) + + other -> + other + end + end + + defp find_users_by_web3_address(address) do + with token when is_binary(token) <- get_m2m_jwt(), + client = OAuth.client(token: token), + {:ok, %OAuth2.Response{status_code: 200, body: users}} when is_list(users) <- + Client.get( + client, + @users_path, + [], + params: %{ + "q" => + ~s|user_id:*siwe*#{address} OR user_id:*Passkey*#{address} OR user_metadata.web3_address_hash:"#{address}" OR (user_id:*Passkey* AND nickname:"#{address}")| + } + ) do + {:ok, users} + else + error -> handle_common_errors(error, "Failed to search user by address") + end + end + + defp update_user_email(user_id, email) do + with token when is_binary(token) <- get_m2m_jwt(), + client = OAuth.client(token: token), + body = %{"user_metadata" => %{"email" => email}}, + {:ok, %OAuth2.Response{status_code: 200, body: user}} <- + Client.patch(client, "#{@users_path}/#{URI.encode(user_id)}", body, @json_content_type) do + {:ok, user} + else + error -> handle_common_errors(error, "Failed to update user email") + end + end + + defp update_user_with_web3_address(user_id, address) do + with token when is_binary(token) <- get_m2m_jwt(), + client = OAuth.client(token: token), + body = %{"user_metadata" => %{"web3_address_hash" => address}}, + {:ok, %OAuth2.Response{status_code: 200, body: user}} <- + Client.patch(client, "#{@users_path}/#{URI.encode(user_id)}", body, @json_content_type) do + {:ok, user} + else + error -> handle_common_errors(error, "Failed to update user address") + end + end + + defp create_web3_user(address, signature) do + with token when is_binary(token) <- get_m2m_jwt(), + client = OAuth.client(token: token), + body = %{ + username: address, + password: signature, + email_verified: true, + connection: "Username-Password-Authentication", + user_metadata: %{web3_address_hash: address} + }, + {:ok, %OAuth2.Response{status_code: 201, body: user}} <- + Client.post(client, @users_path, body, @json_content_type) do + {:ok, user} + else + {:error, + %OAuth2.Response{ + status_code: 400, + body: + %{ + "errorCode" => "invalid_body", + "message" => "Payload validation error: 'Missing required property: email'." + } = body + }} -> + Logger.error([ + "Failed to create web3 user. Need to allow users without email in Username-Password-Authentication connection: ", + inspect(body) + ]) + + {:error, @misconfiguration_detected} + + error -> + handle_common_errors(error, "Failed to create web3 user") + end + end + + defp link_users(primary_user_id, secondary_identity_id, provider) do + with token when is_binary(token) <- get_m2m_jwt(), + client = OAuth.client(token: token), + body = %{ + provider: provider, + user_id: secondary_identity_id + }, + {:ok, %OAuth2.Response{status_code: 201}} <- + Client.post(client, "#{@users_path}/#{URI.encode(primary_user_id)}/identities", body, @json_content_type) do + :ok + else + error -> handle_common_errors(error, "Failed to link accounts") + end + end + + defp create_auth(user) do + conn_stub = %{private: %{auth0_user: user, auth0_token: nil}} + + %Auth{ + uid: user["user_id"], + provider: :auth0, + strategy: Auth0, + info: Auth0.info(conn_stub), + credentials: %Auth.Credentials{}, + extra: Auth0.extra(conn_stub) + } + end + + defp put_client_id_and_secret(map) do + auth0_config = Application.get_env(:ueberauth, OAuth) + + Map.merge( + map, + %{ + client_id: auth0_config[:client_id], + client_secret: auth0_config[:client_secret] + } + ) + end + + defp handle_common_errors(error, error_msg) do + case error do + nil -> + Logger.error("Failed to get M2M JWT") + {:error, @misconfiguration_detected} + + {:error, %OAuth2.Response{status_code: 403, body: %{"errorCode" => "insufficient_scope"} = body}} -> + Logger.error(["#{error_msg}. Insufficient scope: ", inspect(body)]) + {:error, @misconfiguration_detected} + + other -> + Logger.error(["#{error_msg}: ", inspect(other)]) + :error + end + end end diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index 363d98e97099..ece0d86f9f3f 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -124,7 +124,11 @@ defmodule Explorer.Mixfile do {:ueberauth, "~> 0.7"}, {:recon, "~> 2.5"}, {:varint, "~> 1.4"}, - {:blake2, "~> 1.0"} + {:blake2, "~> 1.0"}, + {:ueberauth_auth0, "~> 2.0"}, + {:oauth2, "~> 2.0"}, + {:siwe, github: "royal-markets/siwe-ex", ref: "51c9c08240eb7eea3c35693011f8d260cd9bb3be"}, + {:joken, "~> 2.6"} ] end diff --git a/apps/explorer/priv/account/migrations/20240913194307_account_v2.exs b/apps/explorer/priv/account/migrations/20240913194307_account_v2.exs new file mode 100644 index 000000000000..824f7f6c96fc --- /dev/null +++ b/apps/explorer/priv/account/migrations/20240913194307_account_v2.exs @@ -0,0 +1,50 @@ +defmodule Explorer.Repo.Account.Migrations.AccountV2 do + use Ecto.Migration + + def change do + alter table(:account_identities) do + remove(:name) + remove(:nickname) + add(:otp_sent_at, :"timestamp without time zone", null: true) + end + + alter table(:account_custom_abis) do + add(:user_created, :boolean, default: true) + end + + alter table(:account_tag_addresses) do + add(:user_created, :boolean, default: true) + end + + alter table(:account_tag_transactions) do + add(:user_created, :boolean, default: true) + end + + alter table(:account_watchlist_addresses) do + add(:user_created, :boolean, default: true) + end + + drop_if_exists(unique_index(:account_custom_abis, [:identity_id, :address_hash_hash])) + drop_if_exists(unique_index(:account_tag_addresses, [:identity_id, :address_hash_hash])) + drop_if_exists(unique_index(:account_tag_transactions, [:identity_id, :tx_hash_hash])) + + drop_if_exists( + unique_index(:account_watchlist_addresses, [:watchlist_id, :address_hash_hash], + name: "unique_watchlist_id_address_hash_hash_index" + ) + ) + + create(unique_index(:account_custom_abis, [:identity_id, :address_hash_hash], where: "user_created = true")) + + create(unique_index(:account_tag_addresses, [:identity_id, :address_hash_hash], where: "user_created = true")) + + create(unique_index(:account_tag_transactions, [:identity_id, :tx_hash_hash], where: "user_created = true")) + + create( + unique_index(:account_watchlist_addresses, [:watchlist_id, :address_hash_hash], + name: "unique_watchlist_id_address_hash_hash_index", + where: "user_created = true" + ) + ) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/models/user_from_auth_test.exs b/apps/explorer/test/explorer/account/identity_test.exs similarity index 89% rename from apps/block_scout_web/test/block_scout_web/models/user_from_auth_test.exs rename to apps/explorer/test/explorer/account/identity_test.exs index 2d13f116009e..1acde2e96607 100644 --- a/apps/block_scout_web/test/block_scout_web/models/user_from_auth_test.exs +++ b/apps/explorer/test/explorer/account/identity_test.exs @@ -1,9 +1,7 @@ -defmodule UserFromAuthTest do +defmodule Explorer.Account.IdentityTest do use Explorer.DataCase - alias BlockScoutWeb.Models.UserFromAuth - alias Explorer.Account.Identity - alias Explorer.Account.Watchlist + alias Explorer.Account.{Identity, Watchlist} alias Explorer.Repo alias Ueberauth.Auth alias Ueberauth.Auth.Info @@ -30,12 +28,11 @@ defmodule UserFromAuthTest do uid: "github|666666" } - user_data = UserFromAuth.find_or_create(auth) + user_data = Identity.find_or_create(auth) %{ id: identity_id, email: "john@blockscout.com", - name: "John Snow", uid: "github|666666" } = Identity |> first |> Repo.account_repo().one() @@ -77,12 +74,11 @@ defmodule UserFromAuthTest do uid: "google-oauth2|666666" } - user_data = UserFromAuth.find_or_create(auth) + user_data = Identity.find_or_create(auth) %{ id: identity_id, email: "john@blockscout.com", - name: "John Snow", uid: "google-oauth2|666666" } = Identity |> first |> Repo.account_repo().one() diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index d1e855e23a70..c1390bfb356e 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -60,7 +60,7 @@ defmodule Explorer.Factory do alias Explorer.Utility.{MissingBalanceOfToken, MissingBlockRange} alias Ueberauth.Strategy.Auth0 - alias Ueberauth.Auth.Info + alias Ueberauth.Auth.{Extra, Info} alias Ueberauth.Auth if Application.compile_env(:explorer, :chain_type) == :zksync do @@ -78,13 +78,20 @@ defmodule Explorer.Factory do end def auth_factory do + email = sequence(:email, &"test_user-#{&1}@blockscout.com") + image = sequence("https://example.com/avatar/test_user") + name = sequence("User Test") + nickname = sequence("test_user") + uid = sequence("blockscout|000") + address_hash = to_string(build(:contract_address).hash) + %Auth{ info: %Info{ birthday: nil, description: nil, - email: sequence(:email, &"test_user-#{&1}@blockscout.com"), + email: email, first_name: nil, - image: sequence("https://example.com/avatar/test_user"), + image: image, last_name: nil, location: nil, name: sequence("User Test"), @@ -94,7 +101,36 @@ defmodule Explorer.Factory do }, provider: :auth0, strategy: Auth0, - uid: sequence("blockscout|000") + uid: uid, + extra: %Extra{ + raw_info: %{ + user: %{ + "created_at" => "2024-09-06T13:49:20.481Z", + "email" => email, + "email_verified" => true, + "identities" => [ + %{ + "connection" => "email", + "isSocial" => false, + "provider" => "email", + "user_id" => "66db0852af53e2c0ae80ddb2" + } + ], + "last_ip" => "42.42.42.42", + "last_login" => "2024-09-14T12:14:26.965Z", + "logins_count" => 11, + "name" => name, + "nickname" => nickname, + "picture" => image, + "updated_at" => "2024-09-14T12:14:26.966Z", + "user_id" => uid, + "user_metadata" => %{ + "web3_address_hash" => address_hash + } + }, + token: nil + } + } } end diff --git a/config/runtime.exs b/config/runtime.exs index 8f92fec804d8..aebdeff2475f 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -43,7 +43,8 @@ config :block_scout_web, :recaptcha, v2_secret_key: System.get_env("RE_CAPTCHA_SECRET_KEY"), v3_client_key: System.get_env("RE_CAPTCHA_V3_CLIENT_KEY"), v3_secret_key: System.get_env("RE_CAPTCHA_V3_SECRET_KEY"), - is_disabled: ConfigHelper.parse_bool_env_var("RE_CAPTCHA_DISABLED") + is_disabled: ConfigHelper.parse_bool_env_var("RE_CAPTCHA_DISABLED"), + check_hostname?: ConfigHelper.parse_bool_env_var("RE_CAPTCHA_CHECK_HOSTNAME", "true") network_path = "NETWORK_PATH" @@ -553,7 +554,9 @@ config :explorer, Explorer.Account, sender: System.get_env("ACCOUNT_SENDGRID_SENDER"), template: System.get_env("ACCOUNT_SENDGRID_TEMPLATE") ], - resend_interval: ConfigHelper.parse_time_env_var("ACCOUNT_VERIFICATION_EMAIL_RESEND_INTERVAL", "5m"), + verification_email_resend_interval: + ConfigHelper.parse_time_env_var("ACCOUNT_VERIFICATION_EMAIL_RESEND_INTERVAL", "5m"), + otp_resend_interval: ConfigHelper.parse_time_env_var("ACCOUNT_OTP_RESEND_INTERVAL", "1m"), private_tags_limit: ConfigHelper.parse_integer_env_var("ACCOUNT_PRIVATE_TAGS_LIMIT", 2000), watchlist_addresses_limit: ConfigHelper.parse_integer_env_var("ACCOUNT_WATCHLIST_ADDRESSES_LIMIT", 15), notifications_limit_for_30_days: diff --git a/cspell.json b/cspell.json index 30818229bc8f..11c477faf352 100644 --- a/cspell.json +++ b/cspell.json @@ -272,6 +272,7 @@ "ipos", "itxs", "johnnny", + "joken", "jsons", "juon", "Karnaugh", @@ -383,6 +384,7 @@ "opos", "outcoming", "overengineering", + "passwordless", "pawesome", "paych", "pbcopy", @@ -480,6 +482,7 @@ "shibarium", "shortdoc", "shortify", + "siwe", "SJONRPC", "smallint", "smth", @@ -590,6 +593,7 @@ "upserts", "urijs", "urlset", + "userinfo", "Utqn", "UUPS", "valign", diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 4c4bdafd8561..b4778591b233 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -351,6 +351,7 @@ RE_CAPTCHA_CLIENT_KEY= RE_CAPTCHA_V3_SECRET_KEY= RE_CAPTCHA_V3_CLIENT_KEY= RE_CAPTCHA_DISABLED=false +# RE_CAPTCHA_CHECK_HOSTNAME JSON_RPC= # API_RATE_LIMIT_HAMMER_REDIS_URL=redis://redis-db:6379/1 # API_RATE_LIMIT_IS_BLOCKSCOUT_BEHIND_PROXY=false @@ -386,6 +387,7 @@ DECODE_NOT_A_CONTRACT_CALLS=true # ACCOUNT_SENDGRID_SENDER= # ACCOUNT_SENDGRID_TEMPLATE= # ACCOUNT_VERIFICATION_EMAIL_RESEND_INTERVAL= +# ACCOUNT_OTP_RESEND_INTERVAL= # ACCOUNT_PRIVATE_TAGS_LIMIT=2000 # ACCOUNT_WATCHLIST_ADDRESSES_LIMIT=15 ACCOUNT_CLOAK_KEY= diff --git a/mix.lock b/mix.lock index 275a41c666b6..fe3eb84045d5 100644 --- a/mix.lock +++ b/mix.lock @@ -62,6 +62,7 @@ "exvcr": {:hex, :exvcr, "0.15.2", "2216c8605b5c3e300160c2a5bd896b4928fa51fc3fb3420d3e792ad833ac89ba", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:finch, "~> 0.16", [hex: :finch, repo: "hexpm", optional: true]}, {:httpoison, "~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "2bd4125889bd3953d7fbb7b388c34190c31e292f12896da56ecf0743d40439ed"}, "file_info": {:hex, :file_info, "0.0.4", "2e0e77f211e833f38ead22cb29ce53761d457d80b3ffe0ffe0eb93880b0963b2", [:mix], [{:mimetype_parser, "~> 0.1.2", [hex: :mimetype_parser, repo: "hexpm", optional: false]}], "hexpm", "50e7ad01c2c8b9339010675fe4dc4a113b8d6ca7eddce24d1d74fd0e762781a5"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, + "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, "floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"}, "flow": {:hex, :flow, "1.2.4", "1dd58918287eb286656008777cb32714b5123d3855956f29aa141ebae456922d", [:mix], [{:gen_stage, "~> 1.0", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm", "874adde96368e71870f3510b91e35bc31652291858c86c0e75359cbdd35eb211"}, "gen_stage": {:hex, :gen_stage, "1.2.1", "19d8b5e9a5996d813b8245338a28246307fd8b9c99d1237de199d21efc4c76a1", [:mix], [], "hexpm", "83e8be657fa05b992ffa6ac1e3af6d57aa50aace8f691fcf696ff02f8335b001"}, @@ -69,11 +70,14 @@ "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~>2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, "hammer": {:hex, :hammer, "6.2.1", "5ae9c33e3dceaeb42de0db46bf505bd9c35f259c8defb03390cd7556fea67ee2", [:mix], [{:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm", "b9476d0c13883d2dc0cc72e786bac6ac28911fba7cc2e04b70ce6a6d9c4b2bdc"}, "hammer_backend_redis": {:hex, :hammer_backend_redis, "6.1.2", "eb296bb4924928e24135308b2afc189201fd09411c870c6bbadea444a49b2f2c", [:mix], [{:hammer, "~> 6.0", [hex: :hammer, repo: "hexpm", optional: false]}, {:redix, "~> 1.1", [hex: :redix, repo: "hexpm", optional: false]}], "hexpm", "217ea066278910543a5e9b577d5bf2425419446b94fe76bdd9f255f39feec9fa"}, + "hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"}, "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"}, "httpoison": {:hex, :httpoison, "2.2.1", "87b7ed6d95db0389f7df02779644171d7319d319178f6680438167d7b69b1f3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "51364e6d2f429d80e14fe4b5f8e39719cacd03eb3f9a9286e61e216feac2d2df"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, + "joken": {:hex, :joken, "2.6.2", "5daaf82259ca603af4f0b065475099ada1b2b849ff140ccd37f4b6828ca6892a", [:mix], [{:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5134b5b0a6e37494e46dbf9e4dad53808e5e787904b7c73972651b51cce3d72b"}, + "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm", "fc3499fed7a726995aa659143a248534adc754ebd16ccd437cd93b649a95091f"}, "junit_formatter": {:hex, :junit_formatter, "3.4.0", "d0e8db6c34dab6d3c4154c3b46b21540db1109ae709d6cf99ba7e7a2ce4b1ac2", [:mix], [], "hexpm", "bb36e2ae83f1ced6ab931c4ce51dd3dbef1ef61bb4932412e173b0cfa259dacd"}, "logger_file_backend": {:hex, :logger_file_backend, "0.0.14", "774bb661f1c3fed51b624d2859180c01e386eb1273dc22de4f4a155ef749a602", [:mix], [], "hexpm", "071354a18196468f3904ef09413af20971d55164267427f6257b52cfba03f9e6"}, @@ -88,6 +92,7 @@ "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, "mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"}, "mimetype_parser": {:hex, :mimetype_parser, "0.1.3", "628ac9fe56aa7edcedb534d68397dd66674ab82493c8ebe39acb9a19b666099d", [:mix], [], "hexpm", "7d8f80c567807ce78cd93c938e7f4b0a20b1aaaaab914bf286f68457d9f7a852"}, + "mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"}, "mix_erlang_tasks": {:hex, :mix_erlang_tasks, "0.1.0", "36819fec60b80689eb1380938675af215565a89320a9e29c72c70d97512e4649", [:mix], [], "hexpm", "95d2839c422c482a70c08a8702da8242f86b773f8ab6e8602a4eb72da8da04ed"}, "mock": {:hex, :mock, "0.3.8", "7046a306b71db2488ef54395eeb74df0a7f335a7caca4a3d3875d1fc81c884dd", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "7fa82364c97617d79bb7d15571193fc0c4fe5afd0c932cef09426b3ee6fe2022"}, "mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"}, @@ -95,6 +100,7 @@ "nimble_csv": {:hex, :nimble_csv, "1.2.0", "4e26385d260c61eba9d4412c71cea34421f296d5353f914afe3f2e71cce97722", [:mix], [], "hexpm", "d0628117fcc2148178b034044c55359b26966c6eaa8e2ce15777be3bbc91b12a"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, + "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "number": {:hex, :number, "1.0.5", "d92136f9b9382aeb50145782f116112078b3465b7be58df1f85952b8bb399b0f", [:mix], [{:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "c0733a0a90773a66582b9e92a3f01290987f395c972cb7d685f51dd927cd5169"}, "numbers": {:hex, :numbers, "5.2.4", "f123d5bb7f6acc366f8f445e10a32bd403c8469bdbce8ce049e1f0972b607080", [:mix], [{:coerce, "~> 1.0", [hex: :coerce, repo: "hexpm", optional: false]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "eeccf5c61d5f4922198395bf87a465b6f980b8b862dd22d28198c5e6fab38582"}, "oauth2": {:hex, :oauth2, "2.0.1", "70729503e05378697b958919bb2d65b002ba6b28c8112328063648a9348aaa3f", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "c64e20d4d105bcdbcbe03170fb530d0eddc3a3e6b135a87528a22c8aecf74c52"}, @@ -122,11 +128,12 @@ "quantile_estimator": {:hex, :quantile_estimator, "0.2.1", "ef50a361f11b5f26b5f16d0696e46a9e4661756492c981f7b2229ef42ff1cd15", [:rebar3], [], "hexpm", "282a8a323ca2a845c9e6f787d166348f776c1d4a41ede63046d72d422e3da946"}, "que": {:hex, :que, "0.10.1", "788ed0ec92ed69bdf9cfb29bf41a94ca6355b8d44959bd0669cf706e557ac891", [:mix], [{:ex_utils, "~> 0.1.6", [hex: :ex_utils, repo: "hexpm", optional: false]}, {:memento, "~> 0.3.0", [hex: :memento, repo: "hexpm", optional: false]}], "hexpm", "a737b365253e75dbd24b2d51acc1d851049e87baae08cd0c94e2bc5cd65088d5"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, - "ratio": {:hex, :ratio, "2.4.2", "c8518f3536d49b1b00d88dd20d49f8b11abb7819638093314a6348139f14f9f9", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:numbers, "~> 5.2.0", [hex: :numbers, repo: "hexpm", optional: false]}], "hexpm", "441ef6f73172a3503de65ccf1769030997b0d533b1039422f1e5e0e0b4cbf89e"}, "recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"}, "redix": {:hex, :redix, "1.5.2", "ab854435a663f01ce7b7847f42f5da067eea7a3a10c0a9d560fa52038fd7ab48", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:nimble_options, "~> 0.5.0 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "78538d184231a5d6912f20567d76a49d1be7d3fca0e1aaaa20f4df8e1142dcb8"}, "remote_ip": {:hex, :remote_ip, "1.2.0", "fb078e12a44414f4cef5a75963c33008fe169b806572ccd17257c208a7bc760f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2ff91de19c48149ce19ed230a81d377186e4412552a597d6a5137373e5877cb7"}, + "req": {:hex, :req, "0.5.6", "8fe1eead4a085510fe3d51ad854ca8f20a622aae46e97b302f499dfb84f726ac", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "cfaa8e720945d46654853de39d368f40362c2641c4b2153c886418914b372185"}, "rustler_precompiled": {:hex, :rustler_precompiled, "0.8.1", "8afe0b6f3a9a677ada046cdd23e3f4c6399618b91a6122289324774961281e1e", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "90b8c2297bf7959cfa1c927b2881faad7bb0707183124955369991b76177a166"}, + "siwe": {:git, "https://github.com/royal-markets/siwe-ex.git", "51c9c08240eb7eea3c35693011f8d260cd9bb3be", [ref: "51c9c08240eb7eea3c35693011f8d260cd9bb3be"]}, "sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"}, "spandex": {:hex, :spandex, "3.2.0", "f8cd40146ea988c87f3c14054150c9a47ba17e53cd4515c00e1f93c29c45404d", [:mix], [{:decorator, "~> 1.2", [hex: :decorator, repo: "hexpm", optional: true]}, {:optimal, "~> 0.3.3", [hex: :optimal, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d0a7d5aef4c5af9cf5467f2003e8a5d8d2bdae3823a6cc95d776b9a2251d4d03"}, "spandex_datadog": {:hex, :spandex_datadog, "1.4.0", "0594b9655b0af00ab9137122616bc0208b68ceec01e9916ab13d6fbb33dcce35", [:mix], [{:msgpax, "~> 2.2.1 or ~> 2.3", [hex: :msgpax, repo: "hexpm", optional: false]}, {:spandex, "~> 3.2", [hex: :spandex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "360f8e1b4db238c1749c4872b1697b096429927fa42b8858d0bb782067380123"}, From 6319514bcfcf5dba16d529c99ee75823181e906c Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 22 Oct 2024 17:34:12 +0300 Subject: [PATCH 242/363] fix: Add compatibility with current frontend for some public props (#10998) * fix: Fix renaming of public props * Fix sanitize_duplicated_log_index_logs_test.exs --------- Co-authored-by: Nikita Pozdniakov --- .../controllers/api/v2/address_controller.ex | 14 ++- apps/explorer/config/config.exs | 1 + apps/explorer/config/runtime/test.exs | 1 + apps/explorer/lib/encrypt.ex | 116 ------------------ .../lib/explorer/account/notifier/email.ex | 2 + apps/explorer/lib/explorer/application.ex | 4 +- .../sanitize_duplicated_log_index_logs.ex | 59 +-------- ...or_duplicated_log_index_logs_migration.exs | 30 +++++ .../explorer/account/notifier/email_test.exs | 2 + ...nitize_duplicated_log_index_logs_test.exs} | 0 10 files changed, 52 insertions(+), 177 deletions(-) delete mode 100644 apps/explorer/lib/encrypt.ex create mode 100644 apps/explorer/priv/repo/migrations/20241022133006_add_aux_types_for_duplicated_log_index_logs_migration.exs rename apps/explorer/test/explorer/migrator/{sanitize_duplicated_log_index_logs_test.ex => sanitize_duplicated_log_index_logs_test.exs} (100%) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index 97ea24054eac..40b4b7c40eb4 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -511,7 +511,9 @@ defmodule BlockScoutWeb.API.V2.AddressController do token_balances: :token_balances_count, logs: :logs_count, withdrawals: :withdrawals_count, - internal_transactions: :internal_transactions_count, + # todo: support of 2 props in API endpoint is for compatibility with the current version of frontend. + # It should be ultimately removed. + internal_transactions: [:internal_transactions_count, :internal_txs_count], celo_election_rewards: :celo_election_rewards_count } @@ -523,7 +525,15 @@ defmodule BlockScoutWeb.API.V2.AddressController do |> Map.fetch(counter_name) |> case do {:ok, json_field_name} -> - Map.put(acc, json_field_name, counter_value) + # todo: array-type value processing here is temporary. Please remove it with updating frontend to the new version. + if is_list(json_field_name) do + # credo:disable-for-next-line + Enum.reduce(json_field_name, acc, fn field_name, acc2 -> + Map.put(acc2, field_name, counter_value) + end) + else + Map.put(acc, json_field_name, counter_value) + end :error -> acc diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index 3fd5e07120f1..681086d3506c 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -131,6 +131,7 @@ config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, enabled: true config :explorer, Explorer.Migrator.SanitizeMissingTokenBalances, enabled: true config :explorer, Explorer.Migrator.SanitizeReplacedTransactions, enabled: true config :explorer, Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, enabled: true +config :explorer, Explorer.Migrator.SanitizeDuplicatedLogIndexLogs, enabled: true config :explorer, Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand, enabled: true diff --git a/apps/explorer/config/runtime/test.exs b/apps/explorer/config/runtime/test.exs index a46bd5004fcb..177cfd6b2bd8 100644 --- a/apps/explorer/config/runtime/test.exs +++ b/apps/explorer/config/runtime/test.exs @@ -52,6 +52,7 @@ config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, enabled: false config :explorer, Explorer.Migrator.SanitizeMissingTokenBalances, enabled: false config :explorer, Explorer.Migrator.SanitizeReplacedTransactions, enabled: false config :explorer, Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, enabled: false +config :explorer, Explorer.Migrator.SanitizeDuplicatedLogIndexLogs, enabled: false config :explorer, realtime_events_sender: Explorer.Chain.Events.SimpleSender diff --git a/apps/explorer/lib/encrypt.ex b/apps/explorer/lib/encrypt.ex deleted file mode 100644 index ba8073ba4e62..000000000000 --- a/apps/explorer/lib/encrypt.ex +++ /dev/null @@ -1,116 +0,0 @@ -defmodule Mix.Tasks.Encrypt do - @moduledoc "The encrypt mix task: `mix help encrypt`" - use Mix.Task - - alias Ecto.Changeset - - alias Explorer.Account.{ - CustomABI, - Identity, - PublicTagsRequest, - TagAddress, - TagTransaction, - WatchlistAddress, - WatchlistNotification - } - - alias Explorer.Repo.Account - alias Mix.Task - - @shortdoc "Encrypt" - def run(_) do - Task.run("app.start") - - Identity - |> Account.all() - |> Enum.each(fn element -> - element - |> Changeset.change(%{ - encrypted_uid: element.uid, - encrypted_email: element.email, - encrypted_name: element.name, - encrypted_nickname: element.nickname, - encrypted_avatar: element.avatar, - uid_hash: element.uid - }) - |> Account.update!() - end) - - TagAddress - |> Account.all() - |> Enum.each(fn element -> - element - |> Changeset.change(%{ - encrypted_name: element.name, - encrypted_address_hash: element.address_hash, - address_hash_hash: element.address_hash |> to_string() |> String.downcase() - }) - |> Account.update!() - end) - - TagTransaction - |> Account.all() - |> Enum.each(fn element -> - element - |> Changeset.change(%{ - encrypted_name: element.name, - encrypted_transaction_hash: element.transaction_hash, - transaction_hash_hash: element.transaction_hash |> to_string() |> String.downcase() - }) - |> Account.update!() - end) - - CustomABI - |> Account.all() - |> Enum.each(fn element -> - element - |> Changeset.change(%{ - encrypted_name: element.name, - encrypted_address_hash: element.address_hash, - address_hash_hash: element.address_hash |> to_string() |> String.downcase() - }) - |> Account.update!() - end) - - WatchlistAddress - |> Account.all() - |> Enum.each(fn element -> - element - |> Changeset.change(%{ - encrypted_name: element.name, - encrypted_address_hash: element.address_hash, - address_hash_hash: element.address_hash |> to_string() |> String.downcase() - }) - |> Account.update!() - end) - - WatchlistNotification - |> Account.all() - |> Enum.each(fn element -> - element - |> Changeset.change(%{ - encrypted_name: element.name, - encrypted_from_address_hash: element.from_address_hash, - encrypted_to_address_hash: element.to_address_hash, - encrypted_transaction_hash: element.transaction_hash, - encrypted_subject: element.subject, - from_address_hash_hash: element.from_address_hash |> to_string() |> String.downcase(), - to_address_hash_hash: element.to_address_hash |> to_string() |> String.downcase(), - transaction_hash_hash: element.transaction_hash |> to_string() |> String.downcase(), - subject_hash: element.subject - }) - |> Account.update!() - end) - - PublicTagsRequest - |> Account.all() - |> Enum.each(fn element -> - element - |> Changeset.change(%{ - encrypted_full_name: element.full_name, - encrypted_email: element.email - }) - |> Account.update!() - end) - end -end diff --git a/apps/explorer/lib/explorer/account/notifier/email.ex b/apps/explorer/lib/explorer/account/notifier/email.ex index b1f9606936bf..8f48c05b6e73 100644 --- a/apps/explorer/lib/explorer/account/notifier/email.ex +++ b/apps/explorer/lib/explorer/account/notifier/email.ex @@ -35,6 +35,8 @@ defmodule Explorer.Account.Notifier.Email do |> add_dynamic_field("block_number", notification.block_number) |> add_dynamic_field("amount", amount(notification)) |> add_dynamic_field("name", notification.name) + # todo: keep next line for compatibility with old version of SendGrid template. Remove it when the changes released and Sendgrid template updated. + |> add_dynamic_field("tx_fee", notification.transaction_fee) |> add_dynamic_field("transaction_fee", notification.transaction_fee) |> add_dynamic_field("direction", direction(notification)) |> add_dynamic_field("method", notification.method) diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index b2645f4c30d8..ac5ade4ad4b9 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -146,7 +146,9 @@ defmodule Explorer.Application do configure_mode_dependent_process(Explorer.Migrator.ShrinkInternalTransactions, :indexer), configure_chain_type_dependent_process(Explorer.Chain.Cache.BlackfortValidatorsCounters, :blackfort), configure_chain_type_dependent_process(Explorer.Chain.Cache.StabilityValidatorsCounters, :stability), - configure_chain_type_dependent_process(Explorer.Migrator.SanitizeDuplicatedLogIndexLogs, [ + Explorer.Migrator.SanitizeDuplicatedLogIndexLogs + |> configure() + |> configure_chain_type_dependent_process([ :polygon_zkevm, :rsk, :filecoin diff --git a/apps/explorer/lib/explorer/migrator/sanitize_duplicated_log_index_logs.ex b/apps/explorer/lib/explorer/migrator/sanitize_duplicated_log_index_logs.ex index 2ab9203799e4..7ecfadb4e3bb 100644 --- a/apps/explorer/lib/explorer/migrator/sanitize_duplicated_log_index_logs.ex +++ b/apps/explorer/lib/explorer/migrator/sanitize_duplicated_log_index_logs.ex @@ -199,11 +199,7 @@ defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogs do logs |> Enum.sort_by(&{&1.transaction.index, &1.index, &1.transaction_hash}) - # credo:disable-for-next-line Credo.Check.Refactor.Nesting - |> Enum.map_reduce(0, fn log, index -> - {{log, index}, index + 1} - end) - |> elem(0) + |> Enum.with_index(&{&1, &2}) end end @@ -215,57 +211,4 @@ defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogs do defp token_transfer_to_index(token_transfer) do {token_transfer.transaction_hash, token_transfer.block_hash, token_transfer.log_index} end - - @doc """ - Callback function that is executed before the migration process starts. - """ - @impl FillingMigration - def before_start do - """ - DO $$ - BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'log_id') THEN - CREATE TYPE log_id AS ( - transaction_hash bytea, - block_hash bytea, - log_index integer - ); - END IF; - END$$; - """ - |> Repo.query!() - - """ - DO $$ - BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'nft_id') THEN - CREATE TYPE nft_id AS ( - block_number bigint, - log_index integer - ); - END IF; - END$$; - """ - |> Repo.query!() - - :ok - end - - @doc """ - Callback function that is executed when the migration process finishes. - """ - @impl FillingMigration - def on_finish do - """ - DROP TYPE log_id; - """ - |> Repo.query!([], timeout: :infinity) - - """ - DROP TYPE nft_id; - """ - |> Repo.query!([], timeout: :infinity) - - :ok - end end diff --git a/apps/explorer/priv/repo/migrations/20241022133006_add_aux_types_for_duplicated_log_index_logs_migration.exs b/apps/explorer/priv/repo/migrations/20241022133006_add_aux_types_for_duplicated_log_index_logs_migration.exs new file mode 100644 index 000000000000..f5684a9f45fc --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20241022133006_add_aux_types_for_duplicated_log_index_logs_migration.exs @@ -0,0 +1,30 @@ +defmodule Explorer.Repo.Migrations.AddAuxTypesForDuplicatedLogIndexLogsMigration do + use Ecto.Migration + + def up do + execute(""" + CREATE TYPE log_id AS ( + transaction_hash bytea, + block_hash bytea, + log_index integer + ); + """) + + execute(""" + CREATE TYPE nft_id AS ( + block_number bigint, + log_index integer + ); + """) + end + + def down do + execute(""" + DROP TYPE log_id; + """) + + execute(""" + DROP TYPE nft_id; + """) + end +end diff --git a/apps/explorer/test/explorer/account/notifier/email_test.exs b/apps/explorer/test/explorer/account/notifier/email_test.exs index 387aab7e6fab..7d7b8d574fbb 100644 --- a/apps/explorer/test/explorer/account/notifier/email_test.exs +++ b/apps/explorer/test/explorer/account/notifier/email_test.exs @@ -110,6 +110,8 @@ defmodule Explorer.Account.Notifier.EmailTest do "transaction_url" => "https://eth.blockscout.com/tx/0x5d5ff210261f1b2d6e4af22ea494f428f9997d4ab614a629d4f1390004b3e80d", "transaction_fee" => Decimal.new(210_000), + # todo: keep next line for compatibility with old version of SendGrid template. Remove it when the changes released and Sendgrid template updated. + "tx_fee" => Decimal.new(210_000), "username" => "John Snow" }, template_id: "d-666" diff --git a/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.ex b/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs similarity index 100% rename from apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.ex rename to apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs From eafe7c8b3bb0f8f9ee022a11611c31410c65cf4c Mon Sep 17 00:00:00 2001 From: varasev <33550681+varasev@users.noreply.github.com> Date: Tue, 22 Oct 2024 19:33:44 +0400 Subject: [PATCH 243/363] feat: Scroll rollup: L1 fee parameters in API, `queueIndex` for L2 transactions, and L1 <->L2 messages (#10484) * Add chain type for scroll and l1_fee tx field * l1_fee_scalar and l1_fee_overhead fields in api json output for tx * l2_fee field in api json output for tx * l1_gas_used field in api json output for tx * l1_fee_commit_scalar, l1_fee_blob_scalar, l1_base_fee, l1_blob_base_fee fields in api json output for tx * Partially add specs and docs * Add code specs and docs for Indexer.Fetcher.Scroll.L1FeeParam * Add code specs and docs * Small fix * Add CI matrix chain type * Fix spelling * Rename envs * Update common-blockscout.env * Small fixes * Improve scroll_view.ex * Update scroll_view.ex * Add support of queueIndex field for L1MessageTx in Scroll * Fix for mix credo * Add scroll_bridge db table * Add Scroll bridge messages indexing * Add Indexer.Transform.Scroll.Bridge * Refactoring * Add API for Scroll * Ignore credo warning about nesting level * Add specs and docs * Refactoring * Partially cover review comments * Add some specs and docs * Add GA workflows for scroll * Small refactoring * Add to_import for Explorer.Chain.Scroll.Bridge * Small refactoring * Add specs and docs * Add comments * Add Explorer.Chain.RollupReorgMonitorQueue module * Add sobelow_skip * Add INDEXER_SCROLL_ETH_GET_LOGS_RANGE_SIZE env variable * Update common-blockscout.env * Small refactoring * Add links about Curie upgrade * Small improvement * Small refactoring * Separate db query * Move chain type specific import runners from BlockReferencing to ChainTypeSpecific stage * Add draft indexer for Scroll batches * Add API for Scroll batches * Extend API for Scroll batches * Refactoring * Update common-blockscout.env * Add specs and docs * Remove misprint * Fixes * Small improvement * Refactoring * Refactoring * Refactoring * Refactoring for credo * Partially cover review comments * Add l2_block_status to transaction view for Scroll * RollupL1ReorgMonitor refactoring * Extend docs * Small refactoring of last_l2_block_number function * Extend docs * Extend docs * Fix typo * mix format * Extend docs * Extend docs * Separate eth_getLogs block range size config * Small refactoring * Add comment * Add comments * Optimization * Small optimization * Use Ecto.Multi * Extend docs * Change API response for Scroll batches * Change API response format for Scroll bridge messages * Small refactoring * Update apps/indexer/lib/indexer/transform/scroll/l1_fee_params.ex Co-authored-by: Alexander Kolotov * Update apps/indexer/lib/indexer/fetcher/scroll/l1_fee_param.ex Co-authored-by: Alexander Kolotov * Update apps/indexer/lib/indexer/fetcher/scroll/l1_fee_param.ex Co-authored-by: Alexander Kolotov * Update apps/indexer/lib/indexer/fetcher/scroll/l1_fee_param.ex Co-authored-by: Alexander Kolotov * Update apps/indexer/lib/indexer/fetcher/scroll/bridge.ex Co-authored-by: Alexander Kolotov * Update apps/explorer/lib/explorer/chain/scroll/reader.ex Co-authored-by: Alexander Kolotov * Update apps/explorer/lib/explorer/chain/scroll/reader.ex Co-authored-by: Alexander Kolotov * Update apps/explorer/lib/explorer/chain/scroll/bridge.ex Co-authored-by: Alexander Kolotov * Update apps/explorer/lib/explorer/chain/scroll/batch.ex Co-authored-by: Alexander Kolotov * Update apps/explorer/lib/explorer/chain/scroll/batch_bundle.ex Co-authored-by: Alexander Kolotov * Update apps/indexer/lib/indexer/fetcher/scroll/bridge.ex Co-authored-by: Alexander Kolotov * Update apps/indexer/lib/indexer/fetcher/scroll/bridge.ex Co-authored-by: Alexander Kolotov * Update apps/indexer/lib/indexer/fetcher/scroll/bridge.ex Co-authored-by: Alexander Kolotov * Update apps/indexer/lib/indexer/fetcher/scroll/bridge.ex Co-authored-by: Alexander Kolotov * Update apps/indexer/lib/indexer/fetcher/scroll/bridge.ex Co-authored-by: Alexander Kolotov * Update apps/indexer/lib/indexer/fetcher/scroll/batch.ex Co-authored-by: Alexander Kolotov * Update apps/indexer/lib/indexer/fetcher/scroll/batch.ex Co-authored-by: Alexander Kolotov * Update apps/indexer/lib/indexer/fetcher/scroll/batch.ex Co-authored-by: Alexander Kolotov * Update apps/indexer/lib/indexer/fetcher/scroll/batch.ex Co-authored-by: Alexander Kolotov * Small refactoring * Extend docs * Update apps/explorer/lib/explorer/chain/scroll/reader.ex Co-authored-by: Alexander Kolotov * Update apps/indexer/lib/indexer/helper.ex Co-authored-by: Alexander Kolotov * Update apps/explorer/lib/explorer/chain/scroll/reader.ex Co-authored-by: Alexander Kolotov * Update apps/explorer/lib/explorer/chain/scroll/reader.ex Co-authored-by: Alexander Kolotov * Improve specs * Update apps/explorer/lib/explorer/chain/scroll/reader.ex Co-authored-by: Alexander Kolotov * fix * mix format * Add DA container info for Scroll batches * Fallback INDEXER_SCROLL_L2_MESSENGER_START_BLOCK to FIRST_BLOCK * mix format * Remove redundant lines * Use multi update_all * rename "tx" shorthands --------- Co-authored-by: POA <33550681+poa@users.noreply.github.com> Co-authored-by: Viktor Baranov Co-authored-by: Alexander Kolotov --- .github/workflows/config.yml | 2 +- .github/workflows/pre-release-scroll.yml | 97 +++ .../publish-docker-image-for-scroll.yml | 162 +++++ .github/workflows/release-scroll.yml | 164 +++++ .../lib/block_scout_web/chain.ex | 12 +- .../controllers/api/v2/block_controller.ex | 28 + .../controllers/api/v2/scroll_controller.ex | 154 +++++ .../api/v2/transaction_controller.ex | 103 ++- .../lib/block_scout_web/routers/api_router.ex | 20 + .../views/api/v2/scroll_view.ex | 204 ++++++ .../views/api/v2/transaction_view.ex | 14 + .../lib/ethereum_jsonrpc/receipt.ex | 21 + .../lib/ethereum_jsonrpc/receipts.ex | 3 + .../lib/ethereum_jsonrpc/transaction.ex | 17 +- .../test/ethereum_jsonrpc/receipts_test.exs | 6 +- apps/explorer/config/dev.exs | 3 + apps/explorer/config/prod.exs | 5 + apps/explorer/config/test.exs | 1 + apps/explorer/lib/explorer/application.ex | 1 + .../lib/explorer}/bound_queue.ex | 2 +- apps/explorer/lib/explorer/chain/import.ex | 3 +- .../import/runner/scroll/batch_bundles.ex | 106 ++++ .../chain/import/runner/scroll/batches.ex | 110 ++++ .../import/runner/scroll/bridge_operations.ex | 111 ++++ .../import/runner/scroll/l1_fee_params.ex | 102 +++ .../chain/import/runner/transactions.ex | 74 +++ .../chain/import/stage/block_referencing.ex | 85 +-- .../chain/import/stage/chain_type_specific.ex | 88 +++ .../chain/rollup_reorg_monitor_queue.ex | 91 +++ .../lib/explorer/chain/scroll/batch.ex | 82 +++ .../lib/explorer/chain/scroll/batch_bundle.ex | 58 ++ .../lib/explorer/chain/scroll/bridge.ex | 78 +++ .../lib/explorer/chain/scroll/l1_fee_param.ex | 135 ++++ .../lib/explorer/chain/scroll/reader.ex | 427 +++++++++++++ .../lib/explorer/chain/transaction.ex | 64 +- apps/explorer/lib/explorer/repo.ex | 10 + .../20240710101931_add_fee_fields.exs | 23 + .../20240807114346_add_queue_index_field.exs | 9 + .../20240815102318_add_bridge_table.exs | 24 + .../20240913114630_add_batches_tables.exs | 40 ++ .../20241016105249_rename_fields.exs | 7 + apps/indexer/lib/indexer/block/fetcher.ex | 62 +- .../lib/indexer/block/realtime/fetcher.ex | 83 +-- apps/indexer/lib/indexer/buffered_task.ex | 3 +- apps/indexer/lib/indexer/fetcher/optimism.ex | 23 +- .../indexer/fetcher/optimism/output_root.ex | 25 +- .../fetcher/optimism/transaction_batch.ex | 26 +- .../fetcher/optimism/withdrawal_event.ex | 25 +- .../lib/indexer/fetcher/polygon_edge.ex | 8 + .../indexer/fetcher/polygon_edge/deposit.ex | 21 + .../fetcher/polygon_edge/withdrawal_exit.ex | 21 + .../fetcher/polygon_zkevm/bridge_l1.ex | 24 +- .../polygon_zkevm/transaction_batch.ex | 3 +- .../fetcher/rollup_l1_reorg_monitor.ex | 182 +++--- .../lib/indexer/fetcher/scroll/batch.ex | 588 ++++++++++++++++++ .../lib/indexer/fetcher/scroll/bridge.ex | 338 ++++++++++ .../lib/indexer/fetcher/scroll/bridge_l1.ex | 206 ++++++ .../lib/indexer/fetcher/scroll/bridge_l2.ex | 155 +++++ .../lib/indexer/fetcher/scroll/helper.ex | 14 + .../indexer/fetcher/scroll/l1_fee_param.ex | 355 +++++++++++ .../lib/indexer/fetcher/shibarium/l1.ex | 24 +- apps/indexer/lib/indexer/helper.ex | 26 +- apps/indexer/lib/indexer/supervisor.ex | 16 + .../indexer/transform/scroll/l1_fee_params.ex | 69 ++ .../test/indexer/buffered_task_test.exs | 3 +- config/config_helper.exs | 2 + config/runtime.exs | 40 +- config/runtime/dev.exs | 7 + config/runtime/prod.exs | 6 + docker-compose/envs/common-blockscout.env | 17 + 70 files changed, 4815 insertions(+), 303 deletions(-) create mode 100644 .github/workflows/pre-release-scroll.yml create mode 100644 .github/workflows/publish-docker-image-for-scroll.yml create mode 100644 .github/workflows/release-scroll.yml create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v2/scroll_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/scroll_view.ex rename apps/{indexer/lib/indexer => explorer/lib/explorer}/bound_queue.ex (99%) create mode 100644 apps/explorer/lib/explorer/chain/import/runner/scroll/batch_bundles.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/scroll/batches.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/scroll/bridge_operations.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/scroll/l1_fee_params.ex create mode 100644 apps/explorer/lib/explorer/chain/import/stage/chain_type_specific.ex create mode 100644 apps/explorer/lib/explorer/chain/rollup_reorg_monitor_queue.ex create mode 100644 apps/explorer/lib/explorer/chain/scroll/batch.ex create mode 100644 apps/explorer/lib/explorer/chain/scroll/batch_bundle.ex create mode 100644 apps/explorer/lib/explorer/chain/scroll/bridge.ex create mode 100644 apps/explorer/lib/explorer/chain/scroll/l1_fee_param.ex create mode 100644 apps/explorer/lib/explorer/chain/scroll/reader.ex create mode 100644 apps/explorer/priv/scroll/migrations/20240710101931_add_fee_fields.exs create mode 100644 apps/explorer/priv/scroll/migrations/20240807114346_add_queue_index_field.exs create mode 100644 apps/explorer/priv/scroll/migrations/20240815102318_add_bridge_table.exs create mode 100644 apps/explorer/priv/scroll/migrations/20240913114630_add_batches_tables.exs create mode 100644 apps/explorer/priv/scroll/migrations/20241016105249_rename_fields.exs create mode 100644 apps/indexer/lib/indexer/fetcher/scroll/batch.ex create mode 100644 apps/indexer/lib/indexer/fetcher/scroll/bridge.ex create mode 100644 apps/indexer/lib/indexer/fetcher/scroll/bridge_l1.ex create mode 100644 apps/indexer/lib/indexer/fetcher/scroll/bridge_l2.ex create mode 100644 apps/indexer/lib/indexer/fetcher/scroll/helper.ex create mode 100644 apps/indexer/lib/indexer/fetcher/scroll/l1_fee_param.ex create mode 100644 apps/indexer/lib/indexer/transform/scroll/l1_fee_params.ex diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml index 03d8e8c4b154..45f887f1d55f 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/config.yml @@ -49,7 +49,7 @@ jobs: // Add/remove CI matrix chain types here const defaultChainTypes = ["default"]; - const chainTypes = ["ethereum", "polygon_zkevm", "rsk", "stability", "filecoin", "optimism", "arbitrum", "celo", "zetachain", "zksync", "shibarium", "blackfort"]; + const chainTypes = ["ethereum", "polygon_zkevm", "rsk", "stability", "filecoin", "optimism", "arbitrum", "celo", "zetachain", "zksync", "shibarium", "blackfort", "scroll"]; const extraChainTypes = ["suave", "polygon_edge"]; // Chain type matrix we use in master branch diff --git a/.github/workflows/pre-release-scroll.yml b/.github/workflows/pre-release-scroll.yml new file mode 100644 index 000000000000..6cb1dad63115 --- /dev/null +++ b/.github/workflows/pre-release-scroll.yml @@ -0,0 +1,97 @@ +name: Pre-release for Scroll + +on: + workflow_dispatch: + inputs: + number: + type: number + required: true + +env: + OTP_VERSION: ${{ vars.OTP_VERSION }} + ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + steps: + - uses: actions/checkout@v4 + - name: Setup repo + uses: ./.github/actions/setup-repo + id: setup + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + docker-remote-multi-platform: true + docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} + docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} + + - name: Build and push Docker image for Scroll (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-scroll:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + + - name: Build and push Docker image for Scroll (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-scroll:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + + - name: Build and push Docker image for Scroll (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-scroll:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll \ No newline at end of file diff --git a/.github/workflows/publish-docker-image-for-scroll.yml b/.github/workflows/publish-docker-image-for-scroll.yml new file mode 100644 index 000000000000..f4b7b214fa81 --- /dev/null +++ b/.github/workflows/publish-docker-image-for-scroll.yml @@ -0,0 +1,162 @@ +name: Scroll Publish Docker image + +on: + workflow_dispatch: + push: + branches: + - production-scroll +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + DOCKER_CHAIN_NAME: scroll + steps: + - uses: actions/checkout@v4 + - name: Setup repo + uses: ./.github/actions/setup-repo + id: setup + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + docker-remote-multi-platform: true + docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} + docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} + + - name: Build and push Docker image (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }} + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + + - name: Build and push Docker image (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + + - name: Build and push Docker image (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + + - name: Build and push Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/.github/workflows/release-scroll.yml b/.github/workflows/release-scroll.yml new file mode 100644 index 000000000000..490662ec1f88 --- /dev/null +++ b/.github/workflows/release-scroll.yml @@ -0,0 +1,164 @@ +name: Release for Scroll + +on: + release: + types: [published] + +env: + OTP_VERSION: ${{ vars.OTP_VERSION }} + ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + steps: + - uses: actions/checkout@v4 + - name: Setup repo + uses: ./.github/actions/setup-repo + id: setup + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + docker-remote-multi-platform: true + docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} + docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} + + - name: Build and push Docker image for Scroll (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-scroll:latest, blockscout/blockscout-scroll:${{ env.RELEASE_VERSION }} + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + + - name: Build and push Docker image for Scroll (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-scroll:${{ env.RELEASE_VERSION }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + + - name: Build and push Docker image for Scroll (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-scroll:${{ env.RELEASE_VERSION }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + + - name: Build and push Docker image for Scroll (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-scroll:${{ env.RELEASE_VERSION }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Scroll (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-scroll:${{ env.RELEASE_VERSION }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Scroll (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-scroll:${{ env.RELEASE_VERSION }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/chain.ex b/apps/block_scout_web/lib/block_scout_web/chain.ex index a02b75482915..6bb32125ba31 100644 --- a/apps/block_scout_web/lib/block_scout_web/chain.ex +++ b/apps/block_scout_web/lib/block_scout_web/chain.ex @@ -51,8 +51,8 @@ defmodule BlockScoutWeb.Chain do alias Explorer.Chain.Optimism.Deposit, as: OptimismDeposit alias Explorer.Chain.Optimism.FrameSequence, as: OptimismFrameSequence alias Explorer.Chain.Optimism.OutputRoot, as: OptimismOutputRoot + alias Explorer.Chain.Scroll.Bridge, as: ScrollBridge - alias Explorer.Chain.PolygonZkevm.TransactionBatch alias Explorer.PagingOptions defimpl Poison.Encoder, for: Decimal do @@ -441,6 +441,7 @@ defmodule BlockScoutWeb.Chain do # - Polygon Edge Deposits # - Polygon Edge Withdrawals # - Arbitrum cross chain messages + # - Scroll cross chain messages def paging_options(%{"id" => id_string}) when is_binary(id_string) do case Integer.parse(id_string) do {id, ""} -> @@ -457,6 +458,7 @@ defmodule BlockScoutWeb.Chain do # - Polygon Edge Deposits # - Polygon Edge Withdrawals # - Arbitrum cross chain messages + # - Scroll cross chain messages def paging_options(%{"id" => id}) when is_integer(id) do [paging_options: %{@default_paging_options | key: {id}}] end @@ -724,6 +726,10 @@ defmodule BlockScoutWeb.Chain do } end + defp paging_params(%ScrollBridge{index: id}) do + %{"id" => id} + end + defp paging_params(%{index: index}) do %{"index" => index} end @@ -736,8 +742,8 @@ defmodule BlockScoutWeb.Chain do %{"block_number" => block_number} end - # clause for zkEVM batches pagination - defp paging_params(%TransactionBatch{number: number}) do + # clause for zkEVM & Scroll batches pagination + defp paging_params(%{number: number}) do %{"number" => number} end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex index 56ce57306d26..e889cdf59904 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex @@ -36,6 +36,7 @@ defmodule BlockScoutWeb.API.V2.BlockController do alias Explorer.Chain.Celo.Reader, as: CeloReader alias Explorer.Chain.InternalTransaction alias Explorer.Chain.Optimism.TransactionBatch, as: OptimismTransactionBatch + alias Explorer.Chain.Scroll.Reader, as: ScrollReader case Application.compile_env(:explorer, :chain_type) do :ethereum -> @@ -225,6 +226,33 @@ defmodule BlockScoutWeb.API.V2.BlockController do }) end + @doc """ + Function to handle GET requests to `/api/v2/blocks/scroll-batch/:batch_number` endpoint. + It renders the list of L2 blocks bound to the specified batch. + """ + @spec scroll_batch(Plug.Conn.t(), any()) :: Plug.Conn.t() + def scroll_batch(conn, %{"batch_number" => batch_number} = params) do + full_options = + params + |> select_block_type() + |> Keyword.merge(paging_options(params)) + |> Keyword.merge(@api_true) + + {blocks, next_page} = + batch_number + |> ScrollReader.batch_blocks(full_options) + |> split_list_by_page() + + next_page_params = next_page |> next_page_params(blocks, delete_parameters_from_next_page_params(params)) + + conn + |> put_status(200) + |> render(:blocks, %{ + blocks: blocks |> maybe_preload_ens() |> maybe_preload_metadata(), + next_page_params: next_page_params + }) + end + @doc """ Function to handle GET requests to `/api/v2/blocks/:block_hash_or_number/transactions` endpoint. """ diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/scroll_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/scroll_controller.ex new file mode 100644 index 000000000000..1374369284ad --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/scroll_controller.ex @@ -0,0 +1,154 @@ +defmodule BlockScoutWeb.API.V2.ScrollController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Chain, + only: [ + next_page_params: 3, + paging_options: 1, + split_list_by_page: 1 + ] + + import BlockScoutWeb.PagingHelper, + only: [ + delete_parameters_from_next_page_params: 1 + ] + + alias Explorer.Chain.Scroll.Reader + + @api_true [api?: true] + + action_fallback(BlockScoutWeb.API.V2.FallbackController) + + @batch_necessity_by_association %{:bundle => :optional} + + @doc """ + Function to handle GET requests to `/api/v2/scroll/batches/:number` endpoint. + """ + @spec batch(Plug.Conn.t(), map()) :: Plug.Conn.t() + def batch(conn, %{"number" => number}) do + {number, ""} = Integer.parse(number) + + options = + [necessity_by_association: @batch_necessity_by_association] + |> Keyword.merge(@api_true) + + {_, batch} = Reader.batch(number, options) + + if batch == :not_found do + {:error, :not_found} + else + conn + |> put_status(200) + |> render(:scroll_batch, %{batch: batch}) + end + end + + @doc """ + Function to handle GET requests to `/api/v2/scroll/batches` endpoint. + """ + @spec batches(Plug.Conn.t(), map()) :: Plug.Conn.t() + def batches(conn, params) do + {batches, next_page} = + params + |> paging_options() + |> Keyword.merge(@api_true) + |> Reader.batches() + |> split_list_by_page() + + next_page_params = next_page_params(next_page, batches, delete_parameters_from_next_page_params(params)) + + conn + |> put_status(200) + |> render(:scroll_batches, %{ + batches: batches, + next_page_params: next_page_params + }) + end + + @doc """ + Function to handle GET requests to `/api/v2/scroll/batches/count` endpoint. + """ + @spec batches_count(Plug.Conn.t(), map()) :: Plug.Conn.t() + def batches_count(conn, _params) do + conn + |> put_status(200) + |> render(:scroll_batches_count, %{count: batch_latest_number() + 1}) + end + + @doc """ + Function to handle GET requests to `/api/v2/scroll/deposits` endpoint. + """ + @spec deposits(Plug.Conn.t(), map()) :: Plug.Conn.t() + def deposits(conn, params) do + {deposits, next_page} = + params + |> paging_options() + |> Keyword.merge(@api_true) + |> Reader.deposits() + |> split_list_by_page() + + next_page_params = next_page_params(next_page, deposits, params) + + conn + |> put_status(200) + |> render(:scroll_bridge_items, %{ + items: deposits, + next_page_params: next_page_params, + type: :deposits + }) + end + + @doc """ + Function to handle GET requests to `/api/v2/scroll/deposits/count` endpoint. + """ + @spec deposits_count(Plug.Conn.t(), map()) :: Plug.Conn.t() + def deposits_count(conn, _params) do + count = Reader.deposits_count(@api_true) + + conn + |> put_status(200) + |> render(:scroll_bridge_items_count, %{count: count}) + end + + @doc """ + Function to handle GET requests to `/api/v2/scroll/withdrawals` endpoint. + """ + @spec withdrawals(Plug.Conn.t(), map()) :: Plug.Conn.t() + def withdrawals(conn, params) do + {withdrawals, next_page} = + params + |> paging_options() + |> Keyword.merge(@api_true) + |> Reader.withdrawals() + |> split_list_by_page() + + next_page_params = next_page_params(next_page, withdrawals, params) + + conn + |> put_status(200) + |> render(:scroll_bridge_items, %{ + items: withdrawals, + next_page_params: next_page_params, + type: :withdrawals + }) + end + + @doc """ + Function to handle GET requests to `/api/v2/scroll/withdrawals/count` endpoint. + """ + @spec withdrawals_count(Plug.Conn.t(), map()) :: Plug.Conn.t() + def withdrawals_count(conn, _params) do + count = Reader.withdrawals_count(@api_true) + + conn + |> put_status(200) + |> render(:scroll_bridge_items_count, %{count: count}) + end + + defp batch_latest_number do + case Reader.batch(:latest, @api_true) do + {:ok, batch} -> batch.number + {:error, :not_found} -> -1 + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex index 41b7f7fa52ab..54f9991ad387 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex @@ -44,6 +44,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do alias Explorer.Chain.{Hash, InternalTransaction, Transaction} alias Explorer.Chain.Optimism.TransactionBatch, as: OptimismTransactionBatch alias Explorer.Chain.PolygonZkevm.Reader, as: PolygonZkevmReader + alias Explorer.Chain.Scroll.Reader, as: ScrollReader alias Explorer.Chain.ZkSync.Reader, as: ZkSyncReader alias Explorer.Counters.{FreshPendingTransactionsCounter, Transactions24hStats} alias Indexer.Fetcher.OnDemand.FirstTrace, as: FirstTraceOnDemand @@ -154,11 +155,11 @@ defmodule BlockScoutWeb.API.V2.TransactionController do necessity_by_association_with_actions end - with {:ok, transaction, _transaction_hash} <- - validate_transaction(transaction_hash_string, params, - necessity_by_association: necessity_by_association, - api?: true - ), + options = + [necessity_by_association: necessity_by_association] + |> Keyword.merge(@api_true) + + with {:ok, transaction, _transaction_hash} <- validate_transaction(transaction_hash_string, params, options), preloaded <- Chain.preload_token_transfers( transaction, @@ -210,11 +211,15 @@ defmodule BlockScoutWeb.API.V2.TransactionController do """ @spec polygon_zkevm_batch(Plug.Conn.t(), map()) :: Plug.Conn.t() def polygon_zkevm_batch(conn, %{"batch_number" => batch_number} = _params) do + options = + [necessity_by_association: @transaction_necessity_by_association] + |> Keyword.merge(@api_true) + transactions = batch_number - |> PolygonZkevmReader.batch_transactions(api?: true) + |> PolygonZkevmReader.batch_transactions(@api_true) |> Enum.map(fn transaction -> transaction.hash end) - |> Chain.hashes_to_transactions(api?: true, necessity_by_association: @transaction_necessity_by_association) + |> Chain.hashes_to_transactions(options) conn |> put_status(200) @@ -253,6 +258,41 @@ defmodule BlockScoutWeb.API.V2.TransactionController do l2_block_number_from = OptimismTransactionBatch.edge_l2_block_number(batch_number, :min) l2_block_number_to = OptimismTransactionBatch.edge_l2_block_number(batch_number, :max) + handle_block_range_transactions(conn, params, l2_block_number_from, l2_block_number_to) + end + + @doc """ + Function to handle GET requests to `/api/v2/transactions/scroll-batch/:batch_number` endpoint. + It renders the list of L2 transactions bound to the specified batch. + """ + @spec scroll_batch(Plug.Conn.t(), map()) :: Plug.Conn.t() + def scroll_batch(conn, %{"batch_number" => batch_number_string} = params) do + {batch_number, ""} = Integer.parse(batch_number_string) + + {l2_block_number_from, l2_block_number_to} = + case ScrollReader.batch(batch_number, @api_true) do + {:ok, batch} -> {batch.l2_block_range.from, batch.l2_block_range.to} + _ -> {nil, nil} + end + + handle_block_range_transactions(conn, params, l2_block_number_from, l2_block_number_to) + end + + # Processes and renders transactions for a specified L2 block range into an HTTP response. + # + # This function retrieves a list of transactions for a given L2 block range and formats + # these transactions into an HTTP response. + # + # ## Parameters + # - `conn`: The connection object. + # - `params`: Parameters from the request. + # - `l2_block_number_from`: Start L2 block number of the range. + # - `l2_block_number_to`: End L2 block number of the range. + # + # ## Returns + # - Updated connection object with the transactions data rendered. + @spec handle_block_range_transactions(Plug.Conn.t(), map(), non_neg_integer(), non_neg_integer()) :: Plug.Conn.t() + defp handle_block_range_transactions(conn, params, l2_block_number_from, l2_block_number_to) do transactions_plus_one = if is_nil(l2_block_number_from) or is_nil(l2_block_number_to) do [] @@ -261,8 +301,13 @@ defmodule BlockScoutWeb.API.V2.TransactionController do query = case paging_options do - %PagingOptions{key: {0, 0}, is_index_in_asc_order: false} -> [] - _ -> Transaction.fetch_transactions(paging_options, l2_block_number_from - 1, l2_block_number_to) + %PagingOptions{key: {0, 0}, is_index_in_asc_order: false} -> + [] + + _ -> + # here we need to subtract 1 because the block range inside the `fetch_transactions` function + # starts from the `from_block + 1` + Transaction.fetch_transactions(paging_options, l2_block_number_from - 1, l2_block_number_to) end query @@ -479,9 +524,9 @@ defmodule BlockScoutWeb.API.V2.TransactionController do """ @spec state_changes(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} def state_changes(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do - with {:ok, transaction, _transaction_hash} <- validate_transaction(transaction_hash_string, params, api?: true) do + with {:ok, transaction, _transaction_hash} <- validate_transaction(transaction_hash_string, params) do state_changes_plus_next_page = - transaction |> TransactionStateHelper.state_changes(params |> paging_options() |> Keyword.merge(api?: true)) + transaction |> TransactionStateHelper.state_changes(params |> paging_options() |> Keyword.merge(@api_true)) {state_changes, next_page} = split_list_by_page(state_changes_plus_next_page) @@ -525,16 +570,18 @@ defmodule BlockScoutWeb.API.V2.TransactionController do end def summary(conn, %{"transaction_hash_param" => transaction_hash_string, "just_request_body" => "true"} = params) do + options = + [ + necessity_by_association: %{ + [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + } + ] + |> Keyword.merge(@api_true) + with {:transaction_interpreter_enabled, true} <- {:transaction_interpreter_enabled, TransactionInterpretationService.enabled?()}, - {:ok, transaction, _transaction_hash} <- - validate_transaction(transaction_hash_string, params, - necessity_by_association: %{ - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional - }, - api?: true - ) do + {:ok, transaction, _transaction_hash} <- validate_transaction(transaction_hash_string, params, options) do conn |> json(TransactionInterpretationService.get_request_body(transaction)) end @@ -550,16 +597,18 @@ defmodule BlockScoutWeb.API.V2.TransactionController do | {:transaction_interpreter_enabled, boolean} | Plug.Conn.t() def summary(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do + options = + [ + necessity_by_association: %{ + [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional + } + ] + |> Keyword.merge(@api_true) + with {:transaction_interpreter_enabled, true} <- {:transaction_interpreter_enabled, TransactionInterpretationService.enabled?()}, - {:ok, transaction, _transaction_hash} <- - validate_transaction(transaction_hash_string, params, - necessity_by_association: %{ - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional - }, - api?: true - ) do + {:ok, transaction, _transaction_hash} <- validate_transaction(transaction_hash_string, params, options) do {response, code} = case TransactionInterpretationService.interpret(transaction) do {:ok, response} -> {response, 200} diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index 126a7e5cc8f9..42ec12c12df5 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -143,6 +143,10 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/optimism-batch/:batch_number", V2.TransactionController, :optimism_batch) end + if Application.compile_env(:explorer, :chain_type) == :scroll do + get("/scroll-batch/:batch_number", V2.TransactionController, :scroll_batch) + end + if Application.compile_env(:explorer, :chain_type) == :suave do get("/execution-node/:execution_node_hash_param", V2.TransactionController, :execution_node) end @@ -183,6 +187,10 @@ defmodule BlockScoutWeb.Routers.ApiRouter do if Application.compile_env(:explorer, :chain_type) == :optimism do get("/optimism-batch/:batch_number", V2.BlockController, :optimism_batch) end + + if Application.compile_env(:explorer, :chain_type) == :scroll do + get("/scroll-batch/:batch_number", V2.BlockController, :scroll_batch) + end end scope "/addresses" do @@ -274,6 +282,18 @@ defmodule BlockScoutWeb.Routers.ApiRouter do end end + scope "/scroll" do + if Application.compile_env(:explorer, :chain_type) == :scroll do + get("/batches", V2.ScrollController, :batches) + get("/batches/count", V2.ScrollController, :batches_count) + get("/batches/:number", V2.ScrollController, :batch) + get("/deposits", V2.ScrollController, :deposits) + get("/deposits/count", V2.ScrollController, :deposits_count) + get("/withdrawals", V2.ScrollController, :withdrawals) + get("/withdrawals/count", V2.ScrollController, :withdrawals_count) + end + end + scope "/shibarium" do if Application.compile_env(:explorer, :chain_type) == :shibarium do get("/deposits", V2.ShibariumController, :deposits) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/scroll_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/scroll_view.ex new file mode 100644 index 000000000000..f319f4274ace --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/scroll_view.ex @@ -0,0 +1,204 @@ +defmodule BlockScoutWeb.API.V2.ScrollView do + use BlockScoutWeb, :view + + alias BlockScoutWeb.API.V2.TransactionView + alias Explorer.Chain.Scroll.{Batch, L1FeeParam, Reader} + alias Explorer.Chain.{Data, Transaction} + + @api_true [api?: true] + + @doc """ + Function to render GET requests to `/api/v2/scroll/deposits` and `/api/v2/scroll/withdrawals` endpoints. + """ + @spec render(binary(), map()) :: map() | non_neg_integer() + def render("scroll_bridge_items.json", %{ + items: items, + next_page_params: next_page_params, + type: type + }) do + %{ + items: + Enum.map(items, fn item -> + {origination_transaction_hash, completion_transaction_hash} = + if type == :deposits do + {item.l1_transaction_hash, item.l2_transaction_hash} + else + {item.l2_transaction_hash, item.l1_transaction_hash} + end + + %{ + "id" => item.index, + "origination_transaction_hash" => origination_transaction_hash, + "origination_timestamp" => item.block_timestamp, + "origination_transaction_block_number" => item.block_number, + "completion_transaction_hash" => completion_transaction_hash, + "value" => item.amount + } + end), + next_page_params: next_page_params + } + end + + @doc """ + Function to render GET requests to `/api/v2/scroll/deposits/count` and `/api/v2/scroll/withdrawals/count` endpoints. + """ + def render("scroll_bridge_items_count.json", %{count: count}) do + count + end + + @doc """ + Function to render GET requests to `/api/v2/scroll/batches/:number` endpoint. + """ + def render("scroll_batch.json", %{batch: batch}) do + render_batch(batch) + end + + @doc """ + Function to render GET requests to `/api/v2/scroll/batches` endpoint. + """ + def render("scroll_batches.json", %{ + batches: batches, + next_page_params: next_page_params + }) do + items = + batches + |> Enum.map(fn batch -> + Task.async(fn -> render_batch(batch) end) + end) + |> Task.yield_many(:infinity) + |> Enum.map(fn {_task, {:ok, item}} -> item end) + + %{ + items: items, + next_page_params: next_page_params + } + end + + @doc """ + Function to render GET requests to `/api/v2/scroll/batches/count` endpoint. + """ + def render("scroll_batches_count.json", %{count: count}) do + count + end + + # Transforms the batch info into a map format for HTTP response. + # + # ## Parameters + # - `batch`: An instance of `Explorer.Chain.Scroll.Batch` entry. + # + # ## Returns + # - A map with detailed information about the batch formatted for use + # in JSON HTTP response. + @spec render_batch(Batch.t()) :: map() + defp render_batch(batch) do + {finalize_block_number, finalize_transaction_hash, finalize_timestamp} = + if is_nil(batch.bundle) do + {nil, nil, nil} + else + {batch.bundle.finalize_block_number, batch.bundle.finalize_transaction_hash, batch.bundle.finalize_timestamp} + end + + transaction_count = + Transaction.transaction_count_for_block_range(batch.l2_block_range.from..batch.l2_block_range.to) + + %{ + "number" => batch.number, + "commitment_transaction" => %{ + "block_number" => batch.commit_block_number, + "hash" => batch.commit_transaction_hash, + "timestamp" => batch.commit_timestamp + }, + "confirmation_transaction" => %{ + "block_number" => finalize_block_number, + "hash" => finalize_transaction_hash, + "timestamp" => finalize_timestamp + }, + "data_availability" => %{ + "batch_data_container" => batch.container + }, + "start_block" => batch.l2_block_range.from, + "end_block" => batch.l2_block_range.to, + "transaction_count" => transaction_count + } + end + + @doc """ + Extends the json output with a sub-map containing information related Scroll. + + ## Parameters + - `out_json`: A map defining output json which will be extended. + - `transaction`: Transaction structure containing Scroll related data + + ## Returns + - A map extended with the data related to Scroll rollup. + """ + @spec extend_transaction_json_response(map(), %{ + :__struct__ => Transaction, + :block_number => non_neg_integer(), + :index => non_neg_integer(), + :input => Data.t(), + optional(any()) => any() + }) :: map() + def extend_transaction_json_response(out_json, %Transaction{} = transaction) do + config = Application.get_all_env(:explorer)[L1FeeParam] + + l1_fee_scalar = get_param(:scalar, transaction, config) + l1_fee_commit_scalar = get_param(:commit_scalar, transaction, config) + l1_fee_blob_scalar = get_param(:blob_scalar, transaction, config) + l1_fee_overhead = get_param(:overhead, transaction, config) + l1_base_fee = get_param(:l1_base_fee, transaction, config) + l1_blob_base_fee = get_param(:l1_blob_base_fee, transaction, config) + + l1_gas_used = L1FeeParam.l1_gas_used(transaction, l1_fee_overhead) + + l2_fee = + transaction + |> Transaction.l2_fee(:wei) + |> TransactionView.format_fee() + + l2_block_status = l2_block_status(transaction.block_number) + + params = + %{} + |> add_optional_transaction_field(transaction, :l1_fee) + |> add_optional_transaction_field(transaction, :queue_index) + |> Map.put("l1_fee_scalar", l1_fee_scalar) + |> Map.put("l1_fee_commit_scalar", l1_fee_commit_scalar) + |> Map.put("l1_fee_blob_scalar", l1_fee_blob_scalar) + |> Map.put("l1_fee_overhead", l1_fee_overhead) + |> Map.put("l1_base_fee", l1_base_fee) + |> Map.put("l1_blob_base_fee", l1_blob_base_fee) + |> Map.put("l1_gas_used", l1_gas_used) + |> Map.put("l2_fee", l2_fee) + |> Map.put("l2_block_status", l2_block_status) + + Map.put(out_json, "scroll", params) + end + + defp add_optional_transaction_field(out_json, transaction, field) do + case Map.get(transaction, field) do + nil -> out_json + value -> Map.put(out_json, Atom.to_string(field), value) + end + end + + # sobelow_skip ["DOS.BinToAtom"] + defp get_param(name, transaction, config) + when name in [:scalar, :commit_scalar, :blob_scalar, :overhead, :l1_base_fee, :l1_blob_base_fee] do + name_init = :"#{name}#{:_init}" + + case Reader.get_l1_fee_param_for_transaction(name, transaction, @api_true) do + nil -> config[name_init] + value -> value + end + end + + @spec l2_block_status(non_neg_integer()) :: binary() + defp l2_block_status(block_number) do + case Reader.batch_by_l2_block_number(block_number, @api_true) do + {_batch_number, nil} -> "Committed" + {_batch_number, _bundle_id} -> "Finalized" + nil -> "Confirmed by Sequencer" + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex index 7defad2807f5..b569c7d100d8 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex @@ -876,6 +876,20 @@ defmodule BlockScoutWeb.API.V2.TransactionView do end end + :scroll -> + defp chain_type_transformations(transactions) do + transactions + end + + defp chain_type_fields(result, transaction, single_transaction?, _conn, _watchlist_names) do + if single_transaction? do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + BlockScoutWeb.API.V2.ScrollView.extend_transaction_json_response(result, transaction) + else + result + end + end + :suave -> defp chain_type_transformations(transactions) do transactions diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipt.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipt.ex index 5e1e5f09d68a..3faa882fe025 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipt.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipt.ex @@ -27,6 +27,13 @@ defmodule EthereumJSONRPC.Receipt do ] ) + :scroll -> + @chain_type_fields quote( + do: [ + l1_fee: non_neg_integer() + ] + ) + :arbitrum -> @chain_type_fields quote( do: [ @@ -121,6 +128,9 @@ defmodule EthereumJSONRPC.Receipt do l1_gas_price: 0,\ l1_gas_used: 0\ """ + :scroll -> """ + l1_fee: 0\ + """ :arbitrum -> """ gas_used_for_l1: nil\ """ @@ -170,6 +180,9 @@ defmodule EthereumJSONRPC.Receipt do l1_gas_price: 0,\ l1_gas_used: 0\ """ + :scroll -> """ + l1_fee: 0\ + """ :arbitrum -> """ gas_used_for_l1: nil\ """ @@ -238,6 +251,14 @@ defmodule EthereumJSONRPC.Receipt do }) end + :scroll -> + defp chain_type_fields(params, elixir) do + params + |> Map.merge(%{ + l1_fee: Map.get(elixir, "l1Fee", 0) + }) + end + :arbitrum -> defp chain_type_fields(params, elixir) do params diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipts.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipts.ex index bcd912ac859e..b3c2c04a4d24 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipts.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipts.ex @@ -111,6 +111,9 @@ defmodule EthereumJSONRPC.Receipts do l1_gas_price: 0,\ l1_gas_used: 0\ """ + :scroll -> """ + l1_fee: 0\ + """ :arbitrum -> """ gas_used_for_l1: nil\ """ diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex index 7a1745f4f0a0..8611bdb696c6 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex @@ -35,6 +35,13 @@ defmodule EthereumJSONRPC.Transaction do ] ) + :scroll -> + @chain_type_fields quote( + do: [ + queue_index: non_neg_integer() + ] + ) + :suave -> @chain_type_fields quote( do: [ @@ -124,6 +131,9 @@ defmodule EthereumJSONRPC.Transaction do * `"l1TxOrigin"` - . * `"l1BlockNumber"` - . """ + :scroll -> """ + * `"queueIndex"` - An index of L1MessageTx (replaces Nonce) in Scroll rollup. + """ :suave -> """ * `"executionNode"` - `t:EthereumJSONRPC.address/0` of execution node (used by Suave). * `"requestRecord"` - map of wrapped transaction data (used by Suave). @@ -529,6 +539,11 @@ defmodule EthereumJSONRPC.Transaction do {"blobVersionedHashes", :blob_versioned_hashes} ]) + :scroll -> + put_if_present(params, elixir, [ + {"queueIndex", :queue_index} + ]) + :suave -> wrapped = Map.get(elixir, "requestRecord") @@ -688,7 +703,7 @@ defmodule EthereumJSONRPC.Transaction do do: {"input", value} defp entry_to_elixir({key, quantity}) - when key in ~w(gas gasPrice nonce r s standardV v value type maxPriorityFeePerGas maxFeePerGas maxFeePerBlobGas) and + when key in ~w(gas gasPrice nonce r s standardV v value type maxPriorityFeePerGas maxFeePerGas maxFeePerBlobGas queueIndex) and quantity != nil do {key, quantity_to_integer(quantity)} end diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/receipts_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/receipts_test.exs index 945f3f78bc94..112ba413deb0 100644 --- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/receipts_test.exs +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/receipts_test.exs @@ -86,7 +86,8 @@ defmodule EthereumJSONRPC.ReceiptsTest do "data" => data, "logIndex" => integer_to_quantity(index), "topics" => [first_topic], - "transactionHash" => transaction_hash + "transactionHash" => transaction_hash, + "transactionIndex" => integer_to_quantity(transaction_index) } ], "status" => native_status, @@ -110,7 +111,8 @@ defmodule EthereumJSONRPC.ReceiptsTest do index: ^index, second_topic: nil, third_topic: nil, - transaction_hash: ^transaction_hash + transaction_hash: ^transaction_hash, + transaction_index: ^transaction_index } | _ ], diff --git a/apps/explorer/config/dev.exs b/apps/explorer/config/dev.exs index 08c665e9c4dc..7202a7f3ca6a 100644 --- a/apps/explorer/config/dev.exs +++ b/apps/explorer/config/dev.exs @@ -20,6 +20,9 @@ config :explorer, Explorer.Repo.PolygonEdge, timeout: :timer.seconds(80) # Configure Polygon zkEVM database config :explorer, Explorer.Repo.PolygonZkevm, timeout: :timer.seconds(80) +# Configure Scroll database +config :explorer, Explorer.Repo.Scroll, timeout: :timer.seconds(80) + # Configure ZkSync database config :explorer, Explorer.Repo.ZkSync, timeout: :timer.seconds(80) diff --git a/apps/explorer/config/prod.exs b/apps/explorer/config/prod.exs index 585b7f38df53..0f1f30cd9f78 100644 --- a/apps/explorer/config/prod.exs +++ b/apps/explorer/config/prod.exs @@ -34,6 +34,11 @@ config :explorer, Explorer.Repo.PolygonZkevm, timeout: :timer.seconds(60), ssl_opts: [verify: :verify_none] +config :explorer, Explorer.Repo.Scroll, + prepare: :unnamed, + timeout: :timer.seconds(60), + ssl_opts: [verify: :verify_none] + config :explorer, Explorer.Repo.ZkSync, prepare: :unnamed, timeout: :timer.seconds(60), diff --git a/apps/explorer/config/test.exs b/apps/explorer/config/test.exs index 1e7e5be3aef8..f0f047be57bf 100644 --- a/apps/explorer/config/test.exs +++ b/apps/explorer/config/test.exs @@ -58,6 +58,7 @@ for repo <- [ Explorer.Repo.Optimism, Explorer.Repo.PolygonEdge, Explorer.Repo.PolygonZkevm, + Explorer.Repo.Scroll, Explorer.Repo.ZkSync, Explorer.Repo.Celo, Explorer.Repo.RSK, diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index ac5ade4ad4b9..8e835b65289e 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -169,6 +169,7 @@ defmodule Explorer.Application do Explorer.Repo.Optimism, Explorer.Repo.PolygonEdge, Explorer.Repo.PolygonZkevm, + Explorer.Repo.Scroll, Explorer.Repo.ZkSync, Explorer.Repo.Celo, Explorer.Repo.RSK, diff --git a/apps/indexer/lib/indexer/bound_queue.ex b/apps/explorer/lib/explorer/bound_queue.ex similarity index 99% rename from apps/indexer/lib/indexer/bound_queue.ex rename to apps/explorer/lib/explorer/bound_queue.ex index 44c1f13ec733..daa44deb641d 100644 --- a/apps/indexer/lib/indexer/bound_queue.ex +++ b/apps/explorer/lib/explorer/bound_queue.ex @@ -1,4 +1,4 @@ -defmodule Indexer.BoundQueue do +defmodule Explorer.BoundQueue do @moduledoc """ A queue that tracks its size and can have its size bound to a maximum. """ diff --git a/apps/explorer/lib/explorer/chain/import.ex b/apps/explorer/lib/explorer/chain/import.ex index d0101ed133f7..4886ab9dd3fa 100644 --- a/apps/explorer/lib/explorer/chain/import.ex +++ b/apps/explorer/lib/explorer/chain/import.ex @@ -15,7 +15,8 @@ defmodule Explorer.Chain.Import do Import.Stage.BlockRelated, Import.Stage.BlockReferencing, Import.Stage.BlockFollowing, - Import.Stage.BlockPending + Import.Stage.BlockPending, + Import.Stage.ChainTypeSpecific ] # in order so that foreign keys are inserted before being referenced diff --git a/apps/explorer/lib/explorer/chain/import/runner/scroll/batch_bundles.ex b/apps/explorer/lib/explorer/chain/import/runner/scroll/batch_bundles.ex new file mode 100644 index 000000000000..20633de83635 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/scroll/batch_bundles.ex @@ -0,0 +1,106 @@ +defmodule Explorer.Chain.Import.Runner.Scroll.BatchBundles do + @moduledoc """ + Bulk imports `Explorer.Chain.Scroll.BatchBundle`. + """ + + require Ecto.Query + + import Ecto.Query, only: [from: 2] + + alias Ecto.{Changeset, Multi, Repo} + alias Explorer.Chain.Import + alias Explorer.Chain.Scroll.BatchBundle, as: ScrollBatchBundle + alias Explorer.Prometheus.Instrumenter + + @behaviour Import.Runner + + # milliseconds + @timeout 60_000 + + @type imported :: [ScrollBatchBundle.t()] + + @impl Import.Runner + def ecto_schema_module, do: ScrollBatchBundle + + @impl Import.Runner + def option_key, do: :scroll_batch_bundles + + @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, :insert_scroll_batch_bundles, fn repo, _ -> + Instrumenter.block_import_stage_runner( + fn -> insert(repo, changes_list, insert_options) end, + :block_referencing, + :scroll_batch_bundles, + :scroll_batch_bundles + ) + end) + end + + @impl Import.Runner + def timeout, do: @timeout + + @spec insert(Repo.t(), [map()], %{required(:timeout) => timeout(), required(:timestamps) => Import.timestamps()}) :: + {:ok, [ScrollBatchBundle.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) + + # Enforce ScrollBatchBundle ShareLocks order (see docs: sharelock.md) + ordered_changes_list = Enum.sort_by(changes_list, & &1.final_batch_number) + + {:ok, inserted} = + Import.insert_changes_list( + repo, + ordered_changes_list, + conflict_target: :id, + on_conflict: on_conflict, + for: ScrollBatchBundle, + returning: true, + timeout: timeout, + timestamps: timestamps + ) + + {:ok, inserted} + end + + defp default_on_conflict do + from( + sbb in ScrollBatchBundle, + update: [ + set: [ + # Don't update `id` as it is a primary key and used for the conflict target + final_batch_number: fragment("EXCLUDED.final_batch_number"), + finalize_transaction_hash: fragment("EXCLUDED.finalize_transaction_hash"), + finalize_block_number: fragment("EXCLUDED.finalize_block_number"), + finalize_timestamp: fragment("EXCLUDED.finalize_timestamp"), + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", sbb.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", sbb.updated_at) + ] + ], + where: + fragment( + "(EXCLUDED.final_batch_number, EXCLUDED.finalize_transaction_hash, EXCLUDED.finalize_block_number, EXCLUDED.finalize_timestamp) IS DISTINCT FROM (?, ?, ?, ?)", + sbb.final_batch_number, + sbb.finalize_transaction_hash, + sbb.finalize_block_number, + sbb.finalize_timestamp + ) + ) + end +end diff --git a/apps/explorer/lib/explorer/chain/import/runner/scroll/batches.ex b/apps/explorer/lib/explorer/chain/import/runner/scroll/batches.ex new file mode 100644 index 000000000000..710e22c9429f --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/scroll/batches.ex @@ -0,0 +1,110 @@ +defmodule Explorer.Chain.Import.Runner.Scroll.Batches do + @moduledoc """ + Bulk imports `Explorer.Chain.Scroll.Batch`. + """ + + require Ecto.Query + + import Ecto.Query, only: [from: 2] + + alias Ecto.{Changeset, Multi, Repo} + alias Explorer.Chain.Import + alias Explorer.Chain.Scroll.Batch, as: ScrollBatch + alias Explorer.Prometheus.Instrumenter + + @behaviour Import.Runner + + # milliseconds + @timeout 60_000 + + @type imported :: [ScrollBatch.t()] + + @impl Import.Runner + def ecto_schema_module, do: ScrollBatch + + @impl Import.Runner + def option_key, do: :scroll_batches + + @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, :insert_scroll_batches, fn repo, _ -> + Instrumenter.block_import_stage_runner( + fn -> insert(repo, changes_list, insert_options) end, + :block_referencing, + :scroll_batches, + :scroll_batches + ) + end) + end + + @impl Import.Runner + def timeout, do: @timeout + + @spec insert(Repo.t(), [map()], %{required(:timeout) => timeout(), required(:timestamps) => Import.timestamps()}) :: + {:ok, [ScrollBatch.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) + + # Enforce ScrollBatch ShareLocks order (see docs: sharelock.md) + ordered_changes_list = Enum.sort_by(changes_list, & &1.number) + + {:ok, inserted} = + Import.insert_changes_list( + repo, + ordered_changes_list, + conflict_target: :number, + on_conflict: on_conflict, + for: ScrollBatch, + returning: true, + timeout: timeout, + timestamps: timestamps + ) + + {:ok, inserted} + end + + defp default_on_conflict do + from( + sb in ScrollBatch, + update: [ + set: [ + # Don't update `number` as it is a primary key and used for the conflict target + number: fragment("EXCLUDED.number"), + commit_transaction_hash: fragment("EXCLUDED.commit_transaction_hash"), + commit_block_number: fragment("EXCLUDED.commit_block_number"), + commit_timestamp: fragment("EXCLUDED.commit_timestamp"), + l2_block_range: fragment("EXCLUDED.l2_block_range"), + container: fragment("EXCLUDED.container"), + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", sb.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", sb.updated_at) + ] + ], + where: + fragment( + "(EXCLUDED.number, EXCLUDED.commit_transaction_hash, EXCLUDED.commit_block_number, EXCLUDED.commit_timestamp, EXCLUDED.l2_block_range, EXCLUDED.container) IS DISTINCT FROM (?, ?, ?, ?, ?, ?)", + sb.number, + sb.commit_transaction_hash, + sb.commit_block_number, + sb.commit_timestamp, + sb.l2_block_range, + sb.container + ) + ) + end +end diff --git a/apps/explorer/lib/explorer/chain/import/runner/scroll/bridge_operations.ex b/apps/explorer/lib/explorer/chain/import/runner/scroll/bridge_operations.ex new file mode 100644 index 000000000000..44c8cc4f7fbc --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/scroll/bridge_operations.ex @@ -0,0 +1,111 @@ +defmodule Explorer.Chain.Import.Runner.Scroll.BridgeOperations do + @moduledoc """ + Bulk imports `Explorer.Chain.Scroll.Bridge`. + """ + + require Ecto.Query + + import Ecto.Query, only: [from: 2] + + alias Ecto.{Changeset, Multi, Repo} + alias Explorer.Chain.Import + alias Explorer.Chain.Scroll.Bridge, as: ScrollBridge + alias Explorer.Prometheus.Instrumenter + + @behaviour Import.Runner + + # milliseconds + @timeout 60_000 + + @type imported :: [ScrollBridge.t()] + + @impl Import.Runner + def ecto_schema_module, do: ScrollBridge + + @impl Import.Runner + def option_key, do: :scroll_bridge_operations + + @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, :insert_scroll_bridge_operations, fn repo, _ -> + Instrumenter.block_import_stage_runner( + fn -> insert(repo, changes_list, insert_options) end, + :block_referencing, + :scroll_bridge_operations, + :scroll_bridge_operations + ) + end) + end + + @impl Import.Runner + def timeout, do: @timeout + + @spec insert(Repo.t(), [map()], %{required(:timeout) => timeout(), required(:timestamps) => Import.timestamps()}) :: + {:ok, [ScrollBridge.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) + + # Enforce ScrollBridge ShareLocks order (see docs: sharelock.md) + ordered_changes_list = Enum.sort_by(changes_list, &{&1.type, &1.message_hash}) + + {:ok, inserted} = + Import.insert_changes_list( + repo, + ordered_changes_list, + conflict_target: [:type, :message_hash], + on_conflict: on_conflict, + for: ScrollBridge, + returning: true, + timeout: timeout, + timestamps: timestamps + ) + + {:ok, inserted} + end + + defp default_on_conflict do + from( + sb in ScrollBridge, + update: [ + set: [ + # Don't update `type` as it is part of the composite primary key and used for the conflict target + # Don't update `message_hash` as it is part of the composite primary key and used for the conflict target + index: fragment("COALESCE(EXCLUDED.index, ?)", sb.index), + l1_transaction_hash: fragment("COALESCE(EXCLUDED.l1_transaction_hash, ?)", sb.l1_transaction_hash), + l2_transaction_hash: fragment("COALESCE(EXCLUDED.l2_transaction_hash, ?)", sb.l2_transaction_hash), + amount: fragment("COALESCE(EXCLUDED.amount, ?)", sb.amount), + block_number: fragment("COALESCE(EXCLUDED.block_number, ?)", sb.block_number), + block_timestamp: fragment("COALESCE(EXCLUDED.block_timestamp, ?)", sb.block_timestamp), + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", sb.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", sb.updated_at) + ] + ], + where: + fragment( + "(EXCLUDED.index, EXCLUDED.l1_transaction_hash, EXCLUDED.l2_transaction_hash, EXCLUDED.amount, EXCLUDED.block_number, EXCLUDED.block_timestamp) IS DISTINCT FROM (?, ?, ?, ?, ?, ?)", + sb.index, + sb.l1_transaction_hash, + sb.l2_transaction_hash, + sb.amount, + sb.block_number, + sb.block_timestamp + ) + ) + end +end diff --git a/apps/explorer/lib/explorer/chain/import/runner/scroll/l1_fee_params.ex b/apps/explorer/lib/explorer/chain/import/runner/scroll/l1_fee_params.ex new file mode 100644 index 000000000000..525fd0ffee5e --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/scroll/l1_fee_params.ex @@ -0,0 +1,102 @@ +defmodule Explorer.Chain.Import.Runner.Scroll.L1FeeParams do + @moduledoc """ + Bulk imports `Explorer.Chain.Scroll.L1FeeParam`. + """ + + require Ecto.Query + + alias Ecto.{Changeset, Multi, Repo} + alias Explorer.Chain.Import + alias Explorer.Chain.Scroll.L1FeeParam + alias Explorer.Prometheus.Instrumenter + + import Ecto.Query, only: [from: 2] + + @behaviour Import.Runner + + # milliseconds + @timeout 60_000 + + @type imported :: [L1FeeParam.t()] + + @impl Import.Runner + def ecto_schema_module, do: L1FeeParam + + @impl Import.Runner + def option_key, do: :scroll_l1_fee_params + + @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, :insert_l1_fee_params, fn repo, _ -> + Instrumenter.block_import_stage_runner( + fn -> insert(repo, changes_list, insert_options) end, + :block_referencing, + :scroll_l1_fee_params, + :scroll_l1_fee_params + ) + end) + end + + @impl Import.Runner + def timeout, do: @timeout + + @spec insert(Repo.t(), [map()], %{required(:timeout) => timeout(), required(:timestamps) => Import.timestamps()}) :: + {:ok, [L1FeeParam.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) + + # Enforce L1FeeParam ShareLocks order (see docs: sharelock.md) + ordered_changes_list = Enum.sort_by(changes_list, &{&1.block_number, &1.transaction_index, &1.name}) + + {:ok, inserted} = + Import.insert_changes_list( + repo, + ordered_changes_list, + for: L1FeeParam, + returning: true, + timeout: timeout, + timestamps: timestamps, + conflict_target: [:block_number, :transaction_index, :name], + on_conflict: on_conflict + ) + + {:ok, inserted} + end + + defp default_on_conflict do + from( + param in L1FeeParam, + update: [ + set: [ + # Don't update `block_number` as it is part of the composite primary key and used for the conflict target + # Don't update `transaction_index` as it is part of the composite primary key and used for the conflict target + # Don't update `name` as it is part of the composite primary key and used for the conflict target + value: fragment("EXCLUDED.value"), + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", param.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", param.updated_at) + ] + ], + where: + fragment( + "(EXCLUDED.value) IS DISTINCT FROM (?)", + param.value + ) + ) + end +end diff --git a/apps/explorer/lib/explorer/chain/import/runner/transactions.ex b/apps/explorer/lib/explorer/chain/import/runner/transactions.ex index 83f7388548d2..d16e442f176a 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/transactions.ex @@ -438,6 +438,80 @@ defmodule Explorer.Chain.Import.Runner.Transactions do ) end + :scroll -> + defp default_on_conflict do + from( + transaction in Transaction, + update: [ + set: [ + block_hash: fragment("EXCLUDED.block_hash"), + old_block_hash: transaction.block_hash, + block_number: fragment("EXCLUDED.block_number"), + block_consensus: fragment("EXCLUDED.block_consensus"), + block_timestamp: fragment("EXCLUDED.block_timestamp"), + created_contract_address_hash: fragment("EXCLUDED.created_contract_address_hash"), + created_contract_code_indexed_at: fragment("EXCLUDED.created_contract_code_indexed_at"), + cumulative_gas_used: fragment("EXCLUDED.cumulative_gas_used"), + error: fragment("EXCLUDED.error"), + from_address_hash: fragment("EXCLUDED.from_address_hash"), + gas: fragment("EXCLUDED.gas"), + gas_price: fragment("EXCLUDED.gas_price"), + gas_used: fragment("EXCLUDED.gas_used"), + index: fragment("EXCLUDED.index"), + input: fragment("EXCLUDED.input"), + nonce: fragment("EXCLUDED.nonce"), + r: fragment("EXCLUDED.r"), + s: fragment("EXCLUDED.s"), + status: fragment("EXCLUDED.status"), + to_address_hash: fragment("EXCLUDED.to_address_hash"), + v: fragment("EXCLUDED.v"), + value: fragment("EXCLUDED.value"), + earliest_processing_start: fragment("EXCLUDED.earliest_processing_start"), + revert_reason: fragment("EXCLUDED.revert_reason"), + max_priority_fee_per_gas: fragment("EXCLUDED.max_priority_fee_per_gas"), + max_fee_per_gas: fragment("EXCLUDED.max_fee_per_gas"), + type: fragment("EXCLUDED.type"), + l1_fee: fragment("EXCLUDED.l1_fee"), + queue_index: fragment("EXCLUDED.queue_index"), + # Don't update `hash` as it is part of the primary key and used for the conflict target + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", transaction.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", transaction.updated_at) + ] + ], + where: + fragment( + "(EXCLUDED.block_hash, EXCLUDED.block_number, EXCLUDED.block_consensus, EXCLUDED.block_timestamp, EXCLUDED.created_contract_address_hash, EXCLUDED.created_contract_code_indexed_at, EXCLUDED.cumulative_gas_used, EXCLUDED.from_address_hash, EXCLUDED.gas, EXCLUDED.gas_price, EXCLUDED.gas_used, EXCLUDED.index, EXCLUDED.input, EXCLUDED.nonce, EXCLUDED.r, EXCLUDED.s, EXCLUDED.status, EXCLUDED.to_address_hash, EXCLUDED.v, EXCLUDED.value, EXCLUDED.earliest_processing_start, EXCLUDED.revert_reason, EXCLUDED.max_priority_fee_per_gas, EXCLUDED.max_fee_per_gas, EXCLUDED.type, EXCLUDED.l1_fee, EXCLUDED.queue_index) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + transaction.block_hash, + transaction.block_number, + transaction.block_consensus, + transaction.block_timestamp, + transaction.created_contract_address_hash, + transaction.created_contract_code_indexed_at, + transaction.cumulative_gas_used, + transaction.from_address_hash, + transaction.gas, + transaction.gas_price, + transaction.gas_used, + transaction.index, + transaction.input, + transaction.nonce, + transaction.r, + transaction.s, + transaction.status, + transaction.to_address_hash, + transaction.v, + transaction.value, + transaction.earliest_processing_start, + transaction.revert_reason, + transaction.max_priority_fee_per_gas, + transaction.max_fee_per_gas, + transaction.type, + transaction.l1_fee, + transaction.queue_index + ) + ) + end + _ -> defp default_on_conflict do from( diff --git a/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex b/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex index 5f28a40b01bc..af647c274c6c 100644 --- a/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex +++ b/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex @@ -8,85 +8,22 @@ defmodule Explorer.Chain.Import.Stage.BlockReferencing do @behaviour Stage - @default_runners [ - Runner.Transaction.Forks, - Runner.Logs, - Runner.Tokens, - Runner.TokenInstances, - Runner.TransactionActions, - Runner.Withdrawals, - Runner.SignedAuthorizations - ] - - @extra_runners_by_chain_type %{ - optimism: [ - Runner.Optimism.FrameSequences, - Runner.Optimism.FrameSequenceBlobs, - Runner.Optimism.TransactionBatches, - Runner.Optimism.OutputRoots, - Runner.Optimism.DisputeGames, - Runner.Optimism.Deposits, - Runner.Optimism.Withdrawals, - Runner.Optimism.WithdrawalEvents - ], - polygon_edge: [ - Runner.PolygonEdge.Deposits, - Runner.PolygonEdge.DepositExecutes, - Runner.PolygonEdge.Withdrawals, - Runner.PolygonEdge.WithdrawalExits - ], - polygon_zkevm: [ - Runner.PolygonZkevm.LifecycleTransactions, - Runner.PolygonZkevm.TransactionBatches, - Runner.PolygonZkevm.BatchTransactions, - Runner.PolygonZkevm.BridgeL1Tokens, - Runner.PolygonZkevm.BridgeOperations - ], - zksync: [ - Runner.ZkSync.LifecycleTransactions, - Runner.ZkSync.TransactionBatches, - Runner.ZkSync.BatchTransactions, - Runner.ZkSync.BatchBlocks - ], - shibarium: [ - Runner.Shibarium.BridgeOperations - ], - ethereum: [ - Runner.Beacon.BlobTransactions - ], - arbitrum: [ - Runner.Arbitrum.Messages, - Runner.Arbitrum.LifecycleTransactions, - Runner.Arbitrum.L1Executions, - Runner.Arbitrum.L1Batches, - Runner.Arbitrum.BatchBlocks, - Runner.Arbitrum.BatchTransactions, - Runner.Arbitrum.DaMultiPurposeRecords - ], - celo: [ - Runner.Celo.ValidatorGroupVotes, - Runner.Celo.ElectionRewards, - Runner.Celo.EpochRewards - ] - } - @impl Stage def runners do - chain_type = Application.get_env(:explorer, :chain_type) - chain_type_runners = Map.get(@extra_runners_by_chain_type, chain_type, []) - - @default_runners ++ chain_type_runners + [ + Runner.Transaction.Forks, + Runner.Logs, + Runner.Tokens, + Runner.TokenInstances, + Runner.TransactionActions, + Runner.Withdrawals, + Runner.SignedAuthorizations + ] end @impl Stage - def all_runners do - all_extra_runners = - @extra_runners_by_chain_type - |> Map.values() - |> Enum.concat() - - @default_runners ++ all_extra_runners - end + def all_runners, + do: runners() @impl Stage def multis(runner_to_changes_list, options) do diff --git a/apps/explorer/lib/explorer/chain/import/stage/chain_type_specific.ex b/apps/explorer/lib/explorer/chain/import/stage/chain_type_specific.ex new file mode 100644 index 000000000000..f89781059882 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/stage/chain_type_specific.ex @@ -0,0 +1,88 @@ +defmodule Explorer.Chain.Import.Stage.ChainTypeSpecific do + @moduledoc """ + Imports any chain type specific tables. + """ + + alias Explorer.Chain.Import.{Runner, Stage} + + @behaviour Stage + + @runners_by_chain_type %{ + optimism: [ + Runner.Optimism.FrameSequences, + Runner.Optimism.FrameSequenceBlobs, + Runner.Optimism.TransactionBatches, + Runner.Optimism.OutputRoots, + Runner.Optimism.DisputeGames, + Runner.Optimism.Deposits, + Runner.Optimism.Withdrawals, + Runner.Optimism.WithdrawalEvents + ], + polygon_edge: [ + Runner.PolygonEdge.Deposits, + Runner.PolygonEdge.DepositExecutes, + Runner.PolygonEdge.Withdrawals, + Runner.PolygonEdge.WithdrawalExits + ], + polygon_zkevm: [ + Runner.PolygonZkevm.LifecycleTransactions, + Runner.PolygonZkevm.TransactionBatches, + Runner.PolygonZkevm.BatchTransactions, + Runner.PolygonZkevm.BridgeL1Tokens, + Runner.PolygonZkevm.BridgeOperations + ], + zksync: [ + Runner.ZkSync.LifecycleTransactions, + Runner.ZkSync.TransactionBatches, + Runner.ZkSync.BatchTransactions, + Runner.ZkSync.BatchBlocks + ], + shibarium: [ + Runner.Shibarium.BridgeOperations + ], + ethereum: [ + Runner.Beacon.BlobTransactions + ], + arbitrum: [ + Runner.Arbitrum.Messages, + Runner.Arbitrum.LifecycleTransactions, + Runner.Arbitrum.L1Executions, + Runner.Arbitrum.L1Batches, + Runner.Arbitrum.BatchBlocks, + Runner.Arbitrum.BatchTransactions, + Runner.Arbitrum.DaMultiPurposeRecords + ], + scroll: [ + Runner.Scroll.BatchBundles, + Runner.Scroll.Batches, + Runner.Scroll.BridgeOperations, + Runner.Scroll.L1FeeParams + ], + celo: [ + Runner.Celo.ValidatorGroupVotes, + Runner.Celo.ElectionRewards, + Runner.Celo.EpochRewards + ] + } + + @impl Stage + def runners do + chain_type = Application.get_env(:explorer, :chain_type) + Map.get(@runners_by_chain_type, chain_type, []) + end + + @impl Stage + def all_runners do + @runners_by_chain_type + |> Map.values() + |> Enum.concat() + end + + @impl Stage + def multis(runner_to_changes_list, options) do + {final_multi, final_remaining_runner_to_changes_list} = + Stage.single_multi(runners(), runner_to_changes_list, options) + + {[final_multi], final_remaining_runner_to_changes_list} + end +end diff --git a/apps/explorer/lib/explorer/chain/rollup_reorg_monitor_queue.ex b/apps/explorer/lib/explorer/chain/rollup_reorg_monitor_queue.ex new file mode 100644 index 000000000000..7a7365984eda --- /dev/null +++ b/apps/explorer/lib/explorer/chain/rollup_reorg_monitor_queue.ex @@ -0,0 +1,91 @@ +defmodule Explorer.Chain.RollupReorgMonitorQueue do + @moduledoc """ + A module containing (encapsulating) the reorg monitor queue and functions to manage it. + Mostly used by the `Indexer.Fetcher.RollupL1ReorgMonitor` module. + """ + + alias Explorer.BoundQueue + + @doc """ + Pops the number of reorg block from the front of the queue for the specified rollup module. + + ## Parameters + - `module`: The module for which the block number is popped from the queue. + + ## Returns + - The popped block number. + - `nil` if the reorg queue is empty. + """ + @spec reorg_block_pop(module()) :: non_neg_integer() | nil + def reorg_block_pop(module) do + table_name = reorg_table_name(module) + + case BoundQueue.pop_front(reorg_queue_get(table_name)) do + {:ok, {block_number, updated_queue}} -> + :ets.insert(table_name, {:queue, updated_queue}) + block_number + + {:error, :empty} -> + nil + end + end + + @doc """ + Pushes the number of reorg block to the back of the queue for the specified rollup module. + + ## Parameters + - `block_number`: The reorg block number. + - `module`: The module for which the block number is pushed to the queue. + + ## Returns + - Nothing is returned. + """ + @spec reorg_block_push(non_neg_integer(), module()) :: any() + def reorg_block_push(block_number, module) do + table_name = reorg_table_name(module) + {:ok, updated_queue} = BoundQueue.push_back(reorg_queue_get(table_name), block_number) + :ets.insert(table_name, {:queue, updated_queue}) + end + + # Reads a block number queue instance from the ETS table associated with the queue. + # The table name depends on the module name and formed by the `reorg_table_name` function. + # + # ## Parameters + # - `table_name`: The ETS table name of the queue. + # + # ## Returns + # - `BoundQueue` instance for the queue. The queue may be empty (then %BoundQueue{} is returned). + @spec reorg_queue_get(atom()) :: BoundQueue.t(any()) + defp reorg_queue_get(table_name) do + if :ets.whereis(table_name) == :undefined do + :ets.new(table_name, [ + :set, + :named_table, + :public, + read_concurrency: true, + write_concurrency: true + ]) + end + + with info when info != :undefined <- :ets.info(table_name), + [{_, value}] <- :ets.lookup(table_name, :queue) do + value + else + _ -> %BoundQueue{} + end + end + + # Forms an ETS table name for the block number queue for the given module name. + # + # ## Parameters + # - `module`: The module name (instance) for which the ETS table name should be formed. + # + # ## Returns + # - An atom defining the table name. + # + # sobelow_skip ["DOS.BinToAtom"] + @spec reorg_table_name(module()) :: atom() + defp reorg_table_name(module) do + :"#{module}#{:_reorgs}" + end +end diff --git a/apps/explorer/lib/explorer/chain/scroll/batch.ex b/apps/explorer/lib/explorer/chain/scroll/batch.ex new file mode 100644 index 000000000000..d4d3636cf65b --- /dev/null +++ b/apps/explorer/lib/explorer/chain/scroll/batch.ex @@ -0,0 +1,82 @@ +defmodule Explorer.Chain.Scroll.Batch do + @moduledoc """ + Models a batch for Scroll. + + Changes in the schema should be reflected in the bulk import module: + - Explorer.Chain.Import.Runner.Scroll.Batches + + Migrations: + - Explorer.Repo.Scroll.Migrations.AddBatchesTables + """ + + use Explorer.Schema + + alias Explorer.Chain.Block.Range, as: BlockRange + alias Explorer.Chain.Hash + alias Explorer.Chain.Scroll.BatchBundle + + @optional_attrs ~w(bundle_id)a + + @required_attrs ~w(number commit_transaction_hash commit_block_number commit_timestamp l2_block_range container)a + + @typedoc """ + Descriptor of the batch: + * `number` - A unique batch number. + * `commit_transaction_hash` - A hash of the commit transaction on L1. + * `commit_block_number` - A block number of the commit transaction on L1. + * `commit_timestamp` - A timestamp of the commit block. + * `bundle_id` - An identifier of the batch bundle from the `scroll_batch_bundles` database table. + * `l2_block_range` - A range of L2 blocks included into the batch. + * `container` - A container where the batch info is mostly located (can be :in_calldata, :in_blob4844). + """ + @type to_import :: %{ + number: non_neg_integer(), + commit_transaction_hash: binary(), + commit_block_number: non_neg_integer(), + commit_timestamp: DateTime.t(), + bundle_id: non_neg_integer() | nil, + l2_block_range: BlockRange.t(), + container: :in_calldata | :in_blob4844 + } + + @typedoc """ + * `number` - A unique batch number. + * `commit_transaction_hash` - A hash of the commit transaction on L1. + * `commit_block_number` - A block number of the commit transaction on L1. + * `commit_timestamp` - A timestamp of the commit block. + * `bundle_id` - An identifier of the batch bundle from the `scroll_batch_bundles` database table. + * `l2_block_range` - A range of L2 blocks included into the batch. + * `container` - A container where the batch info is mostly located (can be :in_calldata, :in_blob4844). + """ + @primary_key false + typed_schema "scroll_batches" do + field(:number, :integer, primary_key: true) + field(:commit_transaction_hash, Hash.Full) + field(:commit_block_number, :integer) + field(:commit_timestamp, :utc_datetime_usec) + + belongs_to(:bundle, BatchBundle, + foreign_key: :bundle_id, + references: :id, + type: :integer, + null: true + ) + + field(:l2_block_range, BlockRange, null: false) + field(:container, Ecto.Enum, values: [:in_blob4844, :in_calldata]) + + timestamps() + end + + @doc """ + Checks that the `attrs` are valid. + """ + @spec changeset(Ecto.Schema.t(), map()) :: Ecto.Schema.t() + def changeset(%__MODULE__{} = batches, attrs \\ %{}) do + batches + |> cast(attrs, @required_attrs ++ @optional_attrs) + |> validate_required(@required_attrs) + |> unique_constraint(:number) + |> foreign_key_constraint(:bundle_id) + end +end diff --git a/apps/explorer/lib/explorer/chain/scroll/batch_bundle.ex b/apps/explorer/lib/explorer/chain/scroll/batch_bundle.ex new file mode 100644 index 000000000000..5a36bf72e359 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/scroll/batch_bundle.ex @@ -0,0 +1,58 @@ +defmodule Explorer.Chain.Scroll.BatchBundle do + @moduledoc """ + Models a batch bundle for Scroll. + + Changes in the schema should be reflected in the bulk import module: + - Explorer.Chain.Import.Runner.Scroll.BatchBundles + + Migrations: + - Explorer.Repo.Scroll.Migrations.AddBatchesTables + """ + + use Explorer.Schema + + alias Explorer.Chain.Hash + + @required_attrs ~w(final_batch_number finalize_transaction_hash finalize_block_number finalize_timestamp)a + + @typedoc """ + Descriptor of the batch bundle: + * `final_batch_number` - The last batch number finalized in this bundle. + * `finalize_transaction_hash` - A hash of the finalize transaction on L1. + * `finalize_block_number` - A block number of the finalize transaction on L1. + * `finalize_timestamp` - A timestamp of the finalize block. + """ + @type to_import :: %{ + final_batch_number: non_neg_integer(), + finalize_transaction_hash: binary(), + finalize_block_number: non_neg_integer(), + finalize_timestamp: DateTime.t() + } + + @typedoc """ + * `id` - An internal ID of the bundle. + * `final_batch_number` - The last batch number finalized in this bundle. + * `finalize_transaction_hash` - A hash of the finalize transaction on L1. + * `finalize_block_number` - A block number of the finalize transaction on L1. + * `finalize_timestamp` - A timestamp of the finalize block. + """ + @primary_key {:id, :id, autogenerate: true} + typed_schema "scroll_batch_bundles" do + field(:final_batch_number, :integer) + field(:finalize_transaction_hash, Hash.Full) + field(:finalize_block_number, :integer) + field(:finalize_timestamp, :utc_datetime_usec) + timestamps() + end + + @doc """ + Checks that the `attrs` are valid. + """ + @spec changeset(Ecto.Schema.t(), map()) :: Ecto.Schema.t() + def changeset(%__MODULE__{} = bundles, attrs \\ %{}) do + bundles + |> cast(attrs, @required_attrs) + |> validate_required(@required_attrs) + |> unique_constraint(:id) + end +end diff --git a/apps/explorer/lib/explorer/chain/scroll/bridge.ex b/apps/explorer/lib/explorer/chain/scroll/bridge.ex new file mode 100644 index 000000000000..6cfd280e0b7f --- /dev/null +++ b/apps/explorer/lib/explorer/chain/scroll/bridge.ex @@ -0,0 +1,78 @@ +defmodule Explorer.Chain.Scroll.Bridge do + @moduledoc """ + Models a bridge operation for Scroll. + + Changes in the schema should be reflected in the bulk import module: + - Explorer.Chain.Import.Runner.Scroll.BridgeOperations + + Migrations: + - Explorer.Repo.Scroll.Migrations.AddBridgeTable + """ + + use Explorer.Schema + + alias Explorer.Chain.Hash + + @optional_attrs ~w(index l1_transaction_hash l2_transaction_hash amount block_number block_timestamp)a + + @required_attrs ~w(type message_hash)a + + @typedoc """ + Descriptor of the Scroll bridge message: + * `type` - Type of the bridge operation (:deposit or :withdrawal). + * `index` - Index of the deposit or index of the withdrawal (can be nil). + * `l1_transaction_hash` - L1 transaction hash of the bridge operation (can be nil). + * `l2_transaction_hash` - L2 transaction hash of the bridge operation (can be nil). + * `amount` - Amount of the operation in native token (can be nil). + * `block_number` - Block number of deposit operation for `l1_transaction_hash` + or withdrawal operation for `l2_transaction_hash` (can be nil). + * `block_timestamp` - Timestamp of the block `block_number` (can be nil). + * `message_hash` - Unique hash of the operation (message). + """ + @type to_import :: %{ + type: :deposit | :withdrawal, + index: non_neg_integer() | nil, + l1_transaction_hash: binary() | nil, + l2_transaction_hash: binary() | nil, + amount: non_neg_integer() | nil, + block_number: non_neg_integer() | nil, + block_timestamp: DateTime.t() | nil, + message_hash: binary() + } + + @typedoc """ + * `type` - Type of the bridge operation (:deposit or :withdrawal). + * `index` - Index of the deposit or index of the withdrawal (can be nil). + * `l1_transaction_hash` - L1 transaction hash of the bridge operation (can be nil). + * `l2_transaction_hash` - L2 transaction hash of the bridge operation (can be nil). + * `amount` - Amount of the operation in native token (can be nil). + * `block_number` - Block number of deposit operation for `l1_transaction_hash` + or withdrawal operation for `l2_transaction_hash` (can be nil). + * `block_timestamp` - Timestamp of the block `block_number` (can be nil). + * `message_hash` - Unique hash of the operation (message). + """ + @primary_key false + typed_schema "scroll_bridge" do + field(:type, Ecto.Enum, values: [:deposit, :withdrawal], primary_key: true) + field(:index, :integer) + field(:l1_transaction_hash, Hash.Full) + field(:l2_transaction_hash, Hash.Full) + field(:amount, :decimal) + field(:block_number, :integer) + field(:block_timestamp, :utc_datetime_usec) + field(:message_hash, Hash.Full, primary_key: true) + + timestamps() + end + + @doc """ + Checks that the `attrs` are valid. + """ + @spec changeset(Ecto.Schema.t(), map()) :: Ecto.Schema.t() + def changeset(%__MODULE__{} = operations, attrs \\ %{}) do + operations + |> cast(attrs, @required_attrs ++ @optional_attrs) + |> validate_required(@required_attrs) + |> unique_constraint([:type, :message_hash]) + end +end diff --git a/apps/explorer/lib/explorer/chain/scroll/l1_fee_param.ex b/apps/explorer/lib/explorer/chain/scroll/l1_fee_param.ex new file mode 100644 index 000000000000..1864931f86da --- /dev/null +++ b/apps/explorer/lib/explorer/chain/scroll/l1_fee_param.ex @@ -0,0 +1,135 @@ +defmodule Explorer.Chain.Scroll.L1FeeParam do + @moduledoc """ + Models an L1 fee parameter for Scroll. + + Changes in the schema should be reflected in the bulk import module: + - Explorer.Chain.Import.Runner.Scroll.L1FeeParams + + Migrations: + - Explorer.Repo.Scroll.Migrations.AddFeeFields + """ + + use Explorer.Schema + + import Explorer.Chain, only: [get_last_fetched_counter: 1, upsert_last_fetched_counter: 1] + + alias Explorer.Chain.Transaction + + @counter_type "scroll_l1_fee_params_fetcher_last_block_number" + @required_attrs ~w(block_number transaction_index name value)a + + @typedoc """ + Descriptor of the L1 Fee Parameter change: + * `block_number` - A block number of the transaction where the given parameter value was changed. + * `transaction_index` - An index of the transaction (within the block) where the given parameter value was changed. + * `name` - A name of the parameter (can be one of: `overhead`, `scalar`, `commit_scalar`, `blob_scalar`, `l1_base_fee`, `l1_blob_base_fee`). + * `value` - A new value of the parameter. + """ + @type to_import :: %{ + block_number: non_neg_integer(), + transaction_index: non_neg_integer(), + name: :overhead | :scalar | :commit_scalar | :blob_scalar | :l1_base_fee | :l1_blob_base_fee, + value: non_neg_integer() + } + + @typedoc """ + * `block_number` - A block number of the transaction where the given parameter was changed. + * `transaction_index` - An index of the transaction (within the block) where the given parameter was changed. + * `name` - A name of the parameter (can be one of: `overhead`, `scalar`, `commit_scalar`, `blob_scalar`, `l1_base_fee`, `l1_blob_base_fee`). + * `value` - A new value of the parameter. + """ + @primary_key false + typed_schema "scroll_l1_fee_params" do + field(:block_number, :integer, primary_key: true) + field(:transaction_index, :integer, primary_key: true) + + field(:name, Ecto.Enum, + values: [:overhead, :scalar, :commit_scalar, :blob_scalar, :l1_base_fee, :l1_blob_base_fee], + primary_key: true + ) + + field(:value, :integer) + + timestamps() + end + + @doc """ + Validates that the `attrs` are valid. + """ + @spec changeset(Ecto.Schema.t(), map()) :: Ecto.Schema.t() + def changeset(%__MODULE__{} = params, attrs \\ %{}) do + params + |> cast(attrs, @required_attrs) + |> validate_required(@required_attrs) + |> unique_constraint([:block_number, :transaction_index]) + end + + @doc """ + Calculates gas used on L1 for the specified L2 transaction. + The returning value depends on the transaction block number: + if that's after Curie upgrade, the function returns 0 as all transactions are put into blob. + Otherwise, the value is calculated based on transaction data (and includes the given overhead). + See https://github.com/scroll-tech/go-ethereum/blob/9ec83a509ac7f6dd2d0beb054eb14c19f3e67a72/rollup/fees/rollup_fee.go#L171-L195 + for the implementation and https://scroll.io/blog/compressing-the-gas-scrolls-curie-upgrade for the Curie upgrade description. + + ## Parameters + - `transaction`: Transaction structure containing block number and transaction data. + - `l1_fee_overhead`: The overhead to add to the gas used in case the transaction was created before Curie upgrade. + + ## Returns + - Calculated L1 gas used value (can be 0). + """ + @spec l1_gas_used(Transaction.t(), non_neg_integer()) :: non_neg_integer() + def l1_gas_used(transaction, l1_fee_overhead) do + if transaction.block_number > Application.get_all_env(:explorer)[__MODULE__][:curie_upgrade_block] do + 0 + else + total = + transaction.input.bytes + |> :binary.bin_to_list() + |> Enum.reduce(0, fn byte, acc -> + # credo:disable-for-next-line Credo.Check.Refactor.Nesting + if byte == 0 do + acc + 4 + else + acc + 16 + end + end) + + total + l1_fee_overhead + 4 * 16 + end + end + + @doc """ + Reads the block number from the `last_fetched_counters` table which was + the last handled L2 block on the previous launch of Indexer.Fetcher.Scroll.L1FeeParam module. + + ## Returns + - The last L2 block number. + - Zero if this is the first launch of the module. + """ + @spec last_l2_block_number() :: non_neg_integer() + def last_l2_block_number do + @counter_type + |> get_last_fetched_counter() + |> Decimal.to_integer() + end + + @doc """ + Updates the last handled L2 block by the Indexer.Fetcher.Scroll.L1FeeParam module. + The new block number is written to the `last_fetched_counters` table. + + ## Parameters + - `block_number`: The number of the L2 block. + + ## Returns + - nothing + """ + @spec set_last_l2_block_number(non_neg_integer()) :: any() + def set_last_l2_block_number(block_number) do + upsert_last_fetched_counter(%{ + counter_type: @counter_type, + value: block_number + }) + end +end diff --git a/apps/explorer/lib/explorer/chain/scroll/reader.ex b/apps/explorer/lib/explorer/chain/scroll/reader.ex new file mode 100644 index 000000000000..4c25b778b478 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/scroll/reader.ex @@ -0,0 +1,427 @@ +defmodule Explorer.Chain.Scroll.Reader do + @moduledoc "Contains read functions for Scroll modules." + + import Ecto.Query, + only: [ + from: 2, + limit: 2, + order_by: 2, + order_by: 3, + select: 3, + where: 2, + where: 3 + ] + + import Explorer.Chain, only: [select_repo: 1] + + alias Explorer.Chain.Scroll.{Batch, BatchBundle, Bridge, L1FeeParam} + alias Explorer.{Chain, PagingOptions, Repo} + alias Explorer.Chain.{Block, Transaction} + + @doc """ + Reads a batch by its number from database. + + ## Parameters + - `number`: The batch number. If `:latest`, the function gets + the latest batch from the `scroll_batches` table. + - `options`: A keyword list of options that may include whether to use a replica database. + + ## Returns + - {:ok, batch} when the batch is found in the table. + - {:error, :not_found} when the batch is not found. + """ + @spec batch(non_neg_integer() | :latest, + necessity_by_association: %{atom() => :optional | :required}, + api?: boolean() + ) :: {:ok, Batch.t()} | {:error, :not_found} + @spec batch(non_neg_integer() | :latest) :: {:ok, Batch.t()} | {:error, :not_found} + def batch(number, options \\ []) + + def batch(:latest, options) when is_list(options) do + necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) + + Batch + |> order_by(desc: :number) + |> limit(1) + |> Chain.join_associations(necessity_by_association) + |> select_repo(options).one() + |> case do + nil -> {:error, :not_found} + batch -> {:ok, batch} + end + end + + def batch(number, options) when is_list(options) do + necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) + + Batch + |> where(number: ^number) + |> Chain.join_associations(necessity_by_association) + |> select_repo(options).one() + |> case do + nil -> {:error, :not_found} + batch -> {:ok, batch} + end + end + + @doc """ + Lists `t:Explorer.Chain.Scroll.Batch.t/0`'s' in descending order based on the `number`. + + ## Parameters + - `options`: A keyword list of options that may include whether to use a replica database and paging options. + + ## Returns + - A list of found entities sorted by `number` in descending order. + """ + @spec batches(paging_options: PagingOptions.t(), api?: boolean()) :: [Batch.t()] + @spec batches() :: [Batch.t()] + def batches(options \\ []) do + paging_options = Keyword.get(options, :paging_options, Chain.default_paging_options()) + + case paging_options do + %PagingOptions{key: {0}} -> + [] + + _ -> + base_query = + from(b in Batch, + order_by: [desc: b.number] + ) + + base_query + |> Chain.join_association(:bundle, :optional) + |> page_batches(paging_options) + |> limit(^paging_options.page_size) + |> select_repo(options).all() + end + end + + @doc """ + Retrieves a list of rollup blocks included into a specified batch. + + This function constructs and executes a database query to retrieve a list of rollup blocks, + considering pagination options specified in the `options` parameter. These options dictate + the number of items to retrieve and how many items to skip from the top. + + ## Parameters + - `batch_number`: The batch number. + - `options`: A keyword list of options specifying pagination, association necessity, and + whether to use a replica database. + + ## Returns + - A list of `Explorer.Chain.Block` entries belonging to the specified batch. + """ + @spec batch_blocks(non_neg_integer() | binary(), + necessity_by_association: %{atom() => :optional | :required}, + api?: boolean(), + paging_options: PagingOptions.t() + ) :: [Block.t()] + def batch_blocks(batch_number, options) do + necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) + paging_options = Keyword.get(options, :paging_options, Chain.default_paging_options()) + api = Keyword.get(options, :api?, false) + + case batch(batch_number, api?: api) do + {:ok, batch} -> + query = + from( + b in Block, + where: + b.number >= ^batch.l2_block_range.from and b.number <= ^batch.l2_block_range.to and b.consensus == true + ) + + query + |> page_batch_blocks(paging_options) + |> limit(^paging_options.page_size) + |> order_by(desc: :number) + |> Chain.join_associations(necessity_by_association) + |> select_repo(options).all() + + _ -> + [] + end + end + + @doc """ + Gets batch number and its bundle id (if defined) by the L2 block number. + + ## Parameters + - `block_number`: The L2 block number for which the batch should be determined. + - `options`: A keyword list of options that may include whether to use a replica database. + + ## Returns + - A tuple `{batch_number, bundle_id}`. + - `nil` if the batch is not found. + """ + @spec batch_by_l2_block_number(non_neg_integer()) :: {non_neg_integer(), non_neg_integer() | nil} | nil + def batch_by_l2_block_number(block_number, options \\ []) do + select_repo(options).one( + from( + b in Batch, + where: fragment("int8range(?, ?) <@ l2_block_range", ^block_number, ^(block_number + 1)), + select: {b.number, b.bundle_id} + ) + ) + end + + @doc """ + Gets last known L1 batch item from the `scroll_batches` table. + + ## Returns + - A tuple `{block_number, transaction_hash}` - the block number and L1 transaction hash bound to the batch. + - If the batch is not found, returns `{0, nil}`. + """ + @spec last_l1_batch_item() :: {non_neg_integer(), binary() | nil} + def last_l1_batch_item do + query = + from(b in Batch, + select: {b.commit_block_number, b.commit_transaction_hash}, + order_by: [desc: b.number], + limit: 1 + ) + + query + |> Repo.one() + |> Kernel.||({0, nil}) + end + + @doc """ + Gets `final_batch_number` from the last known L1 bundle. + + ## Returns + - The `final_batch_number` of the last L1 bundle. + - If there are no bundles, returns -1. + """ + @spec last_final_batch_number() :: integer() + def last_final_batch_number do + query = + from(bb in BatchBundle, + select: bb.final_batch_number, + order_by: [desc: bb.id], + limit: 1 + ) + + query + |> Repo.one() + |> Kernel.||(-1) + end + + @doc """ + Gets the last known L1 bridge item (deposit) from the `scroll_bridge` table. + + ## Returns + - A tuple `{block_number, transaction_hash}` - the block number and L1 transaction hash bound to the deposit. + - If the deposit is not found, returns `{0, nil}`. + """ + @spec last_l1_bridge_item() :: {non_neg_integer(), binary() | nil} + def last_l1_bridge_item do + query = + from(b in Bridge, + select: {b.block_number, b.l1_transaction_hash}, + where: b.type == :deposit and not is_nil(b.block_number), + order_by: [desc: b.index], + limit: 1 + ) + + query + |> Repo.one() + |> Kernel.||({0, nil}) + end + + @doc """ + Gets the last known L2 bridge item (withdrawal) from the `scroll_bridge` table. + + ## Returns + - A tuple `{block_number, transaction_hash}` - the block number and L2 transaction hash bound to the withdrawal. + - If the withdrawal is not found, returns `{0, nil}`. + """ + @spec last_l2_bridge_item() :: {non_neg_integer(), binary() | nil} + def last_l2_bridge_item do + query = + from(b in Bridge, + select: {b.block_number, b.l2_transaction_hash}, + where: b.type == :withdrawal and not is_nil(b.block_number), + order_by: [desc: b.index], + limit: 1 + ) + + query + |> Repo.one() + |> Kernel.||({0, nil}) + end + + @doc """ + Gets a value of the specified L1 Fee parameter for the given transaction from database. + If a parameter is not defined for the transaction block number and index, the function returns `nil`. + + ## Parameters + - `name`: A name of the parameter. + - `transaction`: Transaction structure containing block number and transaction index within the block. + - `options`: A keyword list of options that may include whether to use a replica database. + + ## Returns + - The parameter value, or `nil` if not defined. + """ + @spec get_l1_fee_param_for_transaction( + :overhead | :scalar | :commit_scalar | :blob_scalar | :l1_base_fee | :l1_blob_base_fee, + Transaction.t(), + api?: boolean() + ) :: non_neg_integer() | nil + @spec get_l1_fee_param_for_transaction( + :overhead | :scalar | :commit_scalar | :blob_scalar | :l1_base_fee | :l1_blob_base_fee, + Transaction.t() + ) :: non_neg_integer() | nil + def get_l1_fee_param_for_transaction(name, transaction, options \\ []) + + def get_l1_fee_param_for_transaction(_name, %{block_number: 0, index: 0}, _options), do: nil + + # credo:disable-for-next-line /Complexity/ + def get_l1_fee_param_for_transaction(name, transaction, options) + when name in [:overhead, :scalar, :commit_scalar, :blob_scalar, :l1_base_fee, :l1_blob_base_fee] do + base_query = + L1FeeParam + |> select([p], p.value) + |> order_by([p], desc: p.block_number, desc: p.transaction_index) + |> limit(1) + + query = + cond do + transaction.block_number == 0 -> + # transaction.index is greater than 0 here + where(base_query, [p], p.name == ^name and p.block_number == 0 and p.transaction_index < ^transaction.index) + + transaction.index == 0 -> + # transaction.block_number is greater than 0 here + where(base_query, [p], p.name == ^name and p.block_number < ^transaction.block_number) + + true -> + where( + base_query, + [p], + p.name == ^name and + (p.block_number < ^transaction.block_number or + (p.block_number == ^transaction.block_number and p.transaction_index < ^transaction.index)) + ) + end + + select_repo(options).one(query) + end + + @doc """ + Retrieves a list of Scroll deposits (both completed and unclaimed) + sorted in descending order of the index. + + ## Parameters + - `options`: A keyword list of options that may include whether to use a replica database and paging options. + + ## Returns + - A list of deposits. + """ + @spec deposits(paging_options: PagingOptions.t(), api?: boolean()) :: [Bridge.t()] + @spec deposits() :: [Bridge.t()] + def deposits(options \\ []) do + paging_options = Keyword.get(options, :paging_options, Chain.default_paging_options()) + + case paging_options do + %PagingOptions{key: {0}} -> + [] + + _ -> + base_query = + from( + b in Bridge, + where: b.type == :deposit and not is_nil(b.l1_transaction_hash), + order_by: [desc: b.index] + ) + + base_query + |> page_deposits_or_withdrawals(paging_options) + |> limit(^paging_options.page_size) + |> select_repo(options).all() + end + end + + @doc """ + Returns a total number of Scroll deposits (both completed and unclaimed). + """ + @spec deposits_count(api?: boolean()) :: non_neg_integer() | nil + @spec deposits_count() :: non_neg_integer() | nil + def deposits_count(options \\ []) do + query = + from( + b in Bridge, + where: b.type == :deposit and not is_nil(b.l1_transaction_hash) + ) + + select_repo(options).aggregate(query, :count, timeout: :infinity) + end + + @doc """ + Retrieves a list of Scroll withdrawals (both completed and unclaimed) + sorted in descending order of the index. + + ## Parameters + - `options`: A keyword list of options that may include whether to use a replica database. + + ## Returns + - A list of withdrawals. + """ + @spec withdrawals(paging_options: PagingOptions.t(), api?: boolean()) :: [Bridge.t()] + @spec withdrawals() :: [Bridge.t()] + def withdrawals(options \\ []) do + paging_options = Keyword.get(options, :paging_options, Chain.default_paging_options()) + + case paging_options do + %PagingOptions{key: {0}} -> + [] + + _ -> + base_query = + from( + b in Bridge, + where: b.type == :withdrawal and not is_nil(b.l2_transaction_hash), + order_by: [desc: b.index] + ) + + base_query + |> page_deposits_or_withdrawals(paging_options) + |> limit(^paging_options.page_size) + |> select_repo(options).all() + end + end + + @doc """ + Returns a total number of Scroll withdrawals (both completed and unclaimed). + """ + @spec withdrawals_count(api?: boolean()) :: non_neg_integer() | nil + @spec withdrawals_count() :: non_neg_integer() | nil + def withdrawals_count(options \\ []) do + query = + from( + b in Bridge, + where: b.type == :withdrawal and not is_nil(b.l2_transaction_hash) + ) + + select_repo(options).aggregate(query, :count, timeout: :infinity) + end + + defp page_batches(query, %PagingOptions{key: nil}), do: query + + defp page_batches(query, %PagingOptions{key: {number}}) do + from(b in query, where: b.number < ^number) + end + + defp page_batch_blocks(query, %PagingOptions{key: nil}), do: query + + defp page_batch_blocks(query, %PagingOptions{key: {0}}), do: query + + defp page_batch_blocks(query, %PagingOptions{key: {block_number}}) do + from(b in query, where: b.number < ^block_number) + end + + defp page_deposits_or_withdrawals(query, %PagingOptions{key: nil}), do: query + + defp page_deposits_or_withdrawals(query, %PagingOptions{key: {index}}) do + from(b in query, where: b.index < ^index) + end +end diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index e4c0d71defa0..1637fcd4516e 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -51,6 +51,15 @@ defmodule Explorer.Chain.Transaction.Schema do 2 ) + :scroll -> + elem( + quote do + field(:l1_fee, Wei) + field(:queue_index, :integer) + end, + 2 + ) + :suave -> elem( quote do @@ -323,6 +332,9 @@ defmodule Explorer.Chain.Transaction do :optimism -> ~w(l1_fee l1_fee_scalar l1_gas_price l1_gas_used l1_transaction_origin l1_block_number)a + :scroll -> + ~w(l1_fee queue_index)a + :suave -> ~w(execution_node_hash wrapped_type wrapped_nonce wrapped_to_address_hash wrapped_gas wrapped_gas_price wrapped_max_priority_fee_per_gas wrapped_max_fee_per_gas wrapped_value wrapped_input wrapped_v wrapped_r wrapped_s wrapped_hash)a @@ -1794,7 +1806,7 @@ defmodule Explorer.Chain.Transaction do end @doc """ - The fee a `transaction` paid for the `t:Explorer.Transaction.t/0` `gas` + The fee a `transaction` paid for the `t:Explorer.Chain.Transaction.t/0` `gas`. If the transaction is pending, then the fee will be a range of `unit` @@ -1826,28 +1838,25 @@ defmodule Explorer.Chain.Transaction do def fee(%Transaction{gas: _gas, gas_price: nil, gas_used: nil}, _unit), do: {:maximum, nil} def fee(%Transaction{gas: gas, gas_price: gas_price, gas_used: nil} = transaction, unit) do - {:maximum, fee(transaction, gas_price, gas, unit)} + {:maximum, fee_calc(transaction, gas_price, gas, unit)} end - def fee(%Transaction{gas_price: nil, gas_used: gas_used} = transaction, unit) do - if Application.get_env(:explorer, :chain_type) == :optimism do + if Application.compile_env(:explorer, :chain_type) == :optimism do + def fee(%Transaction{gas_price: nil, gas_used: _gas_used}, _unit) do {:actual, nil} - else + end + else + def fee(%Transaction{gas_price: nil, gas_used: gas_used} = transaction, unit) do gas_price = effective_gas_price(transaction) - - {:actual, - gas_price && - gas_price - |> Wei.to(unit) - |> Decimal.mult(gas_used)} + {:actual, gas_price && l2_fee_calc(gas_price, gas_used, unit)} end end def fee(%Transaction{gas_price: gas_price, gas_used: gas_used} = transaction, unit) do - {:actual, fee(transaction, gas_price, gas_used, unit)} + {:actual, fee_calc(transaction, gas_price, gas_used, unit)} end - defp fee(transaction, gas_price, gas, unit) do + defp fee_calc(transaction, gas_price, gas_used, unit) do l1_fee = case Map.get(transaction, :l1_fee) do nil -> Wei.from(Decimal.new(0), :wei) @@ -1855,13 +1864,38 @@ defmodule Explorer.Chain.Transaction do end gas_price - |> Wei.to(unit) - |> Decimal.mult(gas) + |> l2_fee_calc(gas_used, unit) |> Wei.from(unit) |> Wei.sum(l1_fee) |> Wei.to(unit) end + @doc """ + The execution fee a `transaction` paid for the `t:Explorer.Chain.Transaction.t/0` `gas`. + Doesn't include L1 fee. See the description for the `fee` function for parameters and return values. + """ + @spec l2_fee(Transaction.t(), :ether | :gwei | :wei) :: {:maximum, Decimal.t() | nil} | {:actual, Decimal.t() | nil} + def l2_fee(%Transaction{gas: _gas, gas_price: nil, gas_used: nil}, _unit), do: {:maximum, nil} + + def l2_fee(%Transaction{gas: gas, gas_price: gas_price, gas_used: nil}, unit) do + {:maximum, l2_fee_calc(gas_price, gas, unit)} + end + + def l2_fee(%Transaction{gas_price: nil, gas_used: gas_used} = transaction, unit) do + gas_price = effective_gas_price(transaction) + {:actual, gas_price && l2_fee_calc(gas_price, gas_used, unit)} + end + + def l2_fee(%Transaction{gas_price: gas_price, gas_used: gas_used}, unit) do + {:actual, l2_fee_calc(gas_price, gas_used, unit)} + end + + defp l2_fee_calc(gas_price, gas_used, unit) do + gas_price + |> Wei.to(unit) + |> Decimal.mult(gas_used) + end + @doc """ Wrapper around `effective_gas_price/2` """ diff --git a/apps/explorer/lib/explorer/repo.ex b/apps/explorer/lib/explorer/repo.ex index 548165168e03..5f1ff6e174f1 100644 --- a/apps/explorer/lib/explorer/repo.ex +++ b/apps/explorer/lib/explorer/repo.ex @@ -167,6 +167,16 @@ defmodule Explorer.Repo do end end + defmodule Scroll do + use Ecto.Repo, + otp_app: :explorer, + adapter: Ecto.Adapters.Postgres + + def init(_, opts) do + ConfigHelper.init_repo_module(__MODULE__, opts) + end + end + defmodule ZkSync do use Ecto.Repo, otp_app: :explorer, diff --git a/apps/explorer/priv/scroll/migrations/20240710101931_add_fee_fields.exs b/apps/explorer/priv/scroll/migrations/20240710101931_add_fee_fields.exs new file mode 100644 index 000000000000..e3cbaa630f8a --- /dev/null +++ b/apps/explorer/priv/scroll/migrations/20240710101931_add_fee_fields.exs @@ -0,0 +1,23 @@ +defmodule Explorer.Repo.Scroll.Migrations.AddFeeFields do + use Ecto.Migration + + def change do + alter table(:transactions) do + add(:l1_fee, :numeric, precision: 100, null: true) + end + + execute( + "CREATE TYPE scroll_l1_fee_param_names AS ENUM ('overhead', 'scalar', 'commit_scalar', 'blob_scalar', 'l1_base_fee', 'l1_blob_base_fee')", + "DROP TYPE scroll_l1_fee_param_names" + ) + + create table(:scroll_l1_fee_params, primary_key: false) do + add(:block_number, :bigint, null: false, primary_key: true) + add(:tx_index, :integer, null: false, primary_key: true) + add(:name, :scroll_l1_fee_param_names, null: false, primary_key: true) + add(:value, :bigint, null: false) + + timestamps(null: false, type: :utc_datetime_usec) + end + end +end diff --git a/apps/explorer/priv/scroll/migrations/20240807114346_add_queue_index_field.exs b/apps/explorer/priv/scroll/migrations/20240807114346_add_queue_index_field.exs new file mode 100644 index 000000000000..f49c6d9d85b3 --- /dev/null +++ b/apps/explorer/priv/scroll/migrations/20240807114346_add_queue_index_field.exs @@ -0,0 +1,9 @@ +defmodule Explorer.Repo.Scroll.Migrations.AddQueueIndexField do + use Ecto.Migration + + def change do + alter table(:transactions) do + add(:queue_index, :bigint, null: true) + end + end +end diff --git a/apps/explorer/priv/scroll/migrations/20240815102318_add_bridge_table.exs b/apps/explorer/priv/scroll/migrations/20240815102318_add_bridge_table.exs new file mode 100644 index 000000000000..44bcd091b58d --- /dev/null +++ b/apps/explorer/priv/scroll/migrations/20240815102318_add_bridge_table.exs @@ -0,0 +1,24 @@ +defmodule Explorer.Repo.Scroll.Migrations.AddBridgeTable do + use Ecto.Migration + + def change do + execute( + "CREATE TYPE scroll_bridge_op_type AS ENUM ('deposit', 'withdrawal')", + "DROP TYPE scroll_bridge_op_type" + ) + + create table(:scroll_bridge, primary_key: false) do + add(:type, :scroll_bridge_op_type, null: false, primary_key: true) + add(:index, :integer, null: true) + add(:l1_transaction_hash, :bytea, null: true) + add(:l2_transaction_hash, :bytea, null: true) + add(:amount, :numeric, precision: 100, null: true) + add(:block_number, :bigint, null: true) + add(:block_timestamp, :"timestamp without time zone", null: true) + add(:message_hash, :bytea, null: false, primary_key: true) + timestamps(null: false, type: :utc_datetime_usec) + end + + create(index(:scroll_bridge, [:type, :index])) + end +end diff --git a/apps/explorer/priv/scroll/migrations/20240913114630_add_batches_tables.exs b/apps/explorer/priv/scroll/migrations/20240913114630_add_batches_tables.exs new file mode 100644 index 000000000000..139136987e6d --- /dev/null +++ b/apps/explorer/priv/scroll/migrations/20240913114630_add_batches_tables.exs @@ -0,0 +1,40 @@ +defmodule Explorer.Repo.Scroll.Migrations.AddBatchesTables do + use Ecto.Migration + + def change do + execute( + "CREATE TYPE scroll_da_containers_types AS ENUM ('in_blob4844', 'in_calldata')", + "DROP TYPE scroll_da_containers_types" + ) + + create table(:scroll_batch_bundles, primary_key: true) do + add(:final_batch_number, :bigint, null: false) + add(:finalize_transaction_hash, :bytea, null: false) + add(:finalize_block_number, :bigint, null: false) + add(:finalize_timestamp, :"timestamp without time zone", null: false) + timestamps(null: false, type: :utc_datetime_usec) + end + + create table(:scroll_batches, primary_key: false) do + add(:number, :bigint, primary_key: true) + add(:commit_transaction_hash, :bytea, null: false) + add(:commit_block_number, :bigint, null: false) + add(:commit_timestamp, :"timestamp without time zone", null: false) + + add( + :bundle_id, + references(:scroll_batch_bundles, on_delete: :restrict, on_update: :update_all, type: :bigint), + null: true, + default: nil + ) + + add(:l2_block_range, :int8range) + add(:container, :scroll_da_containers_types, null: false) + timestamps(null: false, type: :utc_datetime_usec) + end + + create(index(:scroll_batch_bundles, :finalize_block_number)) + create(index(:scroll_batches, :commit_block_number)) + create(index(:scroll_batches, :l2_block_range)) + end +end diff --git a/apps/explorer/priv/scroll/migrations/20241016105249_rename_fields.exs b/apps/explorer/priv/scroll/migrations/20241016105249_rename_fields.exs new file mode 100644 index 000000000000..304d409c2ec6 --- /dev/null +++ b/apps/explorer/priv/scroll/migrations/20241016105249_rename_fields.exs @@ -0,0 +1,7 @@ +defmodule Explorer.Repo.Scroll.Migrations.RenameFields do + use Ecto.Migration + + def change do + rename(table(:scroll_l1_fee_params), :tx_index, to: :transaction_index) + end +end diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 8a28d507a07f..7672de69f34c 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -54,6 +54,8 @@ defmodule Indexer.Block.Fetcher do alias Indexer.Transform.PolygonEdge.{DepositExecutes, Withdrawals} + alias Indexer.Transform.Scroll.L1FeeParams, as: ScrollL1FeeParams + alias Indexer.Transform.Arbitrum.Messaging, as: ArbitrumMessaging alias Indexer.Transform.Shibarium.Bridge, as: ShibariumBridge @@ -178,6 +180,11 @@ defmodule Indexer.Block.Fetcher do do: DepositExecutes.parse(logs), else: [] ), + scroll_l1_fee_params = + if(callback_module == Indexer.Block.Realtime.Fetcher, + do: ScrollL1FeeParams.parse(logs), + else: [] + ), shibarium_bridge_operations = if(callback_module == Indexer.Block.Realtime.Fetcher, do: ShibariumBridge.parse(blocks, transactions_with_receipts, logs), @@ -246,6 +253,7 @@ defmodule Indexer.Block.Fetcher do polygon_edge_withdrawals: polygon_edge_withdrawals, polygon_edge_deposit_executes: polygon_edge_deposit_executes, polygon_zkevm_bridge_operations: polygon_zkevm_bridge_operations, + scroll_l1_fee_params: scroll_l1_fee_params, shibarium_bridge_operations: shibarium_bridge_operations, celo_gas_tokens: celo_gas_tokens, arbitrum_messages: arbitrum_xlevel_messages @@ -279,41 +287,51 @@ defmodule Indexer.Block.Fetcher do end end - defp import_options(basic_import_options, %{ - transactions_with_receipts: transactions_with_receipts, - optimism_withdrawals: optimism_withdrawals, - polygon_edge_withdrawals: polygon_edge_withdrawals, - polygon_edge_deposit_executes: polygon_edge_deposit_executes, - polygon_zkevm_bridge_operations: polygon_zkevm_bridge_operations, - shibarium_bridge_operations: shibarium_bridge_operations, - celo_gas_tokens: celo_gas_tokens, - arbitrum_messages: arbitrum_xlevel_messages - }) do - case Application.get_env(:explorer, :chain_type) do - :ethereum -> + case Application.compile_env(:explorer, :chain_type) do + :ethereum -> + defp import_options(basic_import_options, %{transactions_with_receipts: transactions_with_receipts}) do basic_import_options |> Map.put_new(:beacon_blob_transactions, %{ params: transactions_with_receipts |> Enum.filter(&Map.has_key?(&1, :max_fee_per_blob_gas)) }) + end - :optimism -> + :optimism -> + defp import_options(basic_import_options, %{optimism_withdrawals: optimism_withdrawals}) do basic_import_options |> Map.put_new(:optimism_withdrawals, %{params: optimism_withdrawals}) + end - :polygon_edge -> + :polygon_edge -> + defp import_options(basic_import_options, %{ + polygon_edge_withdrawals: polygon_edge_withdrawals, + polygon_edge_deposit_executes: polygon_edge_deposit_executes + }) do basic_import_options |> Map.put_new(:polygon_edge_withdrawals, %{params: polygon_edge_withdrawals}) |> Map.put_new(:polygon_edge_deposit_executes, %{params: polygon_edge_deposit_executes}) + end - :polygon_zkevm -> + :polygon_zkevm -> + defp import_options(basic_import_options, %{polygon_zkevm_bridge_operations: polygon_zkevm_bridge_operations}) do basic_import_options |> Map.put_new(:polygon_zkevm_bridge_operations, %{params: polygon_zkevm_bridge_operations}) + end - :shibarium -> + :scroll -> + defp import_options(basic_import_options, %{scroll_l1_fee_params: scroll_l1_fee_params}) do + basic_import_options + |> Map.put_new(:scroll_l1_fee_params, %{params: scroll_l1_fee_params}) + end + + :shibarium -> + defp import_options(basic_import_options, %{shibarium_bridge_operations: shibarium_bridge_operations}) do basic_import_options |> Map.put_new(:shibarium_bridge_operations, %{params: shibarium_bridge_operations}) + end - :celo -> + :celo -> + defp import_options(basic_import_options, %{celo_gas_tokens: celo_gas_tokens}) do tokens = basic_import_options |> Map.get(:tokens, %{}) @@ -324,14 +342,18 @@ defmodule Indexer.Block.Fetcher do :tokens, %{params: (tokens ++ celo_gas_tokens) |> Enum.uniq()} ) + end - :arbitrum -> + :arbitrum -> + defp import_options(basic_import_options, %{arbitrum_messages: arbitrum_xlevel_messages}) do basic_import_options |> Map.put_new(:arbitrum_messages, %{params: arbitrum_xlevel_messages}) + end - _ -> + _ -> + defp import_options(basic_import_options, _) do basic_import_options - end + end end defp update_block_cache([]), do: :ok diff --git a/apps/indexer/lib/indexer/block/realtime/fetcher.ex b/apps/indexer/lib/indexer/block/realtime/fetcher.ex index d2c6e7ab351e..c0d4650efc39 100644 --- a/apps/indexer/lib/indexer/block/realtime/fetcher.ex +++ b/apps/indexer/lib/indexer/block/realtime/fetcher.ex @@ -37,11 +37,6 @@ defmodule Indexer.Block.Realtime.Fetcher do alias Explorer.Utility.MissingRangesManipulator alias Indexer.{Block, Tracer} alias Indexer.Block.Realtime.TaskSupervisor - alias Indexer.Fetcher.Optimism.TransactionBatch, as: OptimismTransactionBatch - alias Indexer.Fetcher.Optimism.Withdrawal, as: OptimismWithdrawal - alias Indexer.Fetcher.PolygonEdge.{DepositExecute, Withdrawal} - alias Indexer.Fetcher.PolygonZkevm.BridgeL2, as: PolygonZkevmBridgeL2 - alias Indexer.Fetcher.Shibarium.L2, as: ShibariumBridgeL2 alias Indexer.Prometheus alias Timex.Duration @@ -296,17 +291,7 @@ defmodule Indexer.Block.Realtime.Fetcher do Indexer.Logger.metadata( fn -> if reorg? do - # we need to remove all rows from `op_transaction_batches` and `op_withdrawals` tables previously written starting from reorg block number - remove_optimism_assets_by_number(block_number_to_fetch) - - # we need to remove all rows from `polygon_edge_withdrawals` and `polygon_edge_deposit_executes` tables previously written starting from reorg block number - remove_polygon_edge_assets_by_number(block_number_to_fetch) - - # we need to remove all rows from `shibarium_bridge` table previously written starting from reorg block number - remove_shibarium_assets_by_number(block_number_to_fetch) - - # we need to remove all rows from `polygon_zkevm_bridge` table previously written starting from reorg block number - remove_polygon_zkevm_assets_by_number(block_number_to_fetch) + remove_assets_by_number(block_number_to_fetch) # give previous fetch attempt (for same block number) a chance to finish # before fetching again, to reduce block consensus mistakes @@ -320,30 +305,54 @@ defmodule Indexer.Block.Realtime.Fetcher do ) end - defp remove_optimism_assets_by_number(block_number_to_fetch) do - if Application.get_env(:explorer, :chain_type) == :optimism do - OptimismTransactionBatch.handle_l2_reorg(block_number_to_fetch) - OptimismWithdrawal.remove(block_number_to_fetch) - end - end + @spec remove_assets_by_number(non_neg_integer()) :: any() - defp remove_polygon_edge_assets_by_number(block_number_to_fetch) do - if Application.get_env(:explorer, :chain_type) == :polygon_edge do - Withdrawal.remove(block_number_to_fetch) - DepositExecute.remove(block_number_to_fetch) - end - end + case Application.compile_env(:explorer, :chain_type) do + :optimism -> + # Removes all rows from `op_transaction_batches` and `op_withdrawals` tables + # previously written starting from the reorg block number + defp remove_assets_by_number(reorg_block) do + # credo:disable-for-lines:2 Credo.Check.Design.AliasUsage + Indexer.Fetcher.Optimism.TransactionBatch.handle_l2_reorg(reorg_block) + Indexer.Fetcher.Optimism.Withdrawal.remove(reorg_block) + end - defp remove_polygon_zkevm_assets_by_number(block_number_to_fetch) do - if Application.get_env(:explorer, :chain_type) == :polygon_zkevm do - PolygonZkevmBridgeL2.reorg_handle(block_number_to_fetch) - end - end + :polygon_edge -> + # Removes all rows from `polygon_edge_withdrawals` and `polygon_edge_deposit_executes` tables + # previously written starting from the reorg block number + defp remove_assets_by_number(reorg_block) do + # credo:disable-for-lines:2 Credo.Check.Design.AliasUsage + Indexer.Fetcher.PolygonEdge.Withdrawal.remove(reorg_block) + Indexer.Fetcher.PolygonEdge.DepositExecute.remove(reorg_block) + end - defp remove_shibarium_assets_by_number(block_number_to_fetch) do - if Application.get_env(:explorer, :chain_type) == :shibarium do - ShibariumBridgeL2.reorg_handle(block_number_to_fetch) - end + :polygon_zkevm -> + # Removes all rows from `polygon_zkevm_bridge` table + # previously written starting from the reorg block number + defp remove_assets_by_number(reorg_block) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + Indexer.Fetcher.PolygonZkevm.BridgeL2.reorg_handle(reorg_block) + end + + :shibarium -> + # Removes all rows from `shibarium_bridge` table + # previously written starting from the reorg block number + defp remove_assets_by_number(reorg_block) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + Indexer.Fetcher.Shibarium.L2.reorg_handle(reorg_block) + end + + :scroll -> + # Removes all rows from `scroll_bridge` and `scroll_l1_fee_params` tables + # previously written starting from the reorg block number + defp remove_assets_by_number(reorg_block) do + # credo:disable-for-lines:2 Credo.Check.Design.AliasUsage + Indexer.Fetcher.Scroll.BridgeL2.reorg_handle(reorg_block) + Indexer.Fetcher.Scroll.L1FeeParam.handle_l2_reorg(reorg_block) + end + + _ -> + defp remove_assets_by_number(_), do: :ok end @decorate span(tracer: Tracer) diff --git a/apps/indexer/lib/indexer/buffered_task.ex b/apps/indexer/lib/indexer/buffered_task.ex index 22fb518bf313..a8d055de0265 100644 --- a/apps/indexer/lib/indexer/buffered_task.ex +++ b/apps/indexer/lib/indexer/buffered_task.ex @@ -304,7 +304,8 @@ defmodule Indexer.BufferedTask do import Indexer.Logger, only: [process: 1] - alias Indexer.{BoundQueue, Memory} + alias Explorer.BoundQueue + alias Indexer.Memory @enforce_keys [ :callback_module, diff --git a/apps/indexer/lib/indexer/fetcher/optimism.ex b/apps/indexer/lib/indexer/fetcher/optimism.ex index e9f1bee03ed7..9e4cc382c8b6 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism.ex @@ -229,7 +229,7 @@ defmodule Indexer.Fetcher.Optimism do optimism_env = Application.get_all_env(:indexer)[__MODULE__] system_config = optimism_env[:optimism_l1_system_config] - optimism_l1_rpc = optimism_env[:optimism_l1_rpc] + optimism_l1_rpc = l1_rpc_url() with {:system_config_valid, true} <- {:system_config_valid, Helper.address_correct?(system_config)}, {:reorg_monitor_started, true} <- @@ -359,4 +359,25 @@ defmodule Indexer.Fetcher.Optimism do nil end end + + @doc """ + Returns L1 RPC URL for an OP module. + """ + @spec l1_rpc_url() :: binary() | nil + def l1_rpc_url do + Application.get_all_env(:indexer)[__MODULE__][:optimism_l1_rpc] + end + + @doc """ + Determines if `Indexer.Fetcher.RollupL1ReorgMonitor` module must be up + before an OP fetcher starts. + + ## Returns + - `true` if the reorg monitor must be active, `false` otherwise. + """ + @spec requires_l1_reorg_monitor?() :: boolean() + def requires_l1_reorg_monitor? do + optimism_config = Application.get_all_env(:indexer)[__MODULE__] + not is_nil(optimism_config[:optimism_l1_system_config]) + end end diff --git a/apps/indexer/lib/indexer/fetcher/optimism/output_root.ex b/apps/indexer/lib/indexer/fetcher/optimism/output_root.ex index cab47ce3423f..c032a878d290 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/output_root.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/output_root.ex @@ -15,7 +15,8 @@ defmodule Indexer.Fetcher.Optimism.OutputRoot do alias Explorer.Application.Constants alias Explorer.{Chain, Helper, Repo} alias Explorer.Chain.Optimism.{DisputeGame, OutputRoot} - alias Indexer.Fetcher.{Optimism, RollupL1ReorgMonitor} + alias Explorer.Chain.RollupReorgMonitorQueue + alias Indexer.Fetcher.Optimism alias Indexer.Helper, as: IndexerHelper @fetcher_name :optimism_output_roots @@ -112,7 +113,7 @@ defmodule Indexer.Fetcher.Optimism.OutputRoot do ) end - reorg_block = RollupL1ReorgMonitor.reorg_block_pop(__MODULE__) + reorg_block = RollupReorgMonitorQueue.reorg_block_pop(__MODULE__) if !is_nil(reorg_block) && reorg_block > 0 do {deleted_count, _} = Repo.delete_all(from(r in OutputRoot, where: r.l1_block_number >= ^reorg_block)) @@ -195,4 +196,24 @@ defmodule Indexer.Fetcher.Optimism.OutputRoot do |> Repo.one() |> Kernel.||({0, nil}) end + + @doc """ + Returns L1 RPC URL for this module. + """ + @spec l1_rpc_url() :: binary() | nil + def l1_rpc_url do + Optimism.l1_rpc_url() + end + + @doc """ + Determines if `Indexer.Fetcher.RollupL1ReorgMonitor` module must be up + before this fetcher starts. + + ## Returns + - `true` if the reorg monitor must be active, `false` otherwise. + """ + @spec requires_l1_reorg_monitor?() :: boolean() + def requires_l1_reorg_monitor? do + Optimism.requires_l1_reorg_monitor?() + end end diff --git a/apps/indexer/lib/indexer/fetcher/optimism/transaction_batch.ex b/apps/indexer/lib/indexer/fetcher/optimism/transaction_batch.ex index cb5b3b148892..8185dec52f80 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/transaction_batch.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/transaction_batch.ex @@ -32,7 +32,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do alias EthereumJSONRPC.{Blocks, Contract} alias Explorer.{Chain, Repo} alias Explorer.Chain.Beacon.Blob, as: BeaconBlob - alias Explorer.Chain.{Block, Hash} + alias Explorer.Chain.{Block, Hash, RollupReorgMonitorQueue} alias Explorer.Chain.Optimism.{FrameSequence, FrameSequenceBlob} alias Explorer.Chain.Optimism.TransactionBatch, as: OptimismTransactionBatch alias HTTPoison.Response @@ -86,7 +86,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do optimism_env = Application.get_all_env(:indexer)[Indexer.Fetcher.Optimism] system_config = optimism_env[:optimism_l1_system_config] - optimism_l1_rpc = optimism_env[:optimism_l1_rpc] + optimism_l1_rpc = l1_rpc_url() with {:system_config_valid, true} <- {:system_config_valid, Helper.address_correct?(system_config)}, @@ -299,7 +299,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do incomplete_channels_acc end - reorg_block = RollupL1ReorgMonitor.reorg_block_pop(__MODULE__) + reorg_block = RollupReorgMonitorQueue.reorg_block_pop(__MODULE__) if !is_nil(reorg_block) && reorg_block > 0 do new_incomplete_channels = handle_l1_reorg(reorg_block, new_incomplete_channels) @@ -920,6 +920,26 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do end end + @doc """ + Returns L1 RPC URL for this module. + """ + @spec l1_rpc_url() :: binary() | nil + def l1_rpc_url do + Optimism.l1_rpc_url() + end + + @doc """ + Determines if `Indexer.Fetcher.RollupL1ReorgMonitor` module must be up + before this fetcher starts. + + ## Returns + - `true` if the reorg monitor must be active, `false` otherwise. + """ + @spec requires_l1_reorg_monitor?() :: boolean() + def requires_l1_reorg_monitor? do + Optimism.requires_l1_reorg_monitor?() + end + defp http_get_request(url, attempts_done \\ 0) do case Application.get_env(:explorer, :http_adapter).get(url) do {:ok, %Response{body: body, status_code: 200}} -> diff --git a/apps/indexer/lib/indexer/fetcher/optimism/withdrawal_event.ex b/apps/indexer/lib/indexer/fetcher/optimism/withdrawal_event.ex index 735db06d42a7..3cb2a55d4a08 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/withdrawal_event.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/withdrawal_event.ex @@ -16,7 +16,8 @@ defmodule Indexer.Fetcher.Optimism.WithdrawalEvent do alias EthereumJSONRPC.Blocks alias Explorer.{Chain, Repo} alias Explorer.Chain.Optimism.WithdrawalEvent - alias Indexer.Fetcher.{Optimism, RollupL1ReorgMonitor} + alias Explorer.Chain.RollupReorgMonitorQueue + alias Indexer.Fetcher.Optimism alias Indexer.Helper @fetcher_name :optimism_withdrawal_events @@ -119,7 +120,7 @@ defmodule Indexer.Fetcher.Optimism.WithdrawalEvent do ) end - reorg_block = RollupL1ReorgMonitor.reorg_block_pop(__MODULE__) + reorg_block = RollupReorgMonitorQueue.reorg_block_pop(__MODULE__) if !is_nil(reorg_block) && reorg_block > 0 do {deleted_count, _} = Repo.delete_all(from(we in WithdrawalEvent, where: we.l1_block_number >= ^reorg_block)) @@ -257,6 +258,26 @@ defmodule Indexer.Fetcher.Optimism.WithdrawalEvent do |> Kernel.||({0, nil}) end + @doc """ + Returns L1 RPC URL for this module. + """ + @spec l1_rpc_url() :: binary() | nil + def l1_rpc_url do + Optimism.l1_rpc_url() + end + + @doc """ + Determines if `Indexer.Fetcher.RollupL1ReorgMonitor` module must be up + before this fetcher starts. + + ## Returns + - `true` if the reorg monitor must be active, `false` otherwise. + """ + @spec requires_l1_reorg_monitor?() :: boolean() + def requires_l1_reorg_monitor? do + Optimism.requires_l1_reorg_monitor?() + end + defp get_blocks_by_events(events, json_rpc_named_arguments, retries) do request = events diff --git a/apps/indexer/lib/indexer/fetcher/polygon_edge.ex b/apps/indexer/lib/indexer/fetcher/polygon_edge.ex index cb343f9c71d8..649255ba22be 100644 --- a/apps/indexer/lib/indexer/fetcher/polygon_edge.ex +++ b/apps/indexer/lib/indexer/fetcher/polygon_edge.ex @@ -610,4 +610,12 @@ defmodule Indexer.Fetcher.PolygonEdge do def repeated_request(req, error_message, json_rpc_named_arguments, retries) do Helper.repeated_call(&json_rpc/2, [req, json_rpc_named_arguments], error_message, retries) end + + @doc """ + Returns L1 RPC URL for a Polygon Edge module. + """ + @spec l1_rpc_url() :: binary() + def l1_rpc_url do + Application.get_all_env(:indexer)[__MODULE__][:polygon_edge_l1_rpc] + end end diff --git a/apps/indexer/lib/indexer/fetcher/polygon_edge/deposit.ex b/apps/indexer/lib/indexer/fetcher/polygon_edge/deposit.ex index 6864d662733b..ceb65d5c610e 100644 --- a/apps/indexer/lib/indexer/fetcher/polygon_edge/deposit.ex +++ b/apps/indexer/lib/indexer/fetcher/polygon_edge/deposit.ex @@ -111,6 +111,27 @@ defmodule Indexer.Fetcher.PolygonEdge.Deposit do end) end + @doc """ + Returns L1 RPC URL for this module. + """ + @spec l1_rpc_url() :: binary() | nil + def l1_rpc_url do + PolygonEdge.l1_rpc_url() + end + + @doc """ + Determines if `Indexer.Fetcher.RollupL1ReorgMonitor` module must be up + for this module. + + ## Returns + - `true` if the reorg monitor must be active, `false` otherwise. + """ + @spec requires_l1_reorg_monitor?() :: boolean() + def requires_l1_reorg_monitor? do + module_config = Application.get_all_env(:indexer)[__MODULE__] + not is_nil(module_config[:start_block_l1]) + end + defp get_blocks_by_events(events, json_rpc_named_arguments, retries) do request = events diff --git a/apps/indexer/lib/indexer/fetcher/polygon_edge/withdrawal_exit.ex b/apps/indexer/lib/indexer/fetcher/polygon_edge/withdrawal_exit.ex index f949dbee7eb6..64d82b394b3b 100644 --- a/apps/indexer/lib/indexer/fetcher/polygon_edge/withdrawal_exit.ex +++ b/apps/indexer/lib/indexer/fetcher/polygon_edge/withdrawal_exit.ex @@ -82,4 +82,25 @@ defmodule Indexer.Fetcher.PolygonEdge.WithdrawalExit do } end) end + + @doc """ + Returns L1 RPC URL for this module. + """ + @spec l1_rpc_url() :: binary() | nil + def l1_rpc_url do + PolygonEdge.l1_rpc_url() + end + + @doc """ + Determines if `Indexer.Fetcher.RollupL1ReorgMonitor` module must be up + for this module. + + ## Returns + - `true` if the reorg monitor must be active, `false` otherwise. + """ + @spec requires_l1_reorg_monitor?() :: boolean() + def requires_l1_reorg_monitor? do + module_config = Application.get_all_env(:indexer)[__MODULE__] + not is_nil(module_config[:start_block_l1]) + end end diff --git a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex index d6b77b89e0c8..6d086d5d4446 100644 --- a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex +++ b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex @@ -15,6 +15,7 @@ defmodule Indexer.Fetcher.PolygonZkevm.BridgeL1 do only: [get_logs_all: 3, import_operations: 1, prepare_operations: 7] alias Explorer.Chain.PolygonZkevm.{Bridge, Reader} + alias Explorer.Chain.RollupReorgMonitorQueue alias Explorer.Repo alias Indexer.Fetcher.RollupL1ReorgMonitor alias Indexer.Helper @@ -210,7 +211,7 @@ defmodule Indexer.Fetcher.PolygonZkevm.BridgeL1 do ) end - reorg_block = RollupL1ReorgMonitor.reorg_block_pop(__MODULE__) + reorg_block = RollupReorgMonitorQueue.reorg_block_pop(__MODULE__) if !is_nil(reorg_block) && reorg_block > 0 do reorg_handle(reorg_block) @@ -244,6 +245,27 @@ defmodule Indexer.Fetcher.PolygonZkevm.BridgeL1 do {:noreply, state} end + @doc """ + Returns L1 RPC URL for this module. + """ + @spec l1_rpc_url() :: binary() + def l1_rpc_url do + Application.get_all_env(:indexer)[__MODULE__][:rpc] + end + + @doc """ + Determines if `Indexer.Fetcher.RollupL1ReorgMonitor` module must be up + for this module. + + ## Returns + - `true` if the reorg monitor must be active, `false` otherwise. + """ + @spec requires_l1_reorg_monitor?() :: boolean() + def requires_l1_reorg_monitor? do + module_config = Application.get_all_env(:indexer)[__MODULE__] + not is_nil(module_config[:start_block]) + end + defp reorg_handle(reorg_block) do {deleted_count, _} = Repo.delete_all(from(b in Bridge, where: b.type == :deposit and b.block_number >= ^reorg_block)) diff --git a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/transaction_batch.ex b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/transaction_batch.ex index 63325ac99b52..0ec617549277 100644 --- a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/transaction_batch.ex +++ b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/transaction_batch.ex @@ -213,8 +213,7 @@ defmodule Indexer.Fetcher.PolygonZkevm.TransactionBatch do responses |> Enum.reduce({[], [], [], Reader.next_id(), hash_to_id}, fn res, {batches, l2_transactions, l1_transactions, next_id, - hash_to_id} = - _acc -> + hash_to_id} = _acc -> number = quantity_to_integer(Map.get(res.result, "number")) # the timestamp is undefined for unfinalized batches diff --git a/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex b/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex index fa7c4723c312..825dec00da9b 100644 --- a/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex +++ b/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex @@ -1,6 +1,10 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do @moduledoc """ - A module to catch L1 reorgs and notify a rollup module about it. + A module to monitor and catch L1 reorgs and make queue of the reorg blocks + (if there are multiple reorgs) for rollup modules using this monitor. + + A rollup module uses the queue to detect a reorg and to do required actions. + In case of reorg, the block number is popped from the queue by that rollup module. """ use GenServer @@ -8,10 +12,45 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do require Logger - alias Indexer.{BoundQueue, Helper} + alias Explorer.Chain.RollupReorgMonitorQueue + alias Indexer.Helper @fetcher_name :rollup_l1_reorg_monitor + @modules_can_use_reorg_monitor (case Application.compile_env(:explorer, :chain_type) do + :optimism -> + [ + Indexer.Fetcher.Optimism.OutputRoot, + Indexer.Fetcher.Optimism.TransactionBatch, + Indexer.Fetcher.Optimism.WithdrawalEvent + ] + + :polygon_edge -> + [ + Indexer.Fetcher.PolygonEdge.Deposit, + Indexer.Fetcher.PolygonEdge.WithdrawalExit + ] + + :polygon_zkevm -> + [ + Indexer.Fetcher.PolygonZkevm.BridgeL1 + ] + + :scroll -> + [ + Indexer.Fetcher.Scroll.Batch, + Indexer.Fetcher.Scroll.BridgeL1 + ] + + :shibarium -> + [ + Indexer.Fetcher.Shibarium.L1 + ] + + _ -> + [] + end) + def child_spec(start_link_arguments) do spec = %{ id: __MODULE__, @@ -27,66 +66,38 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do GenServer.start_link(__MODULE__, args, Keyword.put_new(gen_server_options, :name, __MODULE__)) end + @doc """ + This function initializes L1 blocks reorg monitor for the current rollup + defined by CHAIN_TYPE. If the current chain is not a rollup, the module just doesn't start. + + The monitor is launched for certain modules of the rollup defined in @modules_can_use_reorg_monitor attribute + if a module starts (it can be switched off by configuration parameters). Whether each module starts or not + is defined by the `requires_l1_reorg_monitor?` function of that module. + + The monitor starts an infinite loop of `eth_getBlockByNumber` requests sending them every + `block_check_interval` milliseconds to retrieve the latest block number. To read the latest + block number, RPC node of Layer 1 is used, which URL is defined by `l1_rpc_url` function of the rollup module. + The `block_check_interval` is determined by the `get_block_check_interval` helper function. + After the `block_check_interval` is defined, the function sends `:reorg_monitor` message to the GenServer + to start the monitor loop. + + ## Returns + - `{:ok, state}` with the determined parameters for the monitor loop if at least one rollup module is launched. + - `:ignore` if the monitor is not needed. + """ @impl GenServer def init(_args) do Logger.metadata(fetcher: @fetcher_name) - optimism_modules = [ - Indexer.Fetcher.Optimism.OutputRoot, - Indexer.Fetcher.Optimism.TransactionBatch, - Indexer.Fetcher.Optimism.WithdrawalEvent - ] - - modules_can_use_reorg_monitor = - optimism_modules ++ - [ - Indexer.Fetcher.PolygonEdge.Deposit, - Indexer.Fetcher.PolygonEdge.WithdrawalExit, - Indexer.Fetcher.PolygonZkevm.BridgeL1, - Indexer.Fetcher.Shibarium.L1 - ] - modules_using_reorg_monitor = - modules_can_use_reorg_monitor - |> Enum.reject(fn module -> - if module in optimism_modules do - optimism_config = Application.get_all_env(:indexer)[Indexer.Fetcher.Optimism] - is_nil(optimism_config[:optimism_l1_system_config]) - else - module_config = Application.get_all_env(:indexer)[module] - is_nil(module_config[:start_block]) and is_nil(module_config[:start_block_l1]) - end - end) + @modules_can_use_reorg_monitor + |> Enum.filter(& &1.requires_l1_reorg_monitor?()) if Enum.empty?(modules_using_reorg_monitor) do # don't start reorg monitor as there is no module which would use it :ignore else - # As there cannot be different modules for different rollups at the same time, - # it's correct to only get the first item of the list. - # For example, Indexer.Fetcher.PolygonEdge.Deposit and Indexer.Fetcher.PolygonEdge.WithdrawalExit can be in the list - # because they are for the same rollup, but Indexer.Fetcher.Shibarium.L1 and Indexer.Fetcher.PolygonZkevm.BridgeL1 cannot (as they are for different rollups). - module_using_reorg_monitor = Enum.at(modules_using_reorg_monitor, 0) - - l1_rpc = - cond do - Enum.member?( - [Indexer.Fetcher.PolygonEdge.Deposit, Indexer.Fetcher.PolygonEdge.WithdrawalExit], - module_using_reorg_monitor - ) -> - # there can be more than one PolygonEdge.* modules, so we get the common L1 RPC URL for them from Indexer.Fetcher.PolygonEdge - Application.get_all_env(:indexer)[Indexer.Fetcher.PolygonEdge][:polygon_edge_l1_rpc] - - Enum.member?( - optimism_modules, - module_using_reorg_monitor - ) -> - # there can be more than one Optimism.* modules, so we get the common L1 RPC URL for them from Indexer.Fetcher.Optimism - Application.get_all_env(:indexer)[Indexer.Fetcher.Optimism][:optimism_l1_rpc] - - true -> - Application.get_all_env(:indexer)[module_using_reorg_monitor][:rpc] - end + l1_rpc = Enum.at(modules_using_reorg_monitor, 0).l1_rpc_url() json_rpc_named_arguments = Helper.json_rpc_named_arguments(l1_rpc) @@ -104,6 +115,24 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do end end + @doc """ + Implements the monitor loop which requests RPC node for the latest block every + `block_check_interval` milliseconds using `eth_getBlockByNumber` request. + + In case of reorg, the reorg block number is pushed into rollup module's queue. + The block numbers are then popped by the rollup module from its queue and + used to do some actions needed after reorg. + + ## Parameters + - `:reorg_monitor`: The message triggering the next monitoring iteration. + - `state`: The current state of the process, containing parameters for the + monitoring (such as `block_check_interval`, `json_rpc_named_arguments`, + the list of rollup modules in need of monitoring, the previous latest + block number). + + ## Returns + - `{:noreply, state}` where `state` contains the updated previous latest block number. + """ @impl GenServer def handle_info( :reorg_monitor, @@ -118,58 +147,11 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do if latest < prev_latest do Logger.warning("Reorg detected: previous latest block ##{prev_latest}, current latest block ##{latest}.") - Enum.each(modules, &reorg_block_push(latest, &1)) + Enum.each(modules, &RollupReorgMonitorQueue.reorg_block_push(latest, &1)) end Process.send_after(self(), :reorg_monitor, block_check_interval) {:noreply, %{state | prev_latest: latest}} end - - @doc """ - Pops the number of reorg block from the front of the queue for the specified rollup module. - Returns `nil` if the reorg queue is empty. - """ - @spec reorg_block_pop(module()) :: non_neg_integer() | nil - def reorg_block_pop(module) do - table_name = reorg_table_name(module) - - case BoundQueue.pop_front(reorg_queue_get(table_name)) do - {:ok, {block_number, updated_queue}} -> - :ets.insert(table_name, {:queue, updated_queue}) - block_number - - {:error, :empty} -> - nil - end - end - - defp reorg_block_push(block_number, module) do - table_name = reorg_table_name(module) - {:ok, updated_queue} = BoundQueue.push_back(reorg_queue_get(table_name), block_number) - :ets.insert(table_name, {:queue, updated_queue}) - end - - defp reorg_queue_get(table_name) do - if :ets.whereis(table_name) == :undefined do - :ets.new(table_name, [ - :set, - :named_table, - :public, - read_concurrency: true, - write_concurrency: true - ]) - end - - with info when info != :undefined <- :ets.info(table_name), - [{_, value}] <- :ets.lookup(table_name, :queue) do - value - else - _ -> %BoundQueue{} - end - end - - defp reorg_table_name(module) do - :"#{module}#{:_reorgs}" - end end diff --git a/apps/indexer/lib/indexer/fetcher/scroll/batch.ex b/apps/indexer/lib/indexer/fetcher/scroll/batch.ex new file mode 100644 index 000000000000..10e0c3a32d97 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/scroll/batch.ex @@ -0,0 +1,588 @@ +defmodule Indexer.Fetcher.Scroll.Batch do + @moduledoc """ + The module for scanning L1 RPC node for the `CommitBatch` and `FinalizeBatch` events + which commit and finalize Scroll batches. + + The main function splits the whole block range by chunks and scans L1 Scroll Chain contract + for the batch logs (events) for each chunk. The found events are handled and then imported to the + `scroll_batches` and `scroll_batch_bundles` database tables. + + After historical block range is covered, the process switches to realtime mode and + searches for the batch events in every new block. Reorg blocks are taken into account. + """ + + use GenServer + use Indexer.Fetcher + + require Logger + + import Ecto.Query + + import EthereumJSONRPC, only: [quantity_to_integer: 1] + + alias ABI.{FunctionSelector, TypeDecoder} + alias Ecto.Multi + alias EthereumJSONRPC.Logs + alias Explorer.Chain.Block.Range, as: BlockRange + alias Explorer.Chain.RollupReorgMonitorQueue + alias Explorer.Chain.Scroll.{Batch, BatchBundle, Reader} + alias Explorer.{Chain, Repo} + alias Indexer.Fetcher.RollupL1ReorgMonitor + alias Indexer.Fetcher.Scroll.Helper, as: ScrollHelper + alias Indexer.Helper + + # 32-byte signature of the event CommitBatch(uint256 indexed batchIndex, bytes32 indexed batchHash) + @commit_batch_event "0x2c32d4ae151744d0bf0b9464a3e897a1d17ed2f1af71f7c9a75f12ce0d28238f" + + # 32-byte signature of the event FinalizeBatch(uint256 indexed batchIndex, bytes32 indexed batchHash, bytes32 stateRoot, bytes32 withdrawRoot) + @finalize_batch_event "0x26ba82f907317eedc97d0cbef23de76a43dd6edb563bdb6e9407645b950a7a2d" + + @fetcher_name :scroll_batch + + def child_spec(start_link_arguments) do + spec = %{ + id: __MODULE__, + start: {__MODULE__, :start_link, start_link_arguments}, + restart: :transient, + type: :worker + } + + Supervisor.child_spec(spec, []) + end + + def start_link(args, gen_server_options \\ []) do + GenServer.start_link(__MODULE__, args, Keyword.put_new(gen_server_options, :name, __MODULE__)) + end + + @impl GenServer + def init(_args) do + {:ok, %{}, {:continue, :ok}} + end + + @impl GenServer + def handle_continue(_, state) do + Logger.metadata(fetcher: @fetcher_name) + # two seconds pause needed to avoid exceeding Supervisor restart intensity when DB issues + Process.send_after(self(), :init_with_delay, 2000) + {:noreply, state} + end + + # Validates parameters and initiates searching of the events. + # + # When first launch, the events searching will start from the first block + # and end on the `safe` block (or `latest` one if `safe` is not available). + # If this is not the first launch, the process will start from the block which was + # the last on the previous launch. + @impl GenServer + def handle_info(:init_with_delay, _state) do + env = Application.get_all_env(:indexer)[__MODULE__] + + with {:start_block_undefined, false} <- {:start_block_undefined, is_nil(env[:start_block])}, + {:reorg_monitor_started, true} <- {:reorg_monitor_started, !is_nil(Process.whereis(RollupL1ReorgMonitor))}, + rpc = l1_rpc_url(), + {:rpc_undefined, false} <- {:rpc_undefined, is_nil(rpc)}, + {:scroll_chain_contract_address_is_valid, true} <- + {:scroll_chain_contract_address_is_valid, Helper.address_correct?(env[:scroll_chain_contract])}, + start_block = env[:start_block], + true <- start_block > 0, + {last_l1_block_number, last_l1_transaction_hash} = Reader.last_l1_batch_item(), + json_rpc_named_arguments = Helper.json_rpc_named_arguments(rpc), + {:ok, block_check_interval, safe_block} <- Helper.get_block_check_interval(json_rpc_named_arguments), + {:start_block_valid, true, _, _} <- + {:start_block_valid, + (start_block <= last_l1_block_number || last_l1_block_number == 0) && start_block <= safe_block, + last_l1_block_number, safe_block}, + {:ok, last_l1_transaction} <- + Helper.get_transaction_by_hash(last_l1_transaction_hash, json_rpc_named_arguments), + # here we check for the last known L1 transaction existence to make sure there wasn't reorg + # on L1 while the instance was down, and so we can use `last_l1_block_number` as the starting point + {:l1_transaction_not_found, false} <- + {:l1_transaction_not_found, !is_nil(last_l1_transaction_hash) && is_nil(last_l1_transaction)} do + Process.send(self(), :continue, []) + + {:noreply, + %{ + block_check_interval: block_check_interval, + scroll_chain_contract: env[:scroll_chain_contract], + json_rpc_named_arguments: json_rpc_named_arguments, + end_block: safe_block, + start_block: max(start_block, last_l1_block_number), + eth_get_logs_range_size: Application.get_all_env(:indexer)[Indexer.Fetcher.Scroll][:l1_eth_get_logs_range_size] + }} + else + {:start_block_undefined, true} -> + # the process shouldn't start if the start block is not defined + {:stop, :normal, %{}} + + {:reorg_monitor_started, false} -> + Logger.error("Cannot start this process as Indexer.Fetcher.RollupL1ReorgMonitor is not started.") + {:stop, :normal, %{}} + + {:rpc_undefined, true} -> + Logger.error("L1 RPC URL is not defined.") + {:stop, :normal, %{}} + + {:scroll_chain_contract_address_is_valid, false} -> + Logger.error("L1 ScrollChain contract address is invalid or not defined.") + {:stop, :normal, %{}} + + {:start_block_valid, false, last_l1_block_number, safe_block} -> + Logger.error("Invalid L1 Start Block value. Please, check the value and scroll_batches table.") + Logger.error("last_l1_block_number = #{inspect(last_l1_block_number)}") + Logger.error("safe_block = #{inspect(safe_block)}") + {:stop, :normal, %{}} + + {:error, error_data} -> + Logger.error( + "Cannot get last L1 transaction from RPC by its hash, latest block, or block timestamp by its number due to RPC error: #{inspect(error_data)}" + ) + + {:stop, :normal, %{}} + + {:l1_transaction_not_found, true} -> + Logger.error( + "Cannot find last L1 transaction from RPC by its hash. Probably, there was a reorg on L1 chain. Please, check scroll_batches table." + ) + + {:stop, :normal, %{}} + + _ -> + Logger.error("L1 Start Block is invalid or zero.") + {:stop, :normal, %{}} + end + end + + @doc """ + The main function that scans RPC node for the batch logs (events), parses them, + and imports to the database (into the `scroll_batches` and `scroll_batch_bundles` tables). + + The function splits a given block range by chunks and scans the Scroll Chain contract + for the batch logs (events) for each chunk. The found events are handled and then imported + to the `scroll_batches` and `scroll_batch_bundles` database tables. + + After historical block range is covered, the function switches to realtime mode and + searches for the batch events in every new block. Reorg blocks are taken into account. + + ## Parameters + - `:continue`: The message that triggers the working loop. + - `state`: The state map containing needed data such as the chain contract address and the block range. + + ## Returns + - {:noreply, state} tuple with the updated block range in the `state` to scan logs in. + """ + @impl GenServer + def handle_info( + :continue, + %{ + block_check_interval: block_check_interval, + scroll_chain_contract: scroll_chain_contract, + json_rpc_named_arguments: json_rpc_named_arguments, + end_block: end_block, + start_block: start_block, + eth_get_logs_range_size: eth_get_logs_range_size + } = state + ) do + time_before = Timex.now() + + last_written_block = + start_block..end_block + |> Enum.chunk_every(eth_get_logs_range_size) + |> Enum.reduce_while(start_block - 1, fn current_chunk, _ -> + chunk_start = List.first(current_chunk) + chunk_end = List.last(current_chunk) + + if chunk_start <= chunk_end do + Helper.log_blocks_chunk_handling(chunk_start, chunk_end, start_block, end_block, nil, :L1) + + {batches, bundles, start_by_final_batch_number} = + {chunk_start, chunk_end} + |> get_logs_all(scroll_chain_contract, json_rpc_named_arguments) + |> prepare_items(json_rpc_named_arguments) + + import_items(batches, bundles, start_by_final_batch_number) + + Helper.log_blocks_chunk_handling( + chunk_start, + chunk_end, + start_block, + end_block, + "#{Enum.count(batches)} L1 batch(es), #{Enum.count(bundles)} L1 bundle(s)", + :L1 + ) + end + + reorg_block = RollupReorgMonitorQueue.reorg_block_pop(__MODULE__) + + if !is_nil(reorg_block) && reorg_block > 0 do + reorg_handle(reorg_block) + {:halt, if(reorg_block <= chunk_end, do: reorg_block - 1, else: chunk_end)} + else + {:cont, chunk_end} + end + end) + + new_start_block = last_written_block + 1 + + {:ok, new_end_block} = + Helper.get_block_number_by_tag("latest", json_rpc_named_arguments, Helper.infinite_retries_number()) + + delay = + if new_end_block == last_written_block do + # there is no new block, so wait for some time to let the chain issue the new block + max(block_check_interval - Timex.diff(Timex.now(), time_before, :milliseconds), 0) + else + 0 + end + + Process.send_after(self(), :continue, delay) + + {:noreply, %{state | start_block: new_start_block, end_block: new_end_block}} + end + + @impl GenServer + def handle_info({ref, _result}, state) do + Process.demonitor(ref, [:flush]) + {:noreply, state} + end + + @doc """ + Returns L1 RPC URL for this module. + Returns `nil` if not defined. + """ + @spec l1_rpc_url() :: binary() | nil + def l1_rpc_url do + ScrollHelper.l1_rpc_url() + end + + @doc """ + Determines if `Indexer.Fetcher.RollupL1ReorgMonitor` module must be up + for this module. + + ## Returns + - `true` if the reorg monitor must be active, `false` otherwise. + """ + @spec requires_l1_reorg_monitor?() :: boolean() + def requires_l1_reorg_monitor? do + module_config = Application.get_all_env(:indexer)[__MODULE__] + not is_nil(module_config[:start_block]) + end + + # Fetches `CommitBatch` and `FinalizeBatch` events of the Scroll Chain contract from an RPC node + # for the given range of L1 blocks. + @spec get_logs_all({non_neg_integer(), non_neg_integer()}, binary(), EthereumJSONRPC.json_rpc_named_arguments()) :: [ + %{atom() => any()} + ] + defp get_logs_all({chunk_start, chunk_end}, scroll_chain_contract, json_rpc_named_arguments) do + {:ok, result} = + Helper.get_logs( + chunk_start, + chunk_end, + scroll_chain_contract, + [[@commit_batch_event, @finalize_batch_event]], + json_rpc_named_arguments, + 0, + Helper.infinite_retries_number() + ) + + Logs.elixir_to_params(result) + end + + # Extracts transaction inputs for specified transaction hashes from a list of blocks. + # + # ## Parameters + # - `blocks`: A list of block maps, each containing a "transactions" key with transaction data. + # - `transaction_hashes`: A list of transaction hashes to filter for. + # + # ## Returns + # A map where keys are transaction hashes and values are the corresponding transaction inputs and blob versioned hashes. + @spec get_transaction_input_by_hash([%{String.t() => any()}], [binary()]) :: %{ + binary() => {binary(), [binary()] | [] | nil} + } + defp get_transaction_input_by_hash(blocks, transaction_hashes) do + Enum.reduce(blocks, %{}, fn block, acc -> + block + |> Map.get("transactions", []) + |> Enum.filter(fn transaction -> + Enum.member?(transaction_hashes, transaction["hash"]) + end) + |> Enum.map(fn transaction -> + {transaction["hash"], {transaction["input"], transaction["blobVersionedHashes"]}} + end) + |> Enum.into(%{}) + |> Map.merge(acc) + end) + end + + # Extracts the L2 block range from the call data of a batch commitment + # transaction. + # + # This function decodes the input data from either a `commitBatch` or + # `commitBatchWithBlobProof` function call, extracts the chunks containing L2 + # block numbers, and by identifying the minimum and maximum block numbers in the + # chunks, determines the range of L2 block numbers included in the batch. + # + # ## Parameters + # - `input`: A binary string representing the input data of a batch commitment + # transaction. + # + # ## Returns + # - A `BlockRange.t()` struct containing the minimum and maximum L2 block + # numbers included in the batch. + @spec input_to_l2_block_range(binary()) :: BlockRange.t() + defp input_to_l2_block_range(input) do + chunks = + case input do + # commitBatch(uint8 _version, bytes _parentBatchHeader, bytes[] _chunks, bytes _skippedL1MessageBitmap) + "0x1325aca0" <> encoded_params -> + [_version, _parent_batch_header, chunks, _skipped_l1_message_bitmap] = + TypeDecoder.decode( + Base.decode16!(encoded_params, case: :lower), + %FunctionSelector{ + function: "commitBatch", + types: [ + {:uint, 8}, + :bytes, + {:array, :bytes}, + :bytes + ] + } + ) + + chunks + + # commitBatchWithBlobProof(uint8 _version, bytes _parentBatchHeader, bytes[] _chunks, bytes _skippedL1MessageBitmap, bytes _blobDataProof) + "0x86b053a9" <> encoded_params -> + [_version, _parent_batch_header, chunks, _skipped_l1_message_bitmap, _blob_data_proof] = + TypeDecoder.decode( + Base.decode16!(encoded_params, case: :lower), + %FunctionSelector{ + function: "commitBatchWithBlobProof", + types: [ + {:uint, 8}, + :bytes, + {:array, :bytes}, + :bytes, + :bytes + ] + } + ) + + chunks + end + + {:ok, l2_block_range} = + chunks + |> Enum.reduce([], fn chunk, acc -> + <> = chunk + + chunk_l2_block_numbers = + Enum.map(Range.new(0, chunk_length - 1, 1), fn i -> + # chunk format is described here: https://github.com/scroll-tech/scroll-contracts/blob/main/src/libraries/codec/ChunkCodecV1.sol + chunk_data + |> :binary.part(i * 60, 8) + |> :binary.decode_unsigned() + end) + + acc ++ chunk_l2_block_numbers + end) + |> Enum.min_max() + |> BlockRange.cast() + + l2_block_range + end + + # Imports batches and bundles into the database. + # + # ## Parameters + # - `batches`: List of batch data to be imported. + # - `bundles`: List of bundle data to be imported. + # - `start_by_final_batch_number`: A map defining start batch number by final one for bundles. + # + # ## Returns + # - The result of the database operations. + @spec import_items([Batch.to_import()], [%{atom() => any()}], %{non_neg_integer() => non_neg_integer()}) :: any() + defp import_items([], [], _), do: :ok + + defp import_items(batches, bundles, start_by_final_batch_number) do + {:ok, inserts} = + Chain.import(%{ + scroll_batch_bundles: %{params: bundles}, + scroll_batches: %{params: batches}, + timeout: :infinity + }) + + multi = + inserts + |> Map.get(:insert_scroll_batch_bundles, []) + |> Enum.reduce(Multi.new(), fn bundle, multi_acc -> + start_batch_number = start_by_final_batch_number[bundle.final_batch_number] + + Multi.update_all( + multi_acc, + bundle.id, + from(b in Batch, where: b.number >= ^start_batch_number and b.number <= ^bundle.final_batch_number), + set: [bundle_id: bundle.id] + ) + end) + + Repo.transaction(multi) + end + + # Prepares batch and bundle items from Scroll events for database import. + # + # This function processes a list of CommitBatch and FinalizeBatch events, + # extracting relevant information to create batch and bundle records. + # + # ## Parameters + # - `events`: A list of Scroll events to process. + # - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. + # + # ## Returns + # A tuple containing two lists and a map: + # - List of batches, ready for import to the DB. + # - List of structures describing L1 transactions finalizing batches in form of + # bundles, ready for import to the DB. + # - A map defining start batch number by final one for bundles. + @spec prepare_items([%{atom() => any()}], EthereumJSONRPC.json_rpc_named_arguments()) :: + {[Batch.to_import()], [%{atom() => any()}], %{non_neg_integer() => non_neg_integer()}} + defp prepare_items([], _), do: {[], [], %{}} + + defp prepare_items(events, json_rpc_named_arguments) do + blocks = Helper.get_blocks_by_events(events, json_rpc_named_arguments, Helper.infinite_retries_number(), true) + + commit_transaction_hashes = + events + |> Enum.reduce([], fn event, acc -> + if event.first_topic == @commit_batch_event do + [event.transaction_hash | acc] + else + acc + end + end) + + commit_transaction_input_by_hash = get_transaction_input_by_hash(blocks, commit_transaction_hashes) + + timestamps = + blocks + |> Enum.reduce(%{}, fn block, acc -> + block_number = quantity_to_integer(Map.get(block, "number")) + {:ok, timestamp} = DateTime.from_unix(quantity_to_integer(Map.get(block, "timestamp"))) + Map.put(acc, block_number, timestamp) + end) + + prev_final_batch_number = Reader.last_final_batch_number() + + {_, batches, bundles, start_by_final_batch_number} = + events + |> Enum.reduce({prev_final_batch_number, [], [], %{}}, fn event, + {prev_final_batch_number_acc, batches_acc, bundles_acc, + start_by_final_batch_number_acc} -> + batch_number = quantity_to_integer(event.second_topic) + + if event.first_topic == @commit_batch_event do + commit_block_number = quantity_to_integer(event.block_number) + + # credo:disable-for-lines:2 Credo.Check.Refactor.Nesting + {l2_block_range, container} = + if batch_number == 0 do + {:ok, range} = BlockRange.cast("[0,0]") + {range, :in_calldata} + else + {input, blob_versioned_hashes} = + commit_transaction_input_by_hash + |> Map.get(event.transaction_hash) + + # credo:disable-for-lines:2 Credo.Check.Refactor.Nesting + container = + if is_nil(blob_versioned_hashes) or blob_versioned_hashes == [] do + :in_calldata + else + :in_blob4844 + end + + {input_to_l2_block_range(input), container} + end + + new_batches_acc = [ + %{ + number: batch_number, + commit_transaction_hash: event.transaction_hash, + commit_block_number: commit_block_number, + commit_timestamp: Map.get(timestamps, commit_block_number), + l2_block_range: l2_block_range, + container: container + } + | batches_acc + ] + + {prev_final_batch_number_acc, new_batches_acc, bundles_acc, start_by_final_batch_number_acc} + else + finalize_block_number = quantity_to_integer(event.block_number) + + new_bundles_acc = [ + %{ + final_batch_number: batch_number, + finalize_transaction_hash: event.transaction_hash, + finalize_block_number: finalize_block_number, + finalize_timestamp: Map.get(timestamps, finalize_block_number) + } + | bundles_acc + ] + + start_batch_number = prev_final_batch_number_acc + 1 + + new_start_by_final_batch_number_acc = + Map.put(start_by_final_batch_number_acc, batch_number, start_batch_number) + + {batch_number, batches_acc, new_bundles_acc, new_start_by_final_batch_number_acc} + end + end) + + {batches, bundles, start_by_final_batch_number} + end + + # Handles L1 block reorg: removes all batch rows from the `scroll_batches` table + # created beginning from the reorged block. Also, removes the corresponding rows from + # the `scroll_batch_bundles` table. + # + # ## Parameters + # - `reorg_block`: the block number where reorg has occurred. + # + # ## Returns + # - nothing + @spec reorg_handle(non_neg_integer()) :: any() + defp reorg_handle(reorg_block) do + bundle_ids = + Repo.all( + from(b in Batch, + select: b.bundle_id, + where: b.commit_block_number >= ^reorg_block, + group_by: b.bundle_id + ) + ) + + {:ok, result} = + Multi.new() + |> Multi.delete_all(:delete_batches, from(b in Batch, where: b.bundle_id in ^bundle_ids)) + |> Multi.delete_all( + :delete_bundles, + from(bb in BatchBundle, where: bb.id in ^bundle_ids or bb.finalize_block_number >= ^reorg_block) + ) + |> Repo.transaction() + + deleted_batches_count = elem(result.delete_batches, 0) + deleted_bundles_count = elem(result.delete_bundles, 0) + + if deleted_batches_count > 0 do + Logger.warning( + "As L1 reorg was detected, some batches with commit_block_number >= #{reorg_block} were removed from the scroll_batches table. Number of removed rows: #{deleted_batches_count}." + ) + end + + if deleted_bundles_count > 0 do + Logger.warning( + "As L1 reorg was detected, some bundles with finalize_block_number >= #{reorg_block} were removed from the scroll_batch_bundles table. Number of removed rows: #{deleted_bundles_count}." + ) + end + end +end diff --git a/apps/indexer/lib/indexer/fetcher/scroll/bridge.ex b/apps/indexer/lib/indexer/fetcher/scroll/bridge.ex new file mode 100644 index 000000000000..945e62dd050b --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/scroll/bridge.ex @@ -0,0 +1,338 @@ +defmodule Indexer.Fetcher.Scroll.Bridge do + @moduledoc """ + Contains common functions for Indexer.Fetcher.Scroll.Bridge* modules. + """ + + require Logger + + import EthereumJSONRPC, + only: [ + quantity_to_integer: 1, + timestamp_to_datetime: 1 + ] + + import Explorer.Helper, only: [decode_data: 2] + + alias EthereumJSONRPC.Logs + alias Explorer.Chain + alias Explorer.Chain.RollupReorgMonitorQueue + alias Indexer.Fetcher.Scroll.BridgeL1 + alias Indexer.Helper, as: IndexerHelper + + # 32-byte signature of the event SentMessage(address indexed sender, address indexed target, uint256 value, uint256 messageNonce, uint256 gasLimit, bytes message) + @sent_message_event "0x104371f3b442861a2a7b82a070afbbaab748bb13757bf47769e170e37809ec1e" + @sent_message_event_params [{:uint, 256}, {:uint, 256}, {:uint, 256}, :bytes] + + # 32-byte signature of the event RelayedMessage(bytes32 indexed messageHash) + @relayed_message_event "0x4641df4a962071e12719d8c8c8e5ac7fc4d97b927346a3d7a335b1f7517e133c" + + @doc """ + The main function that scans RPC node for the message logs (events), parses them, + and imports to the database (scroll_bridge table). + + The function works for both L1 and L2 layers (depending on the `module` parameter). + It splits a given block range by chunks and scans the messenger contract for the message + logs (events) for each chunk. The found events are handled and then imported to the + `scroll_bridge` database table. + + After historical block range is covered, the function switches to realtime mode and + searches for the message events in every new block. Reorg blocks are taken into account. + + ## Parameters + - `module`: The module from which the function was called: Indexer.Fetcher.Scroll.BridgeL1 or Indexer.Fetcher.Scroll.BridgeL2. + - `state`: The state map containing needed data such as messenger contract address and the block range. + + ## Returns + - {:noreply, state} tuple with the updated block range in the `state` to scan logs in. + """ + @spec loop(module(), %{ + block_check_interval: non_neg_integer(), + messenger_contract: binary(), + json_rpc_named_arguments: EthereumJSONRPC.json_rpc_named_arguments(), + end_block: non_neg_integer(), + start_block: non_neg_integer() + }) :: + {:noreply, + %{ + block_check_interval: non_neg_integer(), + messenger_contract: binary(), + json_rpc_named_arguments: EthereumJSONRPC.json_rpc_named_arguments(), + end_block: non_neg_integer(), + start_block: non_neg_integer() + }} + def loop( + module, + %{ + block_check_interval: block_check_interval, + messenger_contract: messenger_contract, + json_rpc_named_arguments: json_rpc_named_arguments, + end_block: end_block, + start_block: start_block + } = state + ) do + {layer, eth_get_logs_range_size_config} = + if module == BridgeL1 do + {:L1, :l1_eth_get_logs_range_size} + else + {:L2, :l2_eth_get_logs_range_size} + end + + eth_get_logs_range_size = Application.get_all_env(:indexer)[Indexer.Fetcher.Scroll][eth_get_logs_range_size_config] + + time_before = Timex.now() + + last_written_block = + start_block..end_block + |> Enum.chunk_every(eth_get_logs_range_size) + |> Enum.reduce_while(start_block - 1, fn current_chunk, _ -> + chunk_start = List.first(current_chunk) + chunk_end = List.last(current_chunk) + + if chunk_start <= chunk_end do + IndexerHelper.log_blocks_chunk_handling(chunk_start, chunk_end, start_block, end_block, nil, layer) + + operations = + {chunk_start, chunk_end} + |> get_logs_all(messenger_contract, json_rpc_named_arguments) + |> prepare_operations(layer == :L1, json_rpc_named_arguments) + + import_operations(operations) + + IndexerHelper.log_blocks_chunk_handling( + chunk_start, + chunk_end, + start_block, + end_block, + "#{Enum.count(operations)} #{layer} operation(s)", + layer + ) + end + + reorg_block = RollupReorgMonitorQueue.reorg_block_pop(module) + + if !is_nil(reorg_block) && reorg_block > 0 do + # credo:disable-for-next-line Credo.Check.Refactor.Nesting + if layer == :L1 do + BridgeL1.reorg_handle(reorg_block) + end + + {:halt, if(reorg_block <= chunk_end, do: reorg_block - 1, else: chunk_end)} + else + {:cont, chunk_end} + end + end) + + new_start_block = last_written_block + 1 + + {:ok, new_end_block} = + IndexerHelper.get_block_number_by_tag("latest", json_rpc_named_arguments, IndexerHelper.infinite_retries_number()) + + delay = + if new_end_block == last_written_block do + # there is no new block, so wait for some time to let the chain issue the new block + max(block_check_interval - Timex.diff(Timex.now(), time_before, :milliseconds), 0) + else + 0 + end + + Process.send_after(Process.whereis(module), :continue, delay) + + {:noreply, %{state | start_block: new_start_block, end_block: new_end_block}} + end + + # Fetches `SentMessage` and `RelayedMessage` events of the messenger contract from an RPC node + # for the given range of blocks. + @spec get_logs_all({non_neg_integer(), non_neg_integer()}, binary(), EthereumJSONRPC.json_rpc_named_arguments()) :: + [%{atom() => any()}] + defp get_logs_all({chunk_start, chunk_end}, messenger_contract, json_rpc_named_arguments) do + {:ok, result} = + IndexerHelper.get_logs( + chunk_start, + chunk_end, + messenger_contract, + [[@sent_message_event, @relayed_message_event]], + json_rpc_named_arguments, + 0, + IndexerHelper.infinite_retries_number() + ) + + Logs.elixir_to_params(result) + end + + # Imports the given Scroll messages into database. + # Used by Indexer.Fetcher.Scroll.BridgeL1 and Indexer.Fetcher.Scroll.BridgeL2 fetchers. + # Doesn't return anything. + @spec import_operations([Chain.Scroll.Bridge.to_import()]) :: no_return() + defp import_operations(operations) do + {:ok, _} = + Chain.import(%{ + scroll_bridge_operations: %{params: operations}, + timeout: :infinity + }) + end + + # Converts the list of Scroll messenger events to the list of operations + # preparing them for importing to the database. + @spec prepare_operations([%{atom() => any()}], boolean(), EthereumJSONRPC.json_rpc_named_arguments()) :: [ + Chain.Scroll.Bridge.to_import() + ] + defp prepare_operations(events, is_l1, json_rpc_named_arguments) do + block_to_timestamp = + events + |> Enum.filter(fn event -> event.first_topic == @sent_message_event end) + |> blocks_to_timestamps(json_rpc_named_arguments) + + events + |> Enum.map(fn event -> + {index, amount, block_number, block_timestamp, message_hash} = + case event.first_topic do + @sent_message_event -> + { + sender, + target, + amount, + index, + message + } = sent_message_event_parse(event) + + block_number = quantity_to_integer(event.block_number) + block_timestamp = Map.get(block_to_timestamp, block_number) + + operation_encoded = + ABI.encode("relayMessage(address,address,uint256,uint256,bytes)", [ + sender |> :binary.decode_unsigned(), + target |> :binary.decode_unsigned(), + amount, + index, + message + ]) + + message_hash = + "0x" <> + (operation_encoded + |> ExKeccak.hash_256() + |> Base.encode16(case: :lower)) + + {index, amount, block_number, block_timestamp, message_hash} + + @relayed_message_event -> + message_hash = + event.second_topic + |> String.trim_leading("0x") + |> Base.decode16!(case: :mixed) + + {nil, nil, nil, nil, message_hash} + end + + result = %{ + type: operation_type(event.first_topic, is_l1), + message_hash: message_hash + } + + transaction_hash_field = + if is_l1 do + :l1_transaction_hash + else + :l2_transaction_hash + end + + result + |> extend_result(:index, index) + |> extend_result(transaction_hash_field, event.transaction_hash) + |> extend_result(:amount, amount) + |> extend_result(:block_number, block_number) + |> extend_result(:block_timestamp, block_timestamp) + end) + end + + # Constructs a map defining block timestamps by block numbers (key-value pairs) + # from the list of events. The keys of the resulting map consist of block numbers, + # the values are timestamps. + # + # ## Parameters + # - `events`: The list of events based on which the map is constructed. + # - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. + # + # ## Returns + # - A dictionary associating block timestamps with the their numbers. + @spec blocks_to_timestamps([%{atom() => any()}], EthereumJSONRPC.json_rpc_named_arguments()) :: %{ + non_neg_integer() => DateTime.t() + } + defp blocks_to_timestamps(events, json_rpc_named_arguments) do + events + |> IndexerHelper.get_blocks_by_events(json_rpc_named_arguments, IndexerHelper.infinite_retries_number()) + |> Enum.reduce(%{}, fn block, acc -> + block_number = quantity_to_integer(Map.get(block, "number")) + timestamp = timestamp_to_datetime(Map.get(block, "timestamp")) + Map.put(acc, block_number, timestamp) + end) + end + + # Parses the `SentMessage` event to retrieve message sender address, + # target address, amount, index, body. This components are returned + # in the tuple. + # + # ## Parameters + # - `event`: A map describing the event which needs to be parsed. + # + # ## Returns + # - `{sender, target, amount, index, message}` where + # the `sender` is the message sender address, + # the `target` is the target address, + # the `amount` is the amount of the native token sent within this message, + # the `index` is the message numeric index, + # the `message` is the message body. + @spec sent_message_event_parse(%{atom() => any()}) :: + {binary(), binary(), non_neg_integer(), non_neg_integer(), binary()} + defp sent_message_event_parse(event) do + sender = + event.second_topic + |> String.trim_leading("0x") + |> Base.decode16!(case: :mixed) + + target = + event.third_topic + |> String.trim_leading("0x") + |> Base.decode16!(case: :mixed) + + [ + amount, + index, + _gas_limit, + message + ] = decode_data(event.data, @sent_message_event_params) + + {sender, target, amount, index, message} + end + + # Determines the type of the bridge operation based on the event and the chain layer (L1 or L2). + # + # ## Parameters + # - `first_topic`: The signature of the event (in 0x-prefixed hex string). + # - `is_l1`: A boolean flag defining whether the chain layer is L1. + # + # ## Returns + # - :deposit or :withdrawal + @spec operation_type(binary(), boolean()) :: :deposit | :withdrawal + defp operation_type(first_topic, is_l1) do + if first_topic == @sent_message_event do + if is_l1, do: :deposit, else: :withdrawal + else + if is_l1, do: :withdrawal, else: :deposit + end + end + + # Extends the resulting map with the key-value pair. Used by the `prepare_operations` function. + # If the value is `nil`, the key-value pair is not added. + # + # ## Parameters + # - `result`: The resulting map to be extended. + # - `key`: The key component of the key-value pair. + # - `value`: The value component of the key-value pair. + # + # ## Returns + # - The extended map. + @spec extend_result(map(), atom(), any()) :: map() + defp extend_result(result, _key, value) when is_nil(value), do: result + defp extend_result(result, key, value) when is_atom(key), do: Map.put(result, key, value) +end diff --git a/apps/indexer/lib/indexer/fetcher/scroll/bridge_l1.ex b/apps/indexer/lib/indexer/fetcher/scroll/bridge_l1.ex new file mode 100644 index 000000000000..be75a223b973 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/scroll/bridge_l1.ex @@ -0,0 +1,206 @@ +defmodule Indexer.Fetcher.Scroll.BridgeL1 do + @moduledoc """ + The module for scanning Scroll RPC node on L1 for the message logs (events), parsing them, + and importing to the database (scroll_bridge table). + + The events discovery logic is located in the `Indexer.Fetcher.Scroll.Bridge` module whereas this module + only prepares required parameters for the discovery loop. + + The main function splits the whole block range by chunks and scans L1 Scroll Messenger contract + for the message logs (events) for each chunk. The found events are handled and then imported to the + `scroll_bridge` database table. + + After historical block range is covered, the process switches to realtime mode and + searches for the message events in every new block. Reorg blocks are taken into account. + """ + + use GenServer + use Indexer.Fetcher + + require Logger + + import Ecto.Query + + alias Explorer.Chain.Scroll.{Bridge, Reader} + alias Explorer.Repo + alias Indexer.Fetcher.RollupL1ReorgMonitor + alias Indexer.Fetcher.Scroll.Bridge, as: BridgeFetcher + alias Indexer.Fetcher.Scroll.Helper, as: ScrollHelper + alias Indexer.Helper + + @fetcher_name :scroll_bridge_l1 + + def child_spec(start_link_arguments) do + spec = %{ + id: __MODULE__, + start: {__MODULE__, :start_link, start_link_arguments}, + restart: :transient, + type: :worker + } + + Supervisor.child_spec(spec, []) + end + + def start_link(args, gen_server_options \\ []) do + GenServer.start_link(__MODULE__, args, Keyword.put_new(gen_server_options, :name, __MODULE__)) + end + + @impl GenServer + def init(_args) do + {:ok, %{}, {:continue, :ok}} + end + + @impl GenServer + def handle_continue(_, state) do + Logger.metadata(fetcher: @fetcher_name) + # two seconds pause needed to avoid exceeding Supervisor restart intensity when DB issues + Process.send_after(self(), :init_with_delay, 2000) + {:noreply, state} + end + + # Validates parameters and initiates searching of the events. + # + # When first launch, the events searching will start from the given start block + # and end on the `safe` block (or `latest` one if `safe` is not available). + # If this is not the first launch, the process will start from the block which was + # the last on the previous launch. + @impl GenServer + def handle_info(:init_with_delay, _state) do + env = Application.get_all_env(:indexer)[__MODULE__] + + with {:start_block_undefined, false} <- {:start_block_undefined, is_nil(env[:start_block])}, + {:reorg_monitor_started, true} <- {:reorg_monitor_started, !is_nil(Process.whereis(RollupL1ReorgMonitor))}, + rpc = l1_rpc_url(), + {:rpc_undefined, false} <- {:rpc_undefined, is_nil(rpc)}, + {:messenger_contract_address_is_valid, true} <- + {:messenger_contract_address_is_valid, Helper.address_correct?(env[:messenger_contract])}, + start_block = env[:start_block], + true <- start_block > 0, + {last_l1_block_number, last_l1_transaction_hash} = Reader.last_l1_bridge_item(), + json_rpc_named_arguments = Helper.json_rpc_named_arguments(rpc), + {:ok, block_check_interval, safe_block} <- Helper.get_block_check_interval(json_rpc_named_arguments), + {:start_block_valid, true, _, _} <- + {:start_block_valid, + (start_block <= last_l1_block_number || last_l1_block_number == 0) && start_block <= safe_block, + last_l1_block_number, safe_block}, + {:ok, last_l1_transaction} <- + Helper.get_transaction_by_hash(last_l1_transaction_hash, json_rpc_named_arguments), + # here we check for the last known L1 transaction existence to make sure there wasn't reorg + # on L1 while the instance was down, and so we can use `last_l1_block_number` as the starting point + {:l1_transaction_not_found, false} <- + {:l1_transaction_not_found, !is_nil(last_l1_transaction_hash) && is_nil(last_l1_transaction)} do + Process.send(self(), :continue, []) + + {:noreply, + %{ + block_check_interval: block_check_interval, + messenger_contract: env[:messenger_contract], + json_rpc_named_arguments: json_rpc_named_arguments, + end_block: safe_block, + start_block: max(start_block, last_l1_block_number) + }} + else + {:start_block_undefined, true} -> + # the process shouldn't start if the start block is not defined + {:stop, :normal, %{}} + + {:reorg_monitor_started, false} -> + Logger.error("Cannot start this process as Indexer.Fetcher.RollupL1ReorgMonitor is not started.") + {:stop, :normal, %{}} + + {:rpc_undefined, true} -> + Logger.error("L1 RPC URL is not defined.") + {:stop, :normal, %{}} + + {:messenger_contract_address_is_valid, false} -> + Logger.error("L1ScrollMessenger contract address is invalid or not defined.") + {:stop, :normal, %{}} + + {:start_block_valid, false, last_l1_block_number, safe_block} -> + Logger.error("Invalid L1 Start Block value. Please, check the value and scroll_bridge table.") + Logger.error("last_l1_block_number = #{inspect(last_l1_block_number)}") + Logger.error("safe_block = #{inspect(safe_block)}") + {:stop, :normal, %{}} + + {:error, error_data} -> + Logger.error( + "Cannot get last L1 transaction from RPC by its hash, latest block, or block timestamp by its number due to RPC error: #{inspect(error_data)}" + ) + + {:stop, :normal, %{}} + + {:l1_transaction_not_found, true} -> + Logger.error( + "Cannot find last L1 transaction from RPC by its hash. Probably, there was a reorg on L1 chain. Please, check scroll_bridge table." + ) + + {:stop, :normal, %{}} + + _ -> + Logger.error("L1 Start Block is invalid or zero.") + {:stop, :normal, %{}} + end + end + + # See the description of the `loop` function. + @impl GenServer + def handle_info(:continue, state) do + BridgeFetcher.loop(__MODULE__, state) + end + + @impl GenServer + def handle_info({ref, _result}, state) do + Process.demonitor(ref, [:flush]) + {:noreply, state} + end + + @doc """ + Handles L1 block reorg: removes all Deposit rows from the `scroll_bridge` table + created beginning from the reorged block. + + We only store block number for the initiating transaction of the L1->L2 or L2->L1 message, + so the `block_number` column doesn't contain L2 block numbers for messages initiated on L1 layer (i.e. for Deposits), + and that doesn't contain L1 block numbers for messages initiated on L2 layer (i.e. for Withdrawals). + This is the reason why we can only remove rows for Deposit operations from the `scroll_bridge` table + when a reorg happens on L1 layer. + + ## Parameters + - `reorg_block`: The block number where reorg has occurred. + + ## Returns + - Nothing. + """ + @spec reorg_handle(non_neg_integer()) :: any() + def reorg_handle(reorg_block) do + {deleted_count, _} = + Repo.delete_all(from(b in Bridge, where: b.type == :deposit and b.block_number >= ^reorg_block)) + + if deleted_count > 0 do + Logger.warning( + "As L1 reorg was detected, some deposits with block_number >= #{reorg_block} were removed from scroll_bridge table. Number of removed rows: #{deleted_count}." + ) + end + end + + @doc """ + Returns L1 RPC URL for this module. + Returns `nil` if not defined. + """ + @spec l1_rpc_url() :: binary() | nil + def l1_rpc_url do + ScrollHelper.l1_rpc_url() + end + + @doc """ + Determines if `Indexer.Fetcher.RollupL1ReorgMonitor` module must be up + for this module. + + ## Returns + - `true` if the reorg monitor must be active, `false` otherwise. + """ + @spec requires_l1_reorg_monitor?() :: boolean() + def requires_l1_reorg_monitor? do + module_config = Application.get_all_env(:indexer)[__MODULE__] + not is_nil(module_config[:start_block]) + end +end diff --git a/apps/indexer/lib/indexer/fetcher/scroll/bridge_l2.ex b/apps/indexer/lib/indexer/fetcher/scroll/bridge_l2.ex new file mode 100644 index 000000000000..74504bbdeeab --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/scroll/bridge_l2.ex @@ -0,0 +1,155 @@ +defmodule Indexer.Fetcher.Scroll.BridgeL2 do + @moduledoc """ + The module for scanning Scroll RPC node on L2 for the message logs (events), parsing them, + and importing to the database (scroll_bridge table). + + The events discovery logic is located in the `Indexer.Fetcher.Scroll.Bridge` module whereas this module + only prepares required parameters for the discovery loop. + + The main function splits the whole block range by chunks and scans L2 Scroll Messenger contract + for the message logs (events) for each chunk. The found events are handled and then imported to the + `scroll_bridge` database table. + + After historical block range is covered, the process switches to realtime mode and + searches for the message events in every new block. Reorg blocks are taken into account. + """ + + use GenServer + use Indexer.Fetcher + + require Logger + + import Ecto.Query + + alias Explorer.Chain.RollupReorgMonitorQueue + alias Explorer.Chain.Scroll.{Bridge, Reader} + alias Explorer.Repo + alias Indexer.Fetcher.Scroll.Bridge, as: BridgeFetcher + alias Indexer.Helper + + @fetcher_name :scroll_bridge_l2 + + def child_spec(start_link_arguments) do + spec = %{ + id: __MODULE__, + start: {__MODULE__, :start_link, start_link_arguments}, + restart: :transient, + type: :worker + } + + Supervisor.child_spec(spec, []) + end + + def start_link(args, gen_server_options \\ []) do + GenServer.start_link(__MODULE__, args, Keyword.put_new(gen_server_options, :name, __MODULE__)) + end + + @impl GenServer + def init(args) do + json_rpc_named_arguments = args[:json_rpc_named_arguments] + {:ok, %{}, {:continue, json_rpc_named_arguments}} + end + + @impl GenServer + def handle_continue(json_rpc_named_arguments, _state) do + Logger.metadata(fetcher: @fetcher_name) + # two seconds pause needed to avoid exceeding Supervisor restart intensity when DB issues + Process.send_after(self(), :init_with_delay, 2000) + {:noreply, %{json_rpc_named_arguments: json_rpc_named_arguments}} + end + + # Validates parameters and initiates searching of the events. + # + # When first launch, the events searching will start from the first block of the chain + # and end on the `latest` one. If this is not the first launch, the process will start + # from the block which was the last on the previous launch. + @impl GenServer + def handle_info(:init_with_delay, %{json_rpc_named_arguments: json_rpc_named_arguments} = state) do + env = Application.get_all_env(:indexer)[__MODULE__] + + with {:messenger_contract_address_is_valid, true} <- + {:messenger_contract_address_is_valid, Helper.address_correct?(env[:messenger_contract])}, + {last_l2_block_number, last_l2_transaction_hash} = Reader.last_l2_bridge_item(), + {:ok, block_check_interval, _} <- Helper.get_block_check_interval(json_rpc_named_arguments), + {:ok, latest_block} = Helper.get_block_number_by_tag("latest", json_rpc_named_arguments, 100_000_000), + {:ok, last_l2_transaction} <- + Helper.get_transaction_by_hash(last_l2_transaction_hash, json_rpc_named_arguments), + # here we check for the last known L2 transaction existence to make sure there wasn't reorg + # on L2 while the instance was down, and so we can use `last_l2_block_number` as the starting point + {:l2_transaction_not_found, false} <- + {:l2_transaction_not_found, !is_nil(last_l2_transaction_hash) && is_nil(last_l2_transaction)} do + Process.send(self(), :continue, []) + + {:noreply, + %{ + block_check_interval: block_check_interval, + messenger_contract: env[:messenger_contract], + json_rpc_named_arguments: json_rpc_named_arguments, + end_block: latest_block, + start_block: max(env[:start_block], last_l2_block_number) + }} + else + {:messenger_contract_address_is_valid, false} -> + Logger.error("L2ScrollMessenger contract address is invalid or not defined.") + {:stop, :normal, state} + + {:error, error_data} -> + Logger.error( + "Cannot get last L2 transaction from RPC by its hash, latest block, or block by number due to RPC error: #{inspect(error_data)}" + ) + + {:stop, :normal, state} + + {:l2_transaction_not_found, true} -> + Logger.error( + "Cannot find last L2 transaction from RPC by its hash. Probably, there was a reorg on L2 chain. Please, check scroll_bridge table." + ) + + {:stop, :normal, state} + end + end + + # See the description of the `loop` function. + @impl GenServer + def handle_info(:continue, state) do + BridgeFetcher.loop(__MODULE__, state) + end + + @impl GenServer + def handle_info({ref, _result}, state) do + Process.demonitor(ref, [:flush]) + {:noreply, state} + end + + @doc """ + Handles L2 block reorg: removes all Withdrawal rows from the `scroll_bridge` table + created beginning from the reorged block. + + We only store block number for the initiating transaction of the L1->L2 or L2->L1 message, + so the `block_number` column doesn't contain L2 block numbers for messages initiated on L1 layer (i.e. for Deposits), + and that doesn't contain L1 block numbers for messages initiated on L2 layer (i.e. for Withdrawals). + This is the reason why we can only remove rows for Withdrawal operations from the `scroll_bridge` table + when a reorg happens on L2 layer. + + Also, the reorg block number is put into the reorg monitor queue to let the main loop function + (see `Indexer.Fetcher.Scroll.Bridge` module) use that block number and behave accordingly. + + ## Parameters + - `reorg_block`: The block number where reorg has occurred. + + ## Returns + - nothing + """ + def reorg_handle(reorg_block) do + {deleted_count, _} = + Repo.delete_all(from(b in Bridge, where: b.type == :withdrawal and b.block_number >= ^reorg_block)) + + if deleted_count > 0 do + Logger.warning( + "As L2 reorg was detected, some withdrawals with block_number >= #{reorg_block} were removed from scroll_bridge table. Number of removed rows: #{deleted_count}." + ) + end + + RollupReorgMonitorQueue.reorg_block_push(reorg_block, __MODULE__) + end +end diff --git a/apps/indexer/lib/indexer/fetcher/scroll/helper.ex b/apps/indexer/lib/indexer/fetcher/scroll/helper.ex new file mode 100644 index 000000000000..574ff1aa4634 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/scroll/helper.ex @@ -0,0 +1,14 @@ +defmodule Indexer.Fetcher.Scroll.Helper do + @moduledoc """ + A module to define common Scroll indexer functions. + """ + + @doc """ + Returns L1 RPC URL for Scroll modules. + Returns `nil` if not defined. + """ + @spec l1_rpc_url() :: binary() | nil + def l1_rpc_url do + Application.get_all_env(:indexer)[Indexer.Fetcher.Scroll][:rpc] + end +end diff --git a/apps/indexer/lib/indexer/fetcher/scroll/l1_fee_param.ex b/apps/indexer/lib/indexer/fetcher/scroll/l1_fee_param.ex new file mode 100644 index 000000000000..c0b8d0c23bb7 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/scroll/l1_fee_param.ex @@ -0,0 +1,355 @@ +defmodule Indexer.Fetcher.Scroll.L1FeeParam do + @moduledoc """ + Fills scroll_l1_fee_params DB table. + + The table stores points in the chain (block number and transaction index within it) + when L1 Gas Oracle contract parameters were changed (and the new values of the changed + parameters). These points and values are then used by API to correctly display L1 fee + parameters of L2 transaction (such as `overhead`, `scalar`, etc.) + + This fetcher handles the events that were not handled by the realtime block fetcher + (namely `Indexer.Transform.Scroll.L1FeeParams` module). There are three possible cases when it happens: + 1. A Blockscout instance is deployed for a chain that already has blocks. + 2. A Blockscout instance is upgraded, and the functionality to discover fee parameter changes only becomes available after the upgrade. + 3. The block fetcher process (or entire instance) was halted for some time. + + Example of the parameter value change: + + Let's assume that the `scalar` parameter is initially set to 100. An owner decided + to change it to 200. It initiates a transaction that is included into block number 800 + under index 3. All transactions starting from block number 800 and index 4 will have + the scalar equal to 200. All transactions before index 3 of the block 800 (and preceding + blocks) will have the scalar equal to 100. + """ + + use GenServer + use Indexer.Fetcher + + require Logger + + import Ecto.Query + + import EthereumJSONRPC, only: [quantity_to_integer: 1] + import Explorer.Helper, only: [decode_data: 2] + + alias Explorer.{Chain, Repo} + alias Explorer.Chain.Data + alias Explorer.Chain.Scroll.L1FeeParam, as: ScrollL1FeeParam + alias Indexer.Helper + + @fetcher_name :scroll_l1_fee_params + + # 32-byte signature of the event OverheadUpdated(uint256 overhead) + @overhead_updated_event "0x32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4" + + # 32-byte signature of the event ScalarUpdated(uint256 scalar) + @scalar_updated_event "0x3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a" + + # 32-byte signature of the event CommitScalarUpdated(uint256 scalar) + @commit_scalar_updated_event "0x2ab3f5a4ebbcbf3c24f62f5454f52f10e1a8c9dcc5acac8f19199ce881a6a108" + + # 32-byte signature of the event BlobScalarUpdated(uint256 scalar) + @blob_scalar_updated_event "0x6b332a036d8c3ead57dcb06c87243bd7a2aed015ddf2d0528c2501dae56331aa" + + # 32-byte signature of the event L1BaseFeeUpdated(uint256 l1BaseFee) + @l1_base_fee_updated_event "0x351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c44" + + # 32-byte signature of the event L1BlobBaseFeeUpdated(uint256 l1BlobBaseFee) + @l1_blob_base_fee_updated_event "0x9a14bfb5d18c4c3cf14cae19c23d7cf1bcede357ea40ca1f75cd49542c71c214" + + def child_spec(start_link_arguments) do + spec = %{ + id: __MODULE__, + start: {__MODULE__, :start_link, start_link_arguments}, + restart: :transient, + type: :worker + } + + Supervisor.child_spec(spec, []) + end + + def start_link(args, gen_server_options \\ []) do + GenServer.start_link(__MODULE__, args, Keyword.put_new(gen_server_options, :name, __MODULE__)) + end + + @impl GenServer + def init(args) do + json_rpc_named_arguments = args[:json_rpc_named_arguments] + {:ok, %{}, {:continue, json_rpc_named_arguments}} + end + + # Validates L1 Gas Oracle contract address and initiates searching of gas oracle events. + # + # When first launch, the events searching will start from the first block of the chain + # and end on the `safe` block (or `latest` one if `safe` is not available). + # If this is not the first launch, the process will start from the block which was + # the last on the previous launch (plus one). The block from the previous launch + # is stored in the `last_fetched_counters` database table. + # + # ## Parameters + # - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection on L2. + # - `state`: The current state of the fetcher. + # + # ## Returns + # - `{:noreply, new_state}` where the searching parameters are defined. + # - `{:stop, :normal, state}` in case of invalid L1 Gas Oracle contract address. + @impl GenServer + def handle_continue(json_rpc_named_arguments, state) do + Logger.metadata(fetcher: @fetcher_name) + + env = Application.get_all_env(:indexer)[__MODULE__] + + if Helper.address_correct?(env[:gas_oracle]) do + last_l2_block_number = ScrollL1FeeParam.last_l2_block_number() + + {safe_block, safe_block_is_latest} = Helper.get_safe_block(json_rpc_named_arguments) + + Process.send(self(), :find_events, []) + + {:noreply, + %{ + start_block: min(last_l2_block_number + 1, safe_block), + safe_block: safe_block, + safe_block_is_latest: safe_block_is_latest, + gas_oracle: env[:gas_oracle], + eth_get_logs_range_size: + Application.get_all_env(:indexer)[Indexer.Fetcher.Scroll][:l2_eth_get_logs_range_size], + json_rpc_named_arguments: json_rpc_named_arguments + }} + else + Logger.error("L1 Gas Oracle contract address is invalid or not defined.") + {:stop, :normal, state} + end + end + + # Scans the L1 Gas Oracle contract for the events and saves the found parameter changes + # into `scroll_l1_fee_params` database table. + # + # The scanning process starts from the `start_block` defined by `handle_continue` function + # and ends with the latest one. The `safe_block` can be the latest block if the `safe` one + # is not available on the chain (in this case `safe_block_is_latest` is true). So the process + # works in the following block ranges: `start_block..safe_block` and `(safe_block + 1)..latest_block` + # or `start_block..latest`. + # + # ## Parameters + # - `:find_events`: The message that triggers the event scanning process. + # - `state`: The current state of the fetcher containing the searching parameters. + # + # ## Returns + # - `{:stop, :normal, state}` as a signal for the fetcher to stop working after all blocks are handled. + @impl GenServer + def handle_info( + :find_events, + %{ + start_block: start_block, + safe_block: safe_block, + safe_block_is_latest: safe_block_is_latest, + gas_oracle: gas_oracle, + eth_get_logs_range_size: eth_get_logs_range_size, + json_rpc_named_arguments: json_rpc_named_arguments + } = state + ) do + # find and fill all events between start_block and "safe" block + # the "safe" block can be "latest" (when safe_block_is_latest == true) + scan_block_range(start_block, safe_block, gas_oracle, eth_get_logs_range_size, json_rpc_named_arguments) + + if not safe_block_is_latest do + # find and fill all events between "safe" and "latest" block (excluding "safe") + {:ok, latest_block} = Helper.get_block_number_by_tag("latest", json_rpc_named_arguments) + scan_block_range(safe_block + 1, latest_block, gas_oracle, eth_get_logs_range_size, json_rpc_named_arguments) + end + + {:stop, :normal, state} + end + + @impl GenServer + def handle_info({ref, _result}, state) do + Process.demonitor(ref, [:flush]) + {:noreply, state} + end + + @doc """ + Handles L2 block reorg: removes all rows from the `scroll_l1_fee_params` table + created beginning from the reorged block, and accordingly reduces the last + block number defined in the `last_fetched_counters` database table. + + ## Parameters + - `starting_block`: the block number where reorg has occurred. + + ## Returns + - nothing + """ + @spec handle_l2_reorg(non_neg_integer()) :: any() + def handle_l2_reorg(starting_block) do + Repo.delete_all(from(p in ScrollL1FeeParam, where: p.block_number >= ^starting_block)) + + if ScrollL1FeeParam.last_l2_block_number() >= starting_block do + ScrollL1FeeParam.set_last_l2_block_number(starting_block - 1) + end + end + + @doc """ + Converts event parameters and data into the map which can be + used to write a new row to the `scroll_l1_fee_params` table. + + ## Parameters + - `first_topic`: The 32-byte hash of an event signature (in the form of `0x` prefixed hex string). + - `data`: The event data containing a changed parameter. + - `block_number`: The number of the block when the event transaction appeared. + - `transaction_index`: The event transaction index withing the `block_number` block. + + ## Returns + - A map for one row for `Chain.import` function. + """ + @spec event_to_param(binary(), Data.t(), non_neg_integer(), non_neg_integer()) :: ScrollL1FeeParam.to_import() + def event_to_param(first_topic, data, block_number, transaction_index) + when first_topic in [ + @overhead_updated_event, + @scalar_updated_event, + @commit_scalar_updated_event, + @blob_scalar_updated_event, + @l1_base_fee_updated_event, + @l1_blob_base_fee_updated_event + ] do + name = + case first_topic do + @overhead_updated_event -> :overhead + @scalar_updated_event -> :scalar + @commit_scalar_updated_event -> :commit_scalar + @blob_scalar_updated_event -> :blob_scalar + @l1_base_fee_updated_event -> :l1_base_fee + @l1_blob_base_fee_updated_event -> :l1_blob_base_fee + end + + [value] = decode_data(data, [{:uint, 256}]) + + %{ + block_number: block_number, + transaction_index: transaction_index, + name: name, + value: value + } + end + + @doc """ + Returns a list of signatures of the events that can be emitted + by L1 Gas Oracle contract. + """ + @spec event_signatures() :: [binary()] + def event_signatures do + [ + @overhead_updated_event, + @scalar_updated_event, + @commit_scalar_updated_event, + @blob_scalar_updated_event, + @l1_base_fee_updated_event, + @l1_blob_base_fee_updated_event + ] + end + + # Scans the L1 Gas Oracle contract for the events and saves the found parameter changes + # into `scroll_l1_fee_params` database table for the given L2 block range. + # + # The scanning process starts from the `l2_block_start` and ends with the `l2_block_end`. + # The block range is divided by chunks to avoid RPC node overloading. + # + # ## Parameters + # - `l2_block_start`: The start L2 block of the range. + # - `l2_block_end`: The end L2 block of the range. + # - `gas_oracle`: The L1 Gas Oracle contract address. + # - `eth_get_logs_range_size`: Max size of the blocks chunk. + # - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. + # + # ## Returns + # - Nothing is returned. + @spec scan_block_range( + non_neg_integer(), + non_neg_integer(), + binary(), + non_neg_integer(), + EthereumJSONRPC.json_rpc_named_arguments() + ) :: any() + defp scan_block_range(l2_block_start, l2_block_end, gas_oracle, eth_get_logs_range_size, json_rpc_named_arguments) do + chunks_number = ceil((l2_block_end - l2_block_start + 1) / eth_get_logs_range_size) + chunk_range = Range.new(0, max(chunks_number - 1, 0), 1) + + Enum.reduce(chunk_range, 0, fn current_chunk, count_acc -> + chunk_start = l2_block_start + eth_get_logs_range_size * current_chunk + chunk_end = min(chunk_start + eth_get_logs_range_size - 1, l2_block_end) + + Helper.log_blocks_chunk_handling(chunk_start, chunk_end, l2_block_start, l2_block_end, nil, :L2) + + count = + find_and_save_params( + gas_oracle, + chunk_start, + chunk_end, + json_rpc_named_arguments + ) + + Helper.log_blocks_chunk_handling( + chunk_start, + chunk_end, + l2_block_start, + l2_block_end, + "#{count} event(s) for parameters update", + :L2 + ) + + count_acc + count + end) + end + + # Scans the L1 Gas Oracle contract for the events and saves the found parameter changes + # into `scroll_l1_fee_params` database table for the given L2 block range. + # + # The scanning process starts from the `block_start` and ends with the `block_end`. + # The `block_end` block number is stored in the `last_fetched_counters` database table + # to be able to start from that point at the next launch of the fetcher. + # + # ## Parameters + # - `gas_oracle`: The L1 Gas Oracle contract address. + # - `block_start`: The start L2 block of the range. + # - `block_end`: The end L2 block of the range. + # - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. + # + # ## Returns + # - The number of found and saved items. + @spec find_and_save_params(binary(), non_neg_integer(), non_neg_integer(), EthereumJSONRPC.json_rpc_named_arguments()) :: + non_neg_integer() + defp find_and_save_params( + gas_oracle, + block_start, + block_end, + json_rpc_named_arguments + ) do + {:ok, result} = + Helper.get_logs( + block_start, + block_end, + gas_oracle, + [event_signatures()], + json_rpc_named_arguments + ) + + l1_fee_params = + Enum.map(result, fn event -> + event_to_param( + Enum.at(event["topics"], 0), + event["data"], + quantity_to_integer(event["blockNumber"]), + quantity_to_integer(event["transactionIndex"]) + ) + end) + + {:ok, _} = + Chain.import(%{ + scroll_l1_fee_params: %{params: l1_fee_params}, + timeout: :infinity + }) + + ScrollL1FeeParam.set_last_l2_block_number(block_end) + + Enum.count(l1_fee_params) + end +end diff --git a/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex b/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex index 4c29d2b5d1fb..aa3f7a65d845 100644 --- a/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex +++ b/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex @@ -23,6 +23,7 @@ defmodule Indexer.Fetcher.Shibarium.L1 do import Indexer.Fetcher.Shibarium.Helper, only: [calc_operation_hash: 5, prepare_insert_items: 2, recalculate_cached_count: 0] + alias Explorer.Chain.RollupReorgMonitorQueue alias Explorer.Chain.Shibarium.Bridge alias Explorer.{Chain, Repo} alias Indexer.Fetcher.RollupL1ReorgMonitor @@ -284,7 +285,7 @@ defmodule Indexer.Fetcher.Shibarium.L1 do ) end - reorg_block = RollupL1ReorgMonitor.reorg_block_pop(__MODULE__) + reorg_block = RollupReorgMonitorQueue.reorg_block_pop(__MODULE__) if !is_nil(reorg_block) && reorg_block > 0 do reorg_handle(reorg_block) @@ -318,6 +319,27 @@ defmodule Indexer.Fetcher.Shibarium.L1 do {:noreply, state} end + @doc """ + Returns L1 RPC URL for this module. + """ + @spec l1_rpc_url() :: binary() + def l1_rpc_url do + Application.get_all_env(:indexer)[__MODULE__][:rpc] + end + + @doc """ + Determines if `Indexer.Fetcher.RollupL1ReorgMonitor` module must be up + for this module. + + ## Returns + - `true` if the reorg monitor must be active, `false` otherwise. + """ + @spec requires_l1_reorg_monitor?() :: boolean() + def requires_l1_reorg_monitor? do + module_config = Application.get_all_env(:indexer)[__MODULE__] + not is_nil(module_config[:start_block]) + end + defp filter_deposit_events(events) do Enum.filter(events, fn event -> topic0 = Enum.at(event["topics"], 0) diff --git a/apps/indexer/lib/indexer/helper.ex b/apps/indexer/lib/indexer/helper.ex index 854cb187e811..d01e030d9981 100644 --- a/apps/indexer/lib/indexer/helper.ex +++ b/apps/indexer/lib/indexer/helper.ex @@ -239,7 +239,10 @@ defmodule Indexer.Helper do - `from_block`: The starting block number (integer or hexadecimal string) for the log search. - `to_block`: The ending block number (integer or hexadecimal string) for the log search. - `address`: The address of the contract to filter logs from. - - `topics`: List of topics to filter the logs. + - `topics`: List of topics to filter the logs. The list represents each topic as follows: + [topic0, topic1, topic2, topic3]. The `topicN` can be either some value or + a list of possible values, e.g.: [[topic0_1, topic0_2], topic1, topic2, topic3]. + If a topic is omitted or `nil`, it doesn't take part in the logs filtering. - `json_rpc_named_arguments`: Configuration for the JSON-RPC call. - `id`: (optional) JSON-RPC request identifier, defaults to 0. - `retries`: (optional) Number of retry attempts if the request fails, defaults to 3. @@ -549,11 +552,22 @@ defmodule Indexer.Helper do end @doc """ - Fetches blocks info from the given list of events (logs). - Performs a specified number of retries (up to) if the first attempt returns error. + Fetches blocks info from the given list of events (logs). + Performs a specified number of retries (up to) if the first attempt returns error. + + ## Parameters + - `events`: The list of events to retrieve block numbers from. + - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. + - `retries`: Number of retry attempts if the request fails. + - `transaction_details`: Whether to include transaction details into the resulting list of blocks. + + ## Returns + - The list of blocks. The list is empty if the HTTP response returns error. """ - @spec get_blocks_by_events(list(), list(), non_neg_integer()) :: list() - def get_blocks_by_events(events, json_rpc_named_arguments, retries) do + @spec get_blocks_by_events(list(), EthereumJSONRPC.json_rpc_named_arguments(), non_neg_integer(), boolean()) :: [ + %{String.t() => any()} + ] + def get_blocks_by_events(events, json_rpc_named_arguments, retries, transaction_details \\ false) do events |> Enum.reduce(%{}, fn event, acc -> block_number = Map.get(event, :block_number, event["blockNumber"]) @@ -562,7 +576,7 @@ defmodule Indexer.Helper do |> Stream.map(fn {block_number, _} -> %{number: block_number} end) |> Stream.with_index() |> Enum.into(%{}, fn {params, id} -> {id, params} end) - |> Blocks.requests(&ByNumber.request(&1, false, false)) + |> Blocks.requests(&ByNumber.request(&1, transaction_details, false)) |> Enum.chunk_every(@block_by_number_chunk_size) |> Enum.reduce([], fn current_requests, results_acc -> error_message = diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex index 389b77532476..857a79934792 100644 --- a/apps/indexer/lib/indexer/supervisor.ex +++ b/apps/indexer/lib/indexer/supervisor.ex @@ -168,6 +168,22 @@ defmodule Indexer.Supervisor do [json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor] ]), configure(Indexer.Fetcher.Shibarium.L1.Supervisor, [[memory_monitor: memory_monitor]]), + {Indexer.Fetcher.Scroll.BridgeL1.Supervisor, + [ + [memory_monitor: memory_monitor] + ]}, + {Indexer.Fetcher.Scroll.BridgeL2.Supervisor, + [ + [json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor] + ]}, + {Indexer.Fetcher.Scroll.L1FeeParam.Supervisor, + [ + [json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor] + ]}, + {Indexer.Fetcher.Scroll.Batch.Supervisor, + [ + [memory_monitor: memory_monitor] + ]}, configure(Indexer.Fetcher.PolygonZkevm.BridgeL1.Supervisor, [[memory_monitor: memory_monitor]]), configure(Indexer.Fetcher.PolygonZkevm.BridgeL1Tokens.Supervisor, [[memory_monitor: memory_monitor]]), configure(Indexer.Fetcher.PolygonZkevm.BridgeL2.Supervisor, [ diff --git a/apps/indexer/lib/indexer/transform/scroll/l1_fee_params.ex b/apps/indexer/lib/indexer/transform/scroll/l1_fee_params.ex new file mode 100644 index 000000000000..12875255817c --- /dev/null +++ b/apps/indexer/lib/indexer/transform/scroll/l1_fee_params.ex @@ -0,0 +1,69 @@ +defmodule Indexer.Transform.Scroll.L1FeeParams do + @moduledoc """ + Helper functions for transforming data for Scroll L1 fee parameters + in realtime block fetcher. + """ + + require Logger + + @doc """ + Takes logs from the realtime fetcher, filters them + by signatures (L1 Gas Oracle events), and prepares an output for + `Chain.import` function. It doesn't work if L1 Gas Oracle contract + address is not configured or the chain type is not :scroll. In this case + the returned value is an empty list. + + ## Parameters + - `logs`: A list of log entries to filter for L1 Gas Oracle events. + + ## Returns + - A list of items ready for database import. The list can be empty. + """ + @spec parse([map()]) :: [Explorer.Chain.Scroll.L1FeeParam.to_import()] + def parse(logs) + + if Application.compile_env(:explorer, :chain_type) == :scroll do + def parse(logs) do + prev_metadata = Logger.metadata() + Logger.metadata(fetcher: :scroll_l1_fee_params_realtime) + + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + gas_oracle = Application.get_env(:indexer, Indexer.Fetcher.Scroll.L1FeeParam)[:gas_oracle] + + # credo:disable-for-lines:2 Credo.Check.Design.AliasUsage + items = + if Indexer.Helper.address_correct?(gas_oracle) do + gas_oracle = String.downcase(gas_oracle) + + logs + |> Enum.filter(&fee_param_update_event?(&1, gas_oracle)) + |> Enum.map(fn log -> + Logger.info("Event for parameter update found.") + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + Indexer.Fetcher.Scroll.L1FeeParam.event_to_param( + log.first_topic, + log.data, + log.block_number, + log.transaction_index + ) + end) + else + Logger.error("L1 Gas Oracle contract address is incorrect. Cannot use #{__MODULE__} for parsing logs.") + [] + end + + Logger.reset_metadata(prev_metadata) + + items + end + + defp fee_param_update_event?(log, gas_oracle) do + # credo:disable-for-lines:3 Credo.Check.Design.AliasUsage + !is_nil(log.first_topic) && + String.downcase(log.first_topic) in Indexer.Fetcher.Scroll.L1FeeParam.event_signatures() && + String.downcase(Indexer.Helper.address_hash_to_string(log.address_hash)) == gas_oracle + end + else + def parse(_logs), do: [] + end +end diff --git a/apps/indexer/test/indexer/buffered_task_test.exs b/apps/indexer/test/indexer/buffered_task_test.exs index 9dc3a7d2de6c..09212eeceb84 100644 --- a/apps/indexer/test/indexer/buffered_task_test.exs +++ b/apps/indexer/test/indexer/buffered_task_test.exs @@ -3,7 +3,8 @@ defmodule Indexer.BufferedTaskTest do import Mox - alias Indexer.{BoundQueue, BufferedTask} + alias Explorer.BoundQueue + alias Indexer.BufferedTask alias Indexer.BufferedTaskTest.{RetryableTask, ShrinkableTask} @max_batch_size 2 diff --git a/config/config_helper.exs b/config/config_helper.exs index 882ef3bfc7ae..715cfbbdbeb1 100644 --- a/config/config_helper.exs +++ b/config/config_helper.exs @@ -16,6 +16,7 @@ defmodule ConfigHelper do :polygon_edge -> base_repos ++ [Explorer.Repo.PolygonEdge] :polygon_zkevm -> base_repos ++ [Explorer.Repo.PolygonZkevm] :rsk -> base_repos ++ [Explorer.Repo.RSK] + :scroll -> base_repos ++ [Explorer.Repo.Scroll] :shibarium -> base_repos ++ [Explorer.Repo.Shibarium] :suave -> base_repos ++ [Explorer.Repo.Suave] :filecoin -> base_repos ++ [Explorer.Repo.Filecoin] @@ -310,6 +311,7 @@ defmodule ConfigHelper do "polygon_edge", "polygon_zkevm", "rsk", + "scroll", "shibarium", "stability", "suave", diff --git a/config/runtime.exs b/config/runtime.exs index aebdeff2475f..1ff0ad3901cc 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -656,6 +656,8 @@ config :explorer, Explorer.Chain.Blackfort.Validator, api_url: System.get_env("B ### Indexer ### ############### +first_block = ConfigHelper.parse_integer_env_var("FIRST_BLOCK", 0) + trace_first_block = ConfigHelper.parse_integer_env_var("TRACE_FIRST_BLOCK", 0) trace_last_block = ConfigHelper.parse_integer_or_nil_env_var("TRACE_LAST_BLOCK") @@ -669,7 +671,7 @@ config :indexer, block_transformer: ConfigHelper.block_transformer(), metadata_updater_milliseconds_interval: ConfigHelper.parse_time_env_var("TOKEN_METADATA_UPDATE_INTERVAL", "48h"), block_ranges: System.get_env("BLOCK_RANGES"), - first_block: ConfigHelper.parse_integer_env_var("FIRST_BLOCK", 0), + first_block: first_block, last_block: ConfigHelper.parse_integer_or_nil_env_var("LAST_BLOCK"), trace_block_ranges: trace_block_ranges, trace_first_block: trace_first_block, @@ -1089,6 +1091,42 @@ config :indexer, Indexer.Fetcher.Filecoin.AddressInfo.Supervisor, config :indexer, Indexer.Fetcher.Filecoin.AddressInfo, concurrency: ConfigHelper.parse_integer_env_var("INDEXER_FILECOIN_ADDRESS_INFO_CONCURRENCY", 1) +config :indexer, Indexer.Fetcher.Scroll, + l1_eth_get_logs_range_size: ConfigHelper.parse_integer_env_var("INDEXER_SCROLL_L1_ETH_GET_LOGS_RANGE_SIZE", 250), + l2_eth_get_logs_range_size: ConfigHelper.parse_integer_env_var("INDEXER_SCROLL_L2_ETH_GET_LOGS_RANGE_SIZE", 1000), + rpc: System.get_env("INDEXER_SCROLL_L1_RPC") + +config :indexer, Indexer.Fetcher.Scroll.L1FeeParam, gas_oracle: System.get_env("INDEXER_SCROLL_L2_GAS_ORACLE_CONTRACT") + +config :explorer, Explorer.Chain.Scroll.L1FeeParam, + curie_upgrade_block: ConfigHelper.parse_integer_env_var("SCROLL_L2_CURIE_UPGRADE_BLOCK", 0), + scalar_init: ConfigHelper.parse_integer_env_var("SCROLL_L1_SCALAR_INIT", 0), + overhead_init: ConfigHelper.parse_integer_env_var("SCROLL_L1_OVERHEAD_INIT", 0), + commit_scalar_init: ConfigHelper.parse_integer_env_var("SCROLL_L1_COMMIT_SCALAR_INIT", 0), + blob_scalar_init: ConfigHelper.parse_integer_env_var("SCROLL_L1_BLOB_SCALAR_INIT", 0), + l1_base_fee_init: ConfigHelper.parse_integer_env_var("SCROLL_L1_BASE_FEE_INIT", 0), + l1_blob_base_fee_init: ConfigHelper.parse_integer_env_var("SCROLL_L1_BLOB_BASE_FEE_INIT", 0) + +config :indexer, Indexer.Fetcher.Scroll.L1FeeParam.Supervisor, disabled?: ConfigHelper.chain_type() != :scroll + +config :indexer, Indexer.Fetcher.Scroll.BridgeL1, + messenger_contract: System.get_env("INDEXER_SCROLL_L1_MESSENGER_CONTRACT"), + start_block: ConfigHelper.parse_integer_or_nil_env_var("INDEXER_SCROLL_L1_MESSENGER_START_BLOCK") + +config :indexer, Indexer.Fetcher.Scroll.BridgeL2, + messenger_contract: System.get_env("INDEXER_SCROLL_L2_MESSENGER_CONTRACT"), + start_block: ConfigHelper.parse_integer_env_var("INDEXER_SCROLL_L2_MESSENGER_START_BLOCK", first_block) + +config :indexer, Indexer.Fetcher.Scroll.Batch, + scroll_chain_contract: System.get_env("INDEXER_SCROLL_L1_CHAIN_CONTRACT"), + start_block: ConfigHelper.parse_integer_or_nil_env_var("INDEXER_SCROLL_L1_BATCH_START_BLOCK") + +config :indexer, Indexer.Fetcher.Scroll.BridgeL1.Supervisor, disabled?: ConfigHelper.chain_type() != :scroll + +config :indexer, Indexer.Fetcher.Scroll.BridgeL2.Supervisor, disabled?: ConfigHelper.chain_type() != :scroll + +config :indexer, Indexer.Fetcher.Scroll.Batch.Supervisor, disabled?: ConfigHelper.chain_type() != :scroll + Code.require_file("#{config_env()}.exs", "config/runtime") for config <- "../apps/*/config/runtime/#{config_env()}.exs" |> Path.expand(__DIR__) |> Path.wildcard() do diff --git a/config/runtime/dev.exs b/config/runtime/dev.exs index 0e2d20cc0d35..198d0efbd330 100644 --- a/config/runtime/dev.exs +++ b/config/runtime/dev.exs @@ -117,6 +117,13 @@ config :explorer, Explorer.Repo.PolygonZkevm, # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type pool_size: 1 +# Configure Scroll database +config :explorer, Explorer.Repo.Scroll, + database: database, + hostname: hostname, + url: System.get_env("DATABASE_URL"), + pool_size: 1 + # Configure ZkSync database config :explorer, Explorer.Repo.ZkSync, database: database, diff --git a/config/runtime/prod.exs b/config/runtime/prod.exs index f52ef52b5912..fbb3a50ad2f2 100644 --- a/config/runtime/prod.exs +++ b/config/runtime/prod.exs @@ -88,6 +88,12 @@ config :explorer, Explorer.Repo.PolygonZkevm, pool_size: 1, ssl: ExplorerConfigHelper.ssl_enabled?() +# Configures Scroll database +config :explorer, Explorer.Repo.Scroll, + url: System.get_env("DATABASE_URL"), + pool_size: 1, + ssl: ExplorerConfigHelper.ssl_enabled?() + # Configures ZkSync database config :explorer, Explorer.Repo.ZkSync, url: System.get_env("DATABASE_URL"), diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index b4778591b233..ef36f375f680 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -291,6 +291,23 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_OPTIMISM_L2_MESSAGE_PASSER_CONTRACT= # INDEXER_OPTIMISM_L1_DEPOSITS_BATCH_SIZE= # INDEXER_OPTIMISM_L1_DEPOSITS_TRANSACTION_TYPE= +# INDEXER_SCROLL_L1_RPC= +# INDEXER_SCROLL_L1_MESSENGER_CONTRACT= +# INDEXER_SCROLL_L1_MESSENGER_START_BLOCK= +# INDEXER_SCROLL_L1_CHAIN_CONTRACT= +# INDEXER_SCROLL_L1_BATCH_START_BLOCK= +# INDEXER_SCROLL_L2_MESSENGER_CONTRACT= +# INDEXER_SCROLL_L2_MESSENGER_START_BLOCK= +# INDEXER_SCROLL_L2_GAS_ORACLE_CONTRACT= +# INDEXER_SCROLL_L1_ETH_GET_LOGS_RANGE_SIZE= +# INDEXER_SCROLL_L2_ETH_GET_LOGS_RANGE_SIZE= +# SCROLL_L2_CURIE_UPGRADE_BLOCK= +# SCROLL_L1_SCALAR_INIT= +# SCROLL_L1_OVERHEAD_INIT= +# SCROLL_L1_COMMIT_SCALAR_INIT= +# SCROLL_L1_BLOB_SCALAR_INIT= +# SCROLL_L1_BASE_FEE_INIT= +# SCROLL_L1_BLOB_BASE_FEE_INIT= # ROOTSTOCK_REMASC_ADDRESS= # ROOTSTOCK_BRIDGE_ADDRESS= # ROOTSTOCK_LOCKED_BTC_CACHE_PERIOD= From fc0c5b5315c1e6e5905b1e5ef400c6231f6d6cf7 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Tue, 22 Oct 2024 19:35:44 +0300 Subject: [PATCH 244/363] fix: Fix failed tests (#11000) --- ...anitize_duplicated_log_index_logs_test.exs | 268 +++++++++--------- 1 file changed, 136 insertions(+), 132 deletions(-) diff --git a/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs b/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs index df5fe83d08c5..c9973ef618b6 100644 --- a/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs +++ b/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs @@ -7,138 +7,142 @@ defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogsTest do alias Explorer.Chain.Token.Instance alias Explorer.Migrator.{SanitizeDuplicatedLogIndexLogs, MigrationStatus} - describe "Sanitize duplicated log index logs" do - test "correctly identifies and updates duplicated log index logs" do - block = insert(:block) - - tx1 = :transaction |> insert() |> with_block(block, index: 0) - tx2 = :transaction |> insert() |> with_block(block, index: 1) - - _log1 = insert(:log, transaction: tx1, index: 3, data: "0x01", block: block, block_number: block.number) - _log2 = insert(:log, transaction: tx1, index: 0, data: "0x02", block: block, block_number: block.number) - _log3 = insert(:log, transaction: tx2, index: 3, data: "0x03", block: block, block_number: block.number) - - log4 = insert(:log) - - assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil - - SanitizeDuplicatedLogIndexLogs.start_link([]) - Process.sleep(300) - - assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" - assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true - - updated_logs = Repo.all(Log |> where([log], log.block_number == ^block.number) |> order_by([log], asc: log.index)) - - assert match?( - [ - %{index: 0, data: %Explorer.Chain.Data{bytes: <<2>>}}, - %{index: 1, data: %Explorer.Chain.Data{bytes: <<1>>}}, - %{index: 2, data: %Explorer.Chain.Data{bytes: <<3>>}} - ], - updated_logs - ) - - assert %Log{log4 | address: nil, block: nil, transaction: nil} == %Log{ - Repo.one(Log |> where([log], log.block_number != ^block.number)) - | address: nil, - block: nil, - transaction: nil - } - end - - test "correctly identifies and updates duplicated log index logs & updates corresponding token transfers and token instances" do - block = insert(:block) - 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) - - tx1 = :transaction |> insert() |> with_block(block, index: 0) - tx2 = :transaction |> insert() |> with_block(block, index: 1) - - log1 = insert(:log, transaction: tx1, index: 3, data: "0x01", block: block, block_number: block.number) - log2 = insert(:log, transaction: tx1, index: 0, data: "0x02", block: block, block_number: block.number) - log3 = insert(:log, transaction: tx2, index: 3, data: "0x03", block: block, block_number: block.number) - - log4 = insert(:log) - - _tt1 = - insert(:token_transfer, - token_type: "ERC-721", - block: block, - block_number: block.number, - log_index: log1.index, - token_ids: [instance.token_id], - token_contract_address: token_address, - token_contract_address_hash: token_address.hash, - transaction: tx1, - transaction_hash: tx1.hash, - block_hash: block.hash - ) - - _tt2 = - insert(:token_transfer, - block: block, - block_number: block.number, - log_index: log2.index, - transaction: tx1, - transaction_hash: tx1.hash - ) - - _tt3 = - insert(:token_transfer, - block: block, - block_number: block.number, - log_index: log3.index, - transaction: tx2, - transaction_hash: tx2.hash - ) - - Instance.changeset(instance, %{owner_updated_at_block: block.number, owner_updated_at_log_index: log1.index}) - |> Repo.update!() - - assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil - - SanitizeDuplicatedLogIndexLogs.start_link([]) - Process.sleep(300) - - assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" - assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true - - updated_logs = Repo.all(Log |> where([log], log.block_number == ^block.number) |> order_by([log], asc: log.index)) - - assert match?( - [ - %{index: 0, data: %Explorer.Chain.Data{bytes: <<2>>}}, - %{index: 1, data: %Explorer.Chain.Data{bytes: <<1>>}}, - %{index: 2, data: %Explorer.Chain.Data{bytes: <<3>>}} - ], - updated_logs - ) - - block_number = block.number - assert [%{owner_updated_at_block: ^block_number, owner_updated_at_log_index: 1}] = Repo.all(Instance) - - assert [%{log_index: 1, block_number: ^block_number}] = - Repo.all(TokenTransfer |> where([tt], tt.token_type == "ERC-721")) - - assert %Log{log4 | address: nil, block: nil, transaction: nil} == %Log{ - Repo.one(Log |> where([log], log.block_number != ^block.number)) - | address: nil, - block: nil, - transaction: nil - } - end - - test "correctly handles cases where there are no duplicated log index logs" do - assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil - - SanitizeDuplicatedLogIndexLogs.start_link([]) - Process.sleep(100) - - assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" - assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true + if Application.compile_env(:explorer, :chain_type) != :celo do + describe "Sanitize duplicated log index logs" do + test "correctly identifies and updates duplicated log index logs" do + block = insert(:block) + + tx1 = :transaction |> insert() |> with_block(block, index: 0) + tx2 = :transaction |> insert() |> with_block(block, index: 1) + + _log1 = insert(:log, transaction: tx1, index: 3, data: "0x01", block: block, block_number: block.number) + _log2 = insert(:log, transaction: tx1, index: 0, data: "0x02", block: block, block_number: block.number) + _log3 = insert(:log, transaction: tx2, index: 3, data: "0x03", block: block, block_number: block.number) + + log4 = insert(:log) + + assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil + + SanitizeDuplicatedLogIndexLogs.start_link([]) + Process.sleep(300) + + assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" + assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true + + updated_logs = + Repo.all(Log |> where([log], log.block_number == ^block.number) |> order_by([log], asc: log.index)) + + assert match?( + [ + %{index: 0, data: %Explorer.Chain.Data{bytes: <<2>>}}, + %{index: 1, data: %Explorer.Chain.Data{bytes: <<1>>}}, + %{index: 2, data: %Explorer.Chain.Data{bytes: <<3>>}} + ], + updated_logs + ) + + assert %Log{log4 | address: nil, block: nil, transaction: nil} == %Log{ + Repo.one(Log |> where([log], log.block_number != ^block.number)) + | address: nil, + block: nil, + transaction: nil + } + end + + test "correctly identifies and updates duplicated log index logs & updates corresponding token transfers and token instances" do + block = insert(:block) + 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) + + tx1 = :transaction |> insert() |> with_block(block, index: 0) + tx2 = :transaction |> insert() |> with_block(block, index: 1) + + log1 = insert(:log, transaction: tx1, index: 3, data: "0x01", block: block, block_number: block.number) + log2 = insert(:log, transaction: tx1, index: 0, data: "0x02", block: block, block_number: block.number) + log3 = insert(:log, transaction: tx2, index: 3, data: "0x03", block: block, block_number: block.number) + + log4 = insert(:log) + + _tt1 = + insert(:token_transfer, + token_type: "ERC-721", + block: block, + block_number: block.number, + log_index: log1.index, + token_ids: [instance.token_id], + token_contract_address: token_address, + token_contract_address_hash: token_address.hash, + transaction: tx1, + transaction_hash: tx1.hash, + block_hash: block.hash + ) + + _tt2 = + insert(:token_transfer, + block: block, + block_number: block.number, + log_index: log2.index, + transaction: tx1, + transaction_hash: tx1.hash + ) + + _tt3 = + insert(:token_transfer, + block: block, + block_number: block.number, + log_index: log3.index, + transaction: tx2, + transaction_hash: tx2.hash + ) + + Instance.changeset(instance, %{owner_updated_at_block: block.number, owner_updated_at_log_index: log1.index}) + |> Repo.update!() + + assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil + + SanitizeDuplicatedLogIndexLogs.start_link([]) + Process.sleep(300) + + assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" + assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true + + updated_logs = + Repo.all(Log |> where([log], log.block_number == ^block.number) |> order_by([log], asc: log.index)) + + assert match?( + [ + %{index: 0, data: %Explorer.Chain.Data{bytes: <<2>>}}, + %{index: 1, data: %Explorer.Chain.Data{bytes: <<1>>}}, + %{index: 2, data: %Explorer.Chain.Data{bytes: <<3>>}} + ], + updated_logs + ) + + block_number = block.number + assert [%{owner_updated_at_block: ^block_number, owner_updated_at_log_index: 1}] = Repo.all(Instance) + + assert [%{log_index: 1, block_number: ^block_number}] = + Repo.all(TokenTransfer |> where([tt], tt.token_type == "ERC-721")) + + assert %Log{log4 | address: nil, block: nil, transaction: nil} == %Log{ + Repo.one(Log |> where([log], log.block_number != ^block.number)) + | address: nil, + block: nil, + transaction: nil + } + end + + test "correctly handles cases where there are no duplicated log index logs" do + assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil + + SanitizeDuplicatedLogIndexLogs.start_link([]) + Process.sleep(100) + + assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" + assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true + end end end end From aa3defae186a27a6db218eb238d22bddccb50e72 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 22 Oct 2024 19:48:29 +0300 Subject: [PATCH 245/363] feat: List of internal transactions API v2 endpoint (#10994) * feat: List of internal transactions API v2 endpoint * Process Fedor review * Fix format * Process review comment --- .../api/v2/internal_transaction_controller.ex | 58 ++++++++++++ .../controllers/api/v2/token_controller.ex | 4 +- .../api/v2/token_transfer_controller.ex | 4 +- .../transaction_interpretation.ex | 4 +- .../lib/block_scout_web/routers/api_router.ex | 4 + .../views/api/v2/internal_transaction_view.ex | 60 ++++++++++++ .../views/api/v2/token_transfer_view.ex | 10 +- .../views/api/v2/transaction_view.ex | 34 +------ .../internal_transaction_controller_test.exs | 93 +++++++++++++++++++ apps/explorer/lib/explorer/chain.ex | 26 +++--- .../explorer/chain/internal_transaction.ex | 37 ++++++++ .../lib/explorer/chain/token_transfer.ex | 3 + 12 files changed, 287 insertions(+), 50 deletions(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v2/internal_transaction_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/internal_transaction_view.ex create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/v2/internal_transaction_controller_test.exs diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/internal_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/internal_transaction_controller.ex new file mode 100644 index 000000000000..dcfde7452eb2 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/internal_transaction_controller.ex @@ -0,0 +1,58 @@ +defmodule BlockScoutWeb.API.V2.InternalTransactionController do + use BlockScoutWeb, :controller + alias Explorer.Chain.InternalTransaction + alias Explorer.{Helper, PagingOptions} + + import BlockScoutWeb.Chain, + only: [ + split_list_by_page: 1, + paging_options: 1, + next_page_params: 3 + ] + + import BlockScoutWeb.PagingHelper, + only: [ + delete_parameters_from_next_page_params: 1 + ] + + import Explorer.PagingOptions, only: [default_paging_options: 0] + + action_fallback(BlockScoutWeb.API.V2.FallbackController) + + @api_true [api?: true] + + @doc """ + Function to handle GET requests to `/api/v2/internal-transactions` endpoint. + """ + @spec internal_transactions(Plug.Conn.t(), map()) :: Plug.Conn.t() + def internal_transactions(conn, params) do + paging_options = paging_options(params) + + options = + paging_options + |> Keyword.update(:paging_options, default_paging_options(), fn %PagingOptions{ + page_size: page_size + } = paging_options -> + maybe_parsed_limit = Helper.parse_integer(params["limit"]) + %PagingOptions{paging_options | page_size: min(page_size, maybe_parsed_limit && abs(maybe_parsed_limit))} + end) + |> Keyword.merge(@api_true) + + result = + options + |> InternalTransaction.fetch() + |> split_list_by_page() + + {internal_transactions, next_page} = result + + next_page_params = + next_page |> next_page_params(internal_transactions, delete_parameters_from_next_page_params(params)) + + conn + |> put_status(200) + |> render(:internal_transactions, %{ + internal_transactions: internal_transactions, + next_page_params: next_page_params + }) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex index fe9bc6777e76..506db2ab9fe8 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex @@ -297,8 +297,8 @@ defmodule BlockScoutWeb.API.V2.TokenController do |> Keyword.update(:paging_options, default_paging_options(), fn %PagingOptions{ page_size: page_size } = paging_options -> - mb_parsed_limit = Helper.parse_integer(params["limit"]) - %PagingOptions{paging_options | page_size: min(page_size, mb_parsed_limit && abs(mb_parsed_limit))} + maybe_parsed_limit = Helper.parse_integer(params["limit"]) + %PagingOptions{paging_options | page_size: min(page_size, maybe_parsed_limit && abs(maybe_parsed_limit))} end) |> Keyword.merge(token_transfers_types_options(params)) |> Keyword.merge(tokens_sorting(params)) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_transfer_controller.ex index f353bcc8bcf6..a917e8008bc9 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_transfer_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_transfer_controller.ex @@ -36,8 +36,8 @@ defmodule BlockScoutWeb.API.V2.TokenTransferController do |> Keyword.update(:paging_options, default_paging_options(), fn %PagingOptions{ page_size: page_size } = paging_options -> - mb_parsed_limit = Helper.parse_integer(params["limit"]) - %PagingOptions{paging_options | page_size: min(page_size, mb_parsed_limit && abs(mb_parsed_limit))} + maybe_parsed_limit = Helper.parse_integer(params["limit"]) + %PagingOptions{paging_options | page_size: min(page_size, maybe_parsed_limit && abs(maybe_parsed_limit))} end) |> Keyword.merge(token_transfers_types_options(params)) |> Keyword.merge(@api_true) diff --git a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex index f2daacb0c442..263559cb2351 100644 --- a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex +++ b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex @@ -3,7 +3,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do Module to interact with Transaction Interpretation Service """ - alias BlockScoutWeb.API.V2.{Helper, TokenTransferView, TokenView, TransactionView} + alias BlockScoutWeb.API.V2.{Helper, InternalTransactionView, TokenTransferView, TokenView, TransactionView} alias Ecto.Association.NotLoaded alias Explorer.Chain alias Explorer.Chain.{Data, InternalTransaction, Log, TokenTransfer, Transaction} @@ -203,7 +203,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do defp prepare_internal_transactions(internal_transactions, transaction) do internal_transactions - |> Enum.map(&TransactionView.prepare_internal_transaction(&1, transaction.block)) + |> Enum.map(&InternalTransactionView.prepare_internal_transaction(&1, transaction.block)) end defp fetch_logs(transaction) do diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index 42ec12c12df5..e56e8c002e4e 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -168,6 +168,10 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/", V2.TokenTransferController, :token_transfers) end + scope "/internal-transactions" do + get("/", V2.InternalTransactionController, :internal_transactions) + end + scope "/blocks" do get("/", V2.BlockController, :blocks) get("/:block_hash_or_number", V2.BlockController, :block) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/internal_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/internal_transaction_view.ex new file mode 100644 index 000000000000..6ac34a0e0bad --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/internal_transaction_view.ex @@ -0,0 +1,60 @@ +defmodule BlockScoutWeb.API.V2.InternalTransactionView do + use BlockScoutWeb, :view + + alias BlockScoutWeb.API.V2.Helper + alias Explorer.Chain.{Block, InternalTransaction} + + def render("internal_transaction.json", %{internal_transaction: nil}) do + nil + end + + def render("internal_transaction.json", %{ + internal_transaction: internal_transaction, + block: block + }) do + prepare_internal_transaction(internal_transaction, block) + end + + def render("internal_transactions.json", %{ + internal_transactions: internal_transactions, + next_page_params: next_page_params + }) do + %{ + "items" => Enum.map(internal_transactions, &prepare_internal_transaction(&1, &1.block)), + "next_page_params" => next_page_params + } + end + + @doc """ + Prepares internal transaction object to be returned in the API v2 endpoints. + """ + @spec prepare_internal_transaction(InternalTransaction.t(), Block.t() | nil) :: map() + def prepare_internal_transaction(internal_transaction, block \\ nil) do + %{ + "error" => internal_transaction.error, + "success" => is_nil(internal_transaction.error), + "type" => internal_transaction.call_type || internal_transaction.type, + "transaction_hash" => internal_transaction.transaction_hash, + "transaction_index" => internal_transaction.transaction_index, + "from" => + Helper.address_with_info(nil, internal_transaction.from_address, internal_transaction.from_address_hash, false), + "to" => + Helper.address_with_info(nil, internal_transaction.to_address, internal_transaction.to_address_hash, false), + "created_contract" => + Helper.address_with_info( + nil, + internal_transaction.created_contract_address, + internal_transaction.created_contract_address_hash, + false + ), + "value" => internal_transaction.value, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `block_number` property + "block" => internal_transaction.block_number, + "block_number" => internal_transaction.block_number, + "timestamp" => (block && block.timestamp) || internal_transaction.block.timestamp, + "index" => internal_transaction.index, + "gas_limit" => internal_transaction.gas, + "block_index" => internal_transaction.block_index + } + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_transfer_view.ex index 33c52bd9bd84..6d4ec1d001ef 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_transfer_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_transfer_view.ex @@ -5,7 +5,7 @@ defmodule BlockScoutWeb.API.V2.TokenTransferView do alias BlockScoutWeb.Tokens.Helper, as: TokensHelper alias Ecto.Association.NotLoaded alias Explorer.Chain - alias Explorer.Chain.Transaction + alias Explorer.Chain.{TokenTransfer, Transaction} def render("token_transfer.json", %{token_transfer: nil}) do nil @@ -39,6 +39,10 @@ defmodule BlockScoutWeb.API.V2.TokenTransferView do } end + @doc """ + Prepares token transfer object to be returned in the API v2 endpoints. + """ + @spec prepare_token_transfer(TokenTransfer.t(), Plug.Conn.t() | nil, any()) :: map() def prepare_token_transfer(token_transfer, _conn, decoded_input) do %{ "transaction_hash" => token_transfer.transaction_hash, @@ -61,6 +65,10 @@ defmodule BlockScoutWeb.API.V2.TokenTransferView do } end + @doc """ + Prepares token transfer total value/id transferred to be returned in the API v2 endpoints. + """ + @spec prepare_token_transfer_total(TokenTransfer.t()) :: map() # credo:disable-for-next-line /Complexity/ def prepare_token_transfer_total(token_transfer) do case TokensHelper.token_transfer_amount_for_api(token_transfer) do diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex index b569c7d100d8..36511e45be37 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex @@ -1,7 +1,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do use BlockScoutWeb, :view - alias BlockScoutWeb.API.V2.{ApiView, Helper, TokenTransferView, TokenView} + alias BlockScoutWeb.API.V2.{ApiView, Helper, InternalTransactionView, TokenTransferView, TokenView} alias BlockScoutWeb.{ABIEncodedValueView, TransactionView} alias BlockScoutWeb.Models.GetTransactionTags @@ -155,7 +155,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do block: block }) do %{ - "items" => Enum.map(internal_transactions, &prepare_internal_transaction(&1, block)), + "items" => Enum.map(internal_transactions, &InternalTransactionView.prepare_internal_transaction(&1, block)), "next_page_params" => next_page_params } end @@ -165,7 +165,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do next_page_params: next_page_params }) do %{ - "items" => Enum.map(internal_transactions, &prepare_internal_transaction(&1)), + "items" => Enum.map(internal_transactions, &InternalTransactionView.prepare_internal_transaction(&1)), "next_page_params" => next_page_params } end @@ -252,34 +252,6 @@ defmodule BlockScoutWeb.API.V2.TransactionView do } end - def prepare_internal_transaction(internal_transaction, block \\ nil) do - %{ - "error" => internal_transaction.error, - "success" => is_nil(internal_transaction.error), - "type" => internal_transaction.call_type || internal_transaction.type, - "transaction_hash" => internal_transaction.transaction_hash, - "from" => - Helper.address_with_info(nil, internal_transaction.from_address, internal_transaction.from_address_hash, false), - "to" => - Helper.address_with_info(nil, internal_transaction.to_address, internal_transaction.to_address_hash, false), - "created_contract" => - Helper.address_with_info( - nil, - internal_transaction.created_contract_address, - internal_transaction.created_contract_address_hash, - false - ), - "value" => internal_transaction.value, - "block_number" => internal_transaction.block_number, - # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `block_number` property - "block" => internal_transaction.block_number, - "timestamp" => (block && block.timestamp) || internal_transaction.block.timestamp, - "index" => internal_transaction.index, - "gas_limit" => internal_transaction.gas, - "block_index" => internal_transaction.block_index - } - end - def prepare_log(log, transaction_or_hash, decoded_log, tags_for_address_needed? \\ false) do decoded = process_decoded_log(decoded_log) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/internal_transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/internal_transaction_controller_test.exs new file mode 100644 index 000000000000..2f9dad655803 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/internal_transaction_controller_test.exs @@ -0,0 +1,93 @@ +defmodule BlockScoutWeb.API.V2.InternalTransactionControllerTest do + use BlockScoutWeb.ConnCase + + alias Explorer.Chain.{Address, InternalTransaction} + + describe "/internal-transactions" do + test "empty list", %{conn: conn} do + request = get(conn, "/api/v2/internal-transactions") + + assert response = json_response(request, 200) + assert response["items"] == [] + assert response["next_page_params"] == nil + end + + test "non empty list", %{conn: conn} do + tx = + :transaction + |> insert() + |> with_block() + + insert(:internal_transaction, + transaction: tx, + block_hash: tx.block_hash, + index: 0, + block_index: 0 + ) + + request = get(conn, "/api/v2/internal-transactions") + + assert response = json_response(request, 200) + assert Enum.count(response["items"]) == 1 + assert response["next_page_params"] == nil + end + + test "internal transactions with next_page_params", %{conn: conn} do + transaction = insert(:transaction) |> with_block() + + internal_transaction = + insert(:internal_transaction, + transaction: transaction, + transaction_index: 0, + block_number: transaction.block_number, + block_hash: transaction.block_hash, + index: 0, + block_index: 0 + ) + + transaction_2 = insert(:transaction) |> with_block() + + internal_transactions = + for i <- 0..49 do + insert(:internal_transaction, + transaction: transaction_2, + transaction_index: 0, + block_number: transaction_2.block_number, + block_hash: transaction_2.block_hash, + index: i, + block_index: i + ) + end + + internal_transactions = [internal_transaction | internal_transactions] + + request = get(conn, "/api/v2/internal-transactions") + assert response = json_response(request, 200) + + request_2nd_page = get(conn, "/api/v2/internal-transactions", response["next_page_params"]) + assert response_2nd_page = json_response(request_2nd_page, 200) + + check_paginated_response(response, response_2nd_page, internal_transactions) + end + end + + defp compare_item(%InternalTransaction{} = internal_transaction, json) do + assert Address.checksum(internal_transaction.from_address_hash) == json["from"]["hash"] + assert Address.checksum(internal_transaction.to_address_hash) == json["to"]["hash"] + assert to_string(internal_transaction.transaction_hash) == json["transaction_hash"] + assert internal_transaction.block_number == json["block_number"] + assert internal_transaction.block_index == json["block_index"] + end + + defp check_paginated_response(first_page_resp, second_page_resp, internal_transactions) do + assert Enum.count(first_page_resp["items"]) == 50 + assert first_page_resp["next_page_params"] != nil + compare_item(Enum.at(internal_transactions, 50), Enum.at(first_page_resp["items"], 0)) + + compare_item(Enum.at(internal_transactions, 1), Enum.at(first_page_resp["items"], 49)) + + assert Enum.count(second_page_resp["items"]) == 1 + assert second_page_resp["next_page_params"] == nil + compare_item(Enum.at(internal_transactions, 0), Enum.at(second_page_resp["items"], 0)) + end +end diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 2e4f3525b118..27db2c00e191 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -3411,28 +3411,28 @@ defmodule Explorer.Chain do # todo: keep next clause for compatibility with frontend and remove when new frontend is bound to `index_internal_transaction_desc_order` property def page_internal_transaction(query, %PagingOptions{key: {block_number, transaction_index, index}}, %{ - index_int_tx_desc_order: desc + index_int_tx_desc_order: desc_order }) do - hardcoded_where_for_page_internal_transaction(query, block_number, transaction_index, index, desc) + hardcoded_where_for_page_internal_transaction(query, block_number, transaction_index, index, desc_order) end def page_internal_transaction(query, %PagingOptions{key: {block_number, transaction_index, index}}, %{ - index_internal_transaction_desc_order: desc + index_internal_transaction_desc_order: desc_order }) do - hardcoded_where_for_page_internal_transaction(query, block_number, transaction_index, index, desc) + hardcoded_where_for_page_internal_transaction(query, block_number, transaction_index, index, desc_order) end # todo: keep next clause for compatibility with frontend and remove when new frontend is bound to `index_internal_transaction_desc_order` property - def page_internal_transaction(query, %PagingOptions{key: {0}}, %{index_int_tx_desc_order: desc}) do - if desc do + def page_internal_transaction(query, %PagingOptions{key: {0}}, %{index_int_tx_desc_order: desc_order}) do + if desc_order do query else where(query, [internal_transaction], internal_transaction.index > 0) end end - def page_internal_transaction(query, %PagingOptions{key: {0}}, %{index_internal_transaction_desc_order: desc}) do - if desc do + def page_internal_transaction(query, %PagingOptions{key: {0}}, %{index_internal_transaction_desc_order: desc_order}) do + if desc_order do query else where(query, [internal_transaction], internal_transaction.index > 0) @@ -3440,16 +3440,18 @@ defmodule Explorer.Chain do end # todo: keep next clause for compatibility with frontend and remove when new frontend is bound to `index_internal_transaction_desc_order` property - def page_internal_transaction(query, %PagingOptions{key: {index}}, %{index_int_tx_desc_order: desc}) do - if desc do + def page_internal_transaction(query, %PagingOptions{key: {index}}, %{index_int_tx_desc_order: desc_order}) do + if desc_order do where(query, [internal_transaction], internal_transaction.index < ^index) else where(query, [internal_transaction], internal_transaction.index > ^index) end end - def page_internal_transaction(query, %PagingOptions{key: {index}}, %{index_internal_transaction_desc_order: desc}) do - if desc do + def page_internal_transaction(query, %PagingOptions{key: {index}}, %{ + index_internal_transaction_desc_order: desc_order + }) do + if desc_order do where(query, [internal_transaction], internal_transaction.index < ^index) else where(query, [internal_transaction], internal_transaction.index > ^index) diff --git a/apps/explorer/lib/explorer/chain/internal_transaction.ex b/apps/explorer/lib/explorer/chain/internal_transaction.ex index 2661c63e4ca4..3d62885d34dd 100644 --- a/apps/explorer/lib/explorer/chain/internal_transaction.ex +++ b/apps/explorer/lib/explorer/chain/internal_transaction.ex @@ -5,8 +5,12 @@ defmodule Explorer.Chain.InternalTransaction do alias Explorer.{Chain, PagingOptions} alias Explorer.Chain.{Address, Block, Data, Hash, PendingBlockOperation, Transaction, Wei} + alias Explorer.Chain.DenormalizationHelper alias Explorer.Chain.InternalTransaction.{Action, CallType, Result, Type} + @typep paging_options :: {:paging_options, PagingOptions.t()} + @typep api? :: {:api?, true | false} + @default_paging_options %PagingOptions{page_size: 50} @typedoc """ @@ -813,6 +817,39 @@ defmodule Explorer.Chain.InternalTransaction do ) end + @doc """ + Returns the ordered paginated list of internal transactions (consensus blocks only) from the DB with address, block preloads + """ + @spec fetch([paging_options | api?]) :: [] + def fetch(options) do + paging_options = Keyword.get(options, :paging_options, @default_paging_options) + + case paging_options do + %PagingOptions{key: {0, 0}} -> + [] + + _ -> + preloads = + DenormalizationHelper.extend_transaction_preload([ + :block, + [from_address: [:names, :smart_contract, :proxy_implementations]], + [to_address: [:names, :smart_contract, :proxy_implementations]] + ]) + + __MODULE__ + |> where_nonpending_block() + |> Chain.page_internal_transaction(paging_options, %{index_internal_transaction_desc_order: true}) + |> order_by([internal_transaction], + desc: internal_transaction.block_number, + desc: internal_transaction.transaction_index, + desc: internal_transaction.index + ) + |> limit(^paging_options.page_size) + |> preload(^preloads) + |> Chain.select_repo(options).all() + end + end + defp page_block_internal_transaction(query, %PagingOptions{key: %{block_index: block_index}}) do query |> where([internal_transaction], internal_transaction.block_index > ^block_index) diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index 9ace1518d4b5..1b9e3ac0ac91 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -282,6 +282,9 @@ defmodule Explorer.Chain.TokenTransfer do end end + @doc """ + Returns the ordered paginated list of consensus token transfers (consensus blocks only) from the DB with address, token, transaction preloads + """ @spec fetch([paging_options | api?]) :: [] def fetch(options) do paging_options = Keyword.get(options, :paging_options, @default_paging_options) From 930c481959409be8adf24e5bc5901f6f4009a9db Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Tue, 22 Oct 2024 20:16:23 +0300 Subject: [PATCH 246/363] perf: optimize advanced filters (#10463) * perf: optimize advanced filters * Fix filters order * Fix order by * Fix block_number filtering * Fix filters order and union * Fix tests * Fix: remove excessive limit * Add internal transaction to_address_hash index * Optimize amount filter * Fix filtering after limit * Some fixes Fix address filtering; Fix token transfers; Fix methods search * Remove migration; Fix query inclusion * Optimize internal transactions query * Fix @vbaranov review * Fix renaming issues * Rename function arguments --------- Co-authored-by: Viktor Baranov --- .../api/v2/advanced_filter_controller.ex | 12 +- .../controllers/api/v2/fallback_controller.ex | 7 - .../v2/advanced_filter_controller_test.exs | 249 +++- apps/explorer/lib/explorer/chain.ex | 9 +- .../lib/explorer/chain/advanced_filter.ex | 1125 +++++++++++++---- 5 files changed, 1102 insertions(+), 300 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex index 922788dde650..cfb074a7f8d1 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/advanced_filter_controller.ex @@ -90,6 +90,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do |> Keyword.update(:paging_options, %PagingOptions{page_size: CSVHelper.limit()}, fn paging_options -> %PagingOptions{paging_options | page_size: CSVHelper.limit()} end) + |> Keyword.put(:timeout, :timer.minutes(5)) full_options |> AdvancedFilter.list() @@ -130,8 +131,12 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do ContractMethod.find_contract_method_by_name(query, @api_true) end - with {:method, %ContractMethod{abi: %{"name" => name}, identifier: identifier}} <- {:method, mb_contract_method} do - render(conn, :methods, methods: [%{method_id: "0x" <> Base.encode16(identifier, case: :lower), name: name}]) + case mb_contract_method do + %ContractMethod{abi: %{"name" => name}, identifier: identifier} -> + render(conn, :methods, methods: [%{method_id: "0x" <> Base.encode16(identifier, case: :lower), name: name}]) + + _ -> + render(conn, :methods, methods: []) end end end @@ -189,7 +194,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterController do defp extract_filters(params) do [ - transaction_types: prepare_transaction_types(params["transaction_types"]), + # TODO: remove when frontend is adopted to new naming + transaction_types: prepare_transaction_types(params["transaction_types"] || params["tx_types"]), methods: params["methods"] |> prepare_methods(), age: prepare_age(params["age_from"], params["age_to"]), from_address_hashes: diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex index 0879bd5adf44..127bfe7840ed 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex @@ -282,13 +282,6 @@ defmodule BlockScoutWeb.API.V2.FallbackController do |> render(:message, %{message: @unverified_smart_contract}) end - def call(conn, {:method, _}) do - conn - |> put_status(:not_found) - |> put_view(ApiView) - |> render(:message, %{message: @not_found}) - end - def call(conn, {:is_empty_response, true}) do conn |> put_status(500) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/advanced_filter_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/advanced_filter_controller_test.exs index 9f85979f33a3..cca5e007a527 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/advanced_filter_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/advanced_filter_controller_test.exs @@ -3,7 +3,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do import Mox - alias Explorer.Chain.{AdvancedFilter, Data} + alias Explorer.Chain.SmartContract + alias Explorer.Chain.{AdvancedFilter, Data, Hash} alias Explorer.{Factory, TestHelper} describe "/advanced_filters" do @@ -18,7 +19,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do first_transaction = :transaction |> insert() |> with_block() insert_list(3, :token_transfer, transaction: first_transaction) - for i <- 0..2 do + for i <- 1..3 do insert(:internal_transaction, transaction: first_transaction, block_hash: first_transaction.block_hash, @@ -41,7 +42,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do first_transaction = :transaction |> insert() |> with_block() insert_list(3, :token_transfer, transaction: first_transaction) - for i <- 0..2 do + for i <- 1..3 do insert(:internal_transaction, transaction: first_transaction, block_hash: first_transaction.block_hash, @@ -65,7 +66,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do first_transaction = :transaction |> insert() |> with_block() insert_list(3, :token_transfer, transaction: first_transaction) - for i <- 0..2 do + for i <- 1..3 do insert(:internal_transaction, transaction: first_transaction, block_hash: first_transaction.block_hash, @@ -96,7 +97,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do first_transaction = :transaction |> insert() |> with_block() insert_list(3, :token_transfer, transaction: first_transaction) - for i <- 0..2 do + for i <- 1..3 do insert(:internal_transaction, transaction: first_transaction, block_hash: first_transaction.block_hash, @@ -107,7 +108,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do second_transaction = :transaction |> insert() |> with_block() - for i <- 0..49 do + for i <- 1..50 do insert(:internal_transaction, transaction: second_transaction, block_hash: second_transaction.block_hash, @@ -130,13 +131,20 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do transaction = insert(:transaction) |> with_block() for token_type <- ~w(ERC-20 ERC-404 ERC-721 ERC-1155), + token = insert(:token, type: token_type), _ <- 0..4 do - insert(:token_transfer, transaction: transaction, token_type: token_type) + insert(:token_transfer, + transaction: transaction, + token_type: token_type, + token: token, + token_contract_address_hash: token.contract_address_hash, + token_contract_address: token.contract_address + ) end transaction = :transaction |> insert() |> with_block() - for i <- 0..29 do + for i <- 1..30 do insert(:internal_transaction, transaction: transaction, block_hash: transaction.block_hash, @@ -201,7 +209,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do {:ok, method3} = Data.cast(method_id3_string <> "ab0ba0") {:ok, method4} = Data.cast(method_id4_string <> "ab0ba0") - for i <- 0..4 do + for i <- 1..5 do insert(:internal_transaction, transaction: transaction, to_address_hash: contract_address.hash, @@ -213,7 +221,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do ) end - for i <- 5..9 do + for i <- 6..10 do insert(:internal_transaction, transaction: transaction, to_address_hash: contract_address.hash, @@ -257,25 +265,33 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do end test "filter by age", %{conn: conn} do - first_timestamp = ~U[2023-12-12 00:00:00.000000Z] + [_, tx_a, _, tx_b, _] = + for i <- 0..4 do + tx = :transaction |> insert() |> with_block(status: :ok) - for i <- 0..4 do - transaction = :transaction |> insert() |> with_block(block_timestamp: Timex.shift(first_timestamp, days: i)) + insert(:internal_transaction, + transaction: tx, + index: i + 1, + block_index: i + 1, + block_hash: tx.block_hash, + block: tx.block + ) - insert(:internal_transaction, - transaction: transaction, - block_hash: transaction.block_hash, - index: i, - block_index: i - ) + insert(:token_transfer, + transaction: tx, + block_number: tx.block_number, + log_index: i, + block_hash: tx.block_hash, + block: tx.block + ) - insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) - end + tx + end request = get(conn, "/api/v2/advanced-filters", %{ - "age_from" => "2023-12-14T00:00:00Z", - "age_to" => "2023-12-16T00:00:00Z" + "age_from" => DateTime.to_iso8601(tx_a.block.timestamp), + "age_to" => DateTime.to_iso8601(tx_b.block.timestamp) }) assert response = json_response(request, 200) @@ -297,8 +313,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do from_address_hash: address.hash, from_address: address, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, @@ -312,8 +328,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:internal_transaction, transaction: transaction, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) @@ -341,8 +357,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do from_address_hash: address.hash, from_address: address, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, @@ -356,8 +372,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:internal_transaction, transaction: transaction, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) @@ -391,8 +407,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do from_address_hash: address_to_include.hash, from_address: address_to_include, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, @@ -406,8 +422,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:internal_transaction, transaction: transaction, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) @@ -439,8 +455,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do to_address_hash: address.hash, to_address: address, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, @@ -454,8 +470,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:internal_transaction, transaction: transaction, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) @@ -483,8 +499,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do to_address_hash: address.hash, to_address: address, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, @@ -498,8 +514,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:internal_transaction, transaction: transaction, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) @@ -533,8 +549,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do to_address_hash: address_to_include.hash, to_address: address_to_include, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, @@ -548,8 +564,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:internal_transaction, transaction: transaction, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) @@ -583,8 +599,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do from_address_hash: from_address.hash, from_address: from_address, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, @@ -603,8 +619,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do to_address_hash: to_address.hash, to_address: to_address, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, @@ -632,8 +648,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do from_address_hash: from_address.hash, from_address: from_address, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, @@ -650,8 +666,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:internal_transaction, transaction: transaction, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) @@ -686,8 +702,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do from_address_hash: from_address.hash, from_address: from_address, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, @@ -706,8 +722,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do to_address_hash: to_address.hash, to_address: to_address, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, @@ -735,8 +751,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do from_address_hash: from_address.hash, from_address: from_address, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, @@ -753,8 +769,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:internal_transaction, transaction: transaction, block_hash: transaction.block_hash, - index: i, - block_index: i + index: i + 1, + block_index: i + 1 ) insert(:token_transfer, transaction: transaction, block_number: transaction.block_number, log_index: i) @@ -779,8 +795,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do insert(:internal_transaction, transaction: transaction, block_hash: transaction.block_hash, - index: 0, - block_index: 0, + index: 1, + block_index: 1, value: i * 10 ** 18 ) @@ -908,13 +924,110 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do assert Enum.count(response["items"]) == 3 end + + test "correct query with all filters and pagination", %{conn: conn} do + for address_relation <- [:or, :and] do + method_id_string = "0xa9059cbb" + {:ok, method} = Data.cast(method_id_string <> "ab0ba0") + transaction_from_address = insert(:address) + transaction_to_address = insert(:address) + token_transfer_from_address = insert(:address) + token_transfer_to_address = insert(:address) + token = insert(:token) + {:ok, burn_address_hash} = Hash.Address.cast(SmartContract.burn_address_hash_string()) + + insert_list(5, :transaction) + + transactions = + for _ <- 0..29 do + transaction = + insert(:transaction, + from_address: transaction_from_address, + from_address_hash: transaction_from_address.hash, + to_address: transaction_to_address, + to_address_hash: transaction_to_address.hash, + value: Enum.random(0..1_000_000), + input: method + ) + |> with_block() + + insert(:token_transfer, + transaction: transaction, + block_number: transaction.block_number, + amount: Enum.random(0..1_000_000), + from_address: token_transfer_from_address, + from_address_hash: token_transfer_from_address.hash, + to_address: token_transfer_to_address, + to_address_hash: token_transfer_to_address.hash, + token_contract_address: token.contract_address, + token_contract_address_hash: token.contract_address_hash + ) + + transaction + end + + insert_list(5, :transaction) + + from_timestamp = List.first(transactions).block.timestamp + to_timestamp = List.last(transactions).block.timestamp + + params = %{ + "tx_types" => "coin_transfer,ERC-20", + "methods" => method_id_string, + "age_from" => from_timestamp |> DateTime.to_iso8601(), + "age_to" => to_timestamp |> DateTime.to_iso8601(), + "from_address_hashes_to_include" => "#{transaction_from_address.hash},#{token_transfer_from_address.hash}", + "to_address_hashes_to_include" => "#{transaction_to_address.hash},#{token_transfer_to_address.hash}", + "address_relation" => to_string(address_relation), + "amount_from" => "0", + "amount_to" => "1000000", + "token_contract_address_hashes_to_include" => "native,#{token.contract_address_hash}", + "token_contract_address_hashes_to_exclude" => "#{burn_address_hash}" + } + + request = + get(conn, "/api/v2/advanced-filters", params) + + assert response = json_response(request, 200) + request_2nd_page = get(conn, "/api/v2/advanced-filters", Map.merge(params, response["next_page_params"])) + assert response_2nd_page = json_response(request_2nd_page, 200) + + check_paginated_response( + AdvancedFilter.list( + tx_types: ["COIN_TRANSFER", "ERC-20"], + methods: ["0xa9059cbb"], + age: [from: from_timestamp, to: to_timestamp], + from_address_hashes: [ + include: [transaction_from_address.hash, token_transfer_from_address.hash], + exclude: nil + ], + to_address_hashes: [ + include: [transaction_to_address.hash, token_transfer_to_address.hash], + exclude: nil + ], + address_relation: address_relation, + amount: [from: Decimal.new("0"), to: Decimal.new("1000000")], + token_contract_address_hashes: [ + include: [ + "native", + token.contract_address_hash + ], + exclude: [burn_address_hash] + ], + api?: true + ), + response["items"], + response_2nd_page["items"] + ) + end + end end describe "/advanced_filters/methods?q=" do - test "returns 404 if method does not exist", %{conn: conn} do + test "returns empty list if method does not exist", %{conn: conn} do request = get(conn, "/api/v2/advanced-filters/methods", %{"q" => "foo"}) - assert response = json_response(request, 404) - assert response["message"] == "Not found" + assert response = json_response(request, 200) + assert response == [] end test "finds method by name", %{conn: conn} do diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 27db2c00e191..04446a6e44ec 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -2452,8 +2452,6 @@ defmodule Explorer.Chain do @spec timestamp_to_block_number(DateTime.t(), :before | :after, boolean()) :: {:ok, Block.block_number()} | {:error, :not_found} def timestamp_to_block_number(given_timestamp, closest, from_api) do - {:ok, t} = Timex.format(given_timestamp, "%Y-%m-%d %H:%M:%S", :strftime) - consensus_blocks_query = from( block in Block, @@ -2463,7 +2461,7 @@ defmodule Explorer.Chain do gt_timestamp_query = from( block in consensus_blocks_query, - where: fragment("? >= TO_TIMESTAMP(?, 'YYYY-MM-DD HH24:MI:SS')", block.timestamp, ^t), + where: block.timestamp >= ^given_timestamp, order_by: [asc: block.timestamp], limit: 1, select: block @@ -2472,7 +2470,7 @@ defmodule Explorer.Chain do lt_timestamp_query = from( block in consensus_blocks_query, - where: fragment("? <= TO_TIMESTAMP(?, 'YYYY-MM-DD HH24:MI:SS')", block.timestamp, ^t), + where: block.timestamp <= ^given_timestamp, order_by: [desc: block.timestamp], limit: 1, select: block @@ -2484,8 +2482,7 @@ defmodule Explorer.Chain do from( block in subquery(union_query), select: block, - order_by: - fragment("abs(extract(epoch from (? - TO_TIMESTAMP(?, 'YYYY-MM-DD HH24:MI:SS'))))", block.timestamp, ^t), + order_by: fragment("abs(extract(epoch from (? - ?)))", block.timestamp, ^given_timestamp), limit: 1 ) diff --git a/apps/explorer/lib/explorer/chain/advanced_filter.ex b/apps/explorer/lib/explorer/chain/advanced_filter.ex index b8b2d910ec50..1684670b3ac1 100644 --- a/apps/explorer/lib/explorer/chain/advanced_filter.ex +++ b/apps/explorer/lib/explorer/chain/advanced_filter.ex @@ -8,7 +8,7 @@ defmodule Explorer.Chain.AdvancedFilter do import Ecto.Query alias Explorer.{Chain, Helper, PagingOptions} - alias Explorer.Chain.{Address, Data, Hash, InternalTransaction, TokenTransfer, Transaction} + alias Explorer.Chain.{Address, Data, DenormalizationHelper, Hash, InternalTransaction, TokenTransfer, Transaction} @primary_key false typed_embedded_schema null: false do @@ -74,19 +74,31 @@ defmodule Explorer.Chain.AdvancedFilter do | token_contract_address_hashes() | Chain.paging_options() | Chain.api?() + | {:timeout, timeout()} ] @spec list(options()) :: [__MODULE__.t()] def list(options \\ []) do paging_options = Keyword.get(options, :paging_options) + timeout = Keyword.get(options, :timeout, :timer.seconds(60)) + + age = Keyword.get(options, :age) + + block_numbers_age = + [ + from: age[:from] && Chain.timestamp_to_block_number(age[:from], :after, Keyword.get(options, :api?, false)), + to: age[:to] && Chain.timestamp_to_block_number(age[:to], :before, Keyword.get(options, :api?, false)) + ] + tasks = options + |> Keyword.put(:block_numbers_age, block_numbers_age) |> queries(paging_options) - |> Enum.map(fn query -> Task.async(fn -> Chain.select_repo(options).all(query) end) end) + |> Enum.map(fn query -> Task.async(fn -> Chain.select_repo(options).all(query, timeout: timeout) end) end) tasks - |> Task.yield_many(:timer.seconds(60)) + |> Task.yield_many(timeout: timeout, on_timeout: :kill_task) |> Enum.flat_map(fn {_task, res} -> case res do {:ok, result} -> @@ -102,55 +114,57 @@ defmodule Explorer.Chain.AdvancedFilter do |> Enum.map(&to_advanced_filter/1) |> Enum.sort(&sort_function/2) |> take_page_size(paging_options) + |> Chain.select_repo(options).preload( + from_address: [:names, :smart_contract, :proxy_implementations], + to_address: [:names, :smart_contract, :proxy_implementations], + created_contract_address: [:names, :smart_contract, :proxy_implementations] + ) end defp queries(options, paging_options) do - cond do - only_transactions?(options) -> - [transactions_query(paging_options, options), internal_transactions_query(paging_options, options)] - - only_token_transfers?(options) -> - [token_transfers_query(paging_options, options)] - - true -> - [ - transactions_query(paging_options, options), - internal_transactions_query(paging_options, options), - token_transfers_query(paging_options, options) - ] - end + [] + |> maybe_add_transactions_queries(options, paging_options) + |> maybe_add_token_transfers_queries(options, paging_options) end - defp only_transactions?(options) do - transaction_types = options[:transaction_types] - tokens_to_include = options[:token_contract_address_hashes][:include] + defp maybe_add_transactions_queries(queries, options, paging_options) do + transaction_types = options[:transaction_types] || [] + tokens_to_include = options[:token_contract_address_hashes][:include] || [] + tokens_to_exclude = options[:token_contract_address_hashes][:exclude] || [] - transaction_types == ["COIN_TRANSFER"] or tokens_to_include == ["native"] + if (transaction_types == [] or "COIN_TRANSFER" in transaction_types) and + (tokens_to_include == [] or "native" in tokens_to_include) and + "native" not in tokens_to_exclude do + [transactions_query(paging_options, options), internal_transactions_query(paging_options, options) | queries] + else + queries + end end - defp only_token_transfers?(options) do - transaction_types = options[:transaction_types] - tokens_to_include = options[:token_contract_address_hashes][:include] - tokens_to_exclude = options[:token_contract_address_hashes][:exclude] + defp maybe_add_token_transfers_queries(queries, options, paging_options) do + transaction_types = options[:transaction_types] || [] + tokens_to_include = options[:token_contract_address_hashes][:include] || [] - (is_list(transaction_types) and length(transaction_types) > 0 and "COIN_TRANSFER" not in transaction_types) or - (is_list(tokens_to_include) and length(tokens_to_include) > 0 and "native" not in tokens_to_include) or - (is_list(tokens_to_exclude) and "native" in tokens_to_exclude) + if (transaction_types == [] or not (transaction_types |> Enum.reject(&(&1 == "COIN_TRANSFER")) |> Enum.empty?())) and + (tokens_to_include == [] or not (tokens_to_include |> Enum.reject(&(&1 == "native")) |> Enum.empty?())) do + [token_transfers_query(paging_options, options) | queries] + else + queries + end end defp to_advanced_filter(%Transaction{} = transaction) do + %{value: decimal_transaction_value} = transaction.value + %__MODULE__{ hash: transaction.hash, type: "coin_transfer", input: transaction.input, timestamp: transaction.block_timestamp, - from_address: transaction.from_address, from_address_hash: transaction.from_address_hash, - to_address: transaction.to_address, to_address_hash: transaction.to_address_hash, - created_contract_address: transaction.created_contract_address, created_contract_address_hash: transaction.created_contract_address_hash, - value: transaction.value.value, + value: decimal_transaction_value, fee: transaction |> Transaction.fee(:wei) |> elem(1), block_number: transaction.block_number, transaction_index: transaction.index @@ -158,18 +172,17 @@ defmodule Explorer.Chain.AdvancedFilter do end defp to_advanced_filter(%InternalTransaction{} = internal_transaction) do + %{value: decimal_internal_transaction_value} = internal_transaction.value + %__MODULE__{ hash: internal_transaction.transaction.hash, type: "coin_transfer", input: internal_transaction.input, timestamp: internal_transaction.transaction.block_timestamp, - from_address: internal_transaction.from_address, from_address_hash: internal_transaction.from_address_hash, - to_address: internal_transaction.to_address, to_address_hash: internal_transaction.to_address_hash, - created_contract_address: internal_transaction.created_contract_address, created_contract_address_hash: internal_transaction.created_contract_address_hash, - value: internal_transaction.value.value, + value: decimal_internal_transaction_value, fee: internal_transaction.transaction.gas_price && internal_transaction.gas_used && Decimal.mult(internal_transaction.transaction.gas_price.value, internal_transaction.gas_used), @@ -185,11 +198,8 @@ defmodule Explorer.Chain.AdvancedFilter do type: token_transfer.token_type, input: token_transfer.transaction.input, timestamp: token_transfer.transaction.block_timestamp, - from_address: token_transfer.from_address, from_address_hash: token_transfer.from_address_hash, - to_address: token_transfer.to_address, to_address_hash: token_transfer.to_address_hash, - created_contract_address: nil, created_contract_address_hash: nil, fee: token_transfer.transaction |> Transaction.fee(:wei) |> elem(1), token_transfer: %TokenTransfer{ @@ -247,19 +257,27 @@ defmodule Explorer.Chain.AdvancedFilter do defp transactions_query(paging_options, options) do query = - from(transaction in Transaction, - as: :transaction, - preload: [ - :block, - from_address: [:names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] - ], - order_by: [ - desc: transaction.block_number, - desc: transaction.index - ] - ) + if DenormalizationHelper.transactions_denormalization_finished?() do + from(transaction in Transaction, + as: :transaction, + where: transaction.block_consensus == true, + order_by: [ + desc: transaction.block_number, + desc: transaction.index + ] + ) + else + from(transaction in Transaction, + as: :transaction, + join: block in assoc(transaction, :block), + as: :block, + where: block.consensus == true, + order_by: [ + desc: transaction.block_number, + desc: transaction.index + ] + ) + end query |> page_transactions(paging_options) @@ -286,27 +304,57 @@ defmodule Explorer.Chain.AdvancedFilter do defp internal_transactions_query(paging_options, options) do query = - from(internal_transaction in InternalTransaction, - as: :internal_transaction, - join: transaction in assoc(internal_transaction, :transaction), - as: :transaction, - preload: [ - from_address: [:names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - transaction: transaction - ], - order_by: [ - desc: transaction.block_number, - desc: transaction.index, - desc: internal_transaction.index - ] - ) + if DenormalizationHelper.transactions_denormalization_finished?() do + from(internal_transaction in InternalTransaction, + as: :internal_transaction, + join: transaction in assoc(internal_transaction, :transaction), + as: :transaction, + where: transaction.block_consensus == true, + where: + (internal_transaction.type == :call and internal_transaction.index > 0) or + internal_transaction.type != :call, + order_by: [ + desc: internal_transaction.block_number, + desc: internal_transaction.transaction_index, + desc: internal_transaction.index + ] + ) + else + from(internal_transaction in InternalTransaction, + as: :internal_transaction, + join: transaction in assoc(internal_transaction, :transaction), + as: :transaction, + join: block in assoc(internal_transaction, :block), + as: :block, + where: block.consensus == true, + where: + (internal_transaction.type == :call and internal_transaction.index > 0) or + internal_transaction.type != :call, + order_by: [ + desc: internal_transaction.block_number, + desc: internal_transaction.transaction_index, + desc: internal_transaction.index + ] + ) + end query |> page_internal_transactions(paging_options) |> limit_query(paging_options) - |> apply_transactions_filters(options) + |> apply_internal_transactions_filters(options) + |> limit_query(paging_options) + |> preload([:transaction]) + end + + defp page_internal_transactions(query, %PagingOptions{ + key: %{ + block_number: block_number, + transaction_index: _transaction_index, + internal_transaction_index: nil + } + }) + when block_number < 0 do + query |> where(false) end defp page_internal_transactions(query, %PagingOptions{ @@ -315,25 +363,44 @@ defmodule Explorer.Chain.AdvancedFilter do transaction_index: transaction_index, internal_transaction_index: nil } - }) do - case {block_number, transaction_index} do - {0, 0} -> - query |> where(as(:transaction).block_number == ^block_number and as(:transaction).index == ^transaction_index) + }) + when block_number > 0 and transaction_index <= 0 do + query |> where(as(:transaction).block_number < ^block_number) + end - {0, transaction_index} -> - query - |> where(as(:transaction).block_number == ^block_number and as(:transaction).index <= ^transaction_index) + defp page_internal_transactions(query, %PagingOptions{ + key: %{ + block_number: 0, + transaction_index: 0, + internal_transaction_index: nil + } + }) do + query |> where(as(:transaction).block_number == 0 and as(:transaction).index == 0) + end - {block_number, 0} -> - query |> where(as(:transaction).block_number < ^block_number) + defp page_internal_transactions(query, %PagingOptions{ + key: %{ + block_number: 0, + transaction_index: transaction_index, + internal_transaction_index: nil + } + }) do + query + |> where(as(:transaction).block_number == 0 and as(:transaction).index <= ^transaction_index) + end - _ -> - query - |> where( - as(:transaction).block_number < ^block_number or - (as(:transaction).block_number == ^block_number and as(:transaction).index <= ^transaction_index) - ) - end + defp page_internal_transactions(query, %PagingOptions{ + key: %{ + block_number: block_number, + transaction_index: transaction_index, + internal_transaction_index: nil + } + }) do + query + |> where( + as(:transaction).block_number < ^block_number or + (as(:transaction).block_number == ^block_number and as(:transaction).index <= ^transaction_index) + ) end defp page_internal_transactions(query, %PagingOptions{ @@ -358,68 +425,148 @@ defmodule Explorer.Chain.AdvancedFilter do defp token_transfers_query(paging_options, options) do token_transfer_query = - from(token_transfer in TokenTransfer, - as: :token_transfer, - join: transaction in assoc(token_transfer, :transaction), - as: :transaction, - join: token in assoc(token_transfer, :token), - as: :token, - select: %TokenTransfer{ - token_transfer - | token_id: fragment("UNNEST(?)", token_transfer.token_ids), - amount: - fragment("UNNEST(COALESCE(?, ARRAY[COALESCE(?, 1)]))", token_transfer.amounts, token_transfer.amount), - reverse_index_in_batch: - fragment("GENERATE_SERIES(COALESCE(ARRAY_LENGTH(?, 1), 1), 1, -1)", token_transfer.amounts), - token_decimals: token.decimals - }, - order_by: [ - desc: token_transfer.block_number, - desc: token_transfer.log_index - ] - ) + if DenormalizationHelper.transactions_denormalization_finished?() do + from(token_transfer in TokenTransfer, + as: :token_transfer, + join: transaction in assoc(token_transfer, :transaction), + as: :transaction, + join: token in assoc(token_transfer, :token), + as: :token, + select: %TokenTransfer{ + token_transfer + | token_id: fragment("UNNEST(?)", token_transfer.token_ids), + amount: + fragment("UNNEST(COALESCE(?, ARRAY[COALESCE(?, 1)]))", token_transfer.amounts, token_transfer.amount), + reverse_index_in_batch: + fragment("GENERATE_SERIES(COALESCE(ARRAY_LENGTH(?, 1), 1), 1, -1)", token_transfer.amounts), + token_decimals: token.decimals + }, + where: transaction.block_consensus == true, + order_by: [ + desc: token_transfer.block_number, + desc: token_transfer.log_index + ] + ) + else + from(token_transfer in TokenTransfer, + as: :token_transfer, + join: transaction in assoc(token_transfer, :transaction), + as: :transaction, + join: token in assoc(token_transfer, :token), + as: :token, + join: block in assoc(token_transfer, :block), + as: :block, + select: %TokenTransfer{ + token_transfer + | token_id: fragment("UNNEST(?)", token_transfer.token_ids), + amount: + fragment("UNNEST(COALESCE(?, ARRAY[COALESCE(?, 1)]))", token_transfer.amounts, token_transfer.amount), + reverse_index_in_batch: + fragment("GENERATE_SERIES(COALESCE(ARRAY_LENGTH(?, 1), 1), 1, -1)", token_transfer.amounts), + token_decimals: token.decimals + }, + where: block.consensus == true, + order_by: [ + desc: token_transfer.block_number, + desc: token_transfer.log_index + ] + ) + end + + query_function = + (&make_token_transfer_query_unnested/2) + |> apply_token_transfers_filters(options) + |> page_token_transfers(paging_options) token_transfer_query - |> apply_token_transfers_filters(options) - |> page_token_transfers(paging_options) - |> filter_token_transfers_by_amount(options[:amount][:from], options[:amount][:to]) - |> make_token_transfer_query_unnested() |> limit_query(paging_options) + |> query_function.(false) + |> limit_query(paging_options) + |> preload([:transaction, :token]) + |> select_merge([token_transfer], %{token_ids: [token_transfer.token_id], amounts: [token_transfer.amount]}) + end + + defp page_token_transfers(query_function, %PagingOptions{ + key: %{ + block_number: block_number, + transaction_index: _transaction_index, + token_transfer_index: nil, + internal_transaction_index: nil + } + }) + when block_number < 0 do + fn query, unnested? -> + query |> where(false) |> query_function.(unnested?) + end end - defp page_token_transfers(query, %PagingOptions{ + defp page_token_transfers(query_function, %PagingOptions{ key: %{ block_number: block_number, transaction_index: transaction_index, token_transfer_index: nil, internal_transaction_index: nil } - }) do - case {block_number, transaction_index} do - {0, 0} -> - query |> where(as(:transaction).block_number == ^block_number and as(:transaction).index == ^transaction_index) + }) + when block_number > 0 and transaction_index <= 0 do + fn query, unnested? -> + query |> where([token_transfer], token_transfer.block_number < ^block_number) |> query_function.(unnested?) + end + end - {0, transaction_index} -> - query - |> where( - [token_transfer], - token_transfer.block_number == ^block_number and as(:transaction).index < ^transaction_index - ) + defp page_token_transfers(query_function, %PagingOptions{ + key: %{ + block_number: 0, + transaction_index: 0, + token_transfer_index: nil, + internal_transaction_index: nil + } + }) do + fn query, unnested? -> + query + |> where(as(:transaction).block_number == 0 and as(:transaction).index == 0) + |> query_function.(unnested?) + end + end - {block_number, 0} -> - query |> where([token_transfer], token_transfer.block_number < ^block_number) + defp page_token_transfers(query_function, %PagingOptions{ + key: %{ + block_number: 0, + transaction_index: transaction_index, + token_transfer_index: nil, + internal_transaction_index: nil + } + }) do + fn query, unnested? -> + query + |> where( + [token_transfer], + token_transfer.block_number == 0 and as(:transaction).index < ^transaction_index + ) + |> query_function.(unnested?) + end + end - {block_number, transaction_index} -> - query - |> where( - [token_transfer], - token_transfer.block_number < ^block_number or - (token_transfer.block_number == ^block_number and as(:transaction).index <= ^transaction_index) - ) + defp page_token_transfers(query_function, %PagingOptions{ + key: %{ + block_number: block_number, + transaction_index: transaction_index, + token_transfer_index: nil, + internal_transaction_index: nil + } + }) do + fn query, unnested? -> + query + |> where( + [token_transfer], + token_transfer.block_number < ^block_number or + (token_transfer.block_number == ^block_number and as(:transaction).index <= ^transaction_index) + ) + |> query_function.(unnested?) end end - defp page_token_transfers(query, %PagingOptions{ + defp page_token_transfers(query_function, %PagingOptions{ key: %{ block_number: block_number, transaction_index: transaction_index, @@ -432,10 +579,12 @@ defmodule Explorer.Chain.AdvancedFilter do ^page_transaction_index_dynamic(block_number, transaction_index) ) - query |> where(^dynamic_condition) + fn query, unnested? -> + query |> where(^dynamic_condition) |> query_function.(unnested?) + end end - defp page_token_transfers(query, %PagingOptions{ + defp page_token_transfers(query_function, %PagingOptions{ key: %{ block_number: block_number, token_transfer_index: tt_index, @@ -448,20 +597,21 @@ defmodule Explorer.Chain.AdvancedFilter do ^page_tt_index_dynamic(:token_transfer, block_number, tt_index, tt_batch_index) ) - paged_query = query |> where(^dynamic_condition) - - paged_query - |> make_token_transfer_query_unnested() - |> where( - ^page_tt_batch_index_dynamic( - block_number, - tt_index, - tt_batch_index + fn query, unnested? -> + query + |> where(^dynamic_condition) + |> query_function.(unnested?) + |> where( + ^page_tt_batch_index_dynamic( + block_number, + tt_index, + tt_batch_index + ) ) - ) + end end - defp page_token_transfers(query, _), do: query + defp page_token_transfers(query_function, _), do: query_function defp page_block_number_dynamic(binding, block_number) when block_number > 0 do dynamic(as(^binding).block_number < ^block_number) @@ -471,7 +621,8 @@ defmodule Explorer.Chain.AdvancedFilter do dynamic(false) end - defp page_transaction_index_dynamic(block_number, transaction_index) when transaction_index > 0 do + defp page_transaction_index_dynamic(block_number, transaction_index) + when block_number >= 0 and transaction_index > 0 do dynamic( [transaction: transaction], transaction.block_number == ^block_number and transaction.index < ^transaction_index @@ -482,7 +633,8 @@ defmodule Explorer.Chain.AdvancedFilter do dynamic(false) end - defp page_it_index_dynamic(block_number, transaction_index, it_index) when it_index > 0 do + defp page_it_index_dynamic(block_number, transaction_index, it_index) + when block_number >= 0 and transaction_index >= 0 and it_index > 0 do dynamic( [transaction: transaction, internal_transaction: it], transaction.block_number == ^block_number and transaction.index == ^transaction_index and @@ -495,11 +647,12 @@ defmodule Explorer.Chain.AdvancedFilter do end defp page_tt_index_dynamic(binding, block_number, tt_index, tt_batch_index) - when tt_index > 0 and tt_batch_index > 1 do + when block_number >= 0 and tt_index > 0 and tt_batch_index > 1 do dynamic(as(^binding).block_number == ^block_number and as(^binding).log_index <= ^tt_index) end - defp page_tt_index_dynamic(binding, block_number, tt_index, _tt_batch_index) when tt_index > 0 do + defp page_tt_index_dynamic(binding, block_number, tt_index, _tt_batch_index) + when block_number >= 0 and tt_index > 0 do dynamic(as(^binding).block_number == ^block_number and as(^binding).log_index < ^tt_index) end @@ -507,7 +660,8 @@ defmodule Explorer.Chain.AdvancedFilter do dynamic(false) end - defp page_tt_batch_index_dynamic(block_number, tt_index, tt_batch_index) when tt_batch_index > 1 do + defp page_tt_batch_index_dynamic(block_number, tt_index, tt_batch_index) + when block_number >= 0 and tt_index >= 0 and tt_batch_index > 1 do dynamic( [unnested_token_transfer: tt], ^page_block_number_dynamic(:unnested_token_transfer, block_number) or @@ -529,38 +683,59 @@ defmodule Explorer.Chain.AdvancedFilter do defp limit_query(query, _), do: query - defp apply_token_transfers_filters(query, options) do - query + defp apply_token_transfers_filters(query_function, options) do + query_function |> filter_by_transaction_type(options[:transaction_types]) |> filter_token_transfers_by_methods(options[:methods]) - |> filter_by_token(options[:token_contract_address_hashes][:include], :include) - |> filter_by_token(options[:token_contract_address_hashes][:exclude], :exclude) - |> apply_common_filters(options) + |> filter_token_transfers_by_age(options) + |> filter_by_token(options[:token_contract_address_hashes]) + |> filter_token_transfers_by_addresses( + options[:from_address_hashes], + options[:to_address_hashes], + options[:address_relation] + ) + |> filter_token_transfers_by_amount(options[:amount][:from], options[:amount][:to]) end defp apply_transactions_filters(query, options) do query |> filter_transactions_by_amount(options[:amount][:from], options[:amount][:to]) |> filter_transactions_by_methods(options[:methods]) - |> apply_common_filters(options) + |> only_collated_transactions() + |> filter_by_addresses(options[:from_address_hashes], options[:to_address_hashes], options[:address_relation]) + |> filter_by_age(:transaction, options) end - defp apply_common_filters(query, options) do + defp apply_internal_transactions_filters(query, options) do query + |> filter_transactions_by_amount(options[:amount][:from], options[:amount][:to]) + |> filter_transactions_by_methods(options[:methods]) |> only_collated_transactions() - |> filter_by_timestamp(options[:age][:from], options[:age][:to]) - |> filter_by_addresses(options[:from_address_hashes], options[:to_address_hashes], options[:address_relation]) + |> filter_by_age(:transaction, options) + |> filter_internal_transactions_by_addresses( + options[:from_address_hashes], + options[:to_address_hashes], + options[:address_relation] + ) end defp only_collated_transactions(query) do query |> where(not is_nil(as(:transaction).block_number) and not is_nil(as(:transaction).index)) end - defp filter_by_transaction_type(query, [_ | _] = transaction_types) do - query |> where([token_transfer], token_transfer.token_type in ^transaction_types) + defp filter_by_transaction_type(query_function, [_ | _] = transaction_types) do + if DenormalizationHelper.tt_denormalization_finished?() do + fn query, unnested? -> + query |> where([token_transfer], token_transfer.token_type in ^transaction_types) |> query_function.(unnested?) + end + else + fn query, unnested? -> + query |> where([token: token], token.type in ^transaction_types) |> query_function.(unnested?) + end + end end - defp filter_by_transaction_type(query, _), do: query + defp filter_by_transaction_type(query_function, _), do: query_function defp filter_transactions_by_methods(query, [_ | _] = methods) do prepared_methods = prepare_methods(methods) @@ -570,13 +745,17 @@ defmodule Explorer.Chain.AdvancedFilter do defp filter_transactions_by_methods(query, _), do: query - defp filter_token_transfers_by_methods(query, [_ | _] = methods) do + defp filter_token_transfers_by_methods(query_function, [_ | _] = methods) do prepared_methods = prepare_methods(methods) - query |> where(fragment("substring(? FOR 4)", as(:transaction).input) in ^prepared_methods) + fn query, unnested? -> + query + |> where(fragment("substring(? FOR 4)", as(:transaction).input) in ^prepared_methods) + |> query_function.(unnested?) + end end - defp filter_token_transfers_by_methods(query, _), do: query + defp filter_token_transfers_by_methods(query_function, _), do: query_function defp prepare_methods(methods) do methods @@ -589,16 +768,48 @@ defmodule Explorer.Chain.AdvancedFilter do end) end - defp filter_by_timestamp(query, %DateTime{} = from, %DateTime{} = to) do - query |> where(as(:transaction).block_timestamp >= ^from and as(:transaction).block_timestamp <= ^to) + defp filter_token_transfers_by_age(query_function, options) do + fn query, unnested? -> query |> filter_by_age(:token_transfer, options) |> query_function.(unnested?) end end - defp filter_by_timestamp(query, %DateTime{} = from, _to) do - query |> where(as(:transaction).block_timestamp >= ^from) + defp filter_by_age(query, entity, options) do + query + |> do_filter_by_age(options[:block_numbers_age][:from], options[:age][:from], entity, :from) + |> do_filter_by_age(options[:block_numbers_age][:to], options[:age][:to], entity, :to) + end + + defp do_filter_by_age(query, {:ok, block_number}, _timestamp, entity, direction) do + filter_by_block_number(query, block_number, entity, direction) + end + + defp do_filter_by_age(query, _block_number, timestamp, _entity, direction) do + filter_by_timestamp(query, timestamp, direction) + end + + defp filter_by_block_number(query, from, entity, :from) when not is_nil(from) do + query |> where(as(^entity).block_number >= ^from) + end + + defp filter_by_block_number(query, to, entity, :to) when not is_nil(to) do + query |> where(as(^entity).block_number <= ^to) + end + + defp filter_by_block_number(query, _, _, _), do: query + + defp filter_by_timestamp(query, %DateTime{} = from, :from) do + if DenormalizationHelper.transactions_denormalization_finished?() do + query |> where(as(:transaction).block_timestamp >= ^from) + else + query |> where(as(:block).timestamp >= ^from) + end end - defp filter_by_timestamp(query, _from, %DateTime{} = to) do - query |> where(as(:transaction).block_timestamp <= ^to) + defp filter_by_timestamp(query, %DateTime{} = to, :to) do + if DenormalizationHelper.transactions_denormalization_finished?() do + query |> where(as(:transaction).block_timestamp <= ^to) + else + query |> where(as(:block).timestamp <= ^to) + end end defp filter_by_timestamp(query, _, _), do: query @@ -656,84 +867,566 @@ defmodule Explorer.Chain.AdvancedFilter do dynamic([t], ^from_addresses_dynamic and ^to_addresses_dynamic) end - @eth_decimals 1000_000_000_000_000_000 + defp filter_token_transfers_by_addresses(query_function, from_addresses_params, to_addresses_params, relation) do + case {process_address_inclusion(from_addresses_params), process_address_inclusion(to_addresses_params)} do + {nil, nil} -> query_function + {from, nil} -> do_filter_token_transfers_by_address(query_function, from, :from_address_hash) + {nil, to} -> do_filter_token_transfers_by_address(query_function, to, :to_address_hash) + {from, to} -> do_filter_token_transfers_by_both_addresses(query_function, from, to, relation) + end + end + + defp do_filter_token_transfers_by_address(query_function, {:include, addresses}, field) do + fn query, _unnested? -> + queries = + addresses + |> Enum.map(fn address -> + query |> where([token_transfer], field(token_transfer, ^field) == ^address) |> query_function.(true) + end) + |> map_first(&subquery/1) + |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) + + from(token_transfer in subquery(queries), + as: :unnested_token_transfer, + order_by: [desc: token_transfer.block_number, desc: token_transfer.log_index] + ) + end + end + + defp do_filter_token_transfers_by_address(query_function, {:exclude, addresses}, field) do + fn query, unnested? -> + query |> where([t], field(t, ^field) not in ^addresses) |> query_function.(unnested?) + end + end - defp filter_transactions_by_amount(query, from, to) when not is_nil(from) and not is_nil(to) and from < to do - query |> where([t], t.value / @eth_decimals >= ^from and t.value / @eth_decimals <= ^to) + defp do_filter_token_transfers_by_both_addresses(query_function, {:include, from}, {:include, to}, relation) do + fn query, _unnested? -> + from_queries = + from + |> Enum.map(fn from_address -> + query |> where([token_transfer], token_transfer.from_address_hash == ^from_address) |> query_function.(true) + end) + + to_queries = + to + |> Enum.map(fn to_address -> + query |> where([token_transfer], token_transfer.to_address_hash == ^to_address) |> query_function.(true) + end) + + do_filter_token_transfers_by_both_addresses_to_include(from_queries, to_queries, relation) + end end - defp filter_transactions_by_amount(query, _from, to) when not is_nil(to) do - query |> where([t], t.value / @eth_decimals <= ^to) + defp do_filter_token_transfers_by_both_addresses(query_function, {:include, from}, {:exclude, to}, :and) do + fn query, _unnested? -> + from_queries = + from + |> Enum.map(fn from_address -> + query + |> where( + [token_transfer], + token_transfer.from_address_hash == ^from_address and token_transfer.to_address_hash not in ^to + ) + |> query_function.(true) + end) + |> map_first(&subquery/1) + |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) + + from(token_transfer in subquery(from_queries), + as: :unnested_token_transfer, + order_by: [desc: token_transfer.block_number, desc: token_transfer.log_index] + ) + end end - defp filter_transactions_by_amount(query, from, _to) when not is_nil(from) do - query |> where([t], t.value / @eth_decimals >= ^from) + defp do_filter_token_transfers_by_both_addresses(query_function, {:include, from}, {:exclude, to}, _relation) do + fn query, _unnested? -> + from_queries = + from + |> Enum.map(fn from_address -> + query + |> where( + [token_transfer], + token_transfer.from_address_hash == ^from_address or token_transfer.to_address_hash not in ^to + ) + |> query_function.(true) + end) + |> map_first(&subquery/1) + |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) + + from(token_transfer in subquery(from_queries), + as: :unnested_token_transfer, + order_by: [desc: token_transfer.block_number, desc: token_transfer.log_index] + ) + end end - defp filter_transactions_by_amount(query, _, _), do: query + defp do_filter_token_transfers_by_both_addresses(query_function, {:exclude, from}, {:include, to}, :and) do + fn query, _unnested? -> + to_queries = + to + |> Enum.map(fn to_address -> + query + |> where( + [token_transfer], + token_transfer.to_address_hash == ^to_address and token_transfer.from_address_hash not in ^from + ) + |> query_function.(true) + end) + |> map_first(&subquery/1) + |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) + + from(token_transfer in subquery(to_queries), + as: :unnested_token_transfer, + order_by: [desc: token_transfer.block_number, desc: token_transfer.log_index] + ) + end + end - defp filter_token_transfers_by_amount(query, from, to) when not is_nil(from) and not is_nil(to) and from < to do - unnested_query = make_token_transfer_query_unnested(query) + defp do_filter_token_transfers_by_both_addresses(query_function, {:exclude, from}, {:include, to}, _relation) do + fn query, _unnested? -> + to_queries = + to + |> Enum.map(fn to_address -> + query + |> where( + [token_transfer], + token_transfer.to_address_hash == ^to_address or token_transfer.from_address_hash not in ^from + ) + |> query_function.(true) + end) + |> map_first(&subquery/1) + |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) + + from(token_transfer in subquery(to_queries), + as: :unnested_token_transfer, + order_by: [desc: token_transfer.block_number, desc: token_transfer.log_index] + ) + end + end - unnested_query - |> where( - [unnested_token_transfer: tt], - tt.amount / fragment("10 ^ COALESCE(?, 0)", tt.token_decimals) >= ^from and - tt.amount / fragment("10 ^ COALESCE(?, 0)", tt.token_decimals) <= ^to + defp do_filter_token_transfers_by_both_addresses(query_function, {:exclude, from}, {:exclude, to}, :and) do + fn query, unnested? -> + query + |> where([t], t.from_address_hash not in ^from and t.to_address_hash not in ^to) + |> query_function.(unnested?) + end + end + + defp do_filter_token_transfers_by_both_addresses(query_function, {:exclude, from}, {:exclude, to}, _relation) do + fn query, unnested? -> + query + |> where([t], t.from_address_hash not in ^from or t.to_address_hash not in ^to) + |> query_function.(unnested?) + end + end + + defp do_filter_token_transfers_by_both_addresses_to_include(from_queries, to_queries, relation) do + case relation do + :and -> + united_from_queries = + from_queries |> map_first(&subquery/1) |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) + + united_to_queries = + to_queries |> map_first(&subquery/1) |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) + + from(token_transfer in subquery(intersect_all(united_from_queries, ^united_to_queries)), + as: :unnested_token_transfer, + order_by: [desc: token_transfer.block_number, desc: token_transfer.log_index] + ) + + _ -> + union_query = + from_queries + |> Kernel.++(to_queries) + |> map_first(&subquery/1) + |> Enum.reduce(fn query, acc -> union(acc, ^query) end) + + from(token_transfer in subquery(union_query), + as: :unnested_token_transfer, + order_by: [desc: token_transfer.block_number, desc: token_transfer.log_index] + ) + end + end + + defp filter_internal_transactions_by_addresses(query, from_addresses, to_addresses, relation) do + case {process_address_inclusion(from_addresses), process_address_inclusion(to_addresses)} do + {nil, nil} -> query + {from, nil} -> do_filter_internal_transactions_by_address(query, from, :from_address_hash) + {nil, to} -> do_filter_internal_transactions_by_address(query, to, :to_address_hash) + {from, to} -> do_filter_internal_transactions_by_both_addresses(query, from, to, relation) + end + end + + defp do_filter_internal_transactions_by_address(query, {:include, addresses}, field) do + queries = + addresses + |> Enum.map(fn address -> + query |> where([token_transfer], field(token_transfer, ^field) == ^address) + end) + |> map_first(&subquery/1) + |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) + + from(internal_transaction in subquery(queries), + order_by: [ + desc: internal_transaction.block_number, + desc: internal_transaction.transaction_index, + desc: internal_transaction.index + ] ) end - defp filter_token_transfers_by_amount(query, _from, to) when not is_nil(to) do - unnested_query = make_token_transfer_query_unnested(query) + defp do_filter_internal_transactions_by_address(query, {:exclude, addresses}, field) do + query |> where([t], field(t, ^field) not in ^addresses) + end - unnested_query - |> where( - [unnested_token_transfer: tt], - tt.amount / fragment("10 ^ COALESCE(?, 0)", tt.token_decimals) <= ^to + defp do_filter_internal_transactions_by_both_addresses(query, {:include, from}, {:include, to}, relation) do + from_queries = + from + |> Enum.map(fn from_address -> + query |> where([internal_transaction], internal_transaction.from_address_hash == ^from_address) + end) + + to_queries = + to + |> Enum.map(fn to_address -> + query |> where([internal_transaction], internal_transaction.to_address_hash == ^to_address) + end) + + do_filter_internal_transactions_by_both_addresses_to_include(from_queries, to_queries, relation) + end + + defp do_filter_internal_transactions_by_both_addresses(query, {:include, from}, {:exclude, to}, :and) do + from_queries = + from + |> Enum.map(fn from_address -> + query + |> where( + [internal_transaction], + internal_transaction.from_address_hash == ^from_address and internal_transaction.to_address_hash not in ^to + ) + end) + |> map_first(&subquery/1) + |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) + + from(internal_transaction in subquery(from_queries), + order_by: [ + desc: internal_transaction.block_number, + desc: internal_transaction.transaction_index, + desc: internal_transaction.index + ] + ) + end + + defp do_filter_internal_transactions_by_both_addresses(query, {:include, from}, {:exclude, to}, _relation) do + from_queries = + from + |> Enum.map(fn from_address -> + query + |> where( + [internal_transaction], + internal_transaction.from_address_hash == ^from_address or internal_transaction.to_address_hash not in ^to + ) + end) + |> map_first(&subquery/1) + |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) + + from(internal_transaction in subquery(from_queries), + order_by: [ + desc: internal_transaction.block_number, + desc: internal_transaction.transaction_index, + desc: internal_transaction.index + ] ) end - defp filter_token_transfers_by_amount(query, from, _to) when not is_nil(from) do - unnested_query = make_token_transfer_query_unnested(query) + defp do_filter_internal_transactions_by_both_addresses(query, {:exclude, from}, {:include, to}, :and) do + to_queries = + to + |> Enum.map(fn to_address -> + query + |> where( + [internal_transaction], + internal_transaction.to_address_hash == ^to_address and internal_transaction.from_address_hash not in ^from + ) + end) + |> map_first(&subquery/1) + |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) + + from(internal_transaction in subquery(to_queries), + order_by: [ + desc: internal_transaction.block_number, + desc: internal_transaction.transaction_index, + desc: internal_transaction.index + ] + ) + end - unnested_query - |> where( - [unnested_token_transfer: tt], - tt.amount / fragment("10 ^ COALESCE(?, 0)", tt.token_decimals) >= ^from + defp do_filter_internal_transactions_by_both_addresses(query, {:exclude, from}, {:include, to}, _relation) do + to_queries = + to + |> Enum.map(fn to_address -> + query + |> where( + [internal_transaction], + internal_transaction.to_address_hash == ^to_address or internal_transaction.from_address_hash not in ^from + ) + end) + |> map_first(&subquery/1) + |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) + + from(internal_transaction in subquery(to_queries), + order_by: [ + desc: internal_transaction.block_number, + desc: internal_transaction.transaction_index, + desc: internal_transaction.index + ] ) end - defp filter_token_transfers_by_amount(query, _, _), do: query + defp do_filter_internal_transactions_by_both_addresses(query, {:exclude, from}, {:exclude, to}, :and) do + query + |> where([t], t.from_address_hash not in ^from and t.to_address_hash not in ^to) + end + + defp do_filter_internal_transactions_by_both_addresses(query, {:exclude, from}, {:exclude, to}, _relation) do + query + |> where([t], t.from_address_hash not in ^from or t.to_address_hash not in ^to) + end + + defp do_filter_internal_transactions_by_both_addresses_to_include(from_queries, to_queries, relation) do + case relation do + :and -> + united_from_queries = + from_queries |> map_first(&subquery/1) |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) + + united_to_queries = + to_queries |> map_first(&subquery/1) |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) + + from(internal_transaction in subquery(intersect_all(united_from_queries, ^united_to_queries)), + order_by: [ + desc: internal_transaction.block_number, + desc: internal_transaction.transaction_index, + desc: internal_transaction.index + ] + ) + + _ -> + union_query = + from_queries + |> Kernel.++(to_queries) + |> map_first(&subquery/1) + |> Enum.reduce(fn query, acc -> union(acc, ^query) end) + + from(internal_transaction in subquery(union_query), + order_by: [ + desc: internal_transaction.block_number, + desc: internal_transaction.transaction_index, + desc: internal_transaction.index + ] + ) + end + end + + @eth_decimals 1_000_000_000_000_000_000 + + defp filter_transactions_by_amount(query, from, to) when not is_nil(from) and not is_nil(to) do + if Decimal.positive?(to) and Decimal.lt?(from, to) do + query |> where([t], t.value / @eth_decimals >= ^from and t.value / @eth_decimals <= ^to) + else + query |> where(false) + end + end + + defp filter_transactions_by_amount(query, _from, to) when not is_nil(to) do + if Decimal.positive?(to) do + query |> where([t], t.value / @eth_decimals <= ^to) + else + query |> where(false) + end + end + + defp filter_transactions_by_amount(query, from, _to) when not is_nil(from) do + if Decimal.positive?(from) do + query |> where([t], t.value / @eth_decimals >= ^from) + else + query + end + end + + defp filter_transactions_by_amount(query, _, _), do: query + + defp filter_token_transfers_by_amount(query_function, from, to) do + fn query, unnested? -> + query + |> filter_token_transfers_by_amount_before_subquery(from, to) + |> query_function.(unnested?) + |> filter_token_transfers_by_amount_after_subquery(from, to) + end + end + + defp filter_token_transfers_by_amount_before_subquery(query, from, to) + when not is_nil(from) and not is_nil(to) and from < to do + if Decimal.positive?(to) and Decimal.lt?(from, to) do + query + |> where( + [tt, token: token], + ^to * fragment("10 ^ COALESCE(?, 0)", token.decimals) >= + fragment("ANY(COALESCE(?, ARRAY[COALESCE(?, 1)]))", tt.amounts, tt.amount) and + ^from * fragment("10 ^ COALESCE(?, 0)", token.decimals) <= + fragment("ANY(COALESCE(?, ARRAY[COALESCE(?, 1)]))", tt.amounts, tt.amount) + ) + else + query |> where(false) + end + end + + defp filter_token_transfers_by_amount_before_subquery(query, _from, to) when not is_nil(to) do + if Decimal.positive?(to) do + query + |> where( + [tt, token: token], + ^to * fragment("10 ^ COALESCE(?, 0)", token.decimals) >= + fragment("ANY(COALESCE(?, ARRAY[COALESCE(?, 1)]))", tt.amounts, tt.amount) + ) + else + query |> where(false) + end + end - defp make_token_transfer_query_unnested(query) do - if has_named_binding?(query, :unnested_token_transfer) do + defp filter_token_transfers_by_amount_before_subquery(query, from, _to) when not is_nil(from) do + if Decimal.positive?(from) do + query + |> where( + [tt, token: token], + ^from * fragment("10 ^ COALESCE(?, 0)", token.decimals) <= + fragment("ANY(COALESCE(?, ARRAY[COALESCE(?, 1)]))", tt.amounts, tt.amount) + ) + else query + end + end + + defp filter_token_transfers_by_amount_before_subquery(query, _, _), do: query + + defp filter_token_transfers_by_amount_after_subquery(unnested_query, from, to) + when not is_nil(from) and not is_nil(to) and from < to do + if Decimal.positive?(to) and Decimal.lt?(from, to) do + unnested_query + |> where( + [unnested_token_transfer: tt], + tt.amount / fragment("10 ^ COALESCE(?, 0)", tt.token_decimals) >= ^from and + tt.amount / fragment("10 ^ COALESCE(?, 0)", tt.token_decimals) <= ^to + ) + else + unnested_query |> where(false) + end + end + + defp filter_token_transfers_by_amount_after_subquery(unnested_query, _from, to) when not is_nil(to) do + if Decimal.positive?(to) do + unnested_query + |> where( + [unnested_token_transfer: tt], + tt.amount / fragment("10 ^ COALESCE(?, 0)", tt.token_decimals) <= ^to + ) + else + unnested_query |> where(false) + end + end + + defp filter_token_transfers_by_amount_after_subquery(unnested_query, from, _to) when not is_nil(from) do + if Decimal.positive?(from) do + unnested_query + |> where( + [unnested_token_transfer: tt], + tt.amount / fragment("10 ^ COALESCE(?, 0)", tt.token_decimals) >= ^from + ) else + unnested_query + end + end + + defp filter_token_transfers_by_amount_after_subquery(query, _, _), do: query + + defp make_token_transfer_query_unnested(query, false) do + with_named_binding(query, :unnested_token_transfer, fn query, binding -> from(token_transfer in subquery(query), + as: ^binding + ) + end) + end + + defp make_token_transfer_query_unnested(query, _), do: query + + defp filter_by_token(query_function, token_contract_address_hashes) when is_list(token_contract_address_hashes) do + case process_address_inclusion(token_contract_address_hashes) do + nil -> + query_function + + {include_or_exclude, token_contract_address_hashes} -> + filtered = token_contract_address_hashes |> Enum.reject(&(&1 == "native")) + + if Enum.empty?(filtered) do + query_function + else + do_filter_by_token(query_function, {include_or_exclude, filtered}) + end + end + end + + defp filter_by_token(query_function, _), do: query_function + + defp do_filter_by_token(query_function, {:include, token_contract_address_hashes}) do + fn query, _unnested? -> + queries = + token_contract_address_hashes + |> Enum.map(fn address -> + query + |> where([token_transfer], token_transfer.token_contract_address_hash == ^address) + |> query_function.(true) + end) + |> map_first(&subquery/1) + |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) + + from(token_transfer in subquery(queries), as: :unnested_token_transfer, - preload: [ - :transaction, - :token, - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] - ], - select_merge: %{ - token_ids: [token_transfer.token_id], - amounts: [token_transfer.amount] - } + order_by: [desc: token_transfer.block_number, desc: token_transfer.log_index] ) end end - defp filter_by_token(query, [_ | _] = token_contract_address_hashes, :include) do - filtered = token_contract_address_hashes |> Enum.reject(&(&1 == "native")) - query |> where([token_transfer], token_transfer.token_contract_address_hash in ^filtered) + defp do_filter_by_token(query_function, {:exclude, token_contract_address_hashes}) do + fn query, unnested? -> + query_function.( + from(token_transfer in query, + left_join: to_exclude in fragment("UNNEST(?)", type(^token_contract_address_hashes, {:array, Hash.Address})), + on: token_transfer.token_contract_address_hash == to_exclude, + where: is_nil(to_exclude) + ), + unnested? + ) + end end - defp filter_by_token(query, [_ | _] = token_contract_address_hashes, :exclude) do - filtered = token_contract_address_hashes |> Enum.reject(&(&1 == "native")) - query |> where([token_transfer], token_transfer.token_contract_address_hash not in ^filtered) + defp process_address_inclusion(addresses) when is_list(addresses) do + case {Keyword.get(addresses, :include, []), Keyword.get(addresses, :exclude, [])} do + {to_include, to_exclude} when to_include in [nil, []] and to_exclude in [nil, []] -> + nil + + {to_include, to_exclude} when to_include in [nil, []] and is_list(to_exclude) -> + {:exclude, to_exclude} + + {to_include, to_exclude} when is_list(to_include) -> + case to_include -- (to_exclude || []) do + [] -> nil + to_include -> {:include, to_include} + end + end end - defp filter_by_token(query, _, _), do: query + defp process_address_inclusion(_), do: nil + + defp map_first([h | t], f), do: [f.(h) | t] + defp map_first([], _f), do: [] end From f1fd306bf98189ae033ce8bfa488521e984a7c2d Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 22 Oct 2024 20:23:47 +0300 Subject: [PATCH 247/363] chore: Update version bump script --- bin/version_bump.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/version_bump.sh b/bin/version_bump.sh index bf79d90035f1..97583f4a1f89 100755 --- a/bin/version_bump.sh +++ b/bin/version_bump.sh @@ -11,7 +11,7 @@ MIX_FILES=( CONFIG_FILE="$(pwd)/rel/config.exs" DOCKER_COMPOSE_FILE="$(pwd)/docker-compose/docker-compose.yml" MAKE_FILE="$(pwd)/docker/Makefile" -WORKFLOW_FILES=($(find "$(pwd)/.github/workflows" -type f \( -name "pre-release-*" -o -name "release-*" -o -name "publish-docker-image-*" \))) +WORKFLOW_FILES=($(find "$(pwd)/.github/workflows" -type f \( -name "pre-release*" -o -name "release*" -o -name "publish-regular-docker-image-on-demand*" -o -name "publish-docker-image-*" \))) METADATA_RETRIEVER_FILE="$(pwd)/apps/explorer/lib/explorer/token/metadata_retriever.ex" # Function to bump version From c837858f03c449fa905af523065897ec70ff6b07 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 22 Oct 2024 20:34:58 +0300 Subject: [PATCH 248/363] 6.9.0 --- .github/workflows/pre-release-arbitrum.yml | 2 +- .github/workflows/pre-release-blackfort.yml | 2 +- .github/workflows/pre-release-celo.yml | 2 +- .github/workflows/pre-release-eth.yml | 2 +- .github/workflows/pre-release-optimism.yml | 2 +- .github/workflows/pre-release-redstone.yml | 2 +- .github/workflows/pre-release-shibarium.yml | 2 +- .github/workflows/pre-release-zksync.yml | 2 +- .github/workflows/pre-release.yml | 2 +- .../publish-docker-image-every-push.yml | 2 +- .../publish-docker-image-for-arbitrum.yml | 2 +- .../publish-docker-image-for-blackfort.yml | 2 +- .../publish-docker-image-for-celo.yml | 2 +- .../publish-docker-image-for-core.yml | 2 +- .../publish-docker-image-for-eth-sepolia.yml | 2 +- .../publish-docker-image-for-eth.yml | 2 +- .../publish-docker-image-for-filecoin.yml | 2 +- .../publish-docker-image-for-fuse.yml | 2 +- .../publish-docker-image-for-gnosis-chain.yml | 2 +- .../publish-docker-image-for-l2-staging.yml | 2 +- .../publish-docker-image-for-lukso.yml | 2 +- .../publish-docker-image-for-optimism.yml | 2 +- .../publish-docker-image-for-polygon-edge.yml | 2 +- .../publish-docker-image-for-redstone.yml | 2 +- .../publish-docker-image-for-rootstock.yml | 2 +- .../publish-docker-image-for-shibarium.yml | 2 +- .../publish-docker-image-for-stability.yml | 2 +- .../publish-docker-image-for-suave.yml | 2 +- .../publish-docker-image-for-zetachain.yml | 2 +- .../publish-docker-image-for-zkevm.yml | 2 +- .../publish-docker-image-for-zksync.yml | 2 +- ...publish-docker-image-staging-on-demand.yml | 2 +- ...publish-regular-docker-image-on-demand.yml | 2 +- .github/workflows/release-arbitrum.yml | 2 +- .github/workflows/release-blackfort.yml | 2 +- .github/workflows/release-celo.yml | 2 +- .github/workflows/release-eth.yml | 2 +- .github/workflows/release-filecoin.yml | 2 +- .github/workflows/release-fuse.yml | 2 +- .github/workflows/release-gnosis.yml | 2 +- .github/workflows/release-optimism.yml | 2 +- .github/workflows/release-polygon-edge.yml | 2 +- .github/workflows/release-polygon-zkevm.yml | 2 +- .github/workflows/release-redstone.yml | 2 +- .github/workflows/release-rootstock.yml | 2 +- .github/workflows/release-shibarium.yml | 2 +- .github/workflows/release-stability.yml | 2 +- .github/workflows/release-suave.yml | 2 +- .github/workflows/release-zetachain.yml | 2 +- .github/workflows/release-zksync.yml | 2 +- .github/workflows/release.yml | 2 +- CHANGELOG.md | 87 +++++++++++++++++++ apps/block_scout_web/mix.exs | 2 +- apps/ethereum_jsonrpc/mix.exs | 2 +- .../lib/explorer/token/metadata_retriever.ex | 2 +- apps/explorer/mix.exs | 2 +- apps/indexer/mix.exs | 2 +- docker-compose/docker-compose.yml | 2 +- docker/Makefile | 2 +- mix.exs | 2 +- rel/config.exs | 2 +- 61 files changed, 147 insertions(+), 60 deletions(-) diff --git a/.github/workflows/pre-release-arbitrum.yml b/.github/workflows/pre-release-arbitrum.yml index 3e60ea497347..3222f6f1baf3 100644 --- a/.github/workflows/pre-release-arbitrum.yml +++ b/.github/workflows/pre-release-arbitrum.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-blackfort.yml b/.github/workflows/pre-release-blackfort.yml index 0444d82badf7..f6c3347b4833 100644 --- a/.github/workflows/pre-release-blackfort.yml +++ b/.github/workflows/pre-release-blackfort.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-celo.yml b/.github/workflows/pre-release-celo.yml index a1fd1a2ebdb9..f9f79545c96b 100644 --- a/.github/workflows/pre-release-celo.yml +++ b/.github/workflows/pre-release-celo.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-eth.yml b/.github/workflows/pre-release-eth.yml index 6655abb71e7e..cef696c93d08 100644 --- a/.github/workflows/pre-release-eth.yml +++ b/.github/workflows/pre-release-eth.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-optimism.yml b/.github/workflows/pre-release-optimism.yml index 29b735be0ed1..2257ecf61004 100644 --- a/.github/workflows/pre-release-optimism.yml +++ b/.github/workflows/pre-release-optimism.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-redstone.yml b/.github/workflows/pre-release-redstone.yml index f7b17cd518bf..a6eafec90ffb 100644 --- a/.github/workflows/pre-release-redstone.yml +++ b/.github/workflows/pre-release-redstone.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-shibarium.yml b/.github/workflows/pre-release-shibarium.yml index f78af6c42ae2..fa9780ace761 100644 --- a/.github/workflows/pre-release-shibarium.yml +++ b/.github/workflows/pre-release-shibarium.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-zksync.yml b/.github/workflows/pre-release-zksync.yml index 402c48559b18..5d4f1ef65be8 100644 --- a/.github/workflows/pre-release-zksync.yml +++ b/.github/workflows/pre-release-zksync.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index dc5b20d14116..8b48847a6f81 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/publish-docker-image-every-push.yml b/.github/workflows/publish-docker-image-every-push.yml index 1099b747d88b..4c12e57a85bc 100644 --- a/.github/workflows/publish-docker-image-every-push.yml +++ b/.github/workflows/publish-docker-image-every-push.yml @@ -11,7 +11,7 @@ on: env: OTP_VERSION: ${{ vars.OTP_VERSION }} ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 jobs: push_to_registry: diff --git a/.github/workflows/publish-docker-image-for-arbitrum.yml b/.github/workflows/publish-docker-image-for-arbitrum.yml index 45ced4c1a5ef..b97299556dad 100644 --- a/.github/workflows/publish-docker-image-for-arbitrum.yml +++ b/.github/workflows/publish-docker-image-for-arbitrum.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: arbitrum steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-blackfort.yml b/.github/workflows/publish-docker-image-for-blackfort.yml index 6051b3e63d75..461ee2aac5b2 100644 --- a/.github/workflows/publish-docker-image-for-blackfort.yml +++ b/.github/workflows/publish-docker-image-for-blackfort.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: blackfort steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-celo.yml b/.github/workflows/publish-docker-image-for-celo.yml index 18ddcbc4f064..b11c9021ba65 100644 --- a/.github/workflows/publish-docker-image-for-celo.yml +++ b/.github/workflows/publish-docker-image-for-celo.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: celo API_GRAPHQL_MAX_COMPLEXITY: 10400 steps: diff --git a/.github/workflows/publish-docker-image-for-core.yml b/.github/workflows/publish-docker-image-for-core.yml index 3499b3e18d62..5348327efd10 100644 --- a/.github/workflows/publish-docker-image-for-core.yml +++ b/.github/workflows/publish-docker-image-for-core.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: poa steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-eth-sepolia.yml b/.github/workflows/publish-docker-image-for-eth-sepolia.yml index 8b9a57a66268..84634dc355f2 100644 --- a/.github/workflows/publish-docker-image-for-eth-sepolia.yml +++ b/.github/workflows/publish-docker-image-for-eth-sepolia.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: eth-sepolia steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-eth.yml b/.github/workflows/publish-docker-image-for-eth.yml index 980552d75058..825157591143 100644 --- a/.github/workflows/publish-docker-image-for-eth.yml +++ b/.github/workflows/publish-docker-image-for-eth.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: mainnet steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-filecoin.yml b/.github/workflows/publish-docker-image-for-filecoin.yml index caea5a953417..c2be558e12db 100644 --- a/.github/workflows/publish-docker-image-for-filecoin.yml +++ b/.github/workflows/publish-docker-image-for-filecoin.yml @@ -9,7 +9,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: filecoin steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-fuse.yml b/.github/workflows/publish-docker-image-for-fuse.yml index 52bb4a5901b0..7502d674c06e 100644 --- a/.github/workflows/publish-docker-image-for-fuse.yml +++ b/.github/workflows/publish-docker-image-for-fuse.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: fuse steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-gnosis-chain.yml b/.github/workflows/publish-docker-image-for-gnosis-chain.yml index 068822401f82..c2d2a20bb513 100644 --- a/.github/workflows/publish-docker-image-for-gnosis-chain.yml +++ b/.github/workflows/publish-docker-image-for-gnosis-chain.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: xdai steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-l2-staging.yml b/.github/workflows/publish-docker-image-for-l2-staging.yml index a39648c4235e..84d0e23ca31c 100644 --- a/.github/workflows/publish-docker-image-for-l2-staging.yml +++ b/.github/workflows/publish-docker-image-for-l2-staging.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: optimism-l2-advanced steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-lukso.yml b/.github/workflows/publish-docker-image-for-lukso.yml index b85f6be962d6..59b033a73397 100644 --- a/.github/workflows/publish-docker-image-for-lukso.yml +++ b/.github/workflows/publish-docker-image-for-lukso.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: lukso steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-optimism.yml b/.github/workflows/publish-docker-image-for-optimism.yml index 5361c91dd4a4..50789073db99 100644 --- a/.github/workflows/publish-docker-image-for-optimism.yml +++ b/.github/workflows/publish-docker-image-for-optimism.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: optimism steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-polygon-edge.yml b/.github/workflows/publish-docker-image-for-polygon-edge.yml index 8625850763f1..48ef51d4e781 100644 --- a/.github/workflows/publish-docker-image-for-polygon-edge.yml +++ b/.github/workflows/publish-docker-image-for-polygon-edge.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: polygon-edge steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-redstone.yml b/.github/workflows/publish-docker-image-for-redstone.yml index 56e28fee69ac..e84d908c7379 100644 --- a/.github/workflows/publish-docker-image-for-redstone.yml +++ b/.github/workflows/publish-docker-image-for-redstone.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: redstone steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-rootstock.yml b/.github/workflows/publish-docker-image-for-rootstock.yml index 335f4f475b56..0415956129ab 100644 --- a/.github/workflows/publish-docker-image-for-rootstock.yml +++ b/.github/workflows/publish-docker-image-for-rootstock.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: rsk steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-shibarium.yml b/.github/workflows/publish-docker-image-for-shibarium.yml index fcf2bf62838a..df743fa7eeae 100644 --- a/.github/workflows/publish-docker-image-for-shibarium.yml +++ b/.github/workflows/publish-docker-image-for-shibarium.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: shibarium steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-stability.yml b/.github/workflows/publish-docker-image-for-stability.yml index c106d4ea90a8..7a52c4840739 100644 --- a/.github/workflows/publish-docker-image-for-stability.yml +++ b/.github/workflows/publish-docker-image-for-stability.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: stability steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-suave.yml b/.github/workflows/publish-docker-image-for-suave.yml index bc968c303974..0a31318104db 100644 --- a/.github/workflows/publish-docker-image-for-suave.yml +++ b/.github/workflows/publish-docker-image-for-suave.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: suave steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-zetachain.yml b/.github/workflows/publish-docker-image-for-zetachain.yml index cef02097bc53..278f19c2060f 100644 --- a/.github/workflows/publish-docker-image-for-zetachain.yml +++ b/.github/workflows/publish-docker-image-for-zetachain.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: zetachain steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-zkevm.yml b/.github/workflows/publish-docker-image-for-zkevm.yml index 8df71dbffd30..b102cd7ca55b 100644 --- a/.github/workflows/publish-docker-image-for-zkevm.yml +++ b/.github/workflows/publish-docker-image-for-zkevm.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: zkevm steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-zksync.yml b/.github/workflows/publish-docker-image-for-zksync.yml index 5b397c729883..7d819c0c8dfe 100644 --- a/.github/workflows/publish-docker-image-for-zksync.yml +++ b/.github/workflows/publish-docker-image-for-zksync.yml @@ -9,7 +9,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: zksync steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-staging-on-demand.yml b/.github/workflows/publish-docker-image-staging-on-demand.yml index 2fb56610ff1c..9e184218ff89 100644 --- a/.github/workflows/publish-docker-image-staging-on-demand.yml +++ b/.github/workflows/publish-docker-image-staging-on-demand.yml @@ -12,7 +12,7 @@ on: env: OTP_VERSION: ${{ vars.OTP_VERSION }} ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 jobs: push_to_registry: diff --git a/.github/workflows/publish-regular-docker-image-on-demand.yml b/.github/workflows/publish-regular-docker-image-on-demand.yml index b78f41e02842..5a1b58379145 100644 --- a/.github/workflows/publish-regular-docker-image-on-demand.yml +++ b/.github/workflows/publish-regular-docker-image-on-demand.yml @@ -5,7 +5,7 @@ on: env: OTP_VERSION: ${{ vars.OTP_VERSION }} ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 jobs: push_to_registry: diff --git a/.github/workflows/release-arbitrum.yml b/.github/workflows/release-arbitrum.yml index b5cce835bb37..f6c6f6aa0aac 100644 --- a/.github/workflows/release-arbitrum.yml +++ b/.github/workflows/release-arbitrum.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-blackfort.yml b/.github/workflows/release-blackfort.yml index 74752040c981..b52db507da03 100644 --- a/.github/workflows/release-blackfort.yml +++ b/.github/workflows/release-blackfort.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-celo.yml b/.github/workflows/release-celo.yml index 0ec60c449303..32a8acb49b36 100644 --- a/.github/workflows/release-celo.yml +++ b/.github/workflows/release-celo.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-eth.yml b/.github/workflows/release-eth.yml index e5452d3978d2..6d4d4057a3f0 100644 --- a/.github/workflows/release-eth.yml +++ b/.github/workflows/release-eth.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-filecoin.yml b/.github/workflows/release-filecoin.yml index def2f3a88345..22056a60218a 100644 --- a/.github/workflows/release-filecoin.yml +++ b/.github/workflows/release-filecoin.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-fuse.yml b/.github/workflows/release-fuse.yml index 6805b6d8ee99..816ff059eb3d 100644 --- a/.github/workflows/release-fuse.yml +++ b/.github/workflows/release-fuse.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-gnosis.yml b/.github/workflows/release-gnosis.yml index e2b6f23afeaa..1a2e5d739388 100644 --- a/.github/workflows/release-gnosis.yml +++ b/.github/workflows/release-gnosis.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-optimism.yml b/.github/workflows/release-optimism.yml index dc1735617f43..18308961eb7e 100644 --- a/.github/workflows/release-optimism.yml +++ b/.github/workflows/release-optimism.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-polygon-edge.yml b/.github/workflows/release-polygon-edge.yml index 1174367b6eaf..f7cccd5c429b 100644 --- a/.github/workflows/release-polygon-edge.yml +++ b/.github/workflows/release-polygon-edge.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-polygon-zkevm.yml b/.github/workflows/release-polygon-zkevm.yml index 976e6a8e3a0d..75e1aed33146 100644 --- a/.github/workflows/release-polygon-zkevm.yml +++ b/.github/workflows/release-polygon-zkevm.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-redstone.yml b/.github/workflows/release-redstone.yml index 38a0e546c1d8..c204aca32dd4 100644 --- a/.github/workflows/release-redstone.yml +++ b/.github/workflows/release-redstone.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-rootstock.yml b/.github/workflows/release-rootstock.yml index 3f50374a9b19..d37a5d4f0d5a 100644 --- a/.github/workflows/release-rootstock.yml +++ b/.github/workflows/release-rootstock.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-shibarium.yml b/.github/workflows/release-shibarium.yml index 7c6d4d674a7b..c4b73bc8588e 100644 --- a/.github/workflows/release-shibarium.yml +++ b/.github/workflows/release-shibarium.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-stability.yml b/.github/workflows/release-stability.yml index 9c7264bfc2db..fb51ebd42e89 100644 --- a/.github/workflows/release-stability.yml +++ b/.github/workflows/release-stability.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-suave.yml b/.github/workflows/release-suave.yml index e04521b86b00..9d8c6e16cd4f 100644 --- a/.github/workflows/release-suave.yml +++ b/.github/workflows/release-suave.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-zetachain.yml b/.github/workflows/release-zetachain.yml index a4345fa633ec..cb97a0f34379 100644 --- a/.github/workflows/release-zetachain.yml +++ b/.github/workflows/release-zetachain.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-zksync.yml b/.github/workflows/release-zksync.yml index cf277a0f0df2..f967afac10cc 100644 --- a/.github/workflows/release-zksync.yml +++ b/.github/workflows/release-zksync.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ce6821992079..b8dec9aff049 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b6440b047a5..591d9e583dda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,92 @@ # Changelog +## 6.9.0 + +### 🚀 Features + +- List of internal transactions API v2 endpoint ([#10994](https://github.com/blockscout/blockscout/issues/10994)) +- Scroll rollup: L1 fee parameters in API, `queueIndex` for L2 transactions, and L1 <->L2 messages ([#10484](https://github.com/blockscout/blockscout/issues/10484)) +- Account V2 ([#10706](https://github.com/blockscout/blockscout/issues/10706)) +- Allow to provide DB schema other than public ([#10946](https://github.com/blockscout/blockscout/issues/10946)) +- Add missing filecoin robust addresses ([#10935](https://github.com/blockscout/blockscout/issues/10935)) +- EIP-7702 support ([#10870](https://github.com/blockscout/blockscout/issues/10870)) +- Open access to re-fetch metadata button for token instances without metadata initially fetched ([#10878](https://github.com/blockscout/blockscout/issues/10878)) +- Support snake_case in metadata service ([#10722](https://github.com/blockscout/blockscout/issues/10722)) +- Token transfers list API endpoint ([#10801](https://github.com/blockscout/blockscout/issues/10801)) +- Send archive balances requests to trace url ([#10820](https://github.com/blockscout/blockscout/issues/10820)) +- Add metadata info to tx interpreter request ([#10823](https://github.com/blockscout/blockscout/issues/10823)) +- Api for querying mud systems abi ([#10829](https://github.com/blockscout/blockscout/issues/10829)) +- Arbitrum L1-to-L2 messages with hashed message id ([#10751](https://github.com/blockscout/blockscout/issues/10751)) +- Support CoinMarketCap format in token supply stats ([#10853](https://github.com/blockscout/blockscout/issues/10853)) +- Address scam badge flag ([#10763](https://github.com/blockscout/blockscout/issues/10763)) +- Add verbosity to GraphQL token transfers query ([#10770](https://github.com/blockscout/blockscout/issues/10770)) +- (celo) include token information in API response for address epoch rewards ([#10831](https://github.com/blockscout/blockscout/issues/10831)) +- Add Blackfort validators ([#10744](https://github.com/blockscout/blockscout/issues/10744)) +- Retry ERC-1155 token instance metadata fetch from baseURI + tokenID ([#10766](https://github.com/blockscout/blockscout/issues/10766)) + +### 🐛 Bug Fixes + +- Fix failed tests ([#11000](https://github.com/blockscout/blockscout/issues/11000)) +- Add compatibility with current frontend for some public props ([#10998](https://github.com/blockscout/blockscout/issues/10998)) +- Process foreign key violation in scam addresses assigning functionality ([#10977](https://github.com/blockscout/blockscout/issues/10977)) +- Handle import exceptions in MassiveBlocksFetcher ([#10993](https://github.com/blockscout/blockscout/issues/10993)) +- Workaround for repeating logIndex ([#10880](https://github.com/blockscout/blockscout/issues/10880)) +- Filter out nil implementations from combine_proxy_implementation_addresses_map function result ([#10943](https://github.com/blockscout/blockscout/issues/10943)) +- Delete incorrect coin balances on reorg ([#10879](https://github.com/blockscout/blockscout/issues/10879)) +- Handle delegatecall in state changes ([#10906](https://github.com/blockscout/blockscout/issues/10906)) +- Fix env. variables link in README.md ([#10898](https://github.com/blockscout/blockscout/issues/10898)) +- Add missing block timestamp in election rewards for address response ([#10907](https://github.com/blockscout/blockscout/issues/10907)) +- Add missing build arg to celo workflow ([#10895](https://github.com/blockscout/blockscout/issues/10895)) +- Do not include unrelated token transfers in `tokenTransferTxs` ([#10889](https://github.com/blockscout/blockscout/issues/10889)) +- Fix get current user in app template ([#10844](https://github.com/blockscout/blockscout/issues/10844)) +- Set `API_GRAPHQL_MAX_COMPLEXITY` in build action ([#10843](https://github.com/blockscout/blockscout/issues/10843)) +- Disable archive balances only if latest block is available ([#10851](https://github.com/blockscout/blockscout/issues/10851)) +- Dialyzer warning ([#10845](https://github.com/blockscout/blockscout/issues/10845)) +- Decode revert reason by decoding candidates from the DB ([#10827](https://github.com/blockscout/blockscout/issues/10827)) +- Filecoin stucked pending address operations ([#10832](https://github.com/blockscout/blockscout/issues/10832)) +- Sanitize replaced transactions migration ([#10784](https://github.com/blockscout/blockscout/issues/10784)) +- Repair /metrics endpoint ([#10813](https://github.com/blockscout/blockscout/issues/10813)) +- Revert the deletion of deriving current token balances ([#10811](https://github.com/blockscout/blockscout/issues/10811)) +- Clear null round blocks from missing block ranges ([#10805](https://github.com/blockscout/blockscout/issues/10805)) +- Decode addresses as checksummed ([#10777](https://github.com/blockscout/blockscout/issues/10777)) +- Preload additional sources for bytecode twin smart-contract ([#10692](https://github.com/blockscout/blockscout/issues/10692)) +- Set min query length in the search API endpoints ([#10698](https://github.com/blockscout/blockscout/issues/10698)) +- Proper handling of old batches on Arbitrum Nova ([#10786](https://github.com/blockscout/blockscout/issues/10786)) +- Get rid of heavy DB query to start Arbitrum missed messages discovery process ([#10767](https://github.com/blockscout/blockscout/issues/10767)) +- Revisited approach to choose L1 blocks to discover missing Arbitrum batches ([#10757](https://github.com/blockscout/blockscout/issues/10757)) +- Fix account db repo definition ([#10714](https://github.com/blockscout/blockscout/issues/10714)) +- Allow string IDs in JSON RPC requests ([#10759](https://github.com/blockscout/blockscout/issues/10759)) +- Filter out tokens with skip_metadata: true from token fetcher ([#10736](https://github.com/blockscout/blockscout/issues/10736)) + +### 🚜 Refactor + +- Fixate naming convention for "transaction" and "block number" entities ([#10913](https://github.com/blockscout/blockscout/issues/10913)) +- Use middleware to check if GraphQL API is enabled ([#10772](https://github.com/blockscout/blockscout/issues/10772)) + +### ⚡ Performance + +- Optimize advanced filters ([#10463](https://github.com/blockscout/blockscout/issues/10463)) +- Refactor tx data decoding with fewer DB queries ([#10842](https://github.com/blockscout/blockscout/issues/10842)) + +### ⚙️ Miscellaneous Tasks + +- Update version bump script +- Remove deprecated single implementation property of the smart-contract from the API response ([#10715](https://github.com/blockscout/blockscout/issues/10715)) +- Set indexer memory limit based on system info as a fallback ([#10697](https://github.com/blockscout/blockscout/issues/10697)) +- Set user agent to metadata requests ([#10834](https://github.com/blockscout/blockscout/issues/10834)) +- Reverse internal transactions fetching order ([#10912](https://github.com/blockscout/blockscout/issues/10912)) +- Remove unused fetch_and_lock_by_hashes/1 public function +- Add shrink int txs docker image build for Celo chain type ([#10894](https://github.com/blockscout/blockscout/issues/10894)) +- Ability to work with Blockscout code base within a VSCode devcontainer ([#10838](https://github.com/blockscout/blockscout/issues/10838)) +- Add version bump script ([#10871](https://github.com/blockscout/blockscout/issues/10871)) +- Bump elixir to 1.17.3 and Erlang OTP to 27.1 ([#10284](https://github.com/blockscout/blockscout/issues/10284)) +- Reindex incorrect internal transactions migration ([#10654](https://github.com/blockscout/blockscout/issues/10654)) +- Remove old UI from base Docker image ([#10828](https://github.com/blockscout/blockscout/issues/10828)) +- Add primary key to address_tags table ([#10818](https://github.com/blockscout/blockscout/issues/10818)) +- Refactor OrderedCache preloads ([#10803](https://github.com/blockscout/blockscout/issues/10803)) +- Support non-unique log index for rsk chain type ([#10807](https://github.com/blockscout/blockscout/issues/10807)) +- Add missing symbols ([#10749](https://github.com/blockscout/blockscout/issues/10749)) + ## 6.8.1 ### 🚀 Features diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs index 8e2f7b96db4f..1db8dbb12457 100644 --- a/apps/block_scout_web/mix.exs +++ b/apps/block_scout_web/mix.exs @@ -23,7 +23,7 @@ defmodule BlockScoutWeb.Mixfile do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "6.8.1", + version: "6.9.0", xref: [ exclude: [ Explorer.Chain.PolygonZkevm.Reader, diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs index d168d4bb4f5c..d965cdfe339a 100644 --- a/apps/ethereum_jsonrpc/mix.exs +++ b/apps/ethereum_jsonrpc/mix.exs @@ -23,7 +23,7 @@ defmodule EthereumJsonrpc.MixProject do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "6.8.1" + version: "6.9.0" ] end diff --git a/apps/explorer/lib/explorer/token/metadata_retriever.ex b/apps/explorer/lib/explorer/token/metadata_retriever.ex index 19a94a411ddc..22e958ecb573 100644 --- a/apps/explorer/lib/explorer/token/metadata_retriever.ex +++ b/apps/explorer/lib/explorer/token/metadata_retriever.ex @@ -14,7 +14,7 @@ defmodule Explorer.Token.MetadataRetriever do @no_uri_error "no uri" @vm_execution_error "VM execution error" @invalid_base64_data "invalid data:application/json;base64" - @default_headers [{"User-Agent", "blockscout-6.8.1"}] + @default_headers [{"User-Agent", "blockscout-6.9.0"}] # https://eips.ethereum.org/EIPS/eip-1155#metadata @erc1155_token_id_placeholder "{id}" diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index ece0d86f9f3f..d49c61e1c083 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -24,7 +24,7 @@ defmodule Explorer.Mixfile do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "6.8.1", + version: "6.9.0", xref: [exclude: [BlockScoutWeb.Routers.WebRouter.Helpers, Indexer.Helper]] ] end diff --git a/apps/indexer/mix.exs b/apps/indexer/mix.exs index 6b075cf25331..13c68614732d 100644 --- a/apps/indexer/mix.exs +++ b/apps/indexer/mix.exs @@ -14,7 +14,7 @@ defmodule Indexer.MixProject do elixirc_paths: elixirc_paths(Mix.env()), lockfile: "../../mix.lock", start_permanent: Mix.env() == :prod, - version: "6.8.1", + version: "6.9.0", xref: [ exclude: [ Explorer.Chain.Optimism.Deposit, diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index 123844330881..43134d888572 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -37,7 +37,7 @@ services: CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED: "" CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL: "" ADMIN_PANEL_ENABLED: "" - RELEASE_VERSION: 6.8.1 + RELEASE_VERSION: 6.9.0 links: - db:database environment: diff --git a/docker/Makefile b/docker/Makefile index 246010fc16c9..6b4eecee6861 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -10,7 +10,7 @@ STATS_CONTAINER_NAME := stats STATS_DB_CONTAINER_NAME := stats-db PROXY_CONTAINER_NAME := proxy PG_CONTAINER_NAME := postgres -RELEASE_VERSION ?= '6.8.1' +RELEASE_VERSION ?= '6.9.0' TAG := $(RELEASE_VERSION)-commit-$(shell git log -1 --pretty=format:"%h") STABLE_TAG := $(RELEASE_VERSION) diff --git a/mix.exs b/mix.exs index 6ad3e626b7d5..234d0c2a674c 100644 --- a/mix.exs +++ b/mix.exs @@ -7,7 +7,7 @@ defmodule BlockScout.Mixfile do [ # app: :block_scout, # aliases: aliases(config_env()), - version: "6.8.1", + version: "6.9.0", apps_path: "apps", deps: deps(), dialyzer: dialyzer(), diff --git a/rel/config.exs b/rel/config.exs index 9e050b1477d9..d7141e3c2c67 100644 --- a/rel/config.exs +++ b/rel/config.exs @@ -71,7 +71,7 @@ end # will be used by default release :blockscout do - set version: "6.8.1-beta" + set version: "6.9.0-beta" set applications: [ :runtime_tools, block_scout_web: :permanent, From b48ad1f8f41cdb5cf5f5f7dba1317443928a4b46 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 22 Oct 2024 21:01:14 +0300 Subject: [PATCH 249/363] Fix Scroll workflow --- .github/workflows/pre-release-scroll.yml | 2 +- .github/workflows/publish-docker-image-for-scroll.yml | 2 +- .github/workflows/release-scroll.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pre-release-scroll.yml b/.github/workflows/pre-release-scroll.yml index 6cb1dad63115..feadf6449efb 100644 --- a/.github/workflows/pre-release-scroll.yml +++ b/.github/workflows/pre-release-scroll.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/publish-docker-image-for-scroll.yml b/.github/workflows/publish-docker-image-for-scroll.yml index f4b7b214fa81..87625b84d11f 100644 --- a/.github/workflows/publish-docker-image-for-scroll.yml +++ b/.github/workflows/publish-docker-image-for-scroll.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.9.0 DOCKER_CHAIN_NAME: scroll steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release-scroll.yml b/.github/workflows/release-scroll.yml index 490662ec1f88..ab2c08098d5c 100644 --- a/.github/workflows/release-scroll.yml +++ b/.github/workflows/release-scroll.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: ${{ vars.RELEASE_VERSION }} + RELEASE_VERSION: 6.9.0 steps: - uses: actions/checkout@v4 - name: Setup repo From 2140d6883a48d14654b951959cb55fb73ba89cc5 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 23 Oct 2024 12:08:16 +0300 Subject: [PATCH 250/363] fix: chart API: add compatibility with the current frontend (#11008) * fix: chart API: add compatibility with the current frontend * Rename other shorthands --- .../controllers/api/v2/stats_controller.ex | 3 ++- .../templates/account/tag_transaction/index.html.eex | 2 +- .../templates/account/tag_transaction/row.html.eex | 12 ++++++------ .../address/_verify_other_explorer.html.eex | 4 ++-- .../address/_verify_other_explorer_modal.html.eex | 4 ++-- .../address/_verify_other_explorers.html.eex | 12 ++++++------ .../templates/transaction/_tile.html.eex | 8 ++++---- .../templates/transaction/overview.html.eex | 2 +- .../api/v2/advanced_filter_controller_test.exs | 6 +++--- .../explorer/lib/explorer/account/tag_transaction.ex | 4 ++-- 10 files changed, 29 insertions(+), 28 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex index b429832bd513..84d42690f934 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex @@ -109,7 +109,8 @@ defmodule BlockScoutWeb.API.V2.StatsController do transaction_history_data = date_range |> Enum.map(fn row -> - %{date: row.date, transaction_count: row.number_of_transactions} + # todo: keep `tx_count` for compatibility with frontend and remove when new frontend is bound to `transaction_count` property + %{date: row.date, transaction_count: row.number_of_transactions, tx_count: row.number_of_transactions} end) json(conn, %{ diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/index.html.eex index da97eb6573d0..80d1f479ce43 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/index.html.eex @@ -26,7 +26,7 @@
<%= Enum.map(@transaction_tags, fn at -> - render("row.html", tx_tag: at, conn: @conn) + render("row.html", transaction_tag: at, conn: @conn) end) %>
- <%= render BlockScoutWeb.AddressView, "_link.html", address: from_address, contract: BlockScoutWeb.AddressView.contract?(from_address), use_custom_tooltip: false, trimmed: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: from_address, contract: Address.smart_contract?(from_address), use_custom_tooltip: false, trimmed: false %> <%= render BlockScoutWeb.AddressView, "_labels.html", tags: from_tags %> @@ -24,7 +24,7 @@ style: "position: relative;" %> To - <%= render BlockScoutWeb.AddressView, "_link.html", address: to_address, contract: BlockScoutWeb.AddressView.contract?(to_address), use_custom_tooltip: false, trimmed: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: to_address, contract: Address.smart_contract?(to_address), use_custom_tooltip: false, trimmed: false %> <%= render BlockScoutWeb.AddressView, "_labels.html", tags: to_tags %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex index 3ba1c8049420..b529f940a2c5 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex @@ -245,7 +245,7 @@ text: gettext("Address (external or contract) sending the transaction.") %> <%= gettext "From" %>
- <%= render BlockScoutWeb.AddressView, "_link.html", address: from_address, contract: BlockScoutWeb.AddressView.contract?(from_address), use_custom_tooltip: false, trimmed: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: from_address, contract: Address.smart_contract?(from_address), use_custom_tooltip: false, trimmed: false %> <%= render BlockScoutWeb.AddressView, "_labels.html", tags: @from_tags %> <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], @@ -261,7 +261,7 @@
<%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", text: gettext("Address (external or contract) receiving the transaction.") %> - <%= if BlockScoutWeb.AddressView.contract?(to_address) && !created_address_hash do %> + <%= if Address.smart_contract?(to_address) && !created_address_hash do %> <%= gettext "Interacted With (To)" %> <% else %> <%= gettext "To" %> @@ -271,7 +271,7 @@ <%= cond do %> <% created_address_hash -> %> [<%= gettext("Contract") %>  - <%= render BlockScoutWeb.AddressView, "_link.html", address: to_address, contract: BlockScoutWeb.AddressView.contract?(to_address), use_custom_tooltip: false, trimmed: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: to_address, contract: Address.smart_contract?(to_address), use_custom_tooltip: false, trimmed: false %> <%= render BlockScoutWeb.AddressView, "_labels.html", tags: @to_tags %>  <%= gettext("created") %>] <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", @@ -280,7 +280,7 @@ aria_label: gettext("Copy To Address"), title: gettext("Copy To Address") %> <% recipient_address_hash -> %> - <%= render BlockScoutWeb.AddressView, "_link.html", address: to_address, contract: BlockScoutWeb.AddressView.contract?(to_address), use_custom_tooltip: false, trimmed: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: to_address, contract: Address.smart_contract?(to_address), use_custom_tooltip: false, trimmed: false %> <%= render BlockScoutWeb.AddressView, "_labels.html", tags: @to_tags %> <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_state_change.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_state_change.html.eex index 67a8e63ea389..bd4abe9c19ca 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_state_change.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_state_change.html.eex @@ -9,7 +9,7 @@
- <%= render BlockScoutWeb.AddressView, "_link.html", address: @address, contract: BlockScoutWeb.AddressView.contract?(@address), use_custom_tooltip: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: @address, contract: Address.smart_contract?(@address), use_custom_tooltip: false %> - <%= render BlockScoutWeb.AddressView, "_link.html", address: @address, contract: BlockScoutWeb.AddressView.contract?(@address), use_custom_tooltip: false %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: @address, contract: Address.smart_contract?(@address), use_custom_tooltip: false %>
<%= @tx_tag.name %>
- <%= link(@tx_tag.tx_hash, - to: transaction_path(BlockScoutWeb.Endpoint, :show, @tx_tag.tx_hash), + <%= link(@tx_tag.transaction_hash, + to: transaction_path(BlockScoutWeb.Endpoint, :show, @tx_tag.transaction_hash), "data-test": "transaction_hash_link", class: "text-truncate") %> <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", - additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], clipboard_text: @tx_tag.tx_hash, aria_label: gettext("Copy Address"), title: gettext("Copy Address"), style: "display: inline-block; vertical-align: text-bottom; position: initial; margin-top: 1px;" %> + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], clipboard_text: @tx_tag.transaction_hash, aria_label: gettext("Copy Address"), title: gettext("Copy Address"), style: "display: inline-block; vertical-align: text-bottom; position: initial; margin-top: 1px;" %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_tile.html.eex index 04cefd6cde58..fa7c121574e3 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/_tile.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_tile.html.eex @@ -27,7 +27,7 @@ - <%= @tx_count %> + <%= @transaction_count %> <%= gettext "Transactions" %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/row.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/row.html.eex index c2c5c0ff2804..0fad76aeba83 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/row.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/row.html.eex @@ -1,18 +1,18 @@ -<%= if @tx_tag.transaction_hash do %> +<%= if @transaction_tag.transaction_hash do %> - <%= @tx_tag.name %> + <%= @transaction_tag.name %>
- <%= link(@tx_tag.transaction_hash, - to: transaction_path(BlockScoutWeb.Endpoint, :show, @tx_tag.transaction_hash), + <%= link(@transaction_tag.transaction_hash, + to: transaction_path(BlockScoutWeb.Endpoint, :show, @transaction_tag.transaction_hash), "data-test": "transaction_hash_link", class: "text-truncate") %> <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", - additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], clipboard_text: @tx_tag.transaction_hash, aria_label: gettext("Copy Address"), title: gettext("Copy Address"), style: "display: inline-block; vertical-align: text-bottom; position: initial; margin-top: 1px;" %> + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], clipboard_text: @transaction_tag.transaction_hash, aria_label: gettext("Copy Address"), title: gettext("Copy Address"), style: "display: inline-block; vertical-align: text-bottom; position: initial; margin-top: 1px;" %>
- <%= link "Remove Tag", to: tag_transaction_path(@conn, :delete, @tx_tag.id), method: :delete %> + <%= link "Remove Tag", to: tag_transaction_path(@conn, :delete, @transaction_tag.id), method: :delete %> <% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer.html.eex index 24a31bbc516d..4f656c1f574b 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer.html.eex @@ -1,7 +1,7 @@ <%= if @type=="address" do %> <% else %> - + <% end %>
@@ -9,7 +9,7 @@ <%= if @type=="address" do %>
<%= address_link_to_other_explorer(@address_link, @hash ,true) %>
<% else %> -
<%= address_link_to_other_explorer(@tx_link, @hash ,true) %>
+
<%= address_link_to_other_explorer(@transaction_link, @hash ,true) %>
<% end %>
\ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer_modal.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer_modal.html.eex index 69ffb705e3b7..462ec8ed6e81 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer_modal.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer_modal.html.eex @@ -13,8 +13,8 @@ ) %> <% else %> <%= link( - address_link_to_other_explorer(@tx_link, @hash, false), - to: address_link_to_other_explorer(@tx_link, @hash ,true) + address_link_to_other_explorer(@transaction_link, @hash, false), + to: address_link_to_other_explorer(@transaction_link, @hash ,true) ) %> <% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorers.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorers.html.eex index 21afb575a58f..ca0af97cf98f 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorers.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorers.html.eex @@ -3,9 +3,9 @@

Verify with other Explorers:

- <%= render "_verify_other_explorer.html", hash: @hash, type: @type, header: "Etherscan.io", class: "etherscan", address_link: "https://etherscan.io/address/", tx_link: "https://etherscan.io/tx/" %> - <%= render "_verify_other_explorer.html", hash: @hash, type: @type, header: "Blockchair.com", class: "blockchair", address_link: "https://blockchair.com/ethereum/address/", tx_link: "https://blockchair.com/ethereum/transaction/" %> - <%= render "_verify_other_explorer.html", hash: @hash, type: @type, header: "Etherchain.org", class: "etherchain", address_link: "https://www.etherchain.org/account/", tx_link: "https://www.etherchain.org/tx/" %> + <%= render "_verify_other_explorer.html", hash: @hash, type: @type, header: "Etherscan.io", class: "etherscan", address_link: "https://etherscan.io/address/", transaction_link: "https://etherscan.io/tx/" %> + <%= render "_verify_other_explorer.html", hash: @hash, type: @type, header: "Blockchair.com", class: "blockchair", address_link: "https://blockchair.com/ethereum/address/", transaction_link: "https://blockchair.com/ethereum/transaction/" %> + <%= render "_verify_other_explorer.html", hash: @hash, type: @type, header: "Etherchain.org", class: "etherchain", address_link: "https://www.etherchain.org/account/", transaction_link: "https://www.etherchain.org/tx/" %> @@ -26,9 +26,9 @@
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex index 5e34875f477e..78118d1fded8 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex @@ -1,7 +1,7 @@ <% status = transaction_status(@transaction) %> <% error_in_internal_transaction = @transaction.has_error_in_internal_transactions %> <% current_user = AuthController.current_user(@conn) %> -<% tx_tags = BlockScoutWeb.Models.GetTransactionTags.get_transaction_with_addresses_tags(@transaction, current_user) %> +<% transaction_tags = BlockScoutWeb.Models.GetTransactionTags.get_transaction_with_addresses_tags(@transaction, current_user) %>
@@ -33,10 +33,10 @@ <%= if method_name do %> <%= render BlockScoutWeb.FormView, "_tag.html", text: method_name, additional_classes: ["method", "ml-1"] %> <% end %> - <%= if tx_tags.personal_transaction_tag && tx_tags.personal_transaction_tag.name !== :error do %> - <%= render BlockScoutWeb.FormView, "_tag.html", text: tx_tags.personal_transaction_tag.name, additional_classes: [tag_name_to_label(tx_tags.personal_transaction_tag.name), "ml-1"] %> + <%= if transaction_tags.personal_transaction_tag && transaction_tags.personal_transaction_tag.name !== :error do %> + <%= render BlockScoutWeb.FormView, "_tag.html", text: transaction_tags.personal_transaction_tag.name, additional_classes: [tag_name_to_label(transaction_tags.personal_transaction_tag.name), "ml-1"] %> <% end %> - <%= render BlockScoutWeb.AddressView, "_labels.html", tags: tx_tags %> + <%= render BlockScoutWeb.AddressView, "_labels.html", tags: transaction_tags %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex index bb1b41181dc6..611ed37e9243 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex @@ -48,7 +48,7 @@

<%= gettext "Transaction Details" %> - <% personal_transaction_tag = if assigns[:tx_tags], do: @transaction_tags.personal_transaction_tag, else: nil %> + <% personal_transaction_tag = if assigns[:transaction_tags], do: @transaction_tags.personal_transaction_tag, else: nil %> <%= if personal_transaction_tag && personal_transaction_tag.name !== :error do %> <%= render BlockScoutWeb.FormView, "_tag.html", text: personal_transaction_tag.name, additional_classes: [tag_name_to_label(personal_transaction_tag.name), "ml-1"] %> <% end %> diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/advanced_filter_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/advanced_filter_controller_test.exs index cca5e007a527..cf4c2b6822ee 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/advanced_filter_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/advanced_filter_controller_test.exs @@ -265,7 +265,7 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do end test "filter by age", %{conn: conn} do - [_, tx_a, _, tx_b, _] = + [_, transaction_a, _, transaction_b, _] = for i <- 0..4 do tx = :transaction |> insert() |> with_block(status: :ok) @@ -290,8 +290,8 @@ defmodule BlockScoutWeb.API.V2.AdvancedFilterControllerTest do request = get(conn, "/api/v2/advanced-filters", %{ - "age_from" => DateTime.to_iso8601(tx_a.block.timestamp), - "age_to" => DateTime.to_iso8601(tx_b.block.timestamp) + "age_from" => DateTime.to_iso8601(transaction_a.block.timestamp), + "age_to" => DateTime.to_iso8601(transaction_b.block.timestamp) }) assert response = json_response(request, 200) diff --git a/apps/explorer/lib/explorer/account/tag_transaction.ex b/apps/explorer/lib/explorer/account/tag_transaction.ex index 1fefb8ea3452..24b14fd5c996 100644 --- a/apps/explorer/lib/explorer/account/tag_transaction.ex +++ b/apps/explorer/lib/explorer/account/tag_transaction.ex @@ -131,13 +131,13 @@ defmodule Explorer.Account.TagTransaction do the provided transaction hash and identity ID. ## Parameters - - `tx_hash`: The transaction hash to search for. Can be a `String.t()`, + - `transaction_hash`: The transaction hash to search for. Can be a `String.t()`, `Explorer.Chain.Hash.Full.t()`, or `nil`. - `identity_id`: The identity ID to search for. Can be an `integer()` or `nil`. ## Returns - A list of `Explorer.Account.TagTransaction` structs if matching records are found. - - `nil` if either `tx_hash` or `identity_id` is `nil`. + - `nil` if either `transaction_hash` or `identity_id` is `nil`. """ @spec get_tag_transaction_by_transaction_hash_and_identity_id(String.t() | Hash.Full.t() | nil, integer() | nil) :: [__MODULE__.t()] | nil From b370c0014f1243235a3bec73c86c65b7e4dc1c5e Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Wed, 23 Oct 2024 12:54:10 +0300 Subject: [PATCH 251/363] Add pre-release docker image for scroll with internal txs shrink --- .github/workflows/pre-release-scroll.yml | 72 +++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pre-release-scroll.yml b/.github/workflows/pre-release-scroll.yml index feadf6449efb..90209003e759 100644 --- a/.github/workflows/pre-release-scroll.yml +++ b/.github/workflows/pre-release-scroll.yml @@ -94,4 +94,74 @@ jobs: ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=scroll \ No newline at end of file + CHAIN_TYPE=scroll + + - name: Build and push Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-scroll:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-scroll:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-scroll:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=scroll + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file From d3d768a5ff9a8eca6b6fa85dfb455a3cdfc350b0 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 24 Oct 2024 15:26:38 +0300 Subject: [PATCH 252/363] fix: Add tx_count, tx_types props in the response of address API v2 endpoints for compatibility with current version of the frontend (#11012) --- .../microservice_interfaces/transaction_interpretation.ex | 2 ++ .../lib/block_scout_web/views/api/v2/address_view.ex | 2 ++ 2 files changed, 4 insertions(+) diff --git a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex index 263559cb2351..0fbedf613abf 100644 --- a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex +++ b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex @@ -160,6 +160,8 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do value: transaction_with_meta.value, method: Transaction.method_name(transaction_with_meta, Transaction.format_decoded_input(decoded_input)), status: transaction_with_meta.status, + # todo: keep `tx_types` for compatibility with interpreter and remove when new interpreter is bound to `transaction_types` property + tx_types: TransactionView.transaction_types(transaction_with_meta), transaction_types: TransactionView.transaction_types(transaction_with_meta), raw_input: transaction_with_meta.input, decoded_input: decoded_input_data, diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex index 9b19c5103e39..90611f3b5ec3 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex @@ -81,6 +81,8 @@ defmodule BlockScoutWeb.API.V2.AddressView do def prepare_address({address, transaction_count}) do nil |> Helper.address_with_info(address, address.hash, true) + # todo: keep `tx_count` for compatibility with frontend and remove when new frontend is bound to `transaction_count` property + |> Map.put(:tx_count, to_string(transaction_count)) |> Map.put(:transaction_count, to_string(transaction_count)) |> Map.put(:coin_balance, if(address.fetched_coin_balance, do: address.fetched_coin_balance.value)) end From 684dd5d841acede5d2a3d92808041d3ae3322e4f Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:31:17 +0300 Subject: [PATCH 253/363] fix: Handle stalled async task in MapCache (#11015) --- .../lib/explorer/chain/cache/address_sum.ex | 7 +++-- .../chain/cache/address_sum_minus_burnt.ex | 7 +++-- .../lib/explorer/chain/cache/block.ex | 7 +++-- .../explorer/chain/cache/gas_price_oracle.ex | 7 +++-- .../lib/explorer/chain/cache/gas_usage.ex | 7 +++-- .../chain/cache/pending_block_operation.ex | 7 +++-- .../lib/explorer/chain/cache/transaction.ex | 7 +++-- apps/explorer/lib/explorer/chain/map_cache.ex | 29 +++++++++++++++++++ 8 files changed, 57 insertions(+), 21 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/cache/address_sum.ex b/apps/explorer/lib/explorer/chain/cache/address_sum.ex index 8e2d67f381aa..694cb892dd4d 100644 --- a/apps/explorer/lib/explorer/chain/cache/address_sum.ex +++ b/apps/explorer/lib/explorer/chain/cache/address_sum.ex @@ -17,9 +17,10 @@ defmodule Explorer.Chain.Cache.AddressSum do alias Explorer.Etherscan defp handle_fallback(:sum) do - # This will get the task PID if one exists and launch a new task if not + # This will get the task PID if one exists, check if it's running and launch + # a new task if task doesn't exist or it's not running. # See next `handle_fallback` definition - get_async_task() + safe_get_async_task() {:return, Decimal.new(0)} end @@ -49,7 +50,7 @@ defmodule Explorer.Chain.Cache.AddressSum do # By setting this as a `callback` an async task will be started each time the # `sum` expires (unless there is one already running) - defp async_task_on_deletion({:delete, _, :sum}), do: get_async_task() + defp async_task_on_deletion({:delete, _, :sum}), do: safe_get_async_task() defp async_task_on_deletion(_data), do: nil end diff --git a/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex b/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex index 6e8dfa9289e9..c1f637ac7442 100644 --- a/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex +++ b/apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex @@ -17,9 +17,10 @@ defmodule Explorer.Chain.Cache.AddressSumMinusBurnt do alias Explorer.Chain.Cache.Helper defp handle_fallback(:sum_minus_burnt) do - # This will get the task PID if one exists and launch a new task if not + # This will get the task PID if one exists, check if it's running and launch + # a new task if task doesn't exist or it's not running. # See next `handle_fallback` definition - get_async_task() + safe_get_async_task() {:return, Decimal.new(0)} end @@ -56,7 +57,7 @@ defmodule Explorer.Chain.Cache.AddressSumMinusBurnt do # By setting this as a `callback` an async task will be started each time the # `sum_minus_burnt` expires (unless there is one already running) - defp async_task_on_deletion({:delete, _, :sum_minus_burnt}), do: get_async_task() + defp async_task_on_deletion({:delete, _, :sum_minus_burnt}), do: safe_get_async_task() defp async_task_on_deletion(_data), do: nil end diff --git a/apps/explorer/lib/explorer/chain/cache/block.ex b/apps/explorer/lib/explorer/chain/cache/block.ex index edb97c43d794..68ea79965907 100644 --- a/apps/explorer/lib/explorer/chain/cache/block.ex +++ b/apps/explorer/lib/explorer/chain/cache/block.ex @@ -56,9 +56,10 @@ defmodule Explorer.Chain.Cache.Block do end defp handle_fallback(:count) do - # This will get the task PID if one exists and launch a new task if not + # This will get the task PID if one exists, check if it's running and launch + # a new task if task doesn't exist or it's not running. # See next `handle_fallback` definition - get_async_task() + safe_get_async_task() {:return, nil} end @@ -95,7 +96,7 @@ defmodule Explorer.Chain.Cache.Block do # By setting this as a `callback` an async task will be started each time the # `count` expires (unless there is one already running) - defp async_task_on_deletion({:delete, _, :count}), do: get_async_task() + defp async_task_on_deletion({:delete, _, :count}), do: safe_get_async_task() defp async_task_on_deletion(_data), do: nil diff --git a/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex b/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex index 12c615dad3e6..bc9b590538b1 100644 --- a/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex +++ b/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex @@ -310,9 +310,10 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do defp fast_time_coefficient, do: Application.get_env(:explorer, __MODULE__)[:fast_time_coefficient] defp handle_fallback(:gas_prices) do - # This will get the task PID if one exists and launch a new task if not + # This will get the task PID if one exists, check if it's running and launch + # a new task if task doesn't exist or it's not running. # See next `handle_fallback` definition - get_async_task() + safe_get_async_task() {:return, get_old_gas_prices()} end @@ -353,7 +354,7 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do # `gas_prices` expires (unless there is one already running) defp async_task_on_deletion({:delete, _, :gas_prices}) do set_old_gas_prices(get_gas_prices()) - get_async_task() + safe_get_async_task() end defp async_task_on_deletion(_data), do: nil diff --git a/apps/explorer/lib/explorer/chain/cache/gas_usage.ex b/apps/explorer/lib/explorer/chain/cache/gas_usage.ex index 6a632c97dd24..ddff60dd41ab 100644 --- a/apps/explorer/lib/explorer/chain/cache/gas_usage.ex +++ b/apps/explorer/lib/explorer/chain/cache/gas_usage.ex @@ -37,9 +37,10 @@ defmodule Explorer.Chain.Cache.GasUsage do end defp handle_fallback(:sum) do - # This will get the task PID if one exists and launch a new task if not + # This will get the task PID if one exists, check if it's running and launch + # a new task if task doesn't exist or it's not running. # See next `handle_fallback` definition - get_async_task() + safe_get_async_task() {:return, nil} end @@ -73,7 +74,7 @@ defmodule Explorer.Chain.Cache.GasUsage do # By setting this as a `callback` an async task will be started each time the # `sum` expires (unless there is one already running) - defp async_task_on_deletion({:delete, _, :sum}), do: get_async_task() + defp async_task_on_deletion({:delete, _, :sum}), do: safe_get_async_task() defp async_task_on_deletion(_data), do: nil diff --git a/apps/explorer/lib/explorer/chain/cache/pending_block_operation.ex b/apps/explorer/lib/explorer/chain/cache/pending_block_operation.ex index 7dccbe06ca0b..a746664d61b4 100644 --- a/apps/explorer/lib/explorer/chain/cache/pending_block_operation.ex +++ b/apps/explorer/lib/explorer/chain/cache/pending_block_operation.ex @@ -35,9 +35,10 @@ defmodule Explorer.Chain.Cache.PendingBlockOperation do end defp handle_fallback(:count) do - # This will get the task PID if one exists and launch a new task if not + # This will get the task PID if one exists, check if it's running and launch + # a new task if task doesn't exist or it's not running. # See next `handle_fallback` definition - get_async_task() + safe_get_async_task() {:return, nil} end @@ -67,7 +68,7 @@ defmodule Explorer.Chain.Cache.PendingBlockOperation do # By setting this as a `callback` an async task will be started each time the # `count` expires (unless there is one already running) - defp async_task_on_deletion({:delete, _, :count}), do: get_async_task() + defp async_task_on_deletion({:delete, _, :count}), do: safe_get_async_task() defp async_task_on_deletion(_data), do: nil end diff --git a/apps/explorer/lib/explorer/chain/cache/transaction.ex b/apps/explorer/lib/explorer/chain/cache/transaction.ex index 6ac24f537e6a..969aa53000a2 100644 --- a/apps/explorer/lib/explorer/chain/cache/transaction.ex +++ b/apps/explorer/lib/explorer/chain/cache/transaction.ex @@ -36,9 +36,10 @@ defmodule Explorer.Chain.Cache.Transaction do end defp handle_fallback(:count) do - # This will get the task PID if one exists and launch a new task if not + # This will get the task PID if one exists, check if it's running and launch + # a new task if task doesn't exist or it's not running. # See next `handle_fallback` definition - get_async_task() + safe_get_async_task() {:return, nil} end @@ -68,7 +69,7 @@ defmodule Explorer.Chain.Cache.Transaction do # By setting this as a `callback` an async task will be started each time the # `count` expires (unless there is one already running) - defp async_task_on_deletion({:delete, _, :count}), do: get_async_task() + defp async_task_on_deletion({:delete, _, :count}), do: safe_get_async_task() defp async_task_on_deletion(_data), do: nil end diff --git a/apps/explorer/lib/explorer/chain/map_cache.ex b/apps/explorer/lib/explorer/chain/map_cache.ex index 6dfbe6b27e6c..fbe38db6bbca 100644 --- a/apps/explorer/lib/explorer/chain/map_cache.ex +++ b/apps/explorer/lib/explorer/chain/map_cache.ex @@ -201,6 +201,35 @@ defmodule Explorer.Chain.MapCache do end end + defp named_functions(:async_task) do + quote do + def get_async_task, do: get(:async_task) + + @doc """ + Checks if the asynchronous task has stalled (has finished, but still in cache), + and if so, creates a new replacement task to continue the operation. + """ + def safe_get_async_task do + case get_async_task() do + pid when is_pid(pid) -> + if Process.alive?(pid) do + pid + else + set_async_task(nil) + get_async_task() + end + + not_pid -> + not_pid + end + end + + def set_async_task(value), do: set(:async_task, value) + + def update_async_task(value), do: update(:async_task, value) + end + end + # sobelow_skip ["DOS"] defp named_functions(key) do quote do From 566997e9ec04f394289e11420257aee80f2bf63d Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Mon, 28 Oct 2024 13:05:23 +0400 Subject: [PATCH 254/363] fix: bugs introduced in calldata decoding optimizations (#11025) * fix: incorrect types * fix: empty methods cache --- apps/explorer/lib/explorer/chain/transaction.ex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index 1637fcd4516e..4bcd5df3e6fc 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -1002,7 +1002,7 @@ defmodule Explorer.Chain.Transaction do proxy_implementation_abi_map, options ) do - Map.get_lazy(proxy_implementation_abi_map, smart_contract, fn -> + Map.get_lazy(proxy_implementation_abi_map, smart_contract.address_hash, fn -> Proxy.combine_proxy_implementation_abi(smart_contract, options) end) end @@ -1988,7 +1988,7 @@ defmodule Explorer.Chain.Transaction do empty_methods_map = transactions |> Enum.flat_map(fn - %{input: <>} -> [method_id] + %{input: %{bytes: <>}} -> [method_id] _ -> [] end) |> Enum.into(%{}, &{&1, []}) @@ -2007,12 +2007,12 @@ defmodule Explorer.Chain.Transaction do methods_map = decoded_transactions |> Enum.flat_map(fn - {nil, %{input: <>}} -> [method_id] + {nil, %{input: %{bytes: <>}}} -> [method_id] _ -> [] end) |> Enum.uniq() |> ContractMethod.find_contract_methods(opts) - |> Enum.into(%{}, &{&1.identifier, [&1]}) + |> Enum.into(empty_methods_map, &{&1.identifier, [&1]}) # decode remaining transaction using methods map decoded_transactions From 7e78505e51c37e80d365d94534d0ae0f5e2952b1 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Mon, 28 Oct 2024 12:01:57 +0200 Subject: [PATCH 255/363] Update CHANGELOG --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 591d9e583dda..68d37bba86cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ - EIP-7702 support ([#10870](https://github.com/blockscout/blockscout/issues/10870)) - Open access to re-fetch metadata button for token instances without metadata initially fetched ([#10878](https://github.com/blockscout/blockscout/issues/10878)) - Support snake_case in metadata service ([#10722](https://github.com/blockscout/blockscout/issues/10722)) -- Token transfers list API endpoint ([#10801](https://github.com/blockscout/blockscout/issues/10801)) +- Token transfers list API v2 endpoint ([#10801](https://github.com/blockscout/blockscout/issues/10801)) - Send archive balances requests to trace url ([#10820](https://github.com/blockscout/blockscout/issues/10820)) - Add metadata info to tx interpreter request ([#10823](https://github.com/blockscout/blockscout/issues/10823)) - Api for querying mud systems abi ([#10829](https://github.com/blockscout/blockscout/issues/10829)) @@ -26,6 +26,10 @@ ### 🐛 Bug Fixes +- Bugs introduced in calldata decoding optimizations ([#11025](https://github.com/blockscout/blockscout/issues/11025)) +- Handle stalled async task in MapCache ([#11015](https://github.com/blockscout/blockscout/issues/11015)) +- Add tx_count, tx_types props in the response of address API v2 endpoints for compatibility with current version of the frontend ([#11012](https://github.com/blockscout/blockscout/issues/11012)) +- Chart API: add compatibility with the current frontend ([#11008](https://github.com/blockscout/blockscout/issues/11008)) - Fix failed tests ([#11000](https://github.com/blockscout/blockscout/issues/11000)) - Add compatibility with current frontend for some public props ([#10998](https://github.com/blockscout/blockscout/issues/10998)) - Process foreign key violation in scam addresses assigning functionality ([#10977](https://github.com/blockscout/blockscout/issues/10977)) From b8768fdbcc796dbe1023a0496de0b449eb9f1761 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Mon, 28 Oct 2024 13:03:45 +0200 Subject: [PATCH 256/363] fix: Rename zksync l1/l2 _tx_count columns (#11051) --- .../20241028102407_rename_tx_count_fields_zksync.exs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 apps/explorer/priv/zk_sync/migrations/20241028102407_rename_tx_count_fields_zksync.exs diff --git a/apps/explorer/priv/zk_sync/migrations/20241028102407_rename_tx_count_fields_zksync.exs b/apps/explorer/priv/zk_sync/migrations/20241028102407_rename_tx_count_fields_zksync.exs new file mode 100644 index 000000000000..dd89c64fce22 --- /dev/null +++ b/apps/explorer/priv/zk_sync/migrations/20241028102407_rename_tx_count_fields_zksync.exs @@ -0,0 +1,8 @@ +defmodule Explorer.Repo.ZkSync.Migrations.RenameTxCountFieldsZksync do + use Ecto.Migration + + def change do + rename(table(:zksync_transaction_batches), :l1_tx_count, to: :l1_transaction_count) + rename(table(:zksync_transaction_batches), :l2_tx_count, to: :l2_transaction_count) + end +end From b2cc4669d6dbc7a26d012e7ee4e8fa52e79f6b88 Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:18:59 +0300 Subject: [PATCH 257/363] feat: address transactions block number sorting (#11035) * feat: address transactions block number sorting * Add index ordering Co-Authored-By: Kirill Fedoseev --------- Co-authored-by: Kirill Fedoseev --- .../lib/block_scout_web/paging_helper.ex | 2 + .../api/v2/address_controller_test.exs | 77 ++++++++++++++++++- .../lib/explorer/chain/transaction.ex | 12 +++ 3 files changed, 90 insertions(+), 1 deletion(-) diff --git a/apps/block_scout_web/lib/block_scout_web/paging_helper.ex b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex index 11b9552bf273..483e4150f1fe 100644 --- a/apps/block_scout_web/lib/block_scout_web/paging_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex @@ -287,6 +287,8 @@ defmodule BlockScoutWeb.PagingHelper do def address_transactions_sorting(_), do: [] + defp do_address_transaction_sorting("block_number", "asc"), do: [asc: :block_number, asc: :index] + defp do_address_transaction_sorting("block_number", "desc"), do: [desc: :block_number, desc: :index] defp do_address_transaction_sorting("value", "asc"), do: [asc: :value] defp do_address_transaction_sorting("value", "desc"), do: [desc: :value] defp do_address_transaction_sorting("fee", "asc"), do: [{:dynamic, :fee, :asc_nulls_first, Transaction.dynamic_fee()}] diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs index ba804e55c6fe..ea3318f7ccd2 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/address_controller_test.exs @@ -6,7 +6,6 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do alias ABI.{TypeDecoder, TypeEncoder} alias Explorer.{Chain, Repo, TestHelper} alias Explorer.Chain.Address.Counters - alias Explorer.Chain.Events.Subscriber alias Explorer.Chain.{ Address, @@ -823,6 +822,82 @@ defmodule BlockScoutWeb.API.V2.AddressControllerTest do check_paginated_response(response, response_2nd_page, transactions |> Enum.reverse()) end + + test "can order and paginate by block number ascending", %{conn: conn} do + address = insert(:address) + + transactions_from = + for _ <- 0..24, do: insert(:transaction, from_address: address) |> with_block() + + transactions_to = for _ <- 0..25, do: insert(:transaction, to_address: address) |> with_block() + + transactions = + (transactions_from ++ transactions_to) + |> Enum.sort_by(& &1.block.number) + + request = + get(conn, "/api/v2/addresses/#{address.hash}/transactions", %{"sort" => "block_number", "order" => "asc"}) + + assert response = json_response(request, 200) + + request_2nd_page = + get( + conn, + "/api/v2/addresses/#{address.hash}/transactions", + %{"sort" => "block_number", "order" => "asc"} |> Map.merge(response["next_page_params"]) + ) + + assert response_2nd_page = json_response(request_2nd_page, 200) + + assert Enum.count(response["items"]) == 50 + assert response["next_page_params"] != nil + compare_item(Enum.at(transactions, 0), Enum.at(response["items"], 0)) + compare_item(Enum.at(transactions, 49), Enum.at(response["items"], 49)) + + assert Enum.count(response_2nd_page["items"]) == 1 + assert response_2nd_page["next_page_params"] == nil + compare_item(Enum.at(transactions, 50), Enum.at(response_2nd_page["items"], 0)) + + check_paginated_response(response, response_2nd_page, transactions |> Enum.reverse()) + end + + test "can order and paginate by block number descending", %{conn: conn} do + address = insert(:address) + + transactions_from = + for _ <- 0..24, do: insert(:transaction, from_address: address) |> with_block() + + transactions_to = for _ <- 0..25, do: insert(:transaction, to_address: address) |> with_block() + + transactions = + (transactions_from ++ transactions_to) + |> Enum.sort_by(& &1.block.number, :desc) + + request = + get(conn, "/api/v2/addresses/#{address.hash}/transactions", %{"sort" => "block_number", "order" => "desc"}) + + assert response = json_response(request, 200) + + request_2nd_page = + get( + conn, + "/api/v2/addresses/#{address.hash}/transactions", + %{"sort" => "block_number", "order" => "desc"} |> Map.merge(response["next_page_params"]) + ) + + assert response_2nd_page = json_response(request_2nd_page, 200) + + assert Enum.count(response["items"]) == 50 + assert response["next_page_params"] != nil + compare_item(Enum.at(transactions, 0), Enum.at(response["items"], 0)) + compare_item(Enum.at(transactions, 49), Enum.at(response["items"], 49)) + + assert Enum.count(response_2nd_page["items"]) == 1 + assert response_2nd_page["next_page_params"] == nil + compare_item(Enum.at(transactions, 50), Enum.at(response_2nd_page["items"], 0)) + + check_paginated_response(response, response_2nd_page, transactions |> Enum.reverse()) + end end describe "/addresses/{address_hash}/token-transfers" do diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index 4bcd5df3e6fc..93d2a62df6e5 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -1581,6 +1581,18 @@ defmodule Explorer.Chain.Transaction do end end + defp compare_custom_sorting([{block_order, :block_number}, {index_order, :index}]) do + fn a, b -> + case {Helper.compare(a.block_number, b.block_number), Helper.compare(a.index, b.index)} do + {:eq, :eq} -> compare_default_sorting(a, b) + {:eq, :gt} -> index_order == :desc + {:eq, :lt} -> index_order == :asc + {:gt, _} -> block_order == :desc + {:lt, _} -> block_order == :asc + end + end + end + defp compare_custom_sorting([{:dynamic, :fee, order, _dynamic_fee}]) do fn a, b -> nil_case = From 16faddc70b504d42305fc3e4dfcc6f5f6813d917 Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Mon, 28 Oct 2024 16:54:01 +0300 Subject: [PATCH 258/363] fix: divide by `10^decimals` when calculating token supply in CMC format (#11036) * fix: divide by `10^decimals` when calculating token supply in CMC format * chore: add test * fix: handle `decimals` when set to `nil` --- .../controllers/api/rpc/stats_controller.ex | 21 ++++++--- .../api/rpc/stats_controller_test.exs | 44 +++++++++++++++++++ 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex index 985be306e460..57782bc7a20b 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex @@ -14,7 +14,14 @@ defmodule BlockScoutWeb.API.RPC.StatsController do if Map.get(params, "cmc") == "true" do conn |> put_resp_content_type("text/plain") - |> send_resp(200, token.total_supply && to_cmc_total_supply(token.total_supply)) + |> send_resp( + 200, + token.total_supply && + to_cmc_total_supply( + token.total_supply, + token.decimals + ) + ) else conn |> render( @@ -89,11 +96,15 @@ defmodule BlockScoutWeb.API.RPC.StatsController do {:format, Chain.string_to_address_hash(address_hash_string)} end - @spec to_cmc_total_supply(Decimal.t()) :: String.t() - defp to_cmc_total_supply(total_supply) do + @spec to_cmc_total_supply(Decimal.t(), Decimal.t() | nil) :: String.t() + defp to_cmc_total_supply(total_supply, decimals) do + divider = + 1 + |> Decimal.new(1, Decimal.to_integer(decimals || Decimal.new(0))) + |> Decimal.to_integer() + total_supply - |> Wei.from(:wei) - |> Wei.to(:ether) + |> Decimal.div(divider) |> Decimal.round(@cmc_token_supply_precision) |> Decimal.to_string() end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs index 6312896f7d9d..c9b1cc6e6dae 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs @@ -101,6 +101,50 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do assert response == "110052089.716627912" end + + test "with custom decimals and cmc format", %{conn: conn} do + token = + insert(:token, + total_supply: 1_234_567_890, + decimals: 6 + ) + + params = %{ + "module" => "stats", + "action" => "tokensupply", + "contractaddress" => to_string(token.contract_address_hash), + "cmc" => "true" + } + + assert response = + conn + |> get("/api", params) + |> text_response(200) + + assert response == "1234.567890000" + end + end + + test "with null decimals and cmc format", %{conn: conn} do + token = + insert(:token, + total_supply: 1_234_567_890, + decimals: nil + ) + + params = %{ + "module" => "stats", + "action" => "tokensupply", + "contractaddress" => to_string(token.contract_address_hash), + "cmc" => "true" + } + + assert response = + conn + |> get("/api", params) + |> text_response(200) + + assert response == "1234567890.000000000" end describe "ethsupplyexchange" do From 97d8a2b2df7abc963ecfb186e6d78161b000610e Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 29 Oct 2024 10:25:03 +0200 Subject: [PATCH 259/363] fix: Fix scam badge value in some API endpoints (#11054) --- .../lib/block_scout_web/channels/address_channel.ex | 2 +- .../controllers/api/v2/address_controller.ex | 2 +- .../controllers/api/v2/block_controller.ex | 2 +- .../controllers/api/v2/main_page_controller.ex | 2 +- .../controllers/api/v2/transaction_controller.ex | 8 ++++---- .../transaction_interpretation.ex | 10 +++++----- .../block_scout_web/models/transaction_state_helper.ex | 2 +- apps/block_scout_web/lib/block_scout_web/notifier.ex | 2 +- .../lib/block_scout_web/views/api/v2/helper.ex | 4 ++++ apps/explorer/lib/explorer/chain/advanced_filter.ex | 4 ++-- .../lib/explorer/chain/internal_transaction.ex | 4 ++-- apps/explorer/lib/explorer/chain/token_transfer.ex | 8 ++++---- 12 files changed, 27 insertions(+), 23 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex index b22306431b24..ebc45d00c0c8 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex @@ -49,7 +49,7 @@ defmodule BlockScoutWeb.AddressChannel do end @transaction_associations [ - from_address: [:names, :smart_contract, :proxy_implementations], + from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], to_address: [ :scam_badge, :names, diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index 40b4b7c40eb4..b2a2319cd035 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -53,7 +53,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do necessity_by_association: %{ [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, :block => :optional } diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex index e889cdf59904..28eeeea3168c 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex @@ -85,7 +85,7 @@ defmodule BlockScoutWeb.API.V2.BlockController do necessity_by_association: %{ [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, :block => :optional } diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex index ec99ba4422c6..cb2c41f00101 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex @@ -25,7 +25,7 @@ defmodule BlockScoutWeb.API.V2.MainPageController do %{ :block => :required, [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional } |> Map.merge(@chain_type_transaction_necessity_by_association), diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex index 54f9991ad387..1b92aa8514df 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex @@ -78,7 +78,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do :proxy_implementations ] ] => :optional, - [from_address: [:names, :smart_contract, :proxy_implementations]] => + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, [ to_address: [ @@ -573,7 +573,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do options = [ necessity_by_association: %{ - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional } ] @@ -600,8 +600,8 @@ defmodule BlockScoutWeb.API.V2.TransactionController do options = [ necessity_by_association: %{ - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional } ] |> Keyword.merge(@api_true) diff --git a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex index 0fbedf613abf..17d732bc642e 100644 --- a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex +++ b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex @@ -109,7 +109,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do Chain.select_repo(@api_true).preload(transaction, [ :block, to_address: [:scam_badge, :names, :smart_contract], - from_address: [:names, :smart_contract], + from_address: [:scam_badge, :names, :smart_contract], created_contract_address: [:scam_badge, :names, :token, :smart_contract] ]) @@ -176,8 +176,8 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do full_options = [ necessity_by_association: %{ - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional } ] |> Keyword.merge(@api_true) @@ -254,8 +254,8 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do token_transfer_options = [ necessity_by_association: %{ - [from_address: [:names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, :token => :optional } ] diff --git a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex index 7959c4b02ed9..4f1a6a28041d 100644 --- a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex @@ -77,7 +77,7 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] ], block: [miner: [:names, :smart_contract, :proxy_implementations]], - from_address: [:names, :smart_contract, :proxy_implementations], + from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] ) diff --git a/apps/block_scout_web/lib/block_scout_web/notifier.ex b/apps/block_scout_web/lib/block_scout_web/notifier.ex index d630a31167dd..8c353b68ef6f 100644 --- a/apps/block_scout_web/lib/block_scout_web/notifier.ex +++ b/apps/block_scout_web/lib/block_scout_web/notifier.ex @@ -206,7 +206,7 @@ defmodule BlockScoutWeb.Notifier do base_preloads = [ :block, created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - from_address: [:names, :smart_contract, :proxy_implementations], + from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] ] diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex index d0ffdc96085a..935fdc087dce 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex @@ -159,6 +159,10 @@ defmodule BlockScoutWeb.API.V2.Helper do def address_name(_), do: nil + def address_marked_as_scam?(%Address{scam_badge: %Ecto.Association.NotLoaded{}}) do + false + end + def address_marked_as_scam?(%Address{scam_badge: scam_badge}) when not is_nil(scam_badge) do true end diff --git a/apps/explorer/lib/explorer/chain/advanced_filter.ex b/apps/explorer/lib/explorer/chain/advanced_filter.ex index 1684670b3ac1..62cdc1657bb6 100644 --- a/apps/explorer/lib/explorer/chain/advanced_filter.ex +++ b/apps/explorer/lib/explorer/chain/advanced_filter.ex @@ -115,8 +115,8 @@ defmodule Explorer.Chain.AdvancedFilter do |> Enum.sort(&sort_function/2) |> take_page_size(paging_options) |> Chain.select_repo(options).preload( - from_address: [:names, :smart_contract, :proxy_implementations], - to_address: [:names, :smart_contract, :proxy_implementations], + from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], + to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], created_contract_address: [:names, :smart_contract, :proxy_implementations] ) end diff --git a/apps/explorer/lib/explorer/chain/internal_transaction.ex b/apps/explorer/lib/explorer/chain/internal_transaction.ex index 3d62885d34dd..5d6b4524707a 100644 --- a/apps/explorer/lib/explorer/chain/internal_transaction.ex +++ b/apps/explorer/lib/explorer/chain/internal_transaction.ex @@ -832,8 +832,8 @@ defmodule Explorer.Chain.InternalTransaction do preloads = DenormalizationHelper.extend_transaction_preload([ :block, - [from_address: [:names, :smart_contract, :proxy_implementations]], - [to_address: [:names, :smart_contract, :proxy_implementations]] + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]], + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] ]) __MODULE__ diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index 1b9e3ac0ac91..674efce8ffe3 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -239,7 +239,7 @@ defmodule Explorer.Chain.TokenTransfer do DenormalizationHelper.extend_transaction_preload([ :transaction, :token, - [from_address: [:names, :smart_contract, :proxy_implementations]], + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]], [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] ]) @@ -266,7 +266,7 @@ defmodule Explorer.Chain.TokenTransfer do DenormalizationHelper.extend_transaction_preload([ :transaction, :token, - [from_address: [:names, :smart_contract, :proxy_implementations]], + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]], [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] ]) @@ -299,8 +299,8 @@ defmodule Explorer.Chain.TokenTransfer do DenormalizationHelper.extend_transaction_preload([ :transaction, :token, - [from_address: [:names, :smart_contract, :proxy_implementations]], - [to_address: [:names, :smart_contract, :proxy_implementations]] + [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]], + [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] ]) only_consensus_transfers_query() From daa13a8ca76ea9169e9abb2c607cf5a9a3eca59e Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 29 Oct 2024 11:46:09 +0200 Subject: [PATCH 260/363] Remove internal transactions endpoint because no suitable DB index exists && update CHANGELOG --- CHANGELOG.md | 5 ++++- .../lib/block_scout_web/routers/api_router.ex | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68d37bba86cb..e396fd557fe8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### 🚀 Features -- List of internal transactions API v2 endpoint ([#10994](https://github.com/blockscout/blockscout/issues/10994)) +- Address transactions block number sorting ([#11035](https://github.com/blockscout/blockscout/issues/11035)) - Scroll rollup: L1 fee parameters in API, `queueIndex` for L2 transactions, and L1 <->L2 messages ([#10484](https://github.com/blockscout/blockscout/issues/10484)) - Account V2 ([#10706](https://github.com/blockscout/blockscout/issues/10706)) - Allow to provide DB schema other than public ([#10946](https://github.com/blockscout/blockscout/issues/10946)) @@ -26,6 +26,9 @@ ### 🐛 Bug Fixes +- Fix scam badge value in some API endpoints ([#11054](https://github.com/blockscout/blockscout/issues/11054)) +- Divide by `10^decimals` when calculating token supply in CMC format ([#11036](https://github.com/blockscout/blockscout/issues/11036)) +- Rename zksync l1/l2 _tx_count columns ([#11051](https://github.com/blockscout/blockscout/issues/11051)) - Bugs introduced in calldata decoding optimizations ([#11025](https://github.com/blockscout/blockscout/issues/11025)) - Handle stalled async task in MapCache ([#11015](https://github.com/blockscout/blockscout/issues/11015)) - Add tx_count, tx_types props in the response of address API v2 endpoints for compatibility with current version of the frontend ([#11012](https://github.com/blockscout/blockscout/issues/11012)) diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index e56e8c002e4e..533131077c63 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -168,9 +168,10 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/", V2.TokenTransferController, :token_transfers) end - scope "/internal-transactions" do - get("/", V2.InternalTransactionController, :internal_transactions) - end + # todo: enable this endpoint when DB index for underlying DB query will be installed. + # scope "/internal-transactions" do + # get("/", V2.InternalTransactionController, :internal_transactions) + # end scope "/blocks" do get("/", V2.BlockController, :blocks) From ab7eddd3d0b4914e4d5eca952b8e4137f8188d33 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 29 Oct 2024 12:32:00 +0200 Subject: [PATCH 261/363] Comment test for disabled functionality --- .../internal_transaction_controller_test.exs | 175 +++++++++--------- 1 file changed, 88 insertions(+), 87 deletions(-) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/internal_transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/internal_transaction_controller_test.exs index 2f9dad655803..cee0215b1919 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/internal_transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/internal_transaction_controller_test.exs @@ -3,91 +3,92 @@ defmodule BlockScoutWeb.API.V2.InternalTransactionControllerTest do alias Explorer.Chain.{Address, InternalTransaction} - describe "/internal-transactions" do - test "empty list", %{conn: conn} do - request = get(conn, "/api/v2/internal-transactions") - - assert response = json_response(request, 200) - assert response["items"] == [] - assert response["next_page_params"] == nil - end - - test "non empty list", %{conn: conn} do - tx = - :transaction - |> insert() - |> with_block() - - insert(:internal_transaction, - transaction: tx, - block_hash: tx.block_hash, - index: 0, - block_index: 0 - ) - - request = get(conn, "/api/v2/internal-transactions") - - assert response = json_response(request, 200) - assert Enum.count(response["items"]) == 1 - assert response["next_page_params"] == nil - end - - test "internal transactions with next_page_params", %{conn: conn} do - transaction = insert(:transaction) |> with_block() - - internal_transaction = - insert(:internal_transaction, - transaction: transaction, - transaction_index: 0, - block_number: transaction.block_number, - block_hash: transaction.block_hash, - index: 0, - block_index: 0 - ) - - transaction_2 = insert(:transaction) |> with_block() - - internal_transactions = - for i <- 0..49 do - insert(:internal_transaction, - transaction: transaction_2, - transaction_index: 0, - block_number: transaction_2.block_number, - block_hash: transaction_2.block_hash, - index: i, - block_index: i - ) - end - - internal_transactions = [internal_transaction | internal_transactions] - - request = get(conn, "/api/v2/internal-transactions") - assert response = json_response(request, 200) - - request_2nd_page = get(conn, "/api/v2/internal-transactions", response["next_page_params"]) - assert response_2nd_page = json_response(request_2nd_page, 200) - - check_paginated_response(response, response_2nd_page, internal_transactions) - end - end - - defp compare_item(%InternalTransaction{} = internal_transaction, json) do - assert Address.checksum(internal_transaction.from_address_hash) == json["from"]["hash"] - assert Address.checksum(internal_transaction.to_address_hash) == json["to"]["hash"] - assert to_string(internal_transaction.transaction_hash) == json["transaction_hash"] - assert internal_transaction.block_number == json["block_number"] - assert internal_transaction.block_index == json["block_index"] - end - - defp check_paginated_response(first_page_resp, second_page_resp, internal_transactions) do - assert Enum.count(first_page_resp["items"]) == 50 - assert first_page_resp["next_page_params"] != nil - compare_item(Enum.at(internal_transactions, 50), Enum.at(first_page_resp["items"], 0)) - - compare_item(Enum.at(internal_transactions, 1), Enum.at(first_page_resp["items"], 49)) - - assert Enum.count(second_page_resp["items"]) == 1 - assert second_page_resp["next_page_params"] == nil - compare_item(Enum.at(internal_transactions, 0), Enum.at(second_page_resp["items"], 0)) - end + # todo: enable when /internal-transactions API endpoint will be enabled + # describe "/internal-transactions" do + # test "empty list", %{conn: conn} do + # request = get(conn, "/api/v2/internal-transactions") + + # assert response = json_response(request, 200) + # assert response["items"] == [] + # assert response["next_page_params"] == nil + # end + + # test "non empty list", %{conn: conn} do + # tx = + # :transaction + # |> insert() + # |> with_block() + + # insert(:internal_transaction, + # transaction: tx, + # block_hash: tx.block_hash, + # index: 0, + # block_index: 0 + # ) + + # request = get(conn, "/api/v2/internal-transactions") + + # assert response = json_response(request, 200) + # assert Enum.count(response["items"]) == 1 + # assert response["next_page_params"] == nil + # end + + # test "internal transactions with next_page_params", %{conn: conn} do + # transaction = insert(:transaction) |> with_block() + + # internal_transaction = + # insert(:internal_transaction, + # transaction: transaction, + # transaction_index: 0, + # block_number: transaction.block_number, + # block_hash: transaction.block_hash, + # index: 0, + # block_index: 0 + # ) + + # transaction_2 = insert(:transaction) |> with_block() + + # internal_transactions = + # for i <- 0..49 do + # insert(:internal_transaction, + # transaction: transaction_2, + # transaction_index: 0, + # block_number: transaction_2.block_number, + # block_hash: transaction_2.block_hash, + # index: i, + # block_index: i + # ) + # end + + # internal_transactions = [internal_transaction | internal_transactions] + + # request = get(conn, "/api/v2/internal-transactions") + # assert response = json_response(request, 200) + + # request_2nd_page = get(conn, "/api/v2/internal-transactions", response["next_page_params"]) + # assert response_2nd_page = json_response(request_2nd_page, 200) + + # check_paginated_response(response, response_2nd_page, internal_transactions) + # end + # end + + # defp compare_item(%InternalTransaction{} = internal_transaction, json) do + # assert Address.checksum(internal_transaction.from_address_hash) == json["from"]["hash"] + # assert Address.checksum(internal_transaction.to_address_hash) == json["to"]["hash"] + # assert to_string(internal_transaction.transaction_hash) == json["transaction_hash"] + # assert internal_transaction.block_number == json["block_number"] + # assert internal_transaction.block_index == json["block_index"] + # end + + # defp check_paginated_response(first_page_resp, second_page_resp, internal_transactions) do + # assert Enum.count(first_page_resp["items"]) == 50 + # assert first_page_resp["next_page_params"] != nil + # compare_item(Enum.at(internal_transactions, 50), Enum.at(first_page_resp["items"], 0)) + + # compare_item(Enum.at(internal_transactions, 1), Enum.at(first_page_resp["items"], 49)) + + # assert Enum.count(second_page_resp["items"]) == 1 + # assert second_page_resp["next_page_params"] == nil + # compare_item(Enum.at(internal_transactions, 0), Enum.at(second_page_resp["items"], 0)) + # end end From 9b943cc9ddbf35224b4a75b676f256220ab7c627 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:25:17 +0400 Subject: [PATCH 262/363] fix: Indexer memory limit for api instance (#11066) --- apps/indexer/lib/indexer/memory/monitor.ex | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/indexer/lib/indexer/memory/monitor.ex b/apps/indexer/lib/indexer/memory/monitor.ex index c4032f7632a2..225428af1196 100644 --- a/apps/indexer/lib/indexer/memory/monitor.ex +++ b/apps/indexer/lib/indexer/memory/monitor.ex @@ -47,10 +47,16 @@ defmodule Indexer.Memory.Monitor do @impl GenServer def init(options) when is_map(options) do - state = struct!(__MODULE__, Map.put_new(options, :limit, define_memory_limit())) - {:ok, timer_reference} = :timer.send_interval(state.timer_interval, :check) + case Application.get_env(:explorer, :mode) do + :api -> + :ignore - {:ok, %__MODULE__{state | timer_reference: timer_reference}} + _other_mode -> + state = struct!(__MODULE__, Map.put_new(options, :limit, define_memory_limit())) + {:ok, timer_reference} = :timer.send_interval(state.timer_interval, :check) + + {:ok, %__MODULE__{state | timer_reference: timer_reference}} + end end @impl GenServer From 1a903c869786d56731198e27f63ebada8704943d Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Tue, 29 Oct 2024 17:09:31 +0300 Subject: [PATCH 263/363] fix: celo collated gas price issue (#11067) * refactor: module path for polygon migration * fix: modify constraint --- ...4_modify_collated_gas_price_constraint.exs | 27 +++++++++++++++++++ ...3_modify_collated_gas_price_constraint.exs | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 apps/explorer/priv/celo/migrations/20241029131554_modify_collated_gas_price_constraint.exs diff --git a/apps/explorer/priv/celo/migrations/20241029131554_modify_collated_gas_price_constraint.exs b/apps/explorer/priv/celo/migrations/20241029131554_modify_collated_gas_price_constraint.exs new file mode 100644 index 000000000000..3162375fd4d7 --- /dev/null +++ b/apps/explorer/priv/celo/migrations/20241029131554_modify_collated_gas_price_constraint.exs @@ -0,0 +1,27 @@ +defmodule Explorer.Repo.Celo.Migrations.ModifyCollatedGasPriceConstraint do + use Ecto.Migration + + def up do + execute("ALTER TABLE transactions DROP CONSTRAINT collated_gas_price") + + create( + constraint( + :transactions, + :collated_gas_price, + check: "block_hash IS NULL OR gas_price IS NOT NULL OR max_fee_per_gas IS NOT NULL" + ) + ) + end + + def down do + execute("ALTER TABLE transactions DROP CONSTRAINT collated_gas_price") + + create( + constraint( + :transactions, + :collated_gas_price, + check: "block_hash IS NULL OR gas_price IS NOT NULL" + ) + ) + end +end diff --git a/apps/explorer/priv/polygon_edge/migrations/20230731130103_modify_collated_gas_price_constraint.exs b/apps/explorer/priv/polygon_edge/migrations/20230731130103_modify_collated_gas_price_constraint.exs index 559799e0dee2..9a532bdd5c5b 100644 --- a/apps/explorer/priv/polygon_edge/migrations/20230731130103_modify_collated_gas_price_constraint.exs +++ b/apps/explorer/priv/polygon_edge/migrations/20230731130103_modify_collated_gas_price_constraint.exs @@ -1,4 +1,4 @@ -defmodule Explorer.Repo.Migrations.ModifyCollatedGasPriceConstraint do +defmodule Explorer.Repo.PolygonEdge.Migrations.ModifyCollatedGasPriceConstraint do use Ecto.Migration def change do From 92a814d22bf6366f3baf84eed5ec9b0ebb95f492 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 29 Oct 2024 17:32:14 +0200 Subject: [PATCH 264/363] Add shrink int txs docker image for pre release workflows at Arbitrum, CELO (release also), Op, ZkSync --- .github/workflows/pre-release-arbitrum.yml | 72 +++++++++++++++++- .github/workflows/pre-release-celo.yml | 76 ++++++++++++++++++- .github/workflows/pre-release-optimism.yml | 72 +++++++++++++++++- .github/workflows/pre-release-zksync.yml | 72 +++++++++++++++++- .../publish-docker-image-for-eth.yml | 6 +- .github/workflows/release-celo.yml | 76 ++++++++++++++++++- 6 files changed, 366 insertions(+), 8 deletions(-) diff --git a/.github/workflows/pre-release-arbitrum.yml b/.github/workflows/pre-release-arbitrum.yml index 3222f6f1baf3..4d71a2da3469 100644 --- a/.github/workflows/pre-release-arbitrum.yml +++ b/.github/workflows/pre-release-arbitrum.yml @@ -94,4 +94,74 @@ jobs: ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=arbitrum \ No newline at end of file + CHAIN_TYPE=arbitrum + + - name: Build and push Docker image for Arbitrum (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-arbitrum:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=arbitrum + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Arbitrum (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-arbitrum:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=arbitrum + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Arbitrum (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-arbitrum:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=arbitrum + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/.github/workflows/pre-release-celo.yml b/.github/workflows/pre-release-celo.yml index f9f79545c96b..d6dc26e5c876 100644 --- a/.github/workflows/pre-release-celo.yml +++ b/.github/workflows/pre-release-celo.yml @@ -17,6 +17,7 @@ jobs: runs-on: ubuntu-latest env: RELEASE_VERSION: 6.9.0 + API_GRAPHQL_MAX_COMPLEXITY: 10400 steps: - uses: actions/checkout@v4 - name: Setup repo @@ -94,4 +95,77 @@ jobs: ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=celo \ No newline at end of file + CHAIN_TYPE=celo + + - name: Build and push Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-celo:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + API_GRAPHQL_MAX_COMPLEXITY=${{ env.API_GRAPHQL_MAX_COMPLEXITY }} + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=celo + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-celo:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + API_GRAPHQL_MAX_COMPLEXITY=${{ env.API_GRAPHQL_MAX_COMPLEXITY }} + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=celo + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-celo:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + API_GRAPHQL_MAX_COMPLEXITY=${{ env.API_GRAPHQL_MAX_COMPLEXITY }} + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=celo + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/.github/workflows/pre-release-optimism.yml b/.github/workflows/pre-release-optimism.yml index 2257ecf61004..44d83c232b1f 100644 --- a/.github/workflows/pre-release-optimism.yml +++ b/.github/workflows/pre-release-optimism.yml @@ -94,4 +94,74 @@ jobs: ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=optimism \ No newline at end of file + CHAIN_TYPE=optimism + + - name: Build and push Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-optimism:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=optimism + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-optimism:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=optimism + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-optimism:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=optimism + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/.github/workflows/pre-release-zksync.yml b/.github/workflows/pre-release-zksync.yml index 5d4f1ef65be8..4d4c9c660fbf 100644 --- a/.github/workflows/pre-release-zksync.yml +++ b/.github/workflows/pre-release-zksync.yml @@ -94,4 +94,74 @@ jobs: ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=zksync \ No newline at end of file + CHAIN_TYPE=zksync + + - name: Build and push Docker image for ZkSync (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zksync:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zksync + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for ZkSync (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zksync:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zksync + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for ZkSync (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zksync:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zksync + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/.github/workflows/publish-docker-image-for-eth.yml b/.github/workflows/publish-docker-image-for-eth.yml index 825157591143..491e063c2e28 100644 --- a/.github/workflows/publish-docker-image-for-eth.yml +++ b/.github/workflows/publish-docker-image-for-eth.yml @@ -30,7 +30,7 @@ jobs: context: . file: ./docker/Dockerfile push: true - tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-experimental + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }} labels: ${{ steps.setup.outputs.docker-labels }} platforms: | linux/amd64 @@ -53,7 +53,7 @@ jobs: context: . file: ./docker/Dockerfile push: true - tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-experimental-indexer + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-indexer labels: ${{ steps.setup.outputs.docker-labels }} platforms: | linux/amd64 @@ -75,7 +75,7 @@ jobs: context: . file: ./docker/Dockerfile push: true - tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-experimental-api + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-api labels: ${{ steps.setup.outputs.docker-labels }} platforms: | linux/amd64 diff --git a/.github/workflows/release-celo.yml b/.github/workflows/release-celo.yml index 32a8acb49b36..d87d70006c92 100644 --- a/.github/workflows/release-celo.yml +++ b/.github/workflows/release-celo.yml @@ -14,6 +14,7 @@ jobs: runs-on: ubuntu-latest env: RELEASE_VERSION: 6.9.0 + API_GRAPHQL_MAX_COMPLEXITY: 10400 steps: - uses: actions/checkout@v4 - name: Setup repo @@ -91,4 +92,77 @@ jobs: ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=celo \ No newline at end of file + CHAIN_TYPE=celo + + - name: Build and push Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-celo:${{ env.RELEASE_VERSION }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + API_GRAPHQL_MAX_COMPLEXITY=${{ env.API_GRAPHQL_MAX_COMPLEXITY }} + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=celo + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-celo:${{ env.RELEASE_VERSION }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + API_GRAPHQL_MAX_COMPLEXITY=${{ env.API_GRAPHQL_MAX_COMPLEXITY }} + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=celo + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-celo:${{ env.RELEASE_VERSION }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + API_GRAPHQL_MAX_COMPLEXITY=${{ env.API_GRAPHQL_MAX_COMPLEXITY }} + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=celo + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file From 75519fee6355d24bdc6d3897fd27f7c95c515907 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:55:04 +0300 Subject: [PATCH 265/363] feat: Support zksync foundry verification (#11037) * feat: Support zksync foundry verification * Fix constructor args * Fix credo * Process review comments --- .../api/rpc/contract_controller.ex | 4 ++ .../api/v2/verification_controller.ex | 14 +----- apps/explorer/lib/explorer/chain.ex | 21 ++++++++ .../lib/explorer/chain/smart_contract.ex | 50 +++++++++++++++++++ .../lib/explorer/smart_contract/helper.ex | 16 +++++- .../smart_contract/solidity/publisher.ex | 9 ++++ .../smart_contract/solidity/verifier.ex | 7 +-- cspell.json | 1 + 8 files changed, 105 insertions(+), 17 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex index 4392d51b9e50..a2629b1344d6 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex @@ -631,6 +631,10 @@ defmodule BlockScoutWeb.API.RPC.ContractController do |> required_param(params, "compilerversion", "compiler_version") |> optional_param(params, "constructorArguments", "constructor_arguments") |> optional_param(params, "licenseType", "license_type") + |> (&if(Application.get_env(:explorer, :chain_type) == :zksync, + do: optional_param(&1, params, "zksolcVersion", "zk_compiler_version"), + else: &1 + )).() end defp fetch_verifysourcecode_solidity_single_file_params(params) do diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex index 94cfb3b6c202..3176bde5a8ee 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex @@ -122,21 +122,13 @@ defmodule BlockScoutWeb.API.V2.VerificationController do Logger.info("API v2 smart-contract #{address_hash_string} verification via standard json input") with {:json_input, json_input} <- validate_params_standard_json_input(params) do - constructor_arguments = - if Application.get_env(:explorer, :chain_type) == :zksync do - zksync_get_constructor_arguments(address_hash_string) - else - Map.get(params, "constructor_args", "") - end - verification_params = %{ "address_hash" => String.downcase(address_hash_string), "compiler_version" => compiler_version } |> Map.put("autodetect_constructor_args", Map.get(params, "autodetect_constructor_args", true)) - # - |> Map.put("constructor_arguments", constructor_arguments) + |> Map.put("constructor_arguments", Map.get(params, "constructor_args", "")) |> Map.put("name", Map.get(params, "contract_name", "")) |> Map.put("license_type", Map.get(params, "license_type")) |> (&if(Application.get_env(:explorer, :chain_type) == :zksync, @@ -320,10 +312,6 @@ defmodule BlockScoutWeb.API.V2.VerificationController do end end - defp zksync_get_constructor_arguments(address_hash_string) do - Chain.contract_creation_input_data(address_hash_string) - end - # sobelow_skip ["Traversal.FileModule"] defp validate_params_standard_json_input(%{"files" => files} = params) do with :validated <- validate_address(params), diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 04446a6e44ec..52b0b788a123 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -3200,6 +3200,27 @@ defmodule Explorer.Chain do end end + @doc """ + Fetches contract creation input data from the transaction (not internal transaction). + """ + @spec contract_creation_input_data_from_transaction(String.t()) :: nil | binary() + def contract_creation_input_data_from_transaction(address_hash, options \\ []) do + transaction = + Transaction + |> where([transaction], transaction.created_contract_address_hash == ^address_hash) + |> select_repo(options).one() + + if transaction && transaction.input do + case Data.dump(transaction.input) do + {:ok, bytes} -> + bytes + + _ -> + nil + end + end + end + @doc """ Fetches contract creation input data. """ diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index 2da936337bfa..6cfe68473fe1 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -95,6 +95,7 @@ defmodule Explorer.Chain.SmartContract do use Explorer.Schema + alias ABI.FunctionSelector alias Ecto.{Changeset, Multi} alias Explorer.{Chain, Repo, SortingHelper} @@ -137,6 +138,31 @@ defmodule Explorer.Chain.SmartContract do ~w()a end) + @create_zksync_abi [ + %{ + "inputs" => [ + %{"internalType" => "bytes32", "name" => "_salt", "type" => "bytes32"}, + %{"internalType" => "bytes32", "name" => "_bytecodeHash", "type" => "bytes32"}, + %{"internalType" => "bytes", "name" => "_input", "type" => "bytes"} + ], + "name" => "create2", + "outputs" => [%{"internalType" => "address", "name" => "", "type" => "address"}], + "stateMutability" => "payable", + "type" => "function" + }, + %{ + "inputs" => [ + %{"internalType" => "bytes32", "name" => "_salt", "type" => "bytes32"}, + %{"internalType" => "bytes32", "name" => "_bytecodeHash", "type" => "bytes32"}, + %{"internalType" => "bytes", "name" => "_input", "type" => "bytes"} + ], + "name" => "create", + "outputs" => [%{"internalType" => "address", "name" => "", "type" => "address"}], + "stateMutability" => "payable", + "type" => "function" + } + ] + @doc """ Returns burn address hash """ @@ -1310,4 +1336,28 @@ defmodule Explorer.Chain.SmartContract do end defp filter_contracts(basic_query, _), do: basic_query + + @doc """ + Retrieves the constructor arguments for a zkSync smart contract. + Using @create_zksync_abi function decodes transaction input of contract creation + + ## Parameters + - `binary()`: The binary data representing the smart contract. + + ## Returns + - `nil`: If the constructor arguments cannot be retrieved. + - `binary()`: The constructor arguments in binary format. + """ + @spec zksync_get_constructor_arguments(binary()) :: nil | binary() + def zksync_get_constructor_arguments(address_hash_string) do + creation_input = Chain.contract_creation_input_data_from_transaction(address_hash_string) + + case @create_zksync_abi |> ABI.parse_specification() |> ABI.find_and_decode(creation_input) do + {%FunctionSelector{}, [_, _, constructor_args]} -> + Base.encode16(constructor_args, case: :lower) + + _ -> + nil + end + end end diff --git a/apps/explorer/lib/explorer/smart_contract/helper.ex b/apps/explorer/lib/explorer/smart_contract/helper.ex index 765c04d6be5c..3196373ad155 100644 --- a/apps/explorer/lib/explorer/smart_contract/helper.ex +++ b/apps/explorer/lib/explorer/smart_contract/helper.ex @@ -112,9 +112,23 @@ defmodule Explorer.SmartContract.Helper do end end + @doc """ + Prepares the bytecode for a microservice by processing the given body, creation input, and deployed bytecode. + + ## Parameters + + - body: The body of the request or data to be processed. + - creation_input: The input data used during the creation of the smart contract. + - deployed_bytecode: The bytecode of the deployed smart contract. + + ## Returns + + The processed bytecode ready for the microservice. + """ + @spec prepare_bytecode_for_microservice(map(), binary() | nil, binary() | nil) :: map() def prepare_bytecode_for_microservice(body, creation_input, deployed_bytecode) - def prepare_bytecode_for_microservice(body, empty, deployed_bytecode) when is_nil(empty) do + def prepare_bytecode_for_microservice(body, creation_input, deployed_bytecode) when is_nil(creation_input) do if Application.get_env(:explorer, :chain_type) == :zksync do body |> Map.put("code", deployed_bytecode) diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex b/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex index c59f46bb6de5..cf24dcae9af3 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex @@ -72,6 +72,7 @@ defmodule Explorer.SmartContract.Solidity.Publisher do def publish_with_standard_json_input(%{"address_hash" => address_hash} = params, json_input) do Logger.info(@sc_verification_via_standard_json_input_started) + params = maybe_add_zksync_specific_data(params) case Verifier.evaluate_authenticity_via_standard_json_input(address_hash, params, json_input) do {:ok, @@ -452,4 +453,12 @@ defmodule Explorer.SmartContract.Solidity.Publisher do Map.put(params, "external_libraries", clean_external_libraries) end + + defp maybe_add_zksync_specific_data(params) do + if Application.get_env(:explorer, :chain_type) == :zksync do + Map.put(params, "constructor_arguments", SmartContract.zksync_get_constructor_arguments(params["address_hash"])) + else + params + end + end end diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex b/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex index afc322dca0ca..6777ef07c703 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex @@ -129,17 +129,18 @@ defmodule Explorer.SmartContract.Solidity.Verifier do def evaluate_authenticity_via_standard_json_input_inner(true, address_hash, params, json_input) do {creation_transaction_input, deployed_bytecode, verifier_metadata} = fetch_data_for_verification(address_hash) - compiler_version_map = + verification_params = if Application.get_env(:explorer, :chain_type) == :zksync do %{ "solcCompiler" => params["compiler_version"], - "zkCompiler" => params["zk_compiler_version"] + "zkCompiler" => params["zk_compiler_version"], + "constructorArguments" => params["constructor_arguments"] } else %{"compilerVersion" => params["compiler_version"]} end - compiler_version_map + verification_params |> prepare_bytecode_for_microservice(creation_transaction_input, deployed_bytecode) |> Map.put("input", json_input) |> (&if(Application.get_env(:explorer, :chain_type) == :zksync, diff --git a/cspell.json b/cspell.json index 11c477faf352..eb1d71f81c05 100644 --- a/cspell.json +++ b/cspell.json @@ -641,6 +641,7 @@ "zkatana", "zkbob", "zkevm", + "zksolc", "zksync" ], "enableFiletypes": [ From 608f319765b2707d2bfdadb648cc1fb8dfce650b Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Wed, 30 Oct 2024 11:04:27 +0200 Subject: [PATCH 266/363] Add pre-release docker image generation for Polygon ZkEVM --- .github/workflows/pre-release-celo.yml | 6 +- .github/workflows/pre-release-optimism.yml | 6 +- .../workflows/pre-release-polygon-zkevm.yml | 167 ++++++++++++++++++ .github/workflows/pre-release-scroll.yml | 6 +- .github/workflows/pre-release.yml | 90 ++++++++++ .../publish-docker-image-for-zkevm.yml | 118 ++++++++++++- .github/workflows/release-polygon-zkevm.yml | 72 +++++++- 7 files changed, 453 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/pre-release-polygon-zkevm.yml diff --git a/.github/workflows/pre-release-celo.yml b/.github/workflows/pre-release-celo.yml index d6dc26e5c876..2ee3acae19d7 100644 --- a/.github/workflows/pre-release-celo.yml +++ b/.github/workflows/pre-release-celo.yml @@ -97,7 +97,7 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=celo - - name: Build and push Docker image (indexer + API + shrink internal transactions) + - name: Build and push Docker image for CELO (indexer + API + shrink internal transactions) uses: docker/build-push-action@v5 with: context: . @@ -122,7 +122,7 @@ jobs: CHAIN_TYPE=celo SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - - name: Build and push Docker image (indexer + shrink internal transactions) + - name: Build and push Docker image for CELO (indexer + shrink internal transactions) uses: docker/build-push-action@v5 with: context: . @@ -146,7 +146,7 @@ jobs: CHAIN_TYPE=celo SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - - name: Build and push Docker image (API + shrink internal transactions) + - name: Build and push Docker image for CELO (API + shrink internal transactions) uses: docker/build-push-action@v5 with: context: . diff --git a/.github/workflows/pre-release-optimism.yml b/.github/workflows/pre-release-optimism.yml index 44d83c232b1f..e4e7e20ea165 100644 --- a/.github/workflows/pre-release-optimism.yml +++ b/.github/workflows/pre-release-optimism.yml @@ -96,7 +96,7 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=optimism - - name: Build and push Docker image (indexer + API + shrink internal transactions) + - name: Build and push Docker image for Optimism (indexer + API + shrink internal transactions) uses: docker/build-push-action@v5 with: context: . @@ -120,7 +120,7 @@ jobs: CHAIN_TYPE=optimism SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - - name: Build and push Docker image (indexer + shrink internal transactions) + - name: Build and push Docker image for Optimism (indexer + shrink internal transactions) uses: docker/build-push-action@v5 with: context: . @@ -143,7 +143,7 @@ jobs: CHAIN_TYPE=optimism SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - - name: Build and push Docker image (API + shrink internal transactions) + - name: Build and push Docker image for Optimism (API + shrink internal transactions) uses: docker/build-push-action@v5 with: context: . diff --git a/.github/workflows/pre-release-polygon-zkevm.yml b/.github/workflows/pre-release-polygon-zkevm.yml new file mode 100644 index 000000000000..0b8b51db1de5 --- /dev/null +++ b/.github/workflows/pre-release-polygon-zkevm.yml @@ -0,0 +1,167 @@ +name: Pre-release for Polygon ZkEVM + +on: + workflow_dispatch: + inputs: + number: + type: number + required: true + +env: + OTP_VERSION: ${{ vars.OTP_VERSION }} + ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: 6.9.0 + steps: + - uses: actions/checkout@v4 + - name: Setup repo + uses: ./.github/actions/setup-repo + id: setup + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + docker-remote-multi-platform: true + docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} + docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} + + - name: Build and push Docker image for Polygon ZkEVM (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zkevm:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=polygon_zkevm + + - name: Build and push Docker image for Polygon ZkEVM (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zkevm:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=polygon_zkevm + + - name: Build and push Docker image for Polygon ZkEVM (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zkevm:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=polygon_zkevm + + - name: Build and push Docker image for Polygon ZkEVM (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zkevm:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=polygon_zkevm + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Polygon ZkEVM (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zkevm:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=polygon_zkevm + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Polygon ZkEVM (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zkevm:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=polygon_zkevm + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/.github/workflows/pre-release-scroll.yml b/.github/workflows/pre-release-scroll.yml index 90209003e759..e5143d843eda 100644 --- a/.github/workflows/pre-release-scroll.yml +++ b/.github/workflows/pre-release-scroll.yml @@ -96,7 +96,7 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=scroll - - name: Build and push Docker image (indexer + API + shrink internal transactions) + - name: Build and push Docker image for Scroll (indexer + API + shrink internal transactions) uses: docker/build-push-action@v5 with: context: . @@ -120,7 +120,7 @@ jobs: CHAIN_TYPE=scroll SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - - name: Build and push Docker image (indexer + shrink internal transactions) + - name: Build and push Docker image for Scroll (indexer + shrink internal transactions) uses: docker/build-push-action@v5 with: context: . @@ -143,7 +143,7 @@ jobs: CHAIN_TYPE=scroll SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - - name: Build and push Docker image (API + shrink internal transactions) + - name: Build and push Docker image for Scroll (API + shrink internal transactions) uses: docker/build-push-action@v5 with: context: . diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index 8b48847a6f81..f72a0ab5311b 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -115,3 +115,93 @@ jobs: AMPLITUDE_API_KEY= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} + + - name: Build & Push Core Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + cache-from: type=registry,ref=blockscout/blockscout:buildcache + cache-to: type=registry,ref=blockscout/blockscout:buildcache,mode=max + tags: blockscout/blockscout:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + DECODE_NOT_A_CONTRACT_CALLS=false + MIXPANEL_URL= + MIXPANEL_TOKEN= + AMPLITUDE_URL= + AMPLITUDE_API_KEY= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build & Push Core Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + cache-from: type=registry,ref=blockscout/blockscout:buildcache + cache-to: type=registry,ref=blockscout/blockscout:buildcache,mode=max + tags: blockscout/blockscout:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + DECODE_NOT_A_CONTRACT_CALLS=false + MIXPANEL_URL= + MIXPANEL_TOKEN= + AMPLITUDE_URL= + AMPLITUDE_API_KEY= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build & Push Core Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + cache-from: type=registry,ref=blockscout/blockscout:buildcache + cache-to: type=registry,ref=blockscout/blockscout:buildcache,mode=max + tags: blockscout/blockscout:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + DECODE_NOT_A_CONTRACT_CALLS=false + MIXPANEL_URL= + MIXPANEL_TOKEN= + AMPLITUDE_URL= + AMPLITUDE_API_KEY= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true diff --git a/.github/workflows/publish-docker-image-for-zkevm.yml b/.github/workflows/publish-docker-image-for-zkevm.yml index b102cd7ca55b..ed3d6ecd89fe 100644 --- a/.github/workflows/publish-docker-image-for-zkevm.yml +++ b/.github/workflows/publish-docker-image-for-zkevm.yml @@ -24,7 +24,7 @@ jobs: docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - - name: Build and push Docker image + - name: Build and push Docker image (indexer + API) uses: docker/build-push-action@v5 with: context: . @@ -45,4 +45,118 @@ jobs: CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=polygon_zkevm \ No newline at end of file + CHAIN_TYPE=polygon_zkevm + + - name: Build and push Docker image (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=polygon_zkevm + + - name: Build and push Docker image (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=polygon_zkevm + + - name: Build and push Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=polygon_zkevm + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=polygon_zkevm + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=polygon_zkevm + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/.github/workflows/release-polygon-zkevm.yml b/.github/workflows/release-polygon-zkevm.yml index 75e1aed33146..f9f862d64184 100644 --- a/.github/workflows/release-polygon-zkevm.yml +++ b/.github/workflows/release-polygon-zkevm.yml @@ -91,4 +91,74 @@ jobs: ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=polygon_zkevm \ No newline at end of file + CHAIN_TYPE=polygon_zkevm + + - name: Build and push Docker image for Polygon zkEVM (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zkevm:${{ env.RELEASE_VERSION }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=polygon_zkevm + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Polygon zkEVM (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zkevm:${{ env.RELEASE_VERSION }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=polygon_zkevm + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Polygon zkEVM (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zkevm:${{ env.RELEASE_VERSION }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=polygon_zkevm + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file From a65b7f82ba83ed81912ac36e47d791b8a19e278c Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Wed, 30 Oct 2024 13:20:22 +0400 Subject: [PATCH 267/363] fix: abi cache for non-proxied addresses (#11065) * fix: abi cache for non-proxied addresses * chore: rename --- .../lib/explorer/chain/transaction.ex | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index 93d2a62df6e5..67529bbc3a33 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -769,10 +769,10 @@ defmodule Explorer.Chain.Transaction do boolean(), [Chain.api?()], methods_map, - proxy_implementation_abi_map + smart_contract_full_abi_map ) :: error_type | success_type when methods_map: map(), - proxy_implementation_abi_map: map(), + smart_contract_full_abi_map: map(), error_type: {:error, any()} | {:error, :contract_not_verified | :contract_verified, list()}, success_type: {:ok | binary(), any()} | {:ok, binary(), binary(), list()} def decoded_input_data( @@ -780,7 +780,7 @@ defmodule Explorer.Chain.Transaction do skip_sig_provider? \\ false, options, methods_map \\ %{}, - proxy_implementation_abi_map \\ %{} + smart_contract_full_abi_map \\ %{} ) # skip decoding if there is no to_address @@ -831,7 +831,7 @@ defmodule Explorer.Chain.Transaction do skip_sig_provider?, options, methods_map, - proxy_implementation_abi_map + smart_contract_full_abi_map ) do decoded_input_data( %__MODULE__{ @@ -842,7 +842,7 @@ defmodule Explorer.Chain.Transaction do skip_sig_provider?, options, methods_map, - proxy_implementation_abi_map + smart_contract_full_abi_map ) end @@ -856,7 +856,7 @@ defmodule Explorer.Chain.Transaction do skip_sig_provider?, options, methods_map, - proxy_implementation_abi_map + smart_contract_full_abi_map ) do decoded_input_data( %__MODULE__{ @@ -867,7 +867,7 @@ defmodule Explorer.Chain.Transaction do skip_sig_provider?, options, methods_map, - proxy_implementation_abi_map + smart_contract_full_abi_map ) end @@ -881,7 +881,7 @@ defmodule Explorer.Chain.Transaction do skip_sig_provider?, options, methods_map, - _proxy_implementation_abi_map + _smart_contract_full_abi_map ) do methods = check_methods_cache(method_id, methods_map, options) @@ -922,9 +922,9 @@ defmodule Explorer.Chain.Transaction do skip_sig_provider?, options, methods_map, - proxy_implementation_abi_map + smart_contract_full_abi_map ) do - full_abi = check_full_abi_cache(smart_contract, proxy_implementation_abi_map, options) + full_abi = check_full_abi_cache(smart_contract, smart_contract_full_abi_map, options) case do_decoded_input_data(data, full_abi, hash) do # In some cases transactions use methods of some unpredictable contracts, so we can try to look up for method in a whole DB @@ -938,7 +938,7 @@ defmodule Explorer.Chain.Transaction do skip_sig_provider?, options, methods_map, - proxy_implementation_abi_map + smart_contract_full_abi_map ) do {:error, :contract_not_verified, []} -> decode_function_call_via_sig_provider_wrapper(input, hash, skip_sig_provider?) @@ -999,10 +999,10 @@ defmodule Explorer.Chain.Transaction do defp check_full_abi_cache( smart_contract, - proxy_implementation_abi_map, + smart_contract_full_abi_map, options ) do - Map.get_lazy(proxy_implementation_abi_map, smart_contract.address_hash, fn -> + Map.get_lazy(smart_contract_full_abi_map, smart_contract.address_hash, fn -> Proxy.combine_proxy_implementation_abi(smart_contract, options) end) end @@ -1994,7 +1994,7 @@ defmodule Explorer.Chain.Transaction do """ @spec decode_transactions([Transaction.t()], boolean(), Keyword.t()) :: [nil | {:ok, String.t(), String.t(), map()}] def decode_transactions(transactions, skip_sig_provider?, opts) do - proxy_implementation_abi_map = combine_proxy_implementation_abi_map(transactions) + smart_contract_full_abi_map = combine_smart_contract_full_abi_map(transactions) # first we assemble an empty methods map, so that decoded_input_data will skip ContractMethod.t() lookup and decoding empty_methods_map = @@ -2005,12 +2005,12 @@ defmodule Explorer.Chain.Transaction do end) |> Enum.into(%{}, &{&1, []}) - # try to decode transaction using full abi data from proxy_implementation_abi_map + # try to decode transaction using full abi data from smart_contract_full_abi_map decoded_transactions = transactions |> Enum.map(fn transaction -> transaction - |> decoded_input_data(skip_sig_provider?, opts, empty_methods_map, proxy_implementation_abi_map) + |> decoded_input_data(skip_sig_provider?, opts, empty_methods_map, smart_contract_full_abi_map) |> format_decoded_input() end) |> Enum.zip(transactions) @@ -2032,7 +2032,7 @@ defmodule Explorer.Chain.Transaction do {nil, transaction} -> transaction |> Map.put(:to_address, %NotLoaded{}) - |> decoded_input_data(skip_sig_provider?, opts, methods_map, proxy_implementation_abi_map) + |> decoded_input_data(skip_sig_provider?, opts, methods_map, smart_contract_full_abi_map) |> format_decoded_input() {decoded, _} -> @@ -2040,7 +2040,7 @@ defmodule Explorer.Chain.Transaction do end) end - defp combine_proxy_implementation_abi_map(transactions) do + defp combine_smart_contract_full_abi_map(transactions) do # parse unique address hashes of smart-contracts from to_address and created_contract_address properties of the transactions list unique_to_address_hashes = transactions @@ -2063,11 +2063,16 @@ defmodule Explorer.Chain.Transaction do |> Chain.hashes_to_addresses(necessity_by_association: %{smart_contract: :optional}) |> Enum.into(%{}, &{&1.hash, &1}) + # combine map %{proxy_address_hash => implementation address hashes} + proxy_implementations_map = + multiple_proxy_implementations + |> Enum.into(%{}, &{&1.proxy_address_hash, &1.address_hashes}) + # combine map %{proxy_address_hash => combined proxy abi} - multiple_proxy_implementations - |> Enum.into(%{}, fn proxy_implementations -> + unique_to_address_hashes + |> Enum.into(%{}, fn to_address_hash -> full_abi = - [proxy_implementations.proxy_address_hash | proxy_implementations.address_hashes] + [to_address_hash | Map.get(proxy_implementations_map, to_address_hash, [])] |> Enum.map(&Map.get(addresses_with_smart_contracts, &1)) |> Enum.flat_map(fn %{smart_contract: %{abi: abi}} when is_list(abi) -> abi @@ -2075,7 +2080,7 @@ defmodule Explorer.Chain.Transaction do end) |> Enum.filter(&(!is_nil(&1))) - {proxy_implementations.proxy_address_hash, full_abi} + {to_address_hash, full_abi} end) end From a49bdec27ac0d745affd340625d7e4a6cf5af929 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Wed, 30 Oct 2024 11:23:46 +0200 Subject: [PATCH 268/363] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e396fd557fe8..7e221f85c1d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### 🚀 Features +- Support zksync foundry verification ([#11037](https://github.com/blockscout/blockscout/issues/11037)) - Address transactions block number sorting ([#11035](https://github.com/blockscout/blockscout/issues/11035)) - Scroll rollup: L1 fee parameters in API, `queueIndex` for L2 transactions, and L1 <->L2 messages ([#10484](https://github.com/blockscout/blockscout/issues/10484)) - Account V2 ([#10706](https://github.com/blockscout/blockscout/issues/10706)) @@ -26,6 +27,9 @@ ### 🐛 Bug Fixes +- Abi cache for non-proxied addresses ([#11065](https://github.com/blockscout/blockscout/issues/11065)) +- Celo collated gas price issue ([#11067](https://github.com/blockscout/blockscout/issues/11067)) +- Indexer memory limit for api instance ([#11066](https://github.com/blockscout/blockscout/issues/11066)) - Fix scam badge value in some API endpoints ([#11054](https://github.com/blockscout/blockscout/issues/11054)) - Divide by `10^decimals` when calculating token supply in CMC format ([#11036](https://github.com/blockscout/blockscout/issues/11036)) - Rename zksync l1/l2 _tx_count columns ([#11051](https://github.com/blockscout/blockscout/issues/11051)) From 1b9261f6a925c47629833970cfb6856ba10e5559 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Wed, 30 Oct 2024 12:45:12 +0200 Subject: [PATCH 269/363] Pre-release Docker image for Filecoin --- .github/workflows/pre-release-filecoin.yml | 167 ++++++++++++++++++ .../publish-docker-image-for-celo.yml | 56 +++++- .../publish-docker-image-for-filecoin.yml | 118 ++++++++++++- .github/workflows/release-celo.yml | 9 +- .github/workflows/release-filecoin.yml | 72 +++++++- 5 files changed, 411 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/pre-release-filecoin.yml diff --git a/.github/workflows/pre-release-filecoin.yml b/.github/workflows/pre-release-filecoin.yml new file mode 100644 index 000000000000..61c29d90a5f7 --- /dev/null +++ b/.github/workflows/pre-release-filecoin.yml @@ -0,0 +1,167 @@ +name: Pre-release for Filecoin + +on: + workflow_dispatch: + inputs: + number: + type: number + required: true + +env: + OTP_VERSION: ${{ vars.OTP_VERSION }} + ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: 6.9.0 + steps: + - uses: actions/checkout@v4 + - name: Setup repo + uses: ./.github/actions/setup-repo + id: setup + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + docker-remote-multi-platform: true + docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} + docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} + + - name: Build and push Docker image for Filecoin (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-filecoin:latest, blockscout/blockscout-filecoin:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=filecoin + + - name: Build and push Docker image for Filecoin (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-filecoin:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=filecoin + + - name: Build and push Docker image for Filecoin (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-filecoin:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=filecoin + + - name: Build and push Docker image for Filecoin (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-filecoin:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=filecoin + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Filecoin (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-filecoin:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=filecoin + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Filecoin (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-filecoin:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=filecoin + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/.github/workflows/publish-docker-image-for-celo.yml b/.github/workflows/publish-docker-image-for-celo.yml index b11c9021ba65..b2ed0aa80866 100644 --- a/.github/workflows/publish-docker-image-for-celo.yml +++ b/.github/workflows/publish-docker-image-for-celo.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - - name: Build and push Docker image for Celo (indexer + API) + - name: Build and push Docker image for CELO (indexer + API) uses: docker/build-push-action@v5 with: context: . @@ -37,7 +37,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - API_GRAPHQL_MAX_COMPLEXITY=10400 + API_GRAPHQL_MAX_COMPLEXITY=${{ env.API_GRAPHQL_MAX_COMPLEXITY }} CACHE_EXCHANGE_RATES_PERIOD= API_V1_READ_METHODS_DISABLED=false DISABLE_WEBAPP=false @@ -49,7 +49,53 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} - - name: Build and push Docker image (indexer + API + shrink internal transactions) + - name: Build and push Docker image for CELO (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + API_GRAPHQL_MAX_COMPLEXITY=${{ env.API_GRAPHQL_MAX_COMPLEXITY }} + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + + - name: Build and push Docker image for CELO (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + API_GRAPHQL_MAX_COMPLEXITY=${{ env.API_GRAPHQL_MAX_COMPLEXITY }} + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + + - name: Build and push Docker image for CELO (indexer + API + shrink internal transactions) uses: docker/build-push-action@v5 with: context: . @@ -74,7 +120,7 @@ jobs: CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - - name: Build and push Docker image (indexer + shrink internal transactions) + - name: Build and push Docker image for CELO (indexer + shrink internal transactions) uses: docker/build-push-action@v5 with: context: . @@ -98,7 +144,7 @@ jobs: CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - - name: Build and push Docker image (API + shrink internal transactions) + - name: Build and push Docker image for CELO (API + shrink internal transactions) uses: docker/build-push-action@v5 with: context: . diff --git a/.github/workflows/publish-docker-image-for-filecoin.yml b/.github/workflows/publish-docker-image-for-filecoin.yml index c2be558e12db..e3b8cabb4ed0 100644 --- a/.github/workflows/publish-docker-image-for-filecoin.yml +++ b/.github/workflows/publish-docker-image-for-filecoin.yml @@ -23,7 +23,7 @@ jobs: docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - - name: Build and push Docker image + - name: Build and push Docker image for Filecoin (indexer + API) uses: docker/build-push-action@v5 with: context: . @@ -44,4 +44,118 @@ jobs: CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=filecoin \ No newline at end of file + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + + - name: Build and push Docker image for Filecoin (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + + - name: Build and push Docker image for Filecoin (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + + - name: Build and push Docker image for Filecoin (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Filecoin (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Filecoin (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file diff --git a/.github/workflows/release-celo.yml b/.github/workflows/release-celo.yml index d87d70006c92..e8d89a31a474 100644 --- a/.github/workflows/release-celo.yml +++ b/.github/workflows/release-celo.yml @@ -39,6 +39,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | + API_GRAPHQL_MAX_COMPLEXITY=${{ env.API_GRAPHQL_MAX_COMPLEXITY }} DISABLE_WEBAPP=false API_V1_READ_METHODS_DISABLED=false API_V1_WRITE_METHODS_DISABLED=false @@ -62,6 +63,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | + API_GRAPHQL_MAX_COMPLEXITY=${{ env.API_GRAPHQL_MAX_COMPLEXITY }} DISABLE_API=true DISABLE_WEBAPP=true CACHE_EXCHANGE_RATES_PERIOD= @@ -84,6 +86,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | + API_GRAPHQL_MAX_COMPLEXITY=${{ env.API_GRAPHQL_MAX_COMPLEXITY }} DISABLE_INDEXER=true DISABLE_WEBAPP=true CACHE_EXCHANGE_RATES_PERIOD= @@ -94,7 +97,7 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=celo - - name: Build and push Docker image (indexer + API + shrink internal transactions) + - name: Build and push Docker image for CELO (indexer + API + shrink internal transactions) uses: docker/build-push-action@v5 with: context: . @@ -119,7 +122,7 @@ jobs: CHAIN_TYPE=celo SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - - name: Build and push Docker image (indexer + shrink internal transactions) + - name: Build and push Docker image for CELO (indexer + shrink internal transactions) uses: docker/build-push-action@v5 with: context: . @@ -143,7 +146,7 @@ jobs: CHAIN_TYPE=celo SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - - name: Build and push Docker image (API + shrink internal transactions) + - name: Build and push Docker image for CELO (API + shrink internal transactions) uses: docker/build-push-action@v5 with: context: . diff --git a/.github/workflows/release-filecoin.yml b/.github/workflows/release-filecoin.yml index 22056a60218a..65b85b3486b7 100644 --- a/.github/workflows/release-filecoin.yml +++ b/.github/workflows/release-filecoin.yml @@ -91,4 +91,74 @@ jobs: ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=filecoin \ No newline at end of file + CHAIN_TYPE=filecoin + + - name: Build and push Docker image for Filecoin (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-filecoin:${{ env.RELEASE_VERSION }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=filecoin + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Filecoin (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-filecoin:${{ env.RELEASE_VERSION }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=filecoin + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Filecoin (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-filecoin:${{ env.RELEASE_VERSION }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=filecoin + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file From c3ae5df053bea9a62fb8835e1c4078509c9cdaaf Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:37:50 +0400 Subject: [PATCH 270/363] fix: Import blocks before coin balances (#11049) --- apps/explorer/lib/explorer/chain/import/stage/block_related.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain/import/stage/block_related.ex b/apps/explorer/lib/explorer/chain/import/stage/block_related.ex index a9c25cf03b27..b18808fb7367 100644 --- a/apps/explorer/lib/explorer/chain/import/stage/block_related.ex +++ b/apps/explorer/lib/explorer/chain/import/stage/block_related.ex @@ -10,8 +10,8 @@ defmodule Explorer.Chain.Import.Stage.BlockRelated do @addresses_runner Runner.Addresses @rest_runners [ - Runner.Address.CoinBalances, Runner.Blocks, + Runner.Address.CoinBalances, Runner.Address.CoinBalancesDaily, Runner.Transactions, Runner.TokenTransfers From 8f933d92a65194473d4cff35bc01dcf9a699558d Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:41:48 +0400 Subject: [PATCH 271/363] fix: Filter non-traceable blocks before inserting them to internal txs fetcher queue (#11074) --- .../ethereum_jsonrpc/utility/ranges_helper.ex | 3 ++- .../indexer/fetcher/internal_transaction.ex | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/ranges_helper.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/ranges_helper.ex index 5657fe77299b..3b27e0c13464 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/ranges_helper.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/ranges_helper.ex @@ -56,7 +56,8 @@ defmodule EthereumJSONRPC.Utility.RangesHelper do |> sanitize_ranges() end - defp number_in_ranges?(number, ranges) do + @spec number_in_ranges?(integer(), [Range.t()]) :: boolean() + def number_in_ranges?(number, ranges) do Enum.reduce_while(ranges, false, fn _from.._to//_ = range, _acc -> if number in range, do: {:halt, true}, else: {:cont, false} num_to_latest, _acc -> if number >= num_to_latest, do: {:halt, true}, else: {:cont, false} diff --git a/apps/indexer/lib/indexer/fetcher/internal_transaction.ex b/apps/indexer/lib/indexer/fetcher/internal_transaction.ex index 4e3aaad4e0f4..56d909069b4b 100644 --- a/apps/indexer/lib/indexer/fetcher/internal_transaction.ex +++ b/apps/indexer/lib/indexer/fetcher/internal_transaction.ex @@ -76,13 +76,23 @@ defmodule Indexer.Fetcher.InternalTransaction do @impl BufferedTask def init(initial, reducer, _json_rpc_named_arguments) do - {:ok, final} = - Chain.stream_blocks_with_unfetched_internal_transactions( - initial, + stream_reducer = + if RangesHelper.trace_ranges_present?() do + trace_block_ranges = RangesHelper.get_trace_block_ranges() + + fn block_number, acc -> + # credo:disable-for-next-line Credo.Check.Refactor.Nesting + if RangesHelper.number_in_ranges?(block_number, trace_block_ranges), + do: reducer.(block_number, acc), + else: acc + end + else fn block_number, acc -> reducer.(block_number, acc) end - ) + end + + {:ok, final} = Chain.stream_blocks_with_unfetched_internal_transactions(initial, stream_reducer) final end From 940a95f0e0d74490a3d8486b459a5682ce7e947b Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Wed, 30 Oct 2024 13:43:33 +0200 Subject: [PATCH 272/363] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e221f85c1d1..4f90e7350907 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ ### 🐛 Bug Fixes +- Filter non-traceable blocks before inserting them to internal txs fetcher queue ([#11074](https://github.com/blockscout/blockscout/issues/11074)) +- Import blocks before coin balances ([#11049](https://github.com/blockscout/blockscout/issues/11049)) - Abi cache for non-proxied addresses ([#11065](https://github.com/blockscout/blockscout/issues/11065)) - Celo collated gas price issue ([#11067](https://github.com/blockscout/blockscout/issues/11067)) - Indexer memory limit for api instance ([#11066](https://github.com/blockscout/blockscout/issues/11066)) From 0423c18d3a0dfda318621cc8c353ee895e728045 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Wed, 30 Oct 2024 16:36:53 +0300 Subject: [PATCH 273/363] fix: Fix contract codes fetching for zksync chain type (#11055) * fix: Fix contract codes fetching for zksync chain type * Process review comments * Fix tests * Fix tests * Fix tests --- .../views/address_contract_view.ex | 8 +- apps/explorer/config/config.exs | 1 + apps/explorer/config/runtime/test.exs | 1 + apps/explorer/lib/explorer/application.ex | 3 +- apps/explorer/lib/explorer/chain/address.ex | 13 +++ .../migrator/refetch_contract_codes.ex | 80 +++++++++++++++++++ ...1028102853_add_contract_code_refetched.exs | 13 +++ ...anitize_duplicated_log_index_logs_test.exs | 8 +- apps/explorer/test/support/factory.ex | 13 +++ .../lib/indexer/transform/addresses.ex | 17 ++-- config/runtime.exs | 8 ++ docker-compose/envs/common-blockscout.env | 4 + 12 files changed, 155 insertions(+), 14 deletions(-) create mode 100644 apps/explorer/lib/explorer/migrator/refetch_contract_codes.ex create mode 100644 apps/explorer/priv/zk_sync/migrations/20241028102853_add_contract_code_refetched.exs diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex index efb3dd168fae..0b91ef2cd8e6 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex @@ -118,14 +118,14 @@ defmodule BlockScoutWeb.AddressContractView do {:ok, contract_code} end - def creation_code(%Address{contracts_creation_internal_transaction: %InternalTransaction{}} = address) do - address.contracts_creation_internal_transaction.init - end - def creation_code(%Address{contracts_creation_transaction: %Transaction{}} = address) do address.contracts_creation_transaction.input end + def creation_code(%Address{contracts_creation_internal_transaction: %InternalTransaction{}} = address) do + address.contracts_creation_internal_transaction.init + end + def creation_code(%Address{contracts_creation_transaction: nil}) do nil end diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index 681086d3506c..a1977ac738c1 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -132,6 +132,7 @@ config :explorer, Explorer.Migrator.SanitizeMissingTokenBalances, enabled: true config :explorer, Explorer.Migrator.SanitizeReplacedTransactions, enabled: true config :explorer, Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, enabled: true config :explorer, Explorer.Migrator.SanitizeDuplicatedLogIndexLogs, enabled: true +config :explorer, Explorer.Migrator.RefetchContractCodes, enabled: true config :explorer, Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand, enabled: true diff --git a/apps/explorer/config/runtime/test.exs b/apps/explorer/config/runtime/test.exs index 177cfd6b2bd8..f4be94ed7293 100644 --- a/apps/explorer/config/runtime/test.exs +++ b/apps/explorer/config/runtime/test.exs @@ -53,6 +53,7 @@ config :explorer, Explorer.Migrator.SanitizeMissingTokenBalances, enabled: false config :explorer, Explorer.Migrator.SanitizeReplacedTransactions, enabled: false config :explorer, Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, enabled: false config :explorer, Explorer.Migrator.SanitizeDuplicatedLogIndexLogs, enabled: false +config :explorer, Explorer.Migrator.RefetchContractCodes, 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 8e835b65289e..33aaed879224 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -155,7 +155,8 @@ defmodule Explorer.Application do ]), configure_mode_dependent_process(Explorer.Migrator.SanitizeMissingTokenBalances, :indexer), configure_mode_dependent_process(Explorer.Migrator.SanitizeReplacedTransactions, :indexer), - configure_mode_dependent_process(Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, :indexer) + configure_mode_dependent_process(Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, :indexer), + Explorer.Migrator.RefetchContractCodes |> configure() |> configure_chain_type_dependent_process(:zksync) ] |> List.flatten() diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index f2c4a4ed2a53..1c86455392fa 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -58,6 +58,13 @@ defmodule Explorer.Chain.Address.Schema do ] end + :zksync -> + quote do + [ + field(:contract_code_refetched, :boolean) + ] + end + _ -> [] end) @@ -142,6 +149,9 @@ defmodule Explorer.Chain.Address do :filecoin -> ~w(filecoin_id filecoin_robust filecoin_actor_type)a + :zksync -> + ~w(contract_code_refetched)a + _ -> [] end) @@ -196,6 +206,9 @@ defmodule Explorer.Chain.Address do * `filecoin_id_address` - short f0 Filecoin address that may change during chain reorgs * `filecoin_actor_type` - type of actor associated with the Filecoin address """ + :zksync -> """ + * `contract_code_refetched` - true when Explorer.Migrator.RefetchContractCodes handled this address, or it's unnecessary (for addresses inserted after this) + """ _ -> "" end} `fetched_coin_balance` and `fetched_coin_balance_block_number` may be updated when a new coin_balance row is fetched. diff --git a/apps/explorer/lib/explorer/migrator/refetch_contract_codes.ex b/apps/explorer/lib/explorer/migrator/refetch_contract_codes.ex new file mode 100644 index 000000000000..a20574417c1c --- /dev/null +++ b/apps/explorer/lib/explorer/migrator/refetch_contract_codes.ex @@ -0,0 +1,80 @@ +defmodule Explorer.Migrator.RefetchContractCodes do + @moduledoc """ + Refetch contract_code for. Migration created for running on zksync chain type. + It has an issue with created contract code derived from internal transactions. Such codes are not correct. + So, this migration fetches for all current smart contracts actual bytecode from the JSON RPC node. + """ + + use Explorer.Migrator.FillingMigration + + import Ecto.Query + + alias Explorer.Chain.{Address, Data, Import} + alias Explorer.Chain.Hash.Address, as: AddressHash + alias Explorer.Chain.Import.Runner.Addresses + alias Explorer.Migrator.FillingMigration + alias Explorer.Repo + + require Logger + + @migration_name "refetch_contract_codes" + + @impl FillingMigration + def migration_name, do: @migration_name + + @impl FillingMigration + def last_unprocessed_identifiers(state) do + limit = batch_size() * concurrency() + + ids = + unprocessed_data_query() + |> select([address], address.hash) + |> limit(^limit) + |> Repo.all(timeout: :infinity) + + {ids, state} + end + + @impl FillingMigration + def unprocessed_data_query do + Address + |> where([address], not is_nil(address.contract_code) and not address.contract_code_refetched) + end + + @impl FillingMigration + def update_batch(address_hashes) do + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + address_hashes + |> Enum.map(&address_to_fetch_code_params/1) + |> EthereumJSONRPC.fetch_codes(json_rpc_named_arguments) + |> case do + {:ok, create_address_codes} -> + addresses_params = create_address_codes.params_list |> Enum.map(¶m_to_address/1) |> Enum.sort_by(& &1.hash) + + Addresses.insert(Repo, addresses_params, %{ + timeout: :infinity, + on_conflict: {:replace, [:contract_code, :contract_code_refetched, :updated_at]}, + timestamps: Import.timestamps() + }) + + {:error, reason} -> + Logger.error(fn -> ["failed to fetch contract codes: ", inspect(reason)] end, + error_count: Enum.count(address_hashes) + ) + end + end + + @impl FillingMigration + def update_cache, do: :ok + + defp address_to_fetch_code_params(address_hash) do + %{block_quantity: "latest", address: to_string(address_hash)} + end + + defp param_to_address(%{code: bytecode, address: address_hash}) do + {:ok, address_hash} = AddressHash.cast(address_hash) + {:ok, bytecode} = Data.cast(bytecode) + %{hash: address_hash, contract_code: bytecode, contract_code_refetched: true} + end +end diff --git a/apps/explorer/priv/zk_sync/migrations/20241028102853_add_contract_code_refetched.exs b/apps/explorer/priv/zk_sync/migrations/20241028102853_add_contract_code_refetched.exs new file mode 100644 index 000000000000..7f24a305d115 --- /dev/null +++ b/apps/explorer/priv/zk_sync/migrations/20241028102853_add_contract_code_refetched.exs @@ -0,0 +1,13 @@ +defmodule Explorer.Repo.ZkSync.Migrations.AddContractCodeRefetched do + use Ecto.Migration + + def change do + alter table(:addresses) do + add(:contract_code_refetched, :boolean, default: false) + end + + execute(""" + ALTER TABLE addresses ALTER COLUMN contract_code_refetched SET DEFAULT true; + """) + end +end diff --git a/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs b/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs index c9973ef618b6..7d0c5cfb23f6 100644 --- a/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs +++ b/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs @@ -7,7 +7,7 @@ defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogsTest do alias Explorer.Chain.Token.Instance alias Explorer.Migrator.{SanitizeDuplicatedLogIndexLogs, MigrationStatus} - if Application.compile_env(:explorer, :chain_type) != :celo do + if Application.compile_env(:explorer, :chain_type) in [:polygon_zkevm, :rsk, :filecoin] do describe "Sanitize duplicated log index logs" do test "correctly identifies and updates duplicated log index logs" do block = insert(:block) @@ -24,7 +24,7 @@ defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogsTest do assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil SanitizeDuplicatedLogIndexLogs.start_link([]) - Process.sleep(300) + :timer.sleep(500) assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true @@ -103,7 +103,7 @@ defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogsTest do assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil SanitizeDuplicatedLogIndexLogs.start_link([]) - Process.sleep(300) + :timer.sleep(500) assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true @@ -138,7 +138,7 @@ defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogsTest do assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil SanitizeDuplicatedLogIndexLogs.start_link([]) - Process.sleep(100) + :timer.sleep(100) assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index c1390bfb356e..3a78abcc6d91 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -265,6 +265,19 @@ defmodule Explorer.Factory do %Address{ hash: address_hash() } + |> Map.merge(address_factory_chain_type_fields()) + end + + case Application.compile_env(:explorer, :chain_type) do + :zksync -> + defp address_factory_chain_type_fields() do + %{ + contract_code_refetched: true + } + end + + _ -> + defp address_factory_chain_type_fields(), do: %{} end def address_name_factory do diff --git a/apps/indexer/lib/indexer/transform/addresses.ex b/apps/indexer/lib/indexer/transform/addresses.ex index bec6ee4c7318..59a9e0b8376a 100644 --- a/apps/indexer/lib/indexer/transform/addresses.ex +++ b/apps/indexer/lib/indexer/transform/addresses.ex @@ -73,11 +73,18 @@ defmodule Indexer.Transform.Addresses do %{from: :block_number, to: :fetched_coin_balance_block_number}, %{from: :to_address_hash, to: :hash} ], - [ - %{from: :block_number, to: :fetched_coin_balance_block_number}, - %{from: :created_contract_address_hash, to: :hash}, - %{from: :created_contract_code, to: :contract_code} - ] + if Application.compile_env(:explorer, :chain_type) == :zksync do + [ + %{from: :block_number, to: :fetched_coin_balance_block_number}, + %{from: :created_contract_address_hash, to: :hash} + ] + else + [ + %{from: :block_number, to: :fetched_coin_balance_block_number}, + %{from: :created_contract_address_hash, to: :hash}, + %{from: :created_contract_code, to: :contract_code} + ] + end ], codes: [ [ diff --git a/config/runtime.exs b/config/runtime.exs index 1ff0ad3901cc..a675e1d33338 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -619,6 +619,14 @@ config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers, batch_size: ConfigHelper.parse_integer_env_var("MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_BATCH_SIZE", 50), timeout: ConfigHelper.parse_time_env_var("MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_TIMEOUT", "250ms") +config :explorer, Explorer.Migrator.SanitizeDuplicatedLogIndexLogs, + concurrency: ConfigHelper.parse_integer_env_var("MIGRATION_SANITIZE_DUPLICATED_LOG_INDEX_LOGS_CONCURRENCY", 10), + batch_size: ConfigHelper.parse_integer_env_var("MIGRATION_SANITIZE_DUPLICATED_LOG_INDEX_LOGS_BATCH_SIZE", 500) + +config :explorer, Explorer.Migrator.RefetchContractCodes, + concurrency: ConfigHelper.parse_integer_env_var("MIGRATION_REFETCH_CONTRACT_CODES_CONCURRENCY", 5), + batch_size: ConfigHelper.parse_integer_env_var("MIGRATION_REFETCH_CONTRACT_CODES_BATCH_SIZE", 100) + config :explorer, Explorer.Migrator.ShrinkInternalTransactions, enabled: ConfigHelper.parse_bool_env_var("SHRINK_INTERNAL_TRANSACTIONS_ENABLED"), batch_size: ConfigHelper.parse_integer_env_var("SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE", 100), diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index ef36f375f680..35872c4e7506 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -430,6 +430,10 @@ EIP_1559_ELASTICITY_MULTIPLIER=2 # MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_CONCURRENCY= # MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_BATCH_SIZE= # MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_TIMEOUT= +# MIGRATION_SANITIZE_DUPLICATED_LOG_INDEX_LOGS_CONCURRENCY= +# MIGRATION_SANITIZE_DUPLICATED_LOG_INDEX_LOGS_BATCH_SIZE= +# MIGRATION_REFETCH_CONTRACT_CODES_BATCH_SIZE= +# MIGRATION_REFETCH_CONTRACT_CODES_CONCURRENCY= SOURCIFY_INTEGRATION_ENABLED=false SOURCIFY_SERVER_URL= SOURCIFY_REPO_URL= From e566d99c6764140ecb9fa0aef4d1b436c6683e97 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Wed, 30 Oct 2024 15:39:17 +0200 Subject: [PATCH 274/363] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f90e7350907..da6a111b4395 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ ### 🐛 Bug Fixes +- Fix contract codes fetching for zksync chain type ([#11055](https://github.com/blockscout/blockscout/issues/11055)) - Filter non-traceable blocks before inserting them to internal txs fetcher queue ([#11074](https://github.com/blockscout/blockscout/issues/11074)) - Import blocks before coin balances ([#11049](https://github.com/blockscout/blockscout/issues/11049)) - Abi cache for non-proxied addresses ([#11065](https://github.com/blockscout/blockscout/issues/11065)) From b7f15f8472e3d50da3e74a8a9f5ecc650cf1180b Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 31 Oct 2024 10:20:06 +0200 Subject: [PATCH 275/363] perf: Fix performance of Explorer.Counters.Transactions24hStats.consolidate/0 function (#11082) --- .../explorer/counters/transactions_24h_stats.ex | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/explorer/lib/explorer/counters/transactions_24h_stats.ex b/apps/explorer/lib/explorer/counters/transactions_24h_stats.ex index e82df0530e96..234a6fdeecd3 100644 --- a/apps/explorer/lib/explorer/counters/transactions_24h_stats.ex +++ b/apps/explorer/lib/explorer/counters/transactions_24h_stats.ex @@ -10,7 +10,7 @@ defmodule Explorer.Counters.Transactions24hStats do import Ecto.Query alias Explorer.{Chain, Repo} - alias Explorer.Chain.Transaction + alias Explorer.Chain.{DenormalizationHelper, Transaction} @transaction_count_name "transaction_count_24h" @transaction_fee_sum_name "transaction_fee_sum_24h" @@ -94,15 +94,18 @@ defmodule Explorer.Counters.Transactions24hStats do sum_query = dynamic([_, _], sum(^fee_query)) avg_query = dynamic([_, _], avg(^fee_query)) - query = + base_query = from(transaction in Transaction, join: block in assoc(transaction, :block), - where: block.timestamp >= ago(24, "hour"), select: %{count: count(transaction.hash)}, select_merge: ^%{fee_sum: sum_query}, select_merge: ^%{fee_average: avg_query} ) + query = + base_query + |> where_block_timestamp_in_last_24_hours() + %{ count: count, fee_sum: fee_sum, @@ -125,6 +128,14 @@ defmodule Explorer.Counters.Transactions24hStats do }) end + defp where_block_timestamp_in_last_24_hours(query) do + if DenormalizationHelper.transactions_denormalization_finished?() do + where(query, [transaction, _block], transaction.block_timestamp >= ago(24, "hour")) + else + where(query, [_transaction, block], block.timestamp >= ago(24, "hour")) + end + end + @doc """ Returns a boolean that indicates whether consolidation is enabled From 2687c1d1396e8e8134aa17f20b792cd628ab0c1e Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 31 Oct 2024 11:29:28 +0200 Subject: [PATCH 276/363] fix: Fix tokennfttx API v1 endpoint (#11083) * fix: Fix tokennfttx API v1 endpoint * Add test --- .../views/api/rpc/address_view.ex | 10 +++++- .../api/rpc/address_controller_test.exs | 34 +++++++++++++++++++ apps/explorer/lib/explorer/etherscan.ex | 25 ++++++++++++-- 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex index eca2f51a14e0..5257899b1e08 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.API.RPC.AddressView do alias BlockScoutWeb.API.EthRPC.View, as: EthRPCView alias BlockScoutWeb.API.RPC.RPCView + alias Explorer.Chain.DenormalizationHelper def render("listaccounts.json", %{accounts: accounts}) do accounts = Enum.map(accounts, &prepare_account/1) @@ -210,9 +211,16 @@ defmodule BlockScoutWeb.API.RPC.AddressView do end defp prepare_nft_transfer(token_transfer, max_block_number) do + timestamp = + if DenormalizationHelper.tt_denormalization_finished?() do + to_string(DateTime.to_unix(token_transfer.transaction.block_timestamp)) + else + to_string(DateTime.to_unix(token_transfer.block.timestamp)) + end + %{ "blockNumber" => to_string(token_transfer.block_number), - "timeStamp" => to_string(DateTime.to_unix(token_transfer.block.timestamp)), + "timeStamp" => timestamp, "hash" => to_string(token_transfer.transaction_hash), "nonce" => to_string(token_transfer.transaction.nonce), "blockHash" => to_string(token_transfer.block_hash), diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs index 4426ee891d16..027db4a3df7f 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs @@ -5,6 +5,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do alias BlockScoutWeb.API.RPC.AddressController alias Explorer.Chain + alias Explorer.Chain.Cache.BackgroundMigrations alias Explorer.Chain.{Events.Subscriber, Transaction, Wei} alias Explorer.Counters.{AddressesCounter, AverageBlockTime} alias Indexer.Fetcher.OnDemand.CoinBalance, as: CoinBalanceOnDemand @@ -2373,6 +2374,39 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do %{params: %{"module" => "account", "action" => "tokennfttx"}} end + test "API endpoint works after `transactions` table denormalization finished", %{conn: conn, params: params} do + BackgroundMigrations.set_tt_denormalization_finished(true) + + address = insert(:address) + + transaction = + :transaction + |> insert(from_address: address) + |> with_block() + + token = insert(:token, name: "NFT", type: "ERC-721") + + insert(:token_transfer, + transaction: transaction, + from_address: address, + block_number: transaction.block_number, + token_contract_address: token.contract_address, + token_type: token.type, + token_ids: [100_500] + ) + + new_params = + params + |> Map.put("address", Explorer.Chain.Hash.to_string(address.hash)) + + response = + conn + |> get("/api", new_params) + + assert response.status == 200 + BackgroundMigrations.set_tt_denormalization_finished(false) + end + test "with missing address and contract address hash", %{conn: conn, params: params} do assert response = conn diff --git a/apps/explorer/lib/explorer/etherscan.ex b/apps/explorer/lib/explorer/etherscan.ex index 1daac488682d..92284c03ddfa 100644 --- a/apps/explorer/lib/explorer/etherscan.ex +++ b/apps/explorer/lib/explorer/etherscan.ex @@ -4,7 +4,18 @@ defmodule Explorer.Etherscan do """ import Ecto.Query, - only: [from: 2, where: 3, union: 2, subquery: 1, order_by: 3, limit: 2, offset: 2, preload: 3, select_merge: 3] + only: [ + from: 2, + where: 3, + union: 2, + subquery: 1, + order_by: 3, + limit: 2, + offset: 2, + preload: 2, + preload: 3, + select_merge: 3 + ] import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] @@ -331,7 +342,17 @@ defmodule Explorer.Etherscan do |> where_end_block_match_tt(options) |> limit(^options.page_size) |> offset(^offset(options)) - |> preload([block: block], [{:block, block}, :transaction]) + |> maybe_preload_block() + end + + defp maybe_preload_block(query) do + if DenormalizationHelper.tt_denormalization_finished?() do + query + |> preload(:transaction) + else + query + |> preload([block: block], [{:block, block}, :transaction]) + end end @doc """ From b5b259b8f626853e05e1cbcc1819ab2e46ccfdf3 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Thu, 31 Oct 2024 11:40:25 +0200 Subject: [PATCH 277/363] Update CHANGELOG --- CHANGELOG.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da6a111b4395..e395998b5d3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ ### 🐛 Bug Fixes +- Fix tokennfttx API v1 endpoint ([#11083](https://github.com/blockscout/blockscout/issues/11083)) - Fix contract codes fetching for zksync chain type ([#11055](https://github.com/blockscout/blockscout/issues/11055)) - Filter non-traceable blocks before inserting them to internal txs fetcher queue ([#11074](https://github.com/blockscout/blockscout/issues/11074)) - Import blocks before coin balances ([#11049](https://github.com/blockscout/blockscout/issues/11049)) @@ -79,6 +80,7 @@ ### ⚡ Performance +- Fix performance of Explorer.Counters.Transactions24hStats.consolidate/0 function ([#11082](https://github.com/blockscout/blockscout/issues/11082)) - Optimize advanced filters ([#10463](https://github.com/blockscout/blockscout/issues/10463)) - Refactor tx data decoding with fewer DB queries ([#10842](https://github.com/blockscout/blockscout/issues/10842)) @@ -101,6 +103,44 @@ - Support non-unique log index for rsk chain type ([#10807](https://github.com/blockscout/blockscout/issues/10807)) - Add missing symbols ([#10749](https://github.com/blockscout/blockscout/issues/10749)) +### New ENV Variables + +| Variable | Description | Parameters | +| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | +| `INDEXER_SYSTEM_MEMORY_PERCENTAGE` | Percentage of total memory available to the VM that an application can use if `INDEXER_MEMORY_LIMIT` is not set. Implemented in [#10697](https://github.com/blockscout/blockscout/pull/10697). |

Version: v6.9.0+
Default: 60
Applications: Indexer

| +| `INDEXER_TOKEN_BALANCES_EXPONENTIAL_TIMEOUT_COEFF` | Coefficient to calculate exponential timeout. Implemented in [#10694](https://github.com/blockscout/blockscout/pull/10694). |

Version: v6.9.0+
Default: 100
Applications: Indexer

| +| `INDEXER_INTERNAL_TRANSACTIONS_FETCH_ORDER` | Order of fetching internal transactions from node. Possible values: `asc`, `desc`. Implemented in [#10912](https://github.com/blockscout/blockscout/pull/10912) |

Version: v6.9.0+
Default: asc
Applications: Indexer

| +| `HIDE_SCAM_ADDRESSES` | Hides address of EOA/smart-contract/token from search results if the value is `true` and "scam" badge is assigned to that address. Implemented in [#10763](https://github.com/blockscout/blockscout/pull/10763) |

Version: v6.9.0+
Default: (empty)
Applications: API

| +| `RE_CAPTCHA_CHECK_HOSTNAME` | Disable reCAPTCHA hostname check. More details on [reCaptcha docs](https://developers.google.com/recaptcha/docs/domain\_validation). Implemented in [#10706](https://github.com/blockscout/blockscout/pull/10706) |

Version: v6.9.0+
Default: false
Applications: API

| +| `ACCOUNT_OTP_RESEND_INTERVAL` | Time before resending otp email. Implemented in [#10706](https://github.com/blockscout/blockscout/pull/10706). |

Version: v6.9.0+
Default: 1m
Applications: API

| +| `INDEXER_SCROLL_L1_RPC` | The RPC endpoint for L1 used to fetch Deposit and Withdrawal messages. Implemented in [#10484](https://github.com/blockscout/blockscout/pull/10484). |

Version: v6.9.0+
Default: (empty)
Applications: Indexer

| +| `INDEXER_SCROLL_L1_CHAIN_CONTRACT` | The address of ScrollChain contract on L1. Used to fetch batch and bundle events. Implemented in [#10819](https://github.com/blockscout/blockscout/pull/10819). |

Version: v6.9.0+
Default: (empty)
Applications: Indexer

| +| `INDEXER_SCROLL_L1_BATCH_START_BLOCK` | The number of a start block on L1 to index L1 batches and bundles. If the table of batches is not empty, the process will continue indexing from the last indexed batch. Implemented in [#10819](https://github.com/blockscout/blockscout/pull/10819). |

Version: v6.9.0+
Default: (empty)
Applications: Indexer

| +| `INDEXER_SCROLL_L1_MESSENGER_CONTRACT` | The address of L1 Scroll Messenger contract on L1 used to fetch Deposit and Withdrawal messages. Implemented in [#10484](https://github.com/blockscout/blockscout/pull/10484). |

Version: v6.9.0+
Default: (empty)
Applications: Indexer

| +| `INDEXER_SCROLL_L1_MESSENGER_START_BLOCK` | The number of a start block on L1 to index L1 bridge messages. If the table of bridge operations is not empty, the process will continue indexing from the last indexed L1 message. Implemented in [#10484](https://github.com/blockscout/blockscout/pull/10484). |

Version: v6.9.0+
Default: (empty)
Applications: Indexer

| +| `INDEXER_SCROLL_L2_MESSENGER_CONTRACT` | The address of L2 Scroll Messenger contract on L2 used to fetch Deposit and Withdrawal messages. Implemented in [#10484](https://github.com/blockscout/blockscout/pull/10484). |

Version: v6.9.0+
Default: (empty)
Applications: Indexer

| +| `INDEXER_SCROLL_L2_MESSENGER_START_BLOCK` | The number of a start block on L2 to index L2 bridge messages. If the table of bridge operations is not empty, the process will continue indexing from the last indexed L2 message. Implemented in [#10484](https://github.com/blockscout/blockscout/pull/10484). |

Version: v6.9.0+
Default: `FIRST_BLOCK`
Applications: Indexer

| +| `INDEXER_SCROLL_L2_GAS_ORACLE_CONTRACT` | The address of L1 Gas Oracle contract on L2. Implemented in [#10484](https://github.com/blockscout/blockscout/pull/10484). |

Version: v6.9.0+
Default: (empty)
Applications: Indexer

| +| `INDEXER_SCROLL_L1_ETH_GET_LOGS_RANGE_SIZE` | Block range size for eth\_getLogs request in Scroll indexer modules for Layer 1. Implemented in [#10484](https://github.com/blockscout/blockscout/pull/10484). |

Version: v6.9.0+
Default: `250`
Applications: Indexer

| +| `INDEXER_SCROLL_L2_ETH_GET_LOGS_RANGE_SIZE` | Block range size for eth\_getLogs request in Scroll indexer modules for Layer 2. Implemented in [#10484](https://github.com/blockscout/blockscout/pull/10484). |

Version: v6.9.0+
Default: `1000`
Applications: Indexer

| +| `SCROLL_L2_CURIE_UPGRADE_BLOCK` | L2 block number of the Curie upgrade. Implemented in [#10484](https://github.com/blockscout/blockscout/pull/10484). |

Version: v6.9.0+
Default: `0`
Applications: API

| +| `SCROLL_L1_SCALAR_INIT` | Initial value for `scalar` parameter. Implemented in [#10484](https://github.com/blockscout/blockscout/pull/10484). |

Version: v6.9.0+
Default: `0`
Applications: API

| +| `SCROLL_L1_OVERHEAD_INIT` | Initial value for `overhead` parameter. Implemented in [#10484](https://github.com/blockscout/blockscout/pull/10484). |

Version: v6.9.0+
Default: `0`
Applications: API

| +| `SCROLL_L1_COMMIT_SCALAR_INIT` | Initial value for `commit_scalar` parameter. Implemented in [#10484](https://github.com/blockscout/blockscout/pull/10484). |

Version: v6.9.0+
Default: `0`
Applications: API

| +| `SCROLL_L1_BLOB_SCALAR_INIT` | Initial value for `blob_scalar` parameter. Implemented in [#10484](https://github.com/blockscout/blockscout/pull/10484). |

Version: v6.9.0+
Default: `0`
Applications: API

| +| `SCROLL_L1_BASE_FEE_INIT` | Initial value for `l1_base_fee` parameter. Implemented in [#10484](https://github.com/blockscout/blockscout/pull/10484). |

Version: v6.9.0+
Default: `0`
Applications: API

| +| `SCROLL_L1_BLOB_BASE_FEE_INIT` | Initial value for `l1_blob_base_fee` parameter. Implemented in [#10484](https://github.com/blockscout/blockscout/pull/10484). |

Version: v6.9.0+
Default: `0`
Applications: API

| +| `INDEXER_OPTIMISM_L1_DEPOSITS_TRANSACTION_TYPE` | Defines OP Deposit transaction type (numeric value) which is needed for correct L2 transaction hash calculation by the Deposits indexing module. Implemented in [#10674](https://github.com/blockscout/blockscout/pull/10674). |

Version: v6.9.0+
Default: 126
Applications: Indexer

| +| `INDEXER_DISABLE_CELO_VALIDATOR_GROUP_VOTES_FETCHER` | If set to `true`, the validator group votes fetcher will not be started. Implemented in [#10673](https://github.com/blockscout/blockscout/pull/10673). |

Version: v6.9.0+
Default: false
Applications: Indexer

| +| `FILECOIN_NETWORK_PREFIX` | Specifies the expected network prefix for Filecoin addresses. For more details, refer to the [Filecoin Spec](https://spec.filecoin.io/appendix/address/#section-appendix.address.network-prefix). Available values: `f` (for the mainnet), `t` (for testnets). Implemented in [#10468](https://github.com/blockscout/blockscout/pull/10468). |

Version: v6.9.0+
Default: f
Applications: API, Indexer

| +| `BERYX_API_TOKEN` | [Beryx API](https://docs.zondax.ch/beryx-api) token, used for retrieving Filecoin native addressing information. Implemented in [#10468](https://github.com/blockscout/blockscout/pull/10468). |

Required: ✅
Version: v6.9.0+
Default: (empty)
Applications: Indexer

| +| `BERYX_API_BASE_URL` | [Beryx API](https://docs.zondax.ch/beryx-api) base URL. Implemented in [#10468](https://github.com/blockscout/blockscout/pull/10468). |

Version: v6.9.0+
Default: https://api.zondax.ch/fil/data/v3/mainnet
Applications: Indexer

| +| `INDEXER_DISABLE_FILECOIN_ADDRESS_INFO_FETCHER` | When set to `true`, Filecoin native addressing information will not be fetched, but addresses pending fetch will still be recorded in the database. Implemented in [#10468](https://github.com/blockscout/blockscout/pull/10468). |

Version: v6.9.0+
Default: false
Applications: Indexer

| +| `INDEXER_FILECOIN_ADDRESS_INFO_CONCURRENCY` | Sets the maximum number of concurrent requests made to fetch Filecoin native addressing information. Implemented in [#10468](https://github.com/blockscout/blockscout/pull/10468). |

Version: v6.9.0+
Default: 1
Applications: Indexer

| +| `FILECOIN_PENDING_ADDRESS_OPERATIONS_MIGRATION_BATCH_SIZE` | Specifies the number of address records processed per batch during the backfill of pending address fetch operations. Implemented in [#10468](https://github.com/blockscout/blockscout/pull/10468). |

Version: v6.9.0+
Default: 100
Applications: Indexer

| +| `FILECOIN_PENDING_ADDRESS_OPERATIONS_MIGRATION_CONCURRENCY` | Specifies the number of concurrent processes used during the backfill of pending address fetch operations. Implemented in [#10468](https://github.com/blockscout/blockscout/pull/10468). |

Version: v6.9.0+
Default: 1
Applications: Indexer

| +| `BLACKFORT_VALIDATOR_API_URL` | Variable to define the URL of the Blackfort Validator API. Implemented in [#10744](https://github.com/blockscout/blockscout/pull/10744). |

Version: v6.9.0+
Default: (empty)
Applications: API, Indexer

| + ## 6.8.1 ### 🚀 Features From 0189dec6f3c683f4d68ef821fa4ff7d84abdd236 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Thu, 31 Oct 2024 12:37:02 +0200 Subject: [PATCH 278/363] Add shrink int txs docker image for Shibarium chain type --- .github/workflows/release-shibarium.yml | 72 ++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-shibarium.yml b/.github/workflows/release-shibarium.yml index c4b73bc8588e..8f9d62c84240 100644 --- a/.github/workflows/release-shibarium.yml +++ b/.github/workflows/release-shibarium.yml @@ -91,4 +91,74 @@ jobs: ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta RELEASE_VERSION=${{ env.RELEASE_VERSION }} - CHAIN_TYPE=shibarium \ No newline at end of file + CHAIN_TYPE=shibarium + + - name: Build and push Docker image for Shibarium (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-shibarium:${{ env.RELEASE_VERSION }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=shibarium + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Shibarium (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-shibarium:${{ env.RELEASE_VERSION }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=shibarium + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image for Shibarium (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-shibarium:${{ env.RELEASE_VERSION }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=shibarium + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true \ No newline at end of file From eed883338efcd4f7b87fd221edab8721e9bf19b7 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 1 Nov 2024 11:36:17 +0200 Subject: [PATCH 279/363] chore: Add docker compose file without microservices (#11097) --- docker-compose/README.md | 1 + docker-compose/envs/common-stats.env | 2 + docker-compose/no-services.yml | 66 +++++++++++++++++++++ docker-compose/proxy/explorer.conf.template | 34 +++++++++++ docker-compose/services/nginx-explorer.yml | 14 +++++ 5 files changed, 117 insertions(+) create mode 100644 docker-compose/no-services.yml create mode 100644 docker-compose/proxy/explorer.conf.template create mode 100644 docker-compose/services/nginx-explorer.yml diff --git a/docker-compose/README.md b/docker-compose/README.md index 1283ede3d4d9..fcd8f8b588b8 100644 --- a/docker-compose/README.md +++ b/docker-compose/README.md @@ -53,6 +53,7 @@ The repo contains built-in configs for different JSON RPC clients without need t - Running explorer with external backend: `docker-compose -f external-backend.yml up -d` - Running explorer with external frontend: `docker-compose -f external-frontend.yml up -d` - Running all microservices: `docker-compose -f microservices.yml up -d` +- Running only explorer without microservices: `docker-compose -f no-services.yml up -d` All of the configs assume the Ethereum JSON RPC is running at http://localhost:8545. diff --git a/docker-compose/envs/common-stats.env b/docker-compose/envs/common-stats.env index f5eed636bab6..0d2c38c1af37 100644 --- a/docker-compose/envs/common-stats.env +++ b/docker-compose/envs/common-stats.env @@ -25,3 +25,5 @@ STATS__JAEGER__AGENT_ENDPOINT=localhost:6831 STATS__TRACING__ENABLED=true STATS__TRACING__FORMAT=default + +STATS__BLOCKSCOUT_API_URL=http://host.docker.internal diff --git a/docker-compose/no-services.yml b/docker-compose/no-services.yml new file mode 100644 index 000000000000..d1608dba98c7 --- /dev/null +++ b/docker-compose/no-services.yml @@ -0,0 +1,66 @@ +version: '3.9' + +services: + redis-db: + extends: + file: ./services/redis.yml + service: redis-db + + db-init: + extends: + file: ./services/db.yml + service: db-init + + db: + depends_on: + db-init: + condition: service_completed_successfully + extends: + file: ./services/db.yml + service: db + + backend: + depends_on: + - db + - redis-db + extends: + file: ./services/backend.yml + service: backend + build: + context: .. + dockerfile: ./docker/Dockerfile + args: + CACHE_EXCHANGE_RATES_PERIOD: "" + API_V1_READ_METHODS_DISABLED: "false" + DISABLE_WEBAPP: "false" + API_V1_WRITE_METHODS_DISABLED: "false" + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED: "" + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL: "" + ADMIN_PANEL_ENABLED: "" + RELEASE_VERSION: 6.9.0 + links: + - db:database + environment: + ETHEREUM_JSONRPC_HTTP_URL: http://host.docker.internal:8545/ + ETHEREUM_JSONRPC_TRACE_URL: http://host.docker.internal:8545/ + ETHEREUM_JSONRPC_WS_URL: ws://host.docker.internal:8545/ + CHAIN_ID: '1337' + + frontend: + depends_on: + - backend + extends: + file: ./services/frontend.yml + service: frontend + environment: + NEXT_PUBLIC_STATS_API_HOST: + + proxy: + depends_on: + - backend + - frontend + extends: + file: ./services/nginx-explorer.yml + service: proxy + volumes: + - "./proxy/explorer.conf.template:/etc/nginx/templates/default.conf.template" diff --git a/docker-compose/proxy/explorer.conf.template b/docker-compose/proxy/explorer.conf.template new file mode 100644 index 000000000000..f63e979a0d8d --- /dev/null +++ b/docker-compose/proxy/explorer.conf.template @@ -0,0 +1,34 @@ +map $http_upgrade $connection_upgrade { + + default upgrade; + '' close; +} + +server { + listen 80; + server_name localhost; + proxy_http_version 1.1; + + location ~ ^/(api|socket|sitemap.xml|auth/auth0|auth/auth0/callback|auth/logout) { + proxy_pass ${BACK_PROXY_PASS}; + proxy_http_version 1.1; + proxy_set_header Host "$host"; + proxy_set_header X-Real-IP "$remote_addr"; + proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for"; + proxy_set_header X-Forwarded-Proto "$scheme"; + proxy_set_header Upgrade "$http_upgrade"; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + } + location / { + proxy_pass ${FRONT_PROXY_PASS}; + proxy_http_version 1.1; + proxy_set_header Host "$host"; + proxy_set_header X-Real-IP "$remote_addr"; + proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for"; + proxy_set_header X-Forwarded-Proto "$scheme"; + proxy_set_header Upgrade "$http_upgrade"; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + } +} \ No newline at end of file diff --git a/docker-compose/services/nginx-explorer.yml b/docker-compose/services/nginx-explorer.yml new file mode 100644 index 000000000000..513a054b0575 --- /dev/null +++ b/docker-compose/services/nginx-explorer.yml @@ -0,0 +1,14 @@ +version: '3.9' + +services: + proxy: + image: nginx + container_name: proxy + extra_hosts: + - 'host.docker.internal:host-gateway' + environment: + BACK_PROXY_PASS: ${BACK_PROXY_PASS:-http://backend:4000} + FRONT_PROXY_PASS: ${FRONT_PROXY_PASS:-http://frontend:3000} + ports: + - target: 80 + published: 80 From 8124204897b8fb49ad4017955554bfa3a7ede665 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 14:41:45 +0200 Subject: [PATCH 280/363] chore(deps): bump solc from 0.8.27 to 0.8.28 in /apps/explorer (#11105) Bumps [solc](https://github.com/ethereum/solc-js) from 0.8.27 to 0.8.28. - [Commits](https://github.com/ethereum/solc-js/compare/v0.8.27...v0.8.28) --- updated-dependencies: - dependency-name: solc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/explorer/package-lock.json | 14 +++++++------- apps/explorer/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/explorer/package-lock.json b/apps/explorer/package-lock.json index 64c4a8c66375..9541d6d4f437 100644 --- a/apps/explorer/package-lock.json +++ b/apps/explorer/package-lock.json @@ -7,7 +7,7 @@ "name": "blockscout", "license": "GPL-3.0", "dependencies": { - "solc": "0.8.27" + "solc": "0.8.28" }, "engines": { "node": "18.x", @@ -76,9 +76,9 @@ } }, "node_modules/solc": { - "version": "0.8.27", - "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.27.tgz", - "integrity": "sha512-BNxMol2tUAbkH7HKlXBcBqrGi2aqgv+uMHz26mJyTtlVgWmBA4ktiw0qVKHfkjf2oaHbwtbtaSeE2dhn/gTAKw==", + "version": "0.8.28", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.28.tgz", + "integrity": "sha512-AFCiJ+b4RosyyNhnfdVH4ZR1+TxiL91iluPjw0EJslIu4LXGM9NYqi2z5y8TqochC4tcH9QsHfwWhOIC9jPDKA==", "dependencies": { "command-exists": "^1.2.8", "commander": "^8.1.0", @@ -144,9 +144,9 @@ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" }, "solc": { - "version": "0.8.27", - "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.27.tgz", - "integrity": "sha512-BNxMol2tUAbkH7HKlXBcBqrGi2aqgv+uMHz26mJyTtlVgWmBA4ktiw0qVKHfkjf2oaHbwtbtaSeE2dhn/gTAKw==", + "version": "0.8.28", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.28.tgz", + "integrity": "sha512-AFCiJ+b4RosyyNhnfdVH4ZR1+TxiL91iluPjw0EJslIu4LXGM9NYqi2z5y8TqochC4tcH9QsHfwWhOIC9jPDKA==", "requires": { "command-exists": "^1.2.8", "commander": "^8.1.0", diff --git a/apps/explorer/package.json b/apps/explorer/package.json index 9389c1f2e266..65541b1d7df3 100644 --- a/apps/explorer/package.json +++ b/apps/explorer/package.json @@ -13,6 +13,6 @@ }, "scripts": {}, "dependencies": { - "solc": "0.8.27" + "solc": "0.8.28" } } From 85fe2d6e477aa02b492522119beff849078b2904 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 14:42:11 +0200 Subject: [PATCH 281/363] chore(deps): bump floki from 0.36.2 to 0.36.3 (#11061) Bumps [floki](https://github.com/philss/floki) from 0.36.2 to 0.36.3. - [Release notes](https://github.com/philss/floki/releases) - [Changelog](https://github.com/philss/floki/blob/main/CHANGELOG.md) - [Commits](https://github.com/philss/floki/compare/v0.36.2...v0.36.3) --- updated-dependencies: - dependency-name: floki dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index fe3eb84045d5..e4f8296db1d8 100644 --- a/mix.lock +++ b/mix.lock @@ -63,7 +63,7 @@ "file_info": {:hex, :file_info, "0.0.4", "2e0e77f211e833f38ead22cb29ce53761d457d80b3ffe0ffe0eb93880b0963b2", [:mix], [{:mimetype_parser, "~> 0.1.2", [hex: :mimetype_parser, repo: "hexpm", optional: false]}], "hexpm", "50e7ad01c2c8b9339010675fe4dc4a113b8d6ca7eddce24d1d74fd0e762781a5"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, - "floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"}, + "floki": {:hex, :floki, "0.36.3", "1102f93b16a55bc5383b85ae3ec470f82dee056eaeff9195e8afdf0ef2a43c30", [:mix], [], "hexpm", "fe0158bff509e407735f6d40b3ee0d7deb47f3f3ee7c6c182ad28599f9f6b27a"}, "flow": {:hex, :flow, "1.2.4", "1dd58918287eb286656008777cb32714b5123d3855956f29aa141ebae456922d", [:mix], [{:gen_stage, "~> 1.0", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm", "874adde96368e71870f3510b91e35bc31652291858c86c0e75359cbdd35eb211"}, "gen_stage": {:hex, :gen_stage, "1.2.1", "19d8b5e9a5996d813b8245338a28246307fd8b9c99d1237de199d21efc4c76a1", [:mix], [], "hexpm", "83e8be657fa05b992ffa6ac1e3af6d57aa50aace8f691fcf696ff02f8335b001"}, "gettext": {:hex, :gettext, "0.26.1", "38e14ea5dcf962d1fc9f361b63ea07c0ce715a8ef1f9e82d3dfb8e67e0416715", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "01ce56f188b9dc28780a52783d6529ad2bc7124f9744e571e1ee4ea88bf08734"}, From c057ec3eed18794fd7b46c6f264125bbe7140ff3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 14:42:33 +0200 Subject: [PATCH 282/363] chore(deps): bump chart.js in /apps/block_scout_web/assets (#11108) Bumps [chart.js](https://github.com/chartjs/Chart.js) from 4.4.3 to 4.4.6. - [Release notes](https://github.com/chartjs/Chart.js/releases) - [Commits](https://github.com/chartjs/Chart.js/compare/v4.4.3...v4.4.6) --- updated-dependencies: - dependency-name: chart.js dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 6b8734412c9c..a12f40e6dd09 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -14,7 +14,7 @@ "assert": "^2.1.0", "bignumber.js": "^9.1.2", "bootstrap": "^4.6.0", - "chart.js": "^4.4.3", + "chart.js": "^4.4.6", "chartjs-adapter-luxon": "^1.3.1", "clipboard": "^2.0.11", "core-js": "^3.38.1", @@ -4985,9 +4985,9 @@ } }, "node_modules/chart.js": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", - "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.6.tgz", + "integrity": "sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==", "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -19662,9 +19662,9 @@ "dev": true }, "chart.js": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", - "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.6.tgz", + "integrity": "sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==", "requires": { "@kurkle/color": "^0.3.0" } diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 8bdc16465740..9cf74ace06e5 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -26,7 +26,7 @@ "assert": "^2.1.0", "bignumber.js": "^9.1.2", "bootstrap": "^4.6.0", - "chart.js": "^4.4.3", + "chart.js": "^4.4.6", "chartjs-adapter-luxon": "^1.3.1", "clipboard": "^2.0.11", "core-js": "^3.38.1", From 922c7f3564fc8243a6cde34b155a78ba01382f37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 14:43:13 +0200 Subject: [PATCH 283/363] chore(deps-dev): bump webpack in /apps/block_scout_web/assets (#11107) Bumps [webpack](https://github.com/webpack/webpack) from 5.95.0 to 5.96.1. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.95.0...v5.96.1) --- updated-dependencies: - dependency-name: webpack dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 178 ++++++++++-------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 102 insertions(+), 78 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index a12f40e6dd09..91a4ce2e9bd1 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -92,7 +92,7 @@ "sass": "^1.79.4", "sass-loader": "^14.2.1", "style-loader": "^4.0.0", - "webpack": "^5.95.0", + "webpack": "^5.96.1", "webpack-cli": "^5.1.4" }, "engines": { @@ -3156,10 +3156,30 @@ "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz", "integrity": "sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==" }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true }, "node_modules/@types/graceful-fs": { @@ -3761,9 +3781,9 @@ } }, "node_modules/acorn": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", - "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -3794,15 +3814,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "dev": true, - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -4705,9 +4716,9 @@ ] }, "node_modules/browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "funding": [ { "type": "opencollective", @@ -4723,10 +4734,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -4923,9 +4934,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001655", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz", - "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==", + "version": "1.0.30001676", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz", + "integrity": "sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==", "funding": [ { "type": "opencollective", @@ -6146,9 +6157,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", - "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==" + "version": "1.5.50", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz", + "integrity": "sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==" }, "node_modules/elliptic": { "version": "6.5.7", @@ -14818,9 +14829,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "funding": [ { "type": "opencollective", @@ -14836,8 +14847,8 @@ } ], "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -15491,18 +15502,18 @@ } }, "node_modules/webpack": { - "version": "5.95.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz", - "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==", + "version": "5.96.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", + "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", "dev": true, "dependencies": { - "@types/estree": "^1.0.5", + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", @@ -18193,10 +18204,30 @@ "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz", "integrity": "sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==" }, + "@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true }, "@types/graceful-fs": { @@ -18751,9 +18782,9 @@ } }, "acorn": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", - "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true }, "acorn-globals": { @@ -18774,13 +18805,6 @@ } } }, - "acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "dev": true, - "requires": {} - }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -19465,14 +19489,14 @@ } }, "browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "requires": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "update-browserslist-db": "^1.1.1" } }, "bs58": { @@ -19623,9 +19647,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001655", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz", - "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==" + "version": "1.0.30001676", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz", + "integrity": "sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==" }, "caseless": { "version": "0.12.0", @@ -20524,9 +20548,9 @@ } }, "electron-to-chromium": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", - "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==" + "version": "1.5.50", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz", + "integrity": "sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==" }, "elliptic": { "version": "6.5.7", @@ -27006,12 +27030,12 @@ "dev": true }, "update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "requires": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" } }, "uri-js": { @@ -27557,18 +27581,18 @@ "dev": true }, "webpack": { - "version": "5.95.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz", - "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==", + "version": "5.96.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", + "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", "dev": true, "requires": { - "@types/estree": "^1.0.5", + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 9cf74ace06e5..35d9238e1a6e 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -104,7 +104,7 @@ "sass": "^1.79.4", "sass-loader": "^14.2.1", "style-loader": "^4.0.0", - "webpack": "^5.95.0", + "webpack": "^5.96.1", "webpack-cli": "^5.1.4" }, "jest": { From 3b1396fa975fdc3b97abc9d62d5a20574af185af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 14:44:01 +0200 Subject: [PATCH 284/363] chore(deps-dev): bump mini-css-extract-plugin (#11110) Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 2.9.1 to 2.9.2. - [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases) - [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v2.9.1...v2.9.2) --- updated-dependencies: - dependency-name: mini-css-extract-plugin dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 91a4ce2e9bd1..1989b8f86bfd 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -86,7 +86,7 @@ "file-loader": "^6.2.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "mini-css-extract-plugin": "^2.9.1", + "mini-css-extract-plugin": "^2.9.2", "postcss": "^8.4.47", "postcss-loader": "^8.1.1", "sass": "^1.79.4", @@ -11567,9 +11567,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.1.tgz", - "integrity": "sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", + "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", "dev": true, "dependencies": { "schema-utils": "^4.0.0", @@ -24714,9 +24714,9 @@ } }, "mini-css-extract-plugin": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.1.tgz", - "integrity": "sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", + "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", "dev": true, "requires": { "schema-utils": "^4.0.0", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 35d9238e1a6e..bee4cbb784cf 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -98,7 +98,7 @@ "file-loader": "^6.2.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "mini-css-extract-plugin": "^2.9.1", + "mini-css-extract-plugin": "^2.9.2", "postcss": "^8.4.47", "postcss-loader": "^8.1.1", "sass": "^1.79.4", From 3e28c08651705f236b406ea5144c54775bdb71a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 14:47:29 +0200 Subject: [PATCH 285/363] chore(deps): bump @amplitude/analytics-browser (#11111) Bumps [@amplitude/analytics-browser](https://github.com/amplitude/Amplitude-TypeScript) from 2.9.3 to 2.11.8. - [Release notes](https://github.com/amplitude/Amplitude-TypeScript/releases) - [Commits](https://github.com/amplitude/Amplitude-TypeScript/compare/@amplitude/analytics-browser@2.9.3...@amplitude/analytics-browser@2.11.8) --- updated-dependencies: - dependency-name: "@amplitude/analytics-browser" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 282 +++++++++++++----- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 208 insertions(+), 76 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 1989b8f86bfd..dcdf9d3a1d2c 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.9.3", + "@amplitude/analytics-browser": "^2.11.8", "@fortawesome/fontawesome-free": "^6.6.0", "@tarekraafat/autocomplete.js": "^10.2.7", "@walletconnect/web3-provider": "^1.8.0", @@ -121,14 +121,16 @@ "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==" }, "node_modules/@amplitude/analytics-browser": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.9.3.tgz", - "integrity": "sha512-PnkJrJGRUfdE9nFgQZbWvUjNGnbcFojQROfj7KQeKGJTzQZXMt+xafmfXWDnhsf0JRgbk273Wh7Z6WNXxS1seA==", - "dependencies": { - "@amplitude/analytics-client-common": "^2.2.4", - "@amplitude/analytics-core": "^2.3.0", - "@amplitude/analytics-types": "^2.6.0", - "@amplitude/plugin-page-view-tracking-browser": "^2.2.17", + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.11.8.tgz", + "integrity": "sha512-lFv8deROLwBfSlg92+r1NitWJ6BN45IKwpPLoixA0fZytScXEJqc0Gl5O+BY4qScbFECYt9PFKblhB+jC+IvPg==", + "dependencies": { + "@amplitude/analytics-client-common": "^2.3.4", + "@amplitude/analytics-core": "^2.5.3", + "@amplitude/analytics-remote-config": "^0.4.0", + "@amplitude/analytics-types": "^2.8.3", + "@amplitude/plugin-autocapture-browser": "^1.0.2", + "@amplitude/plugin-page-view-tracking-browser": "^2.3.4", "tslib": "^2.4.1" } }, @@ -138,59 +140,102 @@ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" }, "node_modules/@amplitude/analytics-client-common": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.2.4.tgz", - "integrity": "sha512-+zOW3/Yb4LzK1DfhFCnSOcb8vgeZgIQffLM6yrgzKGedtQOnwDQeKTDI3aaMzckXQPVcJs1M6bJcT/l5TmAkDw==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.3.4.tgz", + "integrity": "sha512-3oqdvca5W4BPblTaxf60YRtlh2uC+N3rA99wowDAhTBJoMJJaauOBoXu5BbiQO1u8Zw/c8ymyr8E20+glyptUg==", "dependencies": { "@amplitude/analytics-connector": "^1.4.8", - "@amplitude/analytics-core": "^2.3.0", - "@amplitude/analytics-types": "^2.6.0", + "@amplitude/analytics-core": "^2.5.3", + "@amplitude/analytics-types": "^2.8.3", "tslib": "^2.4.1" } }, "node_modules/@amplitude/analytics-client-common/node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@amplitude/analytics-connector": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-connector/-/analytics-connector-1.5.0.tgz", - "integrity": "sha512-T8mOYzB9RRxckzhL0NTHwdge9xuFxXEOplC8B1Y3UX3NHa3BLh7DlBUZlCOwQgMc2nxDfnSweDL5S3bhC+W90g==" + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-connector/-/analytics-connector-1.6.1.tgz", + "integrity": "sha512-QAGeOfBQc3tamwcECu6YqAPD4mFI1TLBoWi+n0iViYWUZma2FeDLPMihwIquxI8CVvqpn4gswFZsIPRit3q9tQ==", + "dependencies": { + "@amplitude/experiment-core": "^0.10.0" + } }, "node_modules/@amplitude/analytics-core": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.3.0.tgz", - "integrity": "sha512-Knafvocs29cPOHM3GHyBkP4nXUrh/oXnj2fULoSyh/03Bt63UPoyQCNS/EtQB/dOUhapTP35ZU9yZnrY+jvndQ==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.5.3.tgz", + "integrity": "sha512-dvx3PS0adnHRS22VbuP9YtWg//bQGF2c61Pj5IYXVsemtRRHqiS7XJ860brk3WeQgOkqf3Gyc023DoYcsWGoNQ==", "dependencies": { - "@amplitude/analytics-types": "^2.6.0", + "@amplitude/analytics-types": "^2.8.3", "tslib": "^2.4.1" } }, "node_modules/@amplitude/analytics-core/node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/@amplitude/analytics-remote-config": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-remote-config/-/analytics-remote-config-0.4.1.tgz", + "integrity": "sha512-BYl6kQ9qjztrCACsugpxO+foLaQIC0aSEzoXEAb/gwOzInmqkyyI+Ub+aWTBih4xgB/lhWlOcidWHAmNiTJTNw==", + "dependencies": { + "@amplitude/analytics-client-common": ">=1 <3", + "@amplitude/analytics-core": ">=1 <3", + "@amplitude/analytics-types": ">=1 <3", + "tslib": "^2.4.1" + } + }, + "node_modules/@amplitude/analytics-remote-config/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@amplitude/analytics-types": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.6.0.tgz", - "integrity": "sha512-7MSENvLCTGjec7K45JT+RcOuoPTCvq1MMq/HRLiQK/BMR4taX7f/uXldEc8b//o+ZZP45IBqFroR7Bl8LwJQrQ==" + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.8.3.tgz", + "integrity": "sha512-HNmKVd0ACoi3xTi86xi+is7WgqKT78JA4fYLcM25/ckFkZ1zVCqD1AubaADEh26m34nJ3qDLK5Pob4QptQNPAg==" + }, + "node_modules/@amplitude/experiment-core": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@amplitude/experiment-core/-/experiment-core-0.10.0.tgz", + "integrity": "sha512-FBfM6a4aHp+7OYLYiHO3kni3vCThInT6o4ucuNIB+EIvQ141gQDSjMPOET3ik82fMuJPQITex9sdpZ6cO30ALw==", + "dependencies": { + "js-base64": "^3.7.5" + } + }, + "node_modules/@amplitude/plugin-autocapture-browser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-autocapture-browser/-/plugin-autocapture-browser-1.0.3.tgz", + "integrity": "sha512-XUQWUAw9VqtJPlmOyWjnhsEspyVakd9LuSjVNtLjhwlWv+f/yZM1AAQVUdq/Os1+b5OptSgJQ2pPfRJJHZDXTw==", + "dependencies": { + "@amplitude/analytics-client-common": ">=1 <3", + "@amplitude/analytics-types": "^2.8.2", + "rxjs": "^7.8.1", + "tslib": "^2.4.1" + } + }, + "node_modules/@amplitude/plugin-autocapture-browser/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@amplitude/plugin-page-view-tracking-browser": { - "version": "2.2.17", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.2.17.tgz", - "integrity": "sha512-s19LUuPMvOhvM/36XOUeVSMGOhtSOPA6bhhrR4x/lEsyylsS+rfi6/fILd2B5gojplXKMvVlJXEP+t1fjIr1HA==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.3.4.tgz", + "integrity": "sha512-l7RS5gssG0BPYlgirV0NQ94EPzTOdDkp0z2jqU45D3DQAJXkoloUyw5lw/cbUXYwNulHZTG/BExcERfdvVWkLA==", "dependencies": { - "@amplitude/analytics-client-common": "^2.2.4", - "@amplitude/analytics-types": "^2.6.0", + "@amplitude/analytics-client-common": "^2.3.4", + "@amplitude/analytics-types": "^2.8.3", "tslib": "^2.4.1" } }, "node_modules/@amplitude/plugin-page-view-tracking-browser/node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@ampproject/remapping": { "version": "2.2.0", @@ -10749,6 +10794,11 @@ "resolved": "https://registry.npmjs.org/jquery-mousewheel/-/jquery-mousewheel-3.1.13.tgz", "integrity": "sha512-GXhSjfOPyDemM005YCEHvzrEALhKDIswtxSHSR2e4K/suHVJKJxxRCGz3skPjNxjJjQa9AVSGGlYjv1M3VLIPg==" }, + "node_modules/js-base64": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", + "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==" + }, "node_modules/js-cookie": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", @@ -13646,6 +13696,19 @@ "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/safe-array-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", @@ -15993,14 +16056,16 @@ "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==" }, "@amplitude/analytics-browser": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.9.3.tgz", - "integrity": "sha512-PnkJrJGRUfdE9nFgQZbWvUjNGnbcFojQROfj7KQeKGJTzQZXMt+xafmfXWDnhsf0JRgbk273Wh7Z6WNXxS1seA==", - "requires": { - "@amplitude/analytics-client-common": "^2.2.4", - "@amplitude/analytics-core": "^2.3.0", - "@amplitude/analytics-types": "^2.6.0", - "@amplitude/plugin-page-view-tracking-browser": "^2.2.17", + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.11.8.tgz", + "integrity": "sha512-lFv8deROLwBfSlg92+r1NitWJ6BN45IKwpPLoixA0fZytScXEJqc0Gl5O+BY4qScbFECYt9PFKblhB+jC+IvPg==", + "requires": { + "@amplitude/analytics-client-common": "^2.3.4", + "@amplitude/analytics-core": "^2.5.3", + "@amplitude/analytics-remote-config": "^0.4.0", + "@amplitude/analytics-types": "^2.8.3", + "@amplitude/plugin-autocapture-browser": "^1.0.2", + "@amplitude/plugin-page-view-tracking-browser": "^2.3.4", "tslib": "^2.4.1" }, "dependencies": { @@ -16012,63 +16077,110 @@ } }, "@amplitude/analytics-client-common": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.2.4.tgz", - "integrity": "sha512-+zOW3/Yb4LzK1DfhFCnSOcb8vgeZgIQffLM6yrgzKGedtQOnwDQeKTDI3aaMzckXQPVcJs1M6bJcT/l5TmAkDw==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.3.4.tgz", + "integrity": "sha512-3oqdvca5W4BPblTaxf60YRtlh2uC+N3rA99wowDAhTBJoMJJaauOBoXu5BbiQO1u8Zw/c8ymyr8E20+glyptUg==", "requires": { "@amplitude/analytics-connector": "^1.4.8", - "@amplitude/analytics-core": "^2.3.0", - "@amplitude/analytics-types": "^2.6.0", + "@amplitude/analytics-core": "^2.5.3", + "@amplitude/analytics-types": "^2.8.3", "tslib": "^2.4.1" }, "dependencies": { "tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" } } }, "@amplitude/analytics-connector": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-connector/-/analytics-connector-1.5.0.tgz", - "integrity": "sha512-T8mOYzB9RRxckzhL0NTHwdge9xuFxXEOplC8B1Y3UX3NHa3BLh7DlBUZlCOwQgMc2nxDfnSweDL5S3bhC+W90g==" + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-connector/-/analytics-connector-1.6.1.tgz", + "integrity": "sha512-QAGeOfBQc3tamwcECu6YqAPD4mFI1TLBoWi+n0iViYWUZma2FeDLPMihwIquxI8CVvqpn4gswFZsIPRit3q9tQ==", + "requires": { + "@amplitude/experiment-core": "^0.10.0" + } }, "@amplitude/analytics-core": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.3.0.tgz", - "integrity": "sha512-Knafvocs29cPOHM3GHyBkP4nXUrh/oXnj2fULoSyh/03Bt63UPoyQCNS/EtQB/dOUhapTP35ZU9yZnrY+jvndQ==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.5.3.tgz", + "integrity": "sha512-dvx3PS0adnHRS22VbuP9YtWg//bQGF2c61Pj5IYXVsemtRRHqiS7XJ860brk3WeQgOkqf3Gyc023DoYcsWGoNQ==", "requires": { - "@amplitude/analytics-types": "^2.6.0", + "@amplitude/analytics-types": "^2.8.3", "tslib": "^2.4.1" }, "dependencies": { "tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + } + } + }, + "@amplitude/analytics-remote-config": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-remote-config/-/analytics-remote-config-0.4.1.tgz", + "integrity": "sha512-BYl6kQ9qjztrCACsugpxO+foLaQIC0aSEzoXEAb/gwOzInmqkyyI+Ub+aWTBih4xgB/lhWlOcidWHAmNiTJTNw==", + "requires": { + "@amplitude/analytics-client-common": ">=1 <3", + "@amplitude/analytics-core": ">=1 <3", + "@amplitude/analytics-types": ">=1 <3", + "tslib": "^2.4.1" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" } } }, "@amplitude/analytics-types": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.6.0.tgz", - "integrity": "sha512-7MSENvLCTGjec7K45JT+RcOuoPTCvq1MMq/HRLiQK/BMR4taX7f/uXldEc8b//o+ZZP45IBqFroR7Bl8LwJQrQ==" + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.8.3.tgz", + "integrity": "sha512-HNmKVd0ACoi3xTi86xi+is7WgqKT78JA4fYLcM25/ckFkZ1zVCqD1AubaADEh26m34nJ3qDLK5Pob4QptQNPAg==" + }, + "@amplitude/experiment-core": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@amplitude/experiment-core/-/experiment-core-0.10.0.tgz", + "integrity": "sha512-FBfM6a4aHp+7OYLYiHO3kni3vCThInT6o4ucuNIB+EIvQ141gQDSjMPOET3ik82fMuJPQITex9sdpZ6cO30ALw==", + "requires": { + "js-base64": "^3.7.5" + } + }, + "@amplitude/plugin-autocapture-browser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-autocapture-browser/-/plugin-autocapture-browser-1.0.3.tgz", + "integrity": "sha512-XUQWUAw9VqtJPlmOyWjnhsEspyVakd9LuSjVNtLjhwlWv+f/yZM1AAQVUdq/Os1+b5OptSgJQ2pPfRJJHZDXTw==", + "requires": { + "@amplitude/analytics-client-common": ">=1 <3", + "@amplitude/analytics-types": "^2.8.2", + "rxjs": "^7.8.1", + "tslib": "^2.4.1" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + } + } }, "@amplitude/plugin-page-view-tracking-browser": { - "version": "2.2.17", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.2.17.tgz", - "integrity": "sha512-s19LUuPMvOhvM/36XOUeVSMGOhtSOPA6bhhrR4x/lEsyylsS+rfi6/fILd2B5gojplXKMvVlJXEP+t1fjIr1HA==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.3.4.tgz", + "integrity": "sha512-l7RS5gssG0BPYlgirV0NQ94EPzTOdDkp0z2jqU45D3DQAJXkoloUyw5lw/cbUXYwNulHZTG/BExcERfdvVWkLA==", "requires": { - "@amplitude/analytics-client-common": "^2.2.4", - "@amplitude/analytics-types": "^2.6.0", + "@amplitude/analytics-client-common": "^2.3.4", + "@amplitude/analytics-types": "^2.8.3", "tslib": "^2.4.1" }, "dependencies": { "tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" } } }, @@ -24001,6 +24113,11 @@ "resolved": "https://registry.npmjs.org/jquery-mousewheel/-/jquery-mousewheel-3.1.13.tgz", "integrity": "sha512-GXhSjfOPyDemM005YCEHvzrEALhKDIswtxSHSR2e4K/suHVJKJxxRCGz3skPjNxjJjQa9AVSGGlYjv1M3VLIPg==" }, + "js-base64": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", + "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==" + }, "js-cookie": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", @@ -26179,6 +26296,21 @@ "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" }, + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "requires": { + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + } + } + }, "safe-array-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index bee4cbb784cf..9515d6271571 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.6.0", - "@amplitude/analytics-browser": "^2.9.3", + "@amplitude/analytics-browser": "^2.11.8", "@tarekraafat/autocomplete.js": "^10.2.7", "@walletconnect/web3-provider": "^1.8.0", "assert": "^2.1.0", From 7d16504229643d8f8da6655cf278b68efbdab660 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 14:47:55 +0200 Subject: [PATCH 286/363] chore(deps): bump phoenix_ecto from 4.6.2 to 4.6.3 (#11059) Bumps [phoenix_ecto](https://github.com/phoenixframework/phoenix_ecto) from 4.6.2 to 4.6.3. - [Changelog](https://github.com/phoenixframework/phoenix_ecto/blob/main/CHANGELOG.md) - [Commits](https://github.com/phoenixframework/phoenix_ecto/compare/v4.6.2...v4.6.3) --- updated-dependencies: - dependency-name: phoenix_ecto dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.lock b/mix.lock index e4f8296db1d8..156722563e94 100644 --- a/mix.lock +++ b/mix.lock @@ -108,7 +108,7 @@ "parallel_stream": {:hex, :parallel_stream, "1.1.0", "f52f73eb344bc22de335992377413138405796e0d0ad99d995d9977ac29f1ca9", [:mix], [], "hexpm", "684fd19191aedfaf387bbabbeb8ff3c752f0220c8112eb907d797f4592d6e871"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "phoenix": {:hex, :phoenix, "1.5.14", "2d5db884be496eefa5157505ec0134e66187cb416c072272420c5509d67bf808", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "207f1aa5520320cbb7940d7ff2dde2342162cf513875848f88249ea0ba02fef7"}, - "phoenix_ecto": {:hex, :phoenix_ecto, "4.6.2", "3b83b24ab5a2eb071a20372f740d7118767c272db386831b2e77638c4dcc606d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "3f94d025f59de86be00f5f8c5dd7b5965a3298458d21ab1c328488be3b5fcd59"}, + "phoenix_ecto": {:hex, :phoenix_ecto, "4.6.3", "f686701b0499a07f2e3b122d84d52ff8a31f5def386e03706c916f6feddf69ef", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "909502956916a657a197f94cc1206d9a65247538de8a5e186f7537c895d95764"}, "phoenix_html": {:hex, :phoenix_html, "3.0.4", "232d41884fe6a9c42d09f48397c175cd6f0d443aaa34c7424da47604201df2e1", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "ce17fd3cf815b2ed874114073e743507704b1f5288bb03c304a77458485efc8b"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, @@ -117,7 +117,7 @@ "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, - "postgrex": {:hex, :postgrex, "0.19.1", "73b498508b69aded53907fe48a1fee811be34cc720e69ef4ccd568c8715495ea", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "8bac7885a18f381e091ec6caf41bda7bb8c77912bb0e9285212829afe5d8a8f8"}, + "postgrex": {:hex, :postgrex, "0.19.2", "34d6884a332c7bf1e367fc8b9a849d23b43f7da5c6e263def92784d03f9da468", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "618988886ab7ae8561ebed9a3c7469034bf6a88b8995785a3378746a4b9835ec"}, "prometheus": {:hex, :prometheus, "4.11.0", "b95f8de8530f541bd95951e18e355a840003672e5eda4788c5fa6183406ba29a", [:mix, :rebar3], [{:quantile_estimator, "~> 0.2.1", [hex: :quantile_estimator, repo: "hexpm", optional: false]}], "hexpm", "719862351aabf4df7079b05dc085d2bbcbe3ac0ac3009e956671b1d5ab88247d"}, "prometheus_ecto": {:hex, :prometheus_ecto, "1.4.3", "3dd4da1812b8e0dbee81ea58bb3b62ed7588f2eae0c9e97e434c46807ff82311", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm", "8d66289f77f913b37eda81fd287340c17e61a447549deb28efc254532b2bed82"}, "prometheus_ex": {:git, "https://github.com/lanodan/prometheus.ex", "31f7fbe4b71b79ba27efc2a5085746c4011ceb8f", [branch: "fix/elixir-1.14"]}, From 91e7d7435b101d35cf5cac295f7e4a05e8cd3945 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 14:49:17 +0200 Subject: [PATCH 287/363] chore(deps-dev): bump babel-loader in /apps/block_scout_web/assets (#11112) Bumps [babel-loader](https://github.com/babel/babel-loader) from 9.1.3 to 9.2.1. - [Release notes](https://github.com/babel/babel-loader/releases) - [Changelog](https://github.com/babel/babel-loader/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel-loader/compare/v9.1.3...v9.2.1) --- updated-dependencies: - dependency-name: babel-loader dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 dcdf9d3a1d2c..5f7815f8a767 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -74,7 +74,7 @@ "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.4", "autoprefixer": "^10.4.20", - "babel-loader": "^9.1.3", + "babel-loader": "^9.2.1", "copy-webpack-plugin": "^12.0.2", "css-loader": "^7.1.2", "css-minimizer-webpack-plugin": "^7.0.0", @@ -4366,9 +4366,9 @@ } }, "node_modules/babel-loader": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", - "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", + "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", "dev": true, "dependencies": { "find-cache-dir": "^4.0.0", @@ -19293,9 +19293,9 @@ } }, "babel-loader": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", - "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", + "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", "dev": true, "requires": { "find-cache-dir": "^4.0.0", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 9515d6271571..347a568fd087 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -86,7 +86,7 @@ "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.4", "autoprefixer": "^10.4.20", - "babel-loader": "^9.1.3", + "babel-loader": "^9.2.1", "copy-webpack-plugin": "^12.0.2", "css-loader": "^7.1.2", "css-minimizer-webpack-plugin": "^7.0.0", From 8b422063da94fd17e55c7a606e2cd502ef1cf499 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 14:53:58 +0200 Subject: [PATCH 288/363] chore(deps-dev): bump eslint-plugin-import (#11109) Bumps [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import) from 2.29.1 to 2.31.0. - [Release notes](https://github.com/import-js/eslint-plugin-import/releases) - [Changelog](https://github.com/import-js/eslint-plugin-import/blob/main/CHANGELOG.md) - [Commits](https://github.com/import-js/eslint-plugin-import/compare/v2.29.1...v2.31.0) --- updated-dependencies: - dependency-name: eslint-plugin-import dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 1098 ++++++++++------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 663 insertions(+), 437 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 5f7815f8a767..5748f8543231 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -80,7 +80,7 @@ "css-minimizer-webpack-plugin": "^7.0.0", "eslint": "^8.57.1", "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.29.1", + "eslint-plugin-import": "^2.31.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^6.6.0", "file-loader": "^6.2.0", @@ -3050,6 +3050,12 @@ "rrweb-snapshot": "^2.0.0-alpha.14" } }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, "node_modules/@scure/base": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.8.tgz", @@ -4015,28 +4021,32 @@ } }, "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", "is-string": "^1.0.7" }, "engines": { @@ -4047,16 +4057,17 @@ } }, "node_modules/array.prototype.findlastindex": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", - "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4102,17 +4113,18 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", "is-shared-array-buffer": "^1.0.2" }, "engines": { @@ -4251,9 +4263,12 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -5921,6 +5936,57 @@ "node": ">=12" } }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/debug": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", @@ -6024,10 +6090,11 @@ } }, "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dependencies": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" }, @@ -6342,50 +6409,57 @@ } }, "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", + "is-shared-array-buffer": "^1.0.3", "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", + "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", "object-inspect": "^1.13.1", "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" + "which-typed-array": "^1.1.15" }, "engines": { "node": ">= 0.4" @@ -6419,15 +6493,27 @@ "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==", "dev": true }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" }, "engines": { "node": ">= 0.4" @@ -6662,9 +6748,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "dev": true, "dependencies": { "debug": "^3.2.7" @@ -6708,34 +6794,36 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", "array.prototype.flat": "^1.3.2", "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", "tsconfig-paths": "^3.15.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "node_modules/eslint-plugin-import/node_modules/debug": { @@ -8085,13 +8173,14 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" }, "engines": { "node": ">= 0.4" @@ -8303,9 +8392,9 @@ } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "engines": { "node": ">= 0.4" }, @@ -8325,11 +8414,11 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -8380,9 +8469,9 @@ } }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { "function-bind": "^1.1.2" }, @@ -8600,12 +8689,12 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2", + "es-errors": "^1.3.0", "hasown": "^2.0.0", "side-channel": "^1.0.4" }, @@ -8638,14 +8727,16 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8697,11 +8788,29 @@ } }, "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8813,9 +8922,9 @@ } }, "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "engines": { "node": ">= 0.4" @@ -8892,12 +9001,15 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8946,11 +9058,11 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dependencies": { - "which-typed-array": "^1.1.11" + "which-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -11901,12 +12013,12 @@ } }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, @@ -11918,14 +12030,15 @@ } }, "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -11935,26 +12048,28 @@ } }, "node_modules/object.groupby": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", - "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -12356,6 +12471,14 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { "version": "8.4.47", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", @@ -13403,14 +13526,15 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -13710,13 +13834,13 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -13748,15 +13872,18 @@ } }, "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", "is-regex": "^1.1.4" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -13978,14 +14105,15 @@ } }, "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, "dependencies": { - "define-data-property": "^1.0.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -14254,14 +14382,15 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -14271,28 +14400,31 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -14739,29 +14871,30 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -14771,16 +14904,17 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -14790,14 +14924,20 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -15792,15 +15932,15 @@ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" }, "node_modules/which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -18197,6 +18337,12 @@ "rrweb-snapshot": "^2.0.0-alpha.14" } }, + "@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, "@scure/base": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.8.tgz", @@ -19034,39 +19180,41 @@ } }, "array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" } }, "array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", "is-string": "^1.0.7" } }, "array.prototype.findlastindex": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", - "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" } }, "array.prototype.flat": { @@ -19094,17 +19242,18 @@ } }, "arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "dev": true, "requires": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", "is-shared-array-buffer": "^1.0.2" } }, @@ -19212,9 +19361,12 @@ } }, "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "requires": { + "possible-typed-array-names": "^1.0.0" + } }, "aws-sign2": { "version": "0.7.0", @@ -20449,6 +20601,39 @@ "whatwg-url": "^11.0.0" } }, + "data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "requires": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "requires": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, "debug": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", @@ -20519,10 +20704,11 @@ } }, "define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "requires": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } @@ -20771,50 +20957,57 @@ } }, "es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, "requires": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", + "is-shared-array-buffer": "^1.0.3", "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", + "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", "object-inspect": "^1.13.1", "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" + "which-typed-array": "^1.1.15" } }, "es-define-property": { @@ -20836,15 +21029,24 @@ "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==", "dev": true }, + "es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "requires": { + "es-errors": "^1.3.0" + } + }, "es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, "requires": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" } }, "es-shim-unscopables": { @@ -21150,9 +21352,9 @@ } }, "eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "dev": true, "requires": { "debug": "^3.2.7" @@ -21181,27 +21383,29 @@ } }, "eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, "requires": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", "array.prototype.flat": "^1.3.2", "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", "tsconfig-paths": "^3.15.0" }, "dependencies": { @@ -22145,13 +22349,14 @@ "dev": true }, "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" } }, "getpass": { @@ -22304,9 +22509,9 @@ } }, "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" }, "has-symbols": { "version": "1.0.3", @@ -22314,11 +22519,11 @@ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "requires": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" } }, "hash-base": { @@ -22348,9 +22553,9 @@ } }, "hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "requires": { "function-bind": "^1.1.2" } @@ -22511,12 +22716,12 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, "requires": { - "get-intrinsic": "^1.2.2", + "es-errors": "^1.3.0", "hasown": "^2.0.0", "side-channel": "^1.0.4" } @@ -22537,14 +22742,13 @@ } }, "is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, "requires": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "get-intrinsic": "^1.2.1" } }, "is-arrayish": { @@ -22578,11 +22782,20 @@ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" }, "is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "requires": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + } + }, + "is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "requires": { + "is-typed-array": "^1.1.13" } }, "is-date-object": { @@ -22654,9 +22867,9 @@ } }, "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true }, "is-number": { @@ -22706,12 +22919,12 @@ } }, "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, "requires": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" } }, "is-stream": { @@ -22739,11 +22952,11 @@ } }, "is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "requires": { - "which-typed-array": "^1.1.11" + "which-typed-array": "^1.1.14" } }, "is-typedarray": { @@ -25040,48 +25253,48 @@ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } }, "object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" } }, "object.groupby": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", - "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" } }, "object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" } }, "once": { @@ -25363,6 +25576,11 @@ "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" }, + "possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==" + }, "postcss": { "version": "8.4.47", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", @@ -26076,14 +26294,15 @@ } }, "regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.2" } }, "regexpp": { @@ -26312,13 +26531,13 @@ } }, "safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -26345,13 +26564,13 @@ } }, "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", "is-regex": "^1.1.4" } }, @@ -26509,14 +26728,15 @@ } }, "set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, "requires": { - "define-data-property": "^1.0.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" } }, "set-immediate-shim": { @@ -26719,36 +26939,37 @@ } }, "string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" } }, "string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" } }, "string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" } }, "strip-ansi": { @@ -27055,50 +27276,55 @@ "dev": true }, "typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" } }, "typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, "requires": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" } }, "typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" } }, "typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, "requires": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" } }, "typedarray-to-buffer": { @@ -27870,15 +28096,15 @@ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" }, "which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "has-tostringtag": "^1.0.2" } }, "wildcard": { diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 347a568fd087..0e6eea005167 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -92,7 +92,7 @@ "css-minimizer-webpack-plugin": "^7.0.0", "eslint": "^8.57.1", "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.29.1", + "eslint-plugin-import": "^2.31.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^6.6.0", "file-loader": "^6.2.0", From 86748cf2947a27925bf2e529d29a1611a00181c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 14:54:30 +0200 Subject: [PATCH 289/363] chore(deps): bump tesla from 1.12.2 to 1.13.0 (#11057) Bumps [tesla](https://github.com/elixir-tesla/tesla) from 1.12.2 to 1.13.0. - [Release notes](https://github.com/elixir-tesla/tesla/releases) - [Commits](https://github.com/elixir-tesla/tesla/compare/v1.12.2...v1.13.0) --- updated-dependencies: - dependency-name: tesla dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/explorer/mix.exs | 2 +- mix.exs | 2 +- mix.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index d49c61e1c083..c07e1b385212 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -114,7 +114,7 @@ defmodule Explorer.Mixfile do # `Timex.Duration` for `Explorer.Counters.AverageBlockTime.average_block_time/0` {:timex, "~> 3.7.1"}, {:con_cache, "~> 1.0"}, - {:tesla, "~> 1.12.1"}, + {:tesla, "~> 1.13.0"}, {:cbor, "~> 1.0"}, {:cloak_ecto, "~> 1.3.0"}, {:redix, "~> 1.1"}, diff --git a/mix.exs b/mix.exs index 234d0c2a674c..52e7aa844cb6 100644 --- a/mix.exs +++ b/mix.exs @@ -94,7 +94,7 @@ defmodule BlockScout.Mixfile do [ {:prometheus_ex, git: "https://github.com/lanodan/prometheus.ex", branch: "fix/elixir-1.14", override: true}, {:absinthe_plug, git: "https://github.com/blockscout/absinthe_plug.git", tag: "1.5.8", override: true}, - {:tesla, "~> 1.12.1"}, + {:tesla, "~> 1.13.0"}, # Documentation {:ex_doc, "~> 0.34.1", only: :dev, runtime: false}, {:number, "~> 1.0.3"} diff --git a/mix.lock b/mix.lock index 156722563e94..a858f633e33a 100644 --- a/mix.lock +++ b/mix.lock @@ -142,7 +142,7 @@ "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, - "tesla": {:hex, :tesla, "1.12.2", "1399c2dada208e381fec752e8fc21d6aa646c0a1f7cec8276484324bcba11b1e", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "36bbea8b6e94fa9d089118f31afc622cf4139e3bcd035773b85ca0c6e96e94c1"}, + "tesla": {:hex, :tesla, "1.13.0", "24a068a48d107080dd7c943a593997eee265977a38020eb2ab657cca78a12502", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:mox, "~> 1.0", [hex: :mox, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "7b8fc8f6b0640fa0d090af7889d12eb396460e044b6f8688a8e55e30406a2200"}, "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, "toml": {:hex, :toml, "0.6.2", "38f445df384a17e5d382befe30e3489112a48d3ba4c459e543f748c2f25dd4d1", [:mix], [], "hexpm", "d013e45126d74c0c26a38d31f5e8e9b83ea19fc752470feb9a86071ca5a672fa"}, "typed_ecto_schema": {:hex, :typed_ecto_schema, "0.4.1", "a373ca6f693f4de84cde474a67467a9cb9051a8a7f3f615f1e23dc74b75237fa", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm", "85c6962f79d35bf543dd5659c6adc340fd2480cacc6f25d2cc2933ea6e8fcb3b"}, From 3a00cff5ffd735e506443817ac1bec6f540d928e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Nov 2024 14:55:09 +0200 Subject: [PATCH 290/363] chore(deps): bump oauth2 from 2.0.1 to 2.1.0 (#11060) Bumps [oauth2](https://github.com/scrogson/oauth2) from 2.0.1 to 2.1.0. - [Release notes](https://github.com/scrogson/oauth2/releases) - [Changelog](https://github.com/ueberauth/oauth2/blob/master/CHANGELOG.md) - [Commits](https://github.com/scrogson/oauth2/compare/v2.0.1...v2.1.0) --- updated-dependencies: - dependency-name: oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index a858f633e33a..933f1d60b153 100644 --- a/mix.lock +++ b/mix.lock @@ -103,7 +103,7 @@ "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "number": {:hex, :number, "1.0.5", "d92136f9b9382aeb50145782f116112078b3465b7be58df1f85952b8bb399b0f", [:mix], [{:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "c0733a0a90773a66582b9e92a3f01290987f395c972cb7d685f51dd927cd5169"}, "numbers": {:hex, :numbers, "5.2.4", "f123d5bb7f6acc366f8f445e10a32bd403c8469bdbce8ce049e1f0972b607080", [:mix], [{:coerce, "~> 1.0", [hex: :coerce, repo: "hexpm", optional: false]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "eeccf5c61d5f4922198395bf87a465b6f980b8b862dd22d28198c5e6fab38582"}, - "oauth2": {:hex, :oauth2, "2.0.1", "70729503e05378697b958919bb2d65b002ba6b28c8112328063648a9348aaa3f", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "c64e20d4d105bcdbcbe03170fb530d0eddc3a3e6b135a87528a22c8aecf74c52"}, + "oauth2": {:hex, :oauth2, "2.1.0", "beb657f393814a3a7a8a15bd5e5776ecae341fd344df425342a3b6f1904c2989", [:mix], [{:tesla, "~> 1.5", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "8ac07f85b3307dd1acfeb0ec852f64161b22f57d0ce0c15e616a1dfc8ebe2b41"}, "optimal": {:hex, :optimal, "0.3.6", "46bbf52fbbbd238cda81e02560caa84f93a53c75620f1fe19e81e4ae7b07d1dd", [:mix], [], "hexpm", "1a06ea6a653120226b35b283a1cd10039550f2c566edcdec22b29316d73640fd"}, "parallel_stream": {:hex, :parallel_stream, "1.1.0", "f52f73eb344bc22de335992377413138405796e0d0ad99d995d9977ac29f1ca9", [:mix], [], "hexpm", "684fd19191aedfaf387bbabbeb8ff3c752f0220c8112eb907d797f4592d6e871"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, From 5331f6f51cef45fb260507d3156630ef9d31e8ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 09:54:13 +0200 Subject: [PATCH 291/363] chore(deps): bump @tarekraafat/autocomplete.js (#11113) Bumps [@tarekraafat/autocomplete.js](https://github.com/TarekRaafat/autoComplete.js) from 10.2.7 to 10.2.9. - [Release notes](https://github.com/TarekRaafat/autoComplete.js/releases) - [Changelog](https://github.com/TarekRaafat/autoComplete.js/blob/master/docs/release-notes.md) - [Commits](https://github.com/TarekRaafat/autoComplete.js/compare/v10.2.7...v10.2.9) --- updated-dependencies: - dependency-name: "@tarekraafat/autocomplete.js" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 5748f8543231..de8811d03514 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -9,7 +9,7 @@ "dependencies": { "@amplitude/analytics-browser": "^2.11.8", "@fortawesome/fontawesome-free": "^6.6.0", - "@tarekraafat/autocomplete.js": "^10.2.7", + "@tarekraafat/autocomplete.js": "^10.2.9", "@walletconnect/web3-provider": "^1.8.0", "assert": "^2.1.0", "bignumber.js": "^9.1.2", @@ -3126,9 +3126,9 @@ } }, "node_modules/@tarekraafat/autocomplete.js": { - "version": "10.2.7", - "resolved": "https://registry.npmjs.org/@tarekraafat/autocomplete.js/-/autocomplete.js-10.2.7.tgz", - "integrity": "sha512-iE+dnXI8/LrTaSORrnNdSyXg/bFCbCpz/R5GUdB3ioW+9PVEhglxNcSDQNeCXtrbRG0kOBFUd4unEiwcmqyn8A==", + "version": "10.2.9", + "resolved": "https://registry.npmjs.org/@tarekraafat/autocomplete.js/-/autocomplete.js-10.2.9.tgz", + "integrity": "sha512-A7OP3iJDTWeO85M3Vxu391acu9SmDguormHpMZ13khuyM180dKl9O1gAXSDA322XwkYuUU1Ad7WchW1TQNNuDw==", "funding": [ { "type": "opencollective", @@ -18398,9 +18398,9 @@ } }, "@tarekraafat/autocomplete.js": { - "version": "10.2.7", - "resolved": "https://registry.npmjs.org/@tarekraafat/autocomplete.js/-/autocomplete.js-10.2.7.tgz", - "integrity": "sha512-iE+dnXI8/LrTaSORrnNdSyXg/bFCbCpz/R5GUdB3ioW+9PVEhglxNcSDQNeCXtrbRG0kOBFUd4unEiwcmqyn8A==" + "version": "10.2.9", + "resolved": "https://registry.npmjs.org/@tarekraafat/autocomplete.js/-/autocomplete.js-10.2.9.tgz", + "integrity": "sha512-A7OP3iJDTWeO85M3Vxu391acu9SmDguormHpMZ13khuyM180dKl9O1gAXSDA322XwkYuUU1Ad7WchW1TQNNuDw==" }, "@trysound/sax": { "version": "0.2.0", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 0e6eea005167..b76ffced9a28 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -21,7 +21,7 @@ "dependencies": { "@fortawesome/fontawesome-free": "^6.6.0", "@amplitude/analytics-browser": "^2.11.8", - "@tarekraafat/autocomplete.js": "^10.2.7", + "@tarekraafat/autocomplete.js": "^10.2.9", "@walletconnect/web3-provider": "^1.8.0", "assert": "^2.1.0", "bignumber.js": "^9.1.2", From 2c3fd02ce4c44b7c455fbf086f81117b6aaf52d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 09:54:30 +0200 Subject: [PATCH 292/363] chore(deps): bump elliptic in /apps/block_scout_web/assets (#11068) Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.7 to 6.6.0. - [Commits](https://github.com/indutny/elliptic/compare/v6.5.7...v6.6.0) --- updated-dependencies: - dependency-name: elliptic dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 de8811d03514..af2cc4f17cd0 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -6274,9 +6274,9 @@ "integrity": "sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==" }, "node_modules/elliptic": { - "version": "6.5.7", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", - "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", + "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -20851,9 +20851,9 @@ "integrity": "sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==" }, "elliptic": { - "version": "6.5.7", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", - "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", + "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", "requires": { "bn.js": "^4.11.9", "brorand": "^1.1.0", From ad9cb895b90776e898947e6d376daf92e6c7bab0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 11:28:14 +0200 Subject: [PATCH 293/363] chore(deps): bump core-js in /apps/block_scout_web/assets (#11114) Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.38.1 to 3.39.0. - [Release notes](https://github.com/zloirock/core-js/releases) - [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md) - [Commits](https://github.com/zloirock/core-js/commits/v3.39.0/packages/core-js) --- updated-dependencies: - dependency-name: core-js dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 af2cc4f17cd0..347eaea138ec 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -17,7 +17,7 @@ "chart.js": "^4.4.6", "chartjs-adapter-luxon": "^1.3.1", "clipboard": "^2.0.11", - "core-js": "^3.38.1", + "core-js": "^3.39.0", "crypto-browserify": "^3.12.0", "dropzone": "^5.9.3", "eth-net-props": "^1.0.41", @@ -5302,9 +5302,9 @@ } }, "node_modules/core-js": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", - "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -20149,9 +20149,9 @@ } }, "core-js": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", - "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==" + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==" }, "core-js-compat": { "version": "3.38.1", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index b76ffced9a28..b30e2dc2b7ac 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -29,7 +29,7 @@ "chart.js": "^4.4.6", "chartjs-adapter-luxon": "^1.3.1", "clipboard": "^2.0.11", - "core-js": "^3.38.1", + "core-js": "^3.39.0", "crypto-browserify": "^3.12.0", "dropzone": "^5.9.3", "eth-net-props": "^1.0.41", From 8ecde2e05d1749ef26343dfe246946283aa24afa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 21:25:17 +0200 Subject: [PATCH 294/363] chore(deps-dev): bump credo from 1.7.8 to 1.7.9 (#11135) Bumps [credo](https://github.com/rrrene/credo) from 1.7.8 to 1.7.9. - [Release notes](https://github.com/rrrene/credo/releases) - [Changelog](https://github.com/rrrene/credo/blob/master/CHANGELOG.md) - [Commits](https://github.com/rrrene/credo/compare/v1.7.8...v1.7.9) --- updated-dependencies: - dependency-name: credo dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 933f1d60b153..39297c43cd03 100644 --- a/mix.lock +++ b/mix.lock @@ -29,7 +29,7 @@ "cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"}, - "credo": {:hex, :credo, "1.7.8", "9722ba1681e973025908d542ec3d95db5f9c549251ba5b028e251ad8c24ab8c5", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cb9e87cc64f152f3ed1c6e325e7b894dea8f5ef2e41123bd864e3cd5ceb44968"}, + "credo": {:hex, :credo, "1.7.9", "07bb31907746ae2b5e569197c9e16c0d75c8578a22f01bee63f212047efb2647", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f87c11c34ba579f7c5044f02b2a807e1ed2fa5fdbb24dc7eb4ad59c1904887f3"}, "csv": {:hex, :csv, "2.5.0", "c47b5a5221bf2e56d6e8eb79e77884046d7fd516280dc7d9b674251e0ae46246", [:mix], [{:parallel_stream, "~> 1.0.4 or ~> 1.1.0", [hex: :parallel_stream, repo: "hexpm", optional: false]}], "hexpm", "e821f541487045c7591a1963eeb42afff0dfa99bdcdbeb3410795a2f59c77d34"}, "dataloader": {:hex, :dataloader, "2.0.1", "fa06b057b432b993203003fbff5ff040b7f6483a77e732b7dfc18f34ded2634f", [:mix], [{:ecto, ">= 3.4.3 and < 4.0.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:opentelemetry_process_propagator, "~> 0.2.1 or ~> 0.3", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "da7ff00890e1b14f7457419b9508605a8e66ae2cc2d08c5db6a9f344550efa11"}, "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, From 001ab94af4528dc177a2a62c50b59f3b5a522c33 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:10:58 +0300 Subject: [PATCH 295/363] feat: Add filecoin robust addresses to proxy implementations (#11102) * feat: Add filecoin robust addresses to proxy implementations * Fix tests * Fix tests * Fix credo * Process review comments * Change naming --- apps/block_scout_web/lib/block_scout_web.ex | 5 + .../channels/address_channel.ex | 10 +- .../block_scout_web/channels/block_channel.ex | 2 +- .../controllers/api/v2/address_controller.ex | 47 +++--- .../controllers/api/v2/block_controller.ex | 22 +-- .../api/v2/main_page_controller.ex | 15 +- .../controllers/api/v2/mud_controller.ex | 2 +- .../proxy/account_abstraction_controller.ex | 2 +- .../api/v2/smart_contract_controller.ex | 28 ++-- .../controllers/api/v2/token_controller.ex | 2 +- .../api/v2/transaction_controller.ex | 39 +++-- .../api/v2/validator_controller.ex | 4 +- .../api/v2/withdrawal_controller.ex | 2 +- .../controllers/smart_contract_controller.ex | 20 +-- .../transaction_interpretation.ex | 23 +-- .../models/transaction_state_helper.ex | 15 +- .../lib/block_scout_web/notifier.ex | 22 ++- .../lib/block_scout_web/paging_helper.ex | 8 +- .../templates/address/overview.html.eex | 6 +- .../views/account/api/v2/user_view.ex | 6 +- .../views/api/v2/address_view.ex | 20 +-- .../block_scout_web/views/api/v2/celo_view.ex | 3 +- .../block_scout_web/views/api/v2/helper.ex | 16 +- .../views/api/v2/optimism_view.ex | 2 +- .../views/api/v2/polygon_edge_view.ex | 2 +- .../views/api/v2/shibarium_view.ex | 2 +- .../views/api/v2/smart_contract_view.ex | 24 ++- apps/explorer/lib/explorer/chain.ex | 18 ++- apps/explorer/lib/explorer/chain/address.ex | 26 +--- .../chain/address/current_token_balance.ex | 5 +- .../lib/explorer/chain/advanced_filter.ex | 7 +- .../lib/explorer/chain/celo/epoch_reward.ex | 5 +- .../explorer/chain/internal_transaction.ex | 6 +- .../lib/explorer/chain/smart_contract.ex | 54 +++---- .../explorer/chain/smart_contract/proxy.ex | 76 +++++++--- .../proxy/models/implementation.ex | 137 ++++++++++++++---- .../lib/explorer/chain/token_transfer.ex | 13 +- .../lib/explorer/etherscan/contracts.ex | 7 +- .../microservice_interfaces/metadata.ex | 7 +- .../lib/explorer/smart_contract/helper.ex | 13 +- .../proxy/models/implementation_test.exs | 53 +++++-- apps/explorer/test/explorer/chain_test.exs | 2 +- 42 files changed, 453 insertions(+), 325 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web.ex b/apps/block_scout_web/lib/block_scout_web.ex index 1ce9cca0e7da..f914ed426af0 100644 --- a/apps/block_scout_web/lib/block_scout_web.ex +++ b/apps/block_scout_web/lib/block_scout_web.ex @@ -29,6 +29,7 @@ defmodule BlockScoutWeb do import BlockScoutWeb.ErrorHelper import BlockScoutWeb.Routers.AccountRouter.Helpers, except: [static_path: 2] import Plug.Conn + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] alias BlockScoutWeb.Routers.AdminRouter.Helpers, as: AdminRoutes end @@ -63,6 +64,8 @@ defmodule BlockScoutWeb do import Explorer.Chain.CurrencyHelper, only: [divide_decimals: 2] import BlockScoutWeb.Routers.WebRouter.Helpers, except: [static_path: 2] + + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] end end @@ -80,6 +83,8 @@ defmodule BlockScoutWeb do use Phoenix.Channel use Gettext, backend: BlockScoutWeb.Gettext + + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] end end diff --git a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex index ebc45d00c0c8..86dec8a3f9fb 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex @@ -49,18 +49,18 @@ defmodule BlockScoutWeb.AddressChannel do end @transaction_associations [ - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], + from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], to_address: [ :scam_badge, :names, :smart_contract, - :proxy_implementations + proxy_implementations_association() ], created_contract_address: [ :scam_badge, :names, :smart_contract, - :proxy_implementations + proxy_implementations_association() ] ] ++ @chain_type_transaction_associations @@ -414,8 +414,8 @@ defmodule BlockScoutWeb.AddressChannel do token_transfers |> Repo.preload([ [ - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] + from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()] ] ]), conn: nil diff --git a/apps/block_scout_web/lib/block_scout_web/channels/block_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/block_channel.ex index 2340ab38121a..ffefcaf76d8d 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/block_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/block_channel.ex @@ -27,7 +27,7 @@ defmodule BlockScoutWeb.BlockChannel do ) do rendered_block = BlockViewAPI.render("block.json", %{ - block: block |> Repo.preload(miner: [:names, :smart_contract, :proxy_implementations]), + block: block |> Repo.preload(miner: [:names, :smart_contract, proxy_implementations_association()]), socket: nil }) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index b2a2319cd035..bc09024c9810 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -52,9 +52,10 @@ defmodule BlockScoutWeb.API.V2.AddressController do @transaction_necessity_by_association [ necessity_by_association: %{ - [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => + :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, :block => :optional } |> Map.merge(@chain_type_transaction_necessity_by_association), @@ -63,8 +64,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do @token_transfer_necessity_by_association [ necessity_by_association: %{ - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, :block => :optional, :transaction => :optional, :token => :optional @@ -77,7 +78,6 @@ defmodule BlockScoutWeb.API.V2.AddressController do :names => :optional, :scam_badge => :optional, :token => :optional, - :proxy_implementations => :optional, :signed_authorization => :optional }, api?: true @@ -88,16 +88,14 @@ defmodule BlockScoutWeb.API.V2.AddressController do @contract_address_preloads [ :smart_contract, [contracts_creation_internal_transaction: :from_address], - [contracts_creation_transaction: :from_address], - :proxy_implementations + [contracts_creation_transaction: :from_address] ] _ -> @contract_address_preloads [ :smart_contract, :contracts_creation_internal_transaction, - :contracts_creation_transaction, - :proxy_implementations + :contracts_creation_transaction ] end @@ -115,14 +113,14 @@ defmodule BlockScoutWeb.API.V2.AddressController do account_address: [ :names, :smart_contract, - :proxy_implementations + proxy_implementations_association() ] ] => :optional, [ associated_account_address: [ :names, :smart_contract, - :proxy_implementations + proxy_implementations_association() ] ] => :optional }, @@ -136,8 +134,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do fully_preloaded_address = Address.maybe_preload_smart_contract_associations(address, @contract_address_preloads, @api_true) - {implementations, proxy_type} = - SmartContractHelper.pre_fetch_implementations(fully_preloaded_address) + implementations = SmartContractHelper.pre_fetch_implementations(fully_preloaded_address) CoinBalanceOnDemand.trigger_fetch(address) ContractCodeOnDemand.trigger_fetch(address) @@ -145,9 +142,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do conn |> put_status(200) |> render(:address, %{ - address: fully_preloaded_address |> maybe_preload_ens_to_address(), - implementations: implementations, - proxy_type: proxy_type + address: + %Address{fully_preloaded_address | proxy_implementations: implementations} |> maybe_preload_ens_to_address() }) end end @@ -225,8 +221,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do options = [ necessity_by_association: %{ - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, :block => :optional, :token => :optional, :transaction => :optional @@ -297,9 +293,10 @@ defmodule BlockScoutWeb.API.V2.AddressController do full_options = [ necessity_by_association: %{ - [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => + :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(paging_options(params)) @@ -333,7 +330,9 @@ defmodule BlockScoutWeb.API.V2.AddressController do |> paging_options() |> Keyword.merge(topic: formatted_topic) |> Keyword.merge( - necessity_by_association: %{[address: [:names, :smart_contract, :proxy_implementations]] => :optional} + necessity_by_association: %{ + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional + } ) |> Keyword.merge(@api_true) @@ -378,7 +377,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do full_options = [ necessity_by_association: %{ - [miner: :proxy_implementations] => :optional, + [miner: proxy_implementations_association()] => :optional, miner: :required, nephews: :optional, transactions: :optional, diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex index 28eeeea3168c..ba6bcd1e3fed 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex @@ -84,9 +84,10 @@ defmodule BlockScoutWeb.API.V2.BlockController do @transaction_necessity_by_association [ necessity_by_association: %{ - [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => + :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, :block => :optional } |> Map.merge(@chain_type_transaction_necessity_by_association) @@ -94,9 +95,10 @@ defmodule BlockScoutWeb.API.V2.BlockController do @internal_transaction_necessity_by_association [ necessity_by_association: %{ - [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => + :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } ] @@ -105,7 +107,7 @@ defmodule BlockScoutWeb.API.V2.BlockController do @block_params [ necessity_by_association: %{ - [miner: [:names, :smart_contract, :proxy_implementations]] => :optional, + [miner: [:names, :smart_contract, proxy_implementations_association()]] => :optional, :uncles => :optional, :nephews => :optional, :rewards => :optional, @@ -334,7 +336,9 @@ defmodule BlockScoutWeb.API.V2.BlockController do with {:ok, block} <- block_param_to_block(block_hash_or_number) do full_options = [ - necessity_by_association: %{[address: [:names, :smart_contract, :proxy_implementations]] => :optional}, + necessity_by_association: %{ + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional + }, api?: true ] |> Keyword.merge(paging_options(params)) @@ -415,7 +419,7 @@ defmodule BlockScoutWeb.API.V2.BlockController do with {:ok, reward_type_atom} <- celo_reward_type_to_atom(reward_type), {:ok, block} <- block_param_to_block(block_hash_or_number) do - address_associations = [:names, :smart_contract, :proxy_implementations] + address_associations = [:names, :smart_contract, proxy_implementations_association()] full_options = [ diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex index cb2c41f00101..f82b4f476e64 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex @@ -1,5 +1,5 @@ defmodule BlockScoutWeb.API.V2.MainPageController do - use Phoenix.Controller + use BlockScoutWeb, :controller alias Explorer.{Chain, PagingOptions} alias BlockScoutWeb.API.V2.{BlockView, OptimismView, TransactionView} @@ -24,9 +24,10 @@ defmodule BlockScoutWeb.API.V2.MainPageController do necessity_by_association: %{ :block => :required, - [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => + :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } |> Map.merge(@chain_type_transaction_necessity_by_association), paging_options: %PagingOptions{page_size: 6}, @@ -39,7 +40,11 @@ defmodule BlockScoutWeb.API.V2.MainPageController do blocks = [paging_options: %PagingOptions{page_size: 4}, api?: true] |> Chain.list_blocks() - |> Repo.replica().preload([[miner: [:names, :smart_contract, :proxy_implementations]], :transactions, :rewards]) + |> Repo.replica().preload([ + [miner: [:names, :smart_contract, proxy_implementations_association()]], + :transactions, + :rewards + ]) conn |> put_status(200) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex index 9f4b2f440d1f..bff0df4e29b1 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/mud_controller.ex @@ -37,7 +37,7 @@ defmodule BlockScoutWeb.API.V2.MudController do necessity_by_association: %{ :names => :optional, :smart_contract => :optional, - :proxy_implementations => :optional + proxy_implementations_association() => :optional }, api?: true ) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex index 9571069769bc..ce77a28fd93b 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/account_abstraction_controller.ex @@ -159,7 +159,7 @@ defmodule BlockScoutWeb.API.V2.Proxy.AccountAbstractionController do necessity_by_association: %{ :names => :optional, :smart_contract => :optional, - :proxy_implementations => :optional + proxy_implementations_association() => :optional }, api?: true ) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex index 10e176e60d3a..3eea72553c7c 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/smart_contract_controller.ex @@ -23,8 +23,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do necessity_by_association: %{ :contracts_creation_internal_transaction => :optional, [smart_contract: :smart_contract_additional_sources] => :optional, - :contracts_creation_transaction => :optional, - :proxy_implementations => :optional + :contracts_creation_transaction => :optional }, api?: true ] @@ -39,12 +38,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do _ <- PublishHelper.sourcify_check(address_hash_string), {:not_found, {:ok, address}} <- {:not_found, Chain.find_contract_address(address_hash, @smart_contract_address_options, false)} do - {implementations, proxy_type} = - SmartContractHelper.pre_fetch_implementations(address) + implementations = SmartContractHelper.pre_fetch_implementations(address) conn |> put_status(200) - |> render(:smart_contract, %{address: address, implementations: implementations, proxy_type: proxy_type}) + |> render(:smart_contract, %{address: %Address{address | proxy_implementations: implementations}}) end end @@ -105,11 +103,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do {:not_found, {:ok, address}} <- {:not_found, Chain.find_contract_address(address_hash, @smart_contract_address_options)}, {:not_found, false} <- {:not_found, is_nil(address.smart_contract)} do - implementation_address_hash_strings = - address.smart_contract - |> Implementation.get_implementation(@api_true) - |> Tuple.to_list() - |> List.first() + implementation_address_hash_strings = get_implementations_address_hashes(address) functions = implementation_address_hash_strings @@ -136,11 +130,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do {:not_found, {:ok, address}} <- {:not_found, Chain.find_contract_address(address_hash, @smart_contract_address_options)}, {:not_found, false} <- {:not_found, is_nil(address.smart_contract)} do - implementation_address_hash_strings = - address.smart_contract - |> Implementation.get_implementation(@api_true) - |> Tuple.to_list() - |> List.first() + implementation_address_hash_strings = get_implementations_address_hashes(address) functions = implementation_address_hash_strings @@ -335,4 +325,12 @@ defmodule BlockScoutWeb.API.V2.SmartContractController do {:ok, address_hash, smart_contract} end end + + defp get_implementations_address_hashes(proxy_address) do + implementation = + proxy_address.smart_contract + |> Implementation.get_implementation(@api_true) + + (implementation && implementation.address_hashes) || [] + end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex index 506db2ab9fe8..2e72170b1454 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex @@ -365,7 +365,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do case Chain.nft_instance_from_token_id_and_token_address(token_id, address_hash, @api_true) do {:ok, token_instance} -> token_instance - |> Chain.select_repo(@api_true).preload(owner: [:names, :smart_contract, :proxy_implementations]) + |> Chain.select_repo(@api_true).preload(owner: [:names, :smart_contract, proxy_implementations_association()]) |> Chain.put_owner_to_token_instance(token, @api_true) {:error, :not_found} -> diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex index 1b92aa8514df..4c648221fffd 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex @@ -75,38 +75,45 @@ defmodule BlockScoutWeb.API.V2.TransactionController do :names, :token, :smart_contract, - :proxy_implementations + proxy_implementations_association() + ] + ] => :optional, + [ + from_address: [ + :scam_badge, + :names, + :smart_contract, + proxy_implementations_association() ] ] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => - :optional, [ to_address: [ :scam_badge, :names, :smart_contract, - :proxy_implementations + proxy_implementations_association() ] ] => :optional } |> Map.merge(@chain_type_transaction_necessity_by_association) @token_transfers_necessity_by_association %{ - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } @token_transfers_in_transaction_necessity_by_association %{ - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, token: :required } @internal_transaction_necessity_by_association [ necessity_by_association: %{ - [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => + :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } ] @@ -495,7 +502,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do full_options = [ necessity_by_association: %{ - [address: [:names, :smart_contract, :proxy_implementations]] => :optional + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(paging_options(params)) @@ -573,8 +580,8 @@ defmodule BlockScoutWeb.API.V2.TransactionController do options = [ necessity_by_association: %{ - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(@api_true) @@ -600,8 +607,8 @@ defmodule BlockScoutWeb.API.V2.TransactionController do options = [ necessity_by_association: %{ - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(@api_true) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/validator_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/validator_controller.ex index 78a59a0ddf10..d4946f6e02ad 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/validator_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/validator_controller.ex @@ -30,7 +30,7 @@ defmodule BlockScoutWeb.API.V2.ValidatorController do options = [ necessity_by_association: %{ - [address: [:names, :smart_contract, :proxy_implementations]] => :optional + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(@api_true) @@ -81,7 +81,7 @@ defmodule BlockScoutWeb.API.V2.ValidatorController do options = [ necessity_by_association: %{ - [address: [:names, :smart_contract, :proxy_implementations]] => :optional + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(@api_true) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/withdrawal_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/withdrawal_controller.ex index a58a84ed1599..a8df1ef7d5b6 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/withdrawal_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/withdrawal_controller.ex @@ -14,7 +14,7 @@ defmodule BlockScoutWeb.API.V2.WithdrawalController do full_options = [ necessity_by_association: %{ - [address: [:names, :smart_contract, :proxy_implementations]] => :optional, + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional, block: :optional }, api?: true diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex index ee2d59ae8ff5..7b360faecf21 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex @@ -25,16 +25,7 @@ defmodule BlockScoutWeb.SmartContractController do {:custom_abi, false} <- {:custom_abi, is_custom_abi}, {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), {:ok, address} <- Chain.find_contract_address(address_hash, address_options, true) do - implementation_address_hash_string = - if contract_type == "proxy" do - address.smart_contract - |> Implementation.get_implementation() - |> Tuple.to_list() - |> List.first() - |> List.first() || burn_address_hash_string() - else - burn_address_hash_string() - end + implementation_address_hash_string = implementation_address_hash(contract_type, address) functions = if action == "write" do @@ -107,6 +98,15 @@ defmodule BlockScoutWeb.SmartContractController do def index(conn, _), do: not_found(conn) + defp implementation_address_hash(contract_type, address) do + if contract_type == "proxy" do + implementation = Implementation.get_implementation(address.smart_contract) + (implementation && implementation.address_hashes |> List.first()) || burn_address_hash_string() + else + burn_address_hash_string() + end + end + defp custom_abi_render(conn, %{"hash" => address_hash_string, "type" => contract_type, "action" => action} = params) do with custom_abi <- AddressView.fetch_custom_abi(conn, address_hash_string), false <- is_nil(custom_abi), diff --git a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex index 17d732bc642e..d2af052ea967 100644 --- a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex +++ b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex @@ -9,9 +9,9 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do alias Explorer.Chain.{Data, InternalTransaction, Log, TokenTransfer, Transaction} alias HTTPoison.Response + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] import Explorer.MicroserviceInterfaces.Metadata, only: [maybe_preload_metadata: 1] import Explorer.Utility.Microservice, only: [base_url: 2, check_enabled: 2] - require Logger @post_timeout :timer.minutes(5) @@ -20,9 +20,10 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do @items_limit 50 @internal_transaction_necessity_by_association [ necessity_by_association: %{ - [created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => + :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } ] @@ -176,8 +177,8 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do full_options = [ necessity_by_association: %{ - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(@api_true) @@ -212,7 +213,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do full_options = [ necessity_by_association: %{ - [address: [:names, :smart_contract, :proxy_implementations]] => :optional + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional } ] |> Keyword.merge(@api_true) @@ -234,7 +235,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do log_options = [ necessity_by_association: %{ - [address: [:names, :smart_contract, :proxy_implementations]] => :optional + [address: [:names, :smart_contract, proxy_implementations_association()]] => :optional }, limit: @items_limit ] @@ -254,8 +255,8 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do token_transfer_options = [ necessity_by_association: %{ - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] => :optional, + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] => :optional, :token => :optional } ] @@ -303,7 +304,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do necessity_by_association: %{ :names => :optional, :smart_contract => :optional, - :proxy_implementations => :optional + proxy_implementations_association() => :optional }, api?: true ], diff --git a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex index 4f1a6a28041d..2119853443bf 100644 --- a/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/models/transaction_state_helper.ex @@ -5,6 +5,7 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do import Explorer.PagingOptions, only: [default_paging_options: 0] import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] alias Explorer.Chain.Transaction.StateChange alias Explorer.{Chain, PagingOptions, Repo} @@ -69,16 +70,16 @@ defmodule BlockScoutWeb.Models.TransactionStateHelper do |> Enum.find(&(&1.hash == transaction.hash)) |> Repo.preload( token_transfers: [ - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] + from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()] ], internal_transactions: [ - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] + from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()] ], - block: [miner: [:names, :smart_contract, :proxy_implementations]], - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] + block: [miner: [:names, :smart_contract, proxy_implementations_association()]], + from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()] ) previous_block_number = BlockNumberHelper.previous_block_number(transaction.block_number) diff --git a/apps/block_scout_web/lib/block_scout_web/notifier.ex b/apps/block_scout_web/lib/block_scout_web/notifier.ex index 8c353b68ef6f..dead3bb875b5 100644 --- a/apps/block_scout_web/lib/block_scout_web/notifier.ex +++ b/apps/block_scout_web/lib/block_scout_web/notifier.ex @@ -27,6 +27,8 @@ defmodule BlockScoutWeb.Notifier do alias Explorer.SmartContract.{CompilerVersion, Solidity.CodeCompiler} alias Phoenix.View + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] + @check_broadcast_sequence_period 500 case Application.compile_env(:explorer, :chain_type) do @@ -181,8 +183,18 @@ defmodule BlockScoutWeb.Notifier do DenormalizationHelper.extend_transaction_preload([ :token, :transaction, - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] + from_address: [ + :scam_badge, + :names, + :smart_contract, + proxy_implementations_association() + ], + to_address: [ + :scam_badge, + :names, + :smart_contract, + proxy_implementations_association() + ] ]) ) @@ -205,9 +217,9 @@ defmodule BlockScoutWeb.Notifier do def handle_event({:chain_event, :transactions, :realtime, transactions}) do base_preloads = [ :block, - created_contract_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations] + created_contract_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()] ] preloads = if API_V2.enabled?(), do: [:token_transfers | base_preloads], else: base_preloads diff --git a/apps/block_scout_web/lib/block_scout_web/paging_helper.ex b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex index 483e4150f1fe..8360aadc5ed3 100644 --- a/apps/block_scout_web/lib/block_scout_web/paging_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex @@ -3,6 +3,8 @@ defmodule BlockScoutWeb.PagingHelper do Helper for fetching filters and other url query parameters """ import Explorer.Chain, only: [string_to_transaction_hash: 1] + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] + alias Explorer.Chain.Stability.Validator, as: ValidatorStability alias Explorer.Chain.Transaction alias Explorer.{Helper, PagingOptions, SortingHelper} @@ -158,7 +160,7 @@ defmodule BlockScoutWeb.PagingHelper do [ necessity_by_association: %{ :transactions => :optional, - [miner: [:names, :smart_contract, :proxy_implementations]] => :optional, + [miner: [:names, :smart_contract, proxy_implementations_association()]] => :optional, :nephews => :required, :rewards => :optional }, @@ -169,7 +171,7 @@ defmodule BlockScoutWeb.PagingHelper do [ necessity_by_association: %{ :transactions => :optional, - [miner: [:names, :smart_contract, :proxy_implementations]] => :optional, + [miner: [:names, :smart_contract, proxy_implementations_association()]] => :optional, :rewards => :optional }, block_type: "Reorg" @@ -184,7 +186,7 @@ defmodule BlockScoutWeb.PagingHelper do do: [ necessity_by_association: %{ :transactions => :optional, - [miner: [:names, :smart_contract, :proxy_implementations]] => :optional, + [miner: [:names, :smart_contract, proxy_implementations_association()]] => :optional, :rewards => :optional }, block_type: "Block" diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex index 0e4e20000138..b71337de6c1a 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex @@ -124,9 +124,9 @@ <% end %> <%= if @is_proxy do %> - <% {implementation_addresses, implementation_names, _proxy_type} = Implementation.get_implementation(@address.smart_contract) %> - <% implementation_address_ = Enum.at(implementation_addresses, 0) %> - <% name = Enum.at(implementation_names, 0) %> + <% implementation = Implementation.get_implementation(@address.smart_contract) %> + <% implementation_address_ = implementation && Enum.at(implementation.address_hashes, 0) %> + <% name = implementation && Enum.at(implementation.names, 0) %> <% implementation_address = implementation_address_ || "0x0000000000000000000000000000000000000000" %>
diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex index f9b6a6a30735..b5e9834d7c48 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/account/api/v2/user_view.ex @@ -1,4 +1,6 @@ defmodule BlockScoutWeb.Account.API.V2.UserView do + use BlockScoutWeb, :view + alias BlockScoutWeb.Account.API.V2.AccountView alias BlockScoutWeb.API.V2.Helper alias Ecto.Changeset @@ -198,7 +200,9 @@ defmodule BlockScoutWeb.Account.API.V2.UserView do defp get_address(address_hash) do case Chain.hash_to_address( address_hash, - [necessity_by_association: %{smart_contract: :optional, proxy_implementations: :optional}], + [ + necessity_by_association: %{:smart_contract => :optional, proxy_implementations_association() => :optional} + ], false ) do {:ok, address} -> address diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex index 90611f3b5ec3..11ae92643390 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex @@ -16,8 +16,8 @@ defmodule BlockScoutWeb.API.V2.AddressView do ApiView.render("message.json", assigns) end - def render("address.json", %{address: address, implementations: implementations, proxy_type: proxy_type, conn: conn}) do - prepare_address(address, conn, implementations, proxy_type) + def render("address.json", %{address: address, conn: conn}) do + prepare_address(address, conn) end def render("token_balances.json", %{token_balances: token_balances}) do @@ -90,8 +90,8 @@ defmodule BlockScoutWeb.API.V2.AddressView do @doc """ Prepares address properties for rendering in /addresses and /addresses/:address_hash_param API v2 endpoints """ - @spec prepare_address(Address.t(), Plug.Conn.t() | nil, list(), String.t() | nil) :: map() - def prepare_address(address, conn \\ nil, implementations \\ [], proxy_type \\ nil) do + @spec prepare_address(Address.t(), Plug.Conn.t() | nil) :: map() + def prepare_address(address, conn \\ nil) do base_info = Helper.address_with_info(conn, address, address.hash, true) balance = address.fetched_coin_balance && address.fetched_coin_balance.value @@ -121,17 +121,7 @@ defmodule BlockScoutWeb.API.V2.AddressView do "has_beacon_chain_withdrawals" => Counters.check_if_withdrawals_at_address(address.hash, @api_true) }) - result = - if Enum.empty?(implementations) do - extended_info - else - Map.merge(extended_info, %{ - "proxy_type" => proxy_type, - "implementations" => implementations - }) - end - - result + extended_info |> chain_type_fields(%{address: creation_transaction && creation_transaction.from_address, field_prefix: "creator"}) end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex index b013d67009f2..2f17c7bd84a3 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex @@ -2,6 +2,7 @@ defmodule BlockScoutWeb.API.V2.CeloView do @moduledoc """ View functions for rendering Celo-related data in JSON format. """ + use BlockScoutWeb, :view require Logger @@ -20,7 +21,7 @@ defmodule BlockScoutWeb.API.V2.CeloView do necessity_by_association: %{ :names => :optional, :smart_contract => :optional, - :proxy_implementations => :optional + proxy_implementations_association() => :optional }, api?: true ] diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex index 935fdc087dce..88b3e41f5330 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex @@ -64,20 +64,16 @@ defmodule BlockScoutWeb.API.V2.Helper do def address_with_info(%Address{} = address, _address_hash) do smart_contract? = Address.smart_contract?(address) - {proxy_implementations, implementation_address_hashes, implementation_names, proxy_type} = + proxy_implementations = case address.proxy_implementations do %NotLoaded{} -> - {nil, [], [], nil} + nil nil -> - {nil, [], [], nil} + nil proxy_implementations -> - address_hashes = proxy_implementations.address_hashes - names = proxy_implementations.names - proxy_type = proxy_implementations.proxy_type - - {proxy_implementations, address_hashes, names, proxy_type} + proxy_implementations end %{ @@ -85,8 +81,8 @@ defmodule BlockScoutWeb.API.V2.Helper do "is_contract" => smart_contract?, "name" => address_name(address), "is_scam" => address_marked_as_scam?(address), - "proxy_type" => proxy_type, - "implementations" => Proxy.proxy_object_info(implementation_address_hashes, implementation_names), + "proxy_type" => proxy_implementations && proxy_implementations.proxy_type, + "implementations" => Proxy.proxy_object_info(proxy_implementations), "is_verified" => verified?(address) || verified_minimal_proxy?(proxy_implementations), "ens_domain_name" => address.ens_domain_name, "metadata" => address.metadata diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex index 62e43585aaae..f0217f268cad 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex @@ -232,7 +232,7 @@ defmodule BlockScoutWeb.API.V2.OptimismView do necessity_by_association: %{ :names => :optional, :smart_contract => :optional, - :proxy_implementations => :optional + proxy_implementations_association() => :optional }, api?: true ], diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_edge_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_edge_view.ex index 51b2515997f3..be28baa48367 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_edge_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/polygon_edge_view.ex @@ -90,7 +90,7 @@ defmodule BlockScoutWeb.API.V2.PolygonEdgeView do necessity_by_association: %{ :names => :optional, :smart_contract => :optional, - :proxy_implementations => :optional + proxy_implementations_association() => :optional }, api?: true ], diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/shibarium_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/shibarium_view.ex index 45d46b7a72f3..de9101961f8a 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/shibarium_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/shibarium_view.ex @@ -62,7 +62,7 @@ defmodule BlockScoutWeb.API.V2.ShibariumView do necessity_by_association: %{ :names => :optional, :smart_contract => :optional, - :proxy_implementations => :optional + proxy_implementations_association() => :optional }, api?: true ) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex index f685fe2833fd..d895db108ff4 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex @@ -11,6 +11,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do alias Ecto.Changeset alias Explorer.Chain alias Explorer.Chain.{Address, SmartContract, SmartContractAdditionalSource} + alias Explorer.Chain.SmartContract.Proxy alias Explorer.SmartContract.Helper, as: SmartContractHelper alias Explorer.Visualize.Sol2uml @@ -26,13 +27,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do %{"items" => Enum.map(smart_contracts, &prepare_smart_contract_for_list/1), "next_page_params" => next_page_params} end - def render("smart_contract.json", %{ - address: address, - implementations: implementations, - proxy_type: proxy_type, - conn: conn - }) do - prepare_smart_contract(address, implementations, proxy_type, conn) + def render("smart_contract.json", %{address: address, conn: conn}) do + prepare_smart_contract(address, conn) end def render("read_functions.json", %{functions: functions}) do @@ -156,9 +152,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do # credo:disable-for-next-line def prepare_smart_contract( - %Address{smart_contract: %SmartContract{} = smart_contract} = address, - implementations, - proxy_type, + %Address{smart_contract: %SmartContract{} = smart_contract, proxy_implementations: implementations} = address, conn ) do bytecode_twin = SmartContract.get_address_verified_bytecode_twin_contract(address.hash, @api_true) @@ -202,8 +196,8 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do "has_methods_write" => write_methods?, "has_methods_read_proxy" => is_proxy, "has_methods_write_proxy" => is_proxy && write_methods?, - "proxy_type" => proxy_type, - "implementations" => implementations, + "proxy_type" => implementations && implementations.proxy_type, + "implementations" => Proxy.proxy_object_info(implementations), "sourcify_repo_url" => if(smart_contract.verified_via_sourcify && smart_contract_verified, do: AddressContractView.sourcify_repo_url(address.hash, smart_contract.partially_verified) @@ -240,15 +234,15 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do |> chain_type_fields(%{address_hash: verified_twin_address_hash, field_prefix: "verified_twin"}) end - def prepare_smart_contract(address, implementations, proxy_type, conn) do + def prepare_smart_contract(%Address{proxy_implementations: implementations} = address, conn) do read_custom_abi? = AddressView.has_address_custom_abi_with_read_functions?(conn, address.hash) write_custom_abi? = AddressView.has_address_custom_abi_with_write_functions?(conn, address.hash) %{ "has_custom_methods_read" => read_custom_abi?, "has_custom_methods_write" => write_custom_abi?, - "proxy_type" => proxy_type, - "implementations" => implementations + "proxy_type" => implementations && implementations.proxy_type, + "implementations" => Proxy.proxy_object_info(implementations) } |> Map.merge(bytecode_info(address)) end diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 52b0b788a123..cda1bec0ee52 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -85,6 +85,7 @@ defmodule Explorer.Chain do alias Explorer.Chain.Fetcher.{CheckBytecodeMatchingOnDemand, LookUpSmartContractSourcesOnDemand} alias Explorer.Chain.Import.Runner alias Explorer.Chain.InternalTransaction.{CallType, Type} + alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.Market.MarketHistoryCache alias Explorer.{PagingOptions, Repo} @@ -391,7 +392,12 @@ defmodule Explorer.Chain do base else base - |> preload(transaction: [from_address: [:proxy_implementations], to_address: [:proxy_implementations]]) + |> preload( + transaction: [ + from_address: ^Implementation.proxy_implementations_association(), + to_address: ^Implementation.proxy_implementations_association() + ] + ) end preloaded_query @@ -1113,7 +1119,7 @@ defmodule Explorer.Chain do |> Keyword.get(:necessity_by_association, %{}) |> Map.merge(%{ [smart_contract: :smart_contract_additional_sources] => :optional, - :proxy_implementations => :optional + Implementation.proxy_implementations_association() => :optional }) query = @@ -4393,7 +4399,7 @@ defmodule Explorer.Chain do |> Instance.address_to_unique_token_instances() |> Instance.page_token_instance(paging_options) |> limit(^paging_options.page_size) - |> preload([_], owner: [:names, :smart_contract, :proxy_implementations]) + |> preload([_], owner: [:names, :smart_contract, ^Implementation.proxy_implementations_association()]) |> select_repo(options).all() |> Enum.map(&put_owner_to_token_instance(&1, token, options)) end @@ -4424,7 +4430,11 @@ defmodule Explorer.Chain do owner_address_hash, options |> Keyword.merge( - necessity_by_association: %{names: :optional, smart_contract: :optional, proxy_implementations: :optional} + necessity_by_association: %{ + :names => :optional, + :smart_contract => :optional, + Implementation.proxy_implementations_association() => :optional + } ) ) diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index 1c86455392fa..da1ea6eda596 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -138,12 +138,12 @@ defmodule Explorer.Chain.Address do alias Ecto.Changeset alias Explorer.Helper, as: ExplorerHelper alias Explorer.Chain.Cache.{Accounts, NetVersion} - alias Explorer.Chain.SmartContract.Proxy alias Explorer.Chain.SmartContract.Proxy.EIP7702 - alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.Chain.{Address, Data, Hash, InternalTransaction, Transaction} alias Explorer.{Chain, PagingOptions, Repo} + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] + @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce decompiled verified gas_used transactions_count token_transfers_count)a @chain_type_optional_attrs (case Application.compile_env(:explorer, :chain_type) do :filecoin -> @@ -552,7 +552,7 @@ defmodule Explorer.Chain.Address do from(a in Address, where: a.fetched_coin_balance > ^0, order_by: [desc: a.fetched_coin_balance, asc: a.hash], - preload: [:names, :smart_contract, :proxy_implementations], + preload: [:names, :smart_contract, ^proxy_implementations_association()], select: {a, a.transactions_count} ) @@ -653,26 +653,6 @@ defmodule Explorer.Chain.Address do ) end - @doc """ - Prepares implementations object and proxy type from address - """ - @spec parse_implementation_and_proxy_type(__MODULE__.t()) :: {list(), String.t() | nil} - def parse_implementation_and_proxy_type(address) do - with %__MODULE__{ - proxy_implementations: %Implementation{ - address_hashes: address_hashes, - names: names, - proxy_type: proxy_type - } - } <- address, - false <- address_hashes && Enum.empty?(address_hashes) do - {Proxy.proxy_object_info(address_hashes, names), proxy_type} - else - _ -> - {[], nil} - end - end - @doc """ Retrieves the creation transaction for a given address. diff --git a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex index 32f01a388c14..a40fc51b11a4 100644 --- a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex @@ -11,6 +11,7 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do import Ecto.Changeset import Ecto.Query, only: [from: 2, limit: 2, offset: 2, order_by: 3, preload: 2, dynamic: 2] import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] alias Explorer.{Chain, PagingOptions, Repo} alias Explorer.Chain.{Address, Block, CurrencyHelper, Hash, Token} @@ -85,7 +86,7 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do def token_holders_ordered_by_value(token_contract_address_hash, options \\ []) do token_contract_address_hash |> token_holders_ordered_by_value_query_without_address_preload(options) - |> preload(address: [:names, :smart_contract, :proxy_implementations]) + |> preload(address: [:names, :smart_contract, ^proxy_implementations_association()]) end @doc """ @@ -131,7 +132,7 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do _ -> token_contract_address_hash |> token_holders_by_token_id_query(token_id) - |> preload(address: [:names, :smart_contract, :proxy_implementations]) + |> preload(address: [:names, :smart_contract, ^proxy_implementations_association()]) |> order_by([tb], desc: :value, desc: :address_hash) |> Chain.page_token_balances(paging_options) |> limit(^paging_options.page_size) diff --git a/apps/explorer/lib/explorer/chain/advanced_filter.ex b/apps/explorer/lib/explorer/chain/advanced_filter.ex index 62cdc1657bb6..5924084c64e0 100644 --- a/apps/explorer/lib/explorer/chain/advanced_filter.ex +++ b/apps/explorer/lib/explorer/chain/advanced_filter.ex @@ -6,6 +6,7 @@ defmodule Explorer.Chain.AdvancedFilter do use Explorer.Schema import Ecto.Query + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] alias Explorer.{Chain, Helper, PagingOptions} alias Explorer.Chain.{Address, Data, DenormalizationHelper, Hash, InternalTransaction, TokenTransfer, Transaction} @@ -115,9 +116,9 @@ defmodule Explorer.Chain.AdvancedFilter do |> Enum.sort(&sort_function/2) |> take_page_size(paging_options) |> Chain.select_repo(options).preload( - from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations], - created_contract_address: [:names, :smart_contract, :proxy_implementations] + from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()], + created_contract_address: [:names, :smart_contract, proxy_implementations_association()] ) end diff --git a/apps/explorer/lib/explorer/chain/celo/epoch_reward.ex b/apps/explorer/lib/explorer/chain/celo/epoch_reward.ex index f08d59f33623..85ed60fb1fa5 100644 --- a/apps/explorer/lib/explorer/chain/celo/epoch_reward.ex +++ b/apps/explorer/lib/explorer/chain/celo/epoch_reward.ex @@ -8,6 +8,7 @@ defmodule Explorer.Chain.Celo.EpochReward do import Ecto.Query, only: [from: 2] import Explorer.Chain, only: [select_repo: 1] + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] alias Explorer.Chain.Celo.EpochReward alias Explorer.Chain.{Block, Hash, TokenTransfer} @@ -101,8 +102,8 @@ defmodule Explorer.Chain.Celo.EpochReward do select: {tt.log_index, tt}, preload: [ :token, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]], - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] + [from_address: [:scam_badge, :names, :smart_contract, ^proxy_implementations_association()]], + [to_address: [:scam_badge, :names, :smart_contract, ^proxy_implementations_association()]] ] ) diff --git a/apps/explorer/lib/explorer/chain/internal_transaction.ex b/apps/explorer/lib/explorer/chain/internal_transaction.ex index 5d6b4524707a..25c93324d530 100644 --- a/apps/explorer/lib/explorer/chain/internal_transaction.ex +++ b/apps/explorer/lib/explorer/chain/internal_transaction.ex @@ -8,6 +8,8 @@ defmodule Explorer.Chain.InternalTransaction do alias Explorer.Chain.DenormalizationHelper alias Explorer.Chain.InternalTransaction.{Action, CallType, Result, Type} + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] + @typep paging_options :: {:paging_options, PagingOptions.t()} @typep api? :: {:api?, true | false} @@ -832,8 +834,8 @@ defmodule Explorer.Chain.InternalTransaction do preloads = DenormalizationHelper.extend_transaction_preload([ :block, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]], - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]], + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] ]) __MODULE__ diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index 6cfe68473fe1..09f8d297bb6b 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -644,28 +644,8 @@ defmodule Explorer.Chain.SmartContract do @spec compose_address_for_unverified_smart_contract(map(), any()) :: map() def compose_address_for_unverified_smart_contract(%{smart_contract: smart_contract} = address_result, options) when is_nil(smart_contract) do - smart_contract = %{ - updated: %__MODULE__{ - address_hash: address_result.hash - }, - implementation_updated_at: nil, - implementation_address_fetched?: false, - refetch_necessity_checked?: false - } - - {implementation_address_hash, _names, _proxy_type} = - Implementation.get_implementation( - smart_contract, - Keyword.put(options, :proxy_without_abi?, true) - ) - - implementation_smart_contract = - implementation_address_hash - |> Proxy.implementation_to_smart_contract(options) - address_verified_bytecode_twin_contract = - implementation_smart_contract || - get_address_verified_bytecode_twin_contract(address_result.hash, options).verified_contract + get_address_verified_bytecode_twin_contract(address_result.hash, options).verified_contract if address_verified_bytecode_twin_contract do add_bytecode_twin_info_to_contract(address_result, address_verified_bytecode_twin_contract, address_result.hash) @@ -677,10 +657,10 @@ defmodule Explorer.Chain.SmartContract do def compose_address_for_unverified_smart_contract(address_result, _hash, _options), do: address_result def single_implementation_smart_contract_from_proxy(proxy_hash, options) do - {implementation_address_hashes, _names, _proxy_type} = Implementation.get_implementation(proxy_hash, options) + implementation = Implementation.get_implementation(proxy_hash, options) - if implementation_address_hashes && Enum.count(implementation_address_hashes) == 1 do - implementation_address_hashes + if implementation && Enum.count(implementation.address_hashes) == 1 do + implementation.address_hashes |> Enum.at(0) |> Proxy.implementation_to_smart_contract(options) else @@ -1117,24 +1097,24 @@ defmodule Explorer.Chain.SmartContract do @doc """ Gets smart-contract ABI from the DB for the given address hash of smart-contract """ - @spec get_smart_contract_abi(String.t(), any()) :: any() - def get_smart_contract_abi(address_hash_string, options \\ []) - - def get_smart_contract_abi(address_hash_string, options) - when not is_nil(address_hash_string) do - with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), - {smart_contract, _} = - address_hash - |> address_hash_to_smart_contract_with_bytecode_twin(options, false), - false <- is_nil(smart_contract) do - smart_contract - |> Map.get(:abi) - else + @spec get_smart_contract_abi(String.t() | Hash.Address.t(), any()) :: any() + def get_smart_contract_abi(address_hash, options \\ []) + + def get_smart_contract_abi(address_hash_string, options) when is_binary(address_hash_string) do + case Chain.string_to_address_hash(address_hash_string) do + {:ok, address_hash} -> + get_smart_contract_abi(address_hash, options) + _ -> [] end end + def get_smart_contract_abi(%Hash{} = address_hash, options) do + {smart_contract, _} = address_hash_to_smart_contract_with_bytecode_twin(address_hash, options, false) + (smart_contract && smart_contract.abi) || [] + end + def get_smart_contract_abi(address_hash_string, _) when is_nil(address_hash_string) do [] end diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex index 2412874ccbbc..8f67e8d8f1cd 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex @@ -93,21 +93,11 @@ defmodule Explorer.Chain.SmartContract.Proxy do if options[:skip_implementation_fetch?] do false else - {implementation_address_hash_strings, _implementation_names, _proxy_type} = - get_implementation(smart_contract, options) - - with false <- is_nil(implementation_address_hash_strings), - false <- Enum.empty?(implementation_address_hash_strings) do - implementation_address_hash_strings - |> Enum.reduce_while(false, fn implementation_address_hash_string, acc -> - with {:ok, implementation_address_hash} <- string_to_address_hash(implementation_address_hash_string), - false <- implementation_address_hash.bytes == burn_address_hash.bytes do - {:halt, true} - else - _ -> - {:cont, acc} - end - end) + implementation = get_implementation(smart_contract, options) + + with false <- is_nil(implementation), + false <- Enum.empty?(implementation.address_hashes) do + has_not_burn_address_hash?(implementation.address_hashes, burn_address_hash) else _ -> false @@ -116,6 +106,14 @@ defmodule Explorer.Chain.SmartContract.Proxy do end end + @spec has_not_burn_address_hash?([Hash.Address.t()], Hash.Address.t()) :: boolean() + defp has_not_burn_address_hash?(address_hashes, burn_address_hash) do + address_hashes + |> Enum.reduce_while(false, fn implementation_address_hash, acc -> + if implementation_address_hash.bytes == burn_address_hash.bytes, do: {:cont, acc}, else: {:halt, true} + end) + end + @doc """ Decodes and formats an address output from a smart contract ABI. @@ -158,11 +156,12 @@ defmodule Explorer.Chain.SmartContract.Proxy do options ) when not is_nil(proxy_address_hash) and not is_nil(abi) do - {implementation_address_hash_strings, _names, _proxy_type} = get_implementation(smart_contract, options) + implementation = get_implementation(smart_contract, options) - implementation_address_hash_strings - |> Enum.reduce([], fn implementation_address_hash_string, acc -> - SmartContract.get_smart_contract_abi(implementation_address_hash_string) ++ acc + ((implementation && implementation.address_hashes) || + []) + |> Enum.reduce([], fn implementation_address_hash, acc -> + SmartContract.get_smart_contract_abi(implementation_address_hash) ++ acc end) end @@ -556,25 +555,54 @@ defmodule Explorer.Chain.SmartContract.Proxy do A list of maps containing information about the proxy object. """ - @spec proxy_object_info([String.t() | Hash.Address.t()], [String.t() | nil]) :: [map()] - def proxy_object_info([], []), do: [] + @spec proxy_object_info(Implementation.t() | nil) :: [map()] + def proxy_object_info(nil), do: [] + + def proxy_object_info(proxy_implementation) do + implementations_info = prepare_implementations(proxy_implementation) + implementation_addresses = proxy_implementation.address_hashes + implementation_names = proxy_implementation.names - def proxy_object_info(implementation_addresses, implementation_names) do implementation_addresses |> Enum.zip(implementation_names) |> Enum.reduce([], fn {address, name}, acc -> case address do %Hash{} = address_hash -> - [%{"address" => Address.checksum(address_hash), "name" => name} | acc] + [ + %{"address" => Address.checksum(address_hash), "name" => name} |> chain_type_fields(implementations_info) + | acc + ] _ -> with {:ok, address_hash} <- string_to_address_hash(address), checksummed_address <- Address.checksum(address_hash) do - [%{"address" => checksummed_address, "name" => name} | acc] + [%{"address" => checksummed_address, "name" => name} |> chain_type_fields(implementations_info) | acc] else _ -> acc end end end) end + + if Application.compile_env(:explorer, :chain_type) == :filecoin do + def chain_type_fields(%{"address" => address_hash} = address, implementations_info) do + Map.put(address, "filecoin_robust_address", implementations_info[address_hash]) + end + + def prepare_implementations(%Implementation{addresses: [_ | _] = addresses}) do + Enum.into(addresses, %{}, fn address -> {Address.checksum(address.hash), address.filecoin_robust} end) + end + + def prepare_implementations(_) do + %{} + end + else + def chain_type_fields(address, _proxy_implementations) do + address + end + + def prepare_implementations(_implementations_info) do + :ignore + end + end end diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex index a60fb55887f5..41e82383b0c6 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex @@ -13,12 +13,10 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do select: 3 ] - import Explorer.Chain, only: [select_repo: 1, string_to_address_hash: 1] - + alias Explorer.{Chain, Repo} alias Explorer.Chain.{Address, Hash, SmartContract} alias Explorer.Chain.SmartContract.Proxy alias Explorer.Counters.AverageBlockTime - alias Explorer.Repo alias Timex.Duration @burn_address_hash_string "0x0000000000000000000000000000000000000000" @@ -58,6 +56,8 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do field(:address_hashes, {:array, Hash.Address}, null: false) field(:names, {:array, :string}, null: false) + has_many(:addresses, Address, foreign_key: :hash, references: :address_hashes) + belongs_to( :address, Address, @@ -88,7 +88,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do def get_proxy_implementations(proxy_address_hash, options \\ []) do proxy_address_hash |> get_proxy_implementations_query() - |> select_repo(options).one() + |> Chain.select_repo(options).one() end @doc """ @@ -102,7 +102,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do def get_proxy_implementations_for_multiple_proxies(proxy_address_hashes, options) do proxy_address_hashes |> get_proxy_implementations_by_multiple_hashes_query() - |> select_repo(options).all() + |> Chain.select_repo(options).all() end @doc """ @@ -113,7 +113,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do proxy_address_hash |> get_proxy_implementations_query() |> select([p], p.updated_at) - |> select_repo(options).one() + |> Chain.select_repo(options).one() end defp get_proxy_implementations_query(proxy_address_hash) do @@ -133,7 +133,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do @doc """ Returns implementation address, name and proxy type for the given SmartContract """ - @spec get_implementation(any(), any()) :: {any(), any(), atom() | nil} + @spec get_implementation(any(), any()) :: t() | nil def get_implementation(smart_contract, options \\ []) def get_implementation( @@ -226,35 +226,29 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do case Task.yield(get_implementation_address_hash_task, timeout) || Task.ignore(get_implementation_address_hash_task) do {:ok, :empty} -> - {[], [], nil} + nil {:ok, :error} -> - format_proxy_implementations_response(proxy_implementations) + proxy_implementations {:ok, %__MODULE__{} = result} -> - format_proxy_implementations_response(result) + result _ -> - format_proxy_implementations_response(proxy_implementations) + proxy_implementations end else - format_proxy_implementations_response(proxy_implementations) + proxy_implementations end end - def get_implementation(_, _), do: {[], [], nil} + def get_implementation(_, _), do: nil defp fetch_implementation?(implementation_address_fetched?, refetch_necessity_checked?, implementation_updated_at) do (!implementation_address_fetched? || !refetch_necessity_checked?) && check_implementation_refetch_necessity(implementation_updated_at) end - defp format_proxy_implementations_response(proxy_implementations) do - {(proxy_implementations && db_implementation_data_converter(proxy_implementations.address_hashes)) || [], - (proxy_implementations && db_implementation_data_converter(proxy_implementations.names)) || [], - proxy_implementations && proxy_implementations.proxy_type} - end - @doc """ Function checks by timestamp if new implementation fetching needed """ @@ -344,7 +338,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do {implementation_addresses, implementation_names} = implementation_address_hash_strings |> Enum.map(fn implementation_address_hash_string -> - with {:ok, implementation_address_hash} <- string_to_address_hash(implementation_address_hash_string), + with {:ok, implementation_address_hash} <- Chain.string_to_address_hash(implementation_address_hash_string), {:implementation, {%SmartContract{name: name}, _}} <- { :implementation, SmartContract.address_hash_to_smart_contract_with_bytecode_twin(implementation_address_hash, options) @@ -415,14 +409,6 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do |> Repo.update() end - defp db_implementation_data_converter(nil), do: nil - - defp db_implementation_data_converter(list) when is_list(list), - do: list |> Enum.map(&db_implementation_data_converter(&1)) - - defp db_implementation_data_converter(string) when is_binary(string), do: string - defp db_implementation_data_converter(other), do: to_string(other) - @doc """ Returns proxy's implementation names """ @@ -440,4 +426,99 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do end def names(_, _), do: [] + + if Application.compile_env(:explorer, :chain_type) == :filecoin do + @doc """ + Fetches associated addresses for Filecoin based on the provided nested IDs. + + This function is used in Ecto preload to retrieve addresses for proxy implementations. + + ## Parameters + + - nested_ids: A list of nested IDs for which the associated addresses need to be fetched. + + ## Returns + + - A list of associated addresses for the given nested IDs. + """ + def addresses_association_for_filecoin(nested_ids) do + query = from(address in Address, where: address.hash in ^List.flatten(nested_ids)) + + addresses_map = + query + |> Repo.replica().all() + |> Map.new(&{&1.hash, &1}) + + for ids <- nested_ids, + address <- ids |> Enum.map(&addresses_map[&1]) do + {ids, address} + end + end + + @doc """ + Returns the association for proxy implementations. + + This function is used to retrieve the proxy_implementations associations for address + + ## Examples + + iex> Explorer.Chain.SmartContract.Proxy.Models.Implementation.proxy_implementations_association() + [proxy_implementations: [addresses: &Explorer.Chain.SmartContract.Proxy.Models.Implementation.addresses_association_for_filecoin/1]] + """ + @spec proxy_implementations_association() :: [ + proxy_implementations: [addresses: fun()] + ] + def proxy_implementations_association do + [proxy_implementations: proxy_implementations_addresses_association()] + end + + @doc """ + Returns the association of proxy implementation addresses. + + This function is used to retrieve the addresses associations for proxy + + ## Examples + + iex> Explorer.Chain.SmartContract.Proxy.Models.Implementation.proxy_implementations_addresses_association() + [addresses: &Explorer.Chain.SmartContract.Proxy.Models.Implementation.addresses_association_for_filecoin/1] + + """ + @spec proxy_implementations_association() :: [addresses: fun()] + def proxy_implementations_addresses_association do + [addresses: &__MODULE__.addresses_association_for_filecoin/1] + end + else + @doc """ + Returns the association for proxy implementations. + + This function is used to retrieve the proxy_implementations associations for address + + ## Examples + + iex> Explorer.Chain.SmartContract.Proxy.Models.Implementation.proxy_implementations_association() + :proxy_implementations + + """ + @spec proxy_implementations_association() :: :proxy_implementations + def proxy_implementations_association do + :proxy_implementations + end + + @doc """ + Returns the association of proxy implementation addresses. + + This function is used to retrieve the addresses associations for proxy. + (Returns [] since in chain types other than Filecoin, the addresses are not needed to preload) + + ## Examples + + iex> Explorer.Chain.SmartContract.Proxy.Models.Implementation.proxy_implementations_addresses_association() + [] + + """ + @spec proxy_implementations_addresses_association() :: [] + def proxy_implementations_addresses_association do + [] + end + end end diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index 674efce8ffe3..e5b5efe51a36 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -136,6 +136,7 @@ defmodule Explorer.Chain.TokenTransfer do require Explorer.Chain.TokenTransfer.Schema import Ecto.Changeset + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] alias Explorer.Chain alias Explorer.Chain.{DenormalizationHelper, Hash, Log, TokenTransfer} @@ -239,8 +240,8 @@ defmodule Explorer.Chain.TokenTransfer do DenormalizationHelper.extend_transaction_preload([ :transaction, :token, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]], - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]], + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] ]) only_consensus_transfers_query() @@ -266,8 +267,8 @@ defmodule Explorer.Chain.TokenTransfer do DenormalizationHelper.extend_transaction_preload([ :transaction, :token, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]], - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]], + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] ]) only_consensus_transfers_query() @@ -299,8 +300,8 @@ defmodule Explorer.Chain.TokenTransfer do DenormalizationHelper.extend_transaction_preload([ :transaction, :token, - [from_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]], - [to_address: [:scam_badge, :names, :smart_contract, :proxy_implementations]] + [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]], + [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] ]) only_consensus_transfers_query() diff --git a/apps/explorer/lib/explorer/etherscan/contracts.ex b/apps/explorer/lib/explorer/etherscan/contracts.ex index 56d4f2e24a5e..50475f455255 100644 --- a/apps/explorer/lib/explorer/etherscan/contracts.ex +++ b/apps/explorer/lib/explorer/etherscan/contracts.ex @@ -90,14 +90,13 @@ defmodule Explorer.Etherscan.Contracts do def append_proxy_info(%Address{smart_contract: smart_contract} = address) when not is_nil(smart_contract) do updated_smart_contract = if Proxy.proxy_contract?(smart_contract) do + implementation = Implementation.get_implementation(smart_contract) + smart_contract |> Map.put(:is_proxy, true) |> Map.put( :implementation_address_hash_strings, - smart_contract - |> Implementation.get_implementation() - |> Tuple.to_list() - |> List.first() + implementation.address_hashes ) else smart_contract diff --git a/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex b/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex index 60c1476b1715..ac3b1f78898b 100644 --- a/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex +++ b/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex @@ -9,6 +9,7 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do alias HTTPoison.Response import Explorer.Chain.Address.MetadataPreloader, only: [maybe_preload_meta: 3] + import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] require Logger @request_timeout :timer.seconds(5) @@ -143,7 +144,11 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do "items", addresses |> Chain.hashes_to_addresses( - necessity_by_association: %{names: :optional, smart_contract: :optional, proxy_implementations: :optional} + necessity_by_association: %{ + :names => :optional, + :smart_contract => :optional, + proxy_implementations_association() => :optional + } ) |> Enum.map(fn address -> {address, address.transactions_count} end) )} diff --git a/apps/explorer/lib/explorer/smart_contract/helper.ex b/apps/explorer/lib/explorer/smart_contract/helper.ex index 3196373ad155..e7b6c655ffe5 100644 --- a/apps/explorer/lib/explorer/smart_contract/helper.ex +++ b/apps/explorer/lib/explorer/smart_contract/helper.ex @@ -242,9 +242,9 @@ defmodule Explorer.SmartContract.Helper do @doc """ Pre-fetches implementation for unverified smart contract or verified proxy smart-contract """ - @spec pre_fetch_implementations(Address.t()) :: {any(), atom() | nil} + @spec pre_fetch_implementations(Address.t()) :: Implementation.t() | nil def pre_fetch_implementations(address) do - {implementation_address_hashes, implementation_names, proxy_type} = + implementation = with {:verified_smart_contract, %SmartContract{}} <- {:verified_smart_contract, address.smart_contract}, {:proxy?, true} <- {:proxy?, address_is_proxy?(address, @api_true)} do Implementation.get_implementation(address.smart_contract, @api_true) @@ -256,17 +256,14 @@ defmodule Explorer.SmartContract.Helper do } Implementation.get_implementation(smart_contract, @api_true) - else - {[], [], nil} end {:proxy?, false} -> - {[], [], nil} + nil end - implementations = Proxy.proxy_object_info(implementation_address_hashes, implementation_names) - - {implementations, proxy_type} + implementation + |> Chain.select_repo(@api_true).preload(Implementation.proxy_implementations_addresses_association()) end @doc """ diff --git a/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs b/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs index 506ba65a1fc4..c1074ecc3ba0 100644 --- a/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs +++ b/apps/explorer/test/explorer/chain/smart_contract/proxy/models/implementation_test.exs @@ -28,7 +28,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do # fetch nil implementation and don't save it to db TestHelper.get_eip1967_implementation_zero_addresses() - assert {[], [], nil} = Implementation.get_implementation(smart_contract) + assert is_nil(Implementation.get_implementation(smart_contract)) verify!(EthereumJSONRPC.Mox) assert_empty_implementation(smart_contract.address_hash) @@ -43,9 +43,13 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do string_implementation_address_hash = to_string(implementation_smart_contract.address_hash) expect_address_in_oz_slot_response(string_implementation_address_hash) + implementation_address_hash = implementation_smart_contract.address_hash - assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = - Implementation.get_implementation(smart_contract) + assert %Implementation{ + address_hashes: [^implementation_address_hash], + names: ["implementation"], + proxy_type: :eip1967 + } = Implementation.get_implementation(smart_contract) verify!(EthereumJSONRPC.Mox) @@ -57,8 +61,11 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do TestHelper.get_eip1967_implementation_error_response() - assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = - Implementation.get_implementation(smart_contract) + assert %Implementation{ + address_hashes: [^implementation_address_hash], + names: ["implementation"], + proxy_type: :eip1967 + } = Implementation.get_implementation(smart_contract) verify!(EthereumJSONRPC.Mox) @@ -78,8 +85,11 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do Application.put_env(:explorer, :proxy, proxy) - assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = - Implementation.get_implementation(smart_contract) + assert %Implementation{ + address_hashes: [^implementation_address_hash], + names: ["implementation"], + proxy_type: :eip1967 + } = Implementation.get_implementation(smart_contract) {contract_2, _} = SmartContract.address_hash_to_smart_contract_with_bytecode_twin(smart_contract.address_hash) implementation_2 = Implementation.get_proxy_implementations(smart_contract.address_hash) @@ -96,7 +106,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do TestHelper.get_eip1967_implementation_zero_addresses() - assert {[], [], nil} = Implementation.get_implementation(smart_contract) + assert is_nil(Implementation.get_implementation(smart_contract)) verify!(EthereumJSONRPC.Mox) @@ -113,7 +123,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do test "get_implementation/1 for twins contract" do # return nils for nil - assert {[], [], nil} = Implementation.get_implementation(nil) + assert is_nil(Implementation.get_implementation(nil)) smart_contract = insert(:smart_contract) twin_address = insert(:contract_address) @@ -130,11 +140,15 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do Application.put_env(:explorer, :proxy, proxy) # fetch nil implementation - assert {[], [], :unknown} = Implementation.get_implementation(bytecode_twin) + assert %Implementation{address_hashes: [], names: [], proxy_type: :unknown} = + Implementation.get_implementation(bytecode_twin) + verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) - assert {[], [], :unknown} = Implementation.get_implementation(bytecode_twin) + assert %Implementation{address_hashes: [], names: [], proxy_type: :unknown} = + Implementation.get_implementation(bytecode_twin) + verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) @@ -149,8 +163,13 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do string_implementation_address_hash = to_string(implementation_smart_contract.address_hash) expect_address_in_oz_slot_response(string_implementation_address_hash) + implementation_address_hash = implementation_smart_contract.address_hash - assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = + assert %Implementation{ + address_hashes: [^implementation_address_hash], + names: ["implementation"], + proxy_type: :eip1967 + } = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) @@ -165,7 +184,11 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do refute_implementations(smart_contract.address_hash) - assert {[^string_implementation_address_hash], ["implementation"], :eip1967} = + assert %Implementation{ + address_hashes: [^implementation_address_hash], + names: ["implementation"], + proxy_type: :eip1967 + } = Implementation.get_implementation(bytecode_twin) verify!(EthereumJSONRPC.Mox) @@ -178,11 +201,11 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation.Test do _implementation_smart_contract = insert(:smart_contract, name: "implementation") # fetch nil implementation - assert {[], [], nil} = Implementation.get_implementation(bytecode_twin) + assert is_nil(Implementation.get_implementation(bytecode_twin)) verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) - assert {[], [], nil} = Implementation.get_implementation(bytecode_twin) + assert is_nil(Implementation.get_implementation(bytecode_twin)) verify!(EthereumJSONRPC.Mox) refute_implementations(smart_contract.address_hash) diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index ffd74e6520f7..1fe83e8f1e3b 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -2597,7 +2597,7 @@ defmodule Explorer.ChainTest do :contracts_creation_transaction, :token, [smart_contract: :smart_contract_additional_sources], - :proxy_implementations + Explorer.Chain.SmartContract.Proxy.Models.Implementation.proxy_implementations_association() ]) options = [ From 1223faae6d4287d567bd7eb621744a35f03112df Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:58:58 +0300 Subject: [PATCH 296/363] fix: Fix sitemap timeout; optimize OrderedCache preloads (#11131) * fix: Fix sitemap timeout; optimize OrderedCache preloads * Process review comments --- .../lib/block_scout_web/templates/robots/sitemap.xml.eex | 4 ++-- .../lib/block_scout_web/views/robots_view.ex | 2 +- apps/explorer/lib/explorer/chain.ex | 2 +- apps/explorer/lib/explorer/chain/address.ex | 9 ++++++++- apps/explorer/lib/explorer/chain/ordered_cache.ex | 3 ++- apps/explorer/lib/explorer/chain/token.ex | 9 +++++++-- 6 files changed, 21 insertions(+), 8 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/robots/sitemap.xml.eex b/apps/block_scout_web/lib/block_scout_web/templates/robots/sitemap.xml.eex index 0e67d48ab1c8..e633bc29e33f 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/robots/sitemap.xml.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/robots/sitemap.xml.eex @@ -2,7 +2,7 @@ <% host = APIDocsView.blockscout_url(true) %> <% date = to_string(Date.utc_today()) %> <% non_parameterized_urls = ["/", "/txs", "/blocks", "/accounts", "/verified-contracts", "/tokens", "/apps", "/stats", "/api-docs", "/graphiql", "/search-results", "/withdrawals", "/l2-deposits", "/l2-output-roots", "/l2-txn-batches", "/l2-withdrawals"] %> -<% params = [paging_options: %PagingOptions{page_size: limit()}] %> +<% params = [paging_options: %PagingOptions{page_size: limit()}, necessity_by_association: %{}] %> <%= for url <- non_parameterized_urls do %> @@ -50,4 +50,4 @@ <%= date %> <% end %> - \ No newline at end of file + diff --git a/apps/block_scout_web/lib/block_scout_web/views/robots_view.ex b/apps/block_scout_web/lib/block_scout_web/views/robots_view.ex index 9939ec3e684f..628ed672e019 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/robots_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/robots_view.ex @@ -5,6 +5,6 @@ defmodule BlockScoutWeb.RobotsView do alias Explorer.{Chain, PagingOptions} alias Explorer.Chain.{Address, Token} - @limit 200 + @limit 50 defp limit, do: @limit end diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index cda1bec0ee52..3065e3ada2de 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -795,7 +795,7 @@ defmodule Explorer.Chain do from(contract in SmartContract, inner_join: address in Address, on: contract.address_hash == address.hash, - order_by: [desc: address.transactions_count], + order_by: [desc: address.fetched_coin_balance], limit: ^limit, select: contract.address_hash ) diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index da1ea6eda596..0729633b3825 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -543,6 +543,13 @@ defmodule Explorer.Chain.Address do defp fetch_top_addresses(options) do paging_options = Keyword.get(options, :paging_options, @default_paging_options) + necessity_by_association = + Keyword.get(options, :necessity_by_association, %{ + :names => :optional, + :smart_contract => :optional, + proxy_implementations_association() => :optional + }) + case paging_options do %PagingOptions{key: {0, _hash}} -> [] @@ -552,11 +559,11 @@ defmodule Explorer.Chain.Address do from(a in Address, where: a.fetched_coin_balance > ^0, order_by: [desc: a.fetched_coin_balance, asc: a.hash], - preload: [:names, :smart_contract, ^proxy_implementations_association()], select: {a, a.transactions_count} ) base_query + |> Chain.join_associations(necessity_by_association) |> ExplorerHelper.maybe_hide_scam_addresses(:hash) |> page_addresses(paging_options) |> limit(^paging_options.page_size) diff --git a/apps/explorer/lib/explorer/chain/ordered_cache.ex b/apps/explorer/lib/explorer/chain/ordered_cache.ex index 46b1f8f9391c..631bbf8ab73e 100644 --- a/apps/explorer/lib/explorer/chain/ordered_cache.ex +++ b/apps/explorer/lib/explorer/chain/ordered_cache.ex @@ -247,9 +247,10 @@ defmodule Explorer.Chain.OrderedCache do ConCache.update(cache_name(), ids_list_key(), fn ids -> updated_list = elements + |> Enum.sort_by(&element_to_id(&1), &prevails?(&1, &2)) + |> Enum.take(max_size()) |> do_preloads() |> Enum.map(&{element_to_id(&1), &1}) - |> Enum.sort(&prevails?(&1, &2)) |> merge_and_update(ids || [], max_size()) # ids_list is set to never expire diff --git a/apps/explorer/lib/explorer/chain/token.ex b/apps/explorer/lib/explorer/chain/token.ex index f97e0f511316..83f1bd9d306c 100644 --- a/apps/explorer/lib/explorer/chain/token.ex +++ b/apps/explorer/lib/explorer/chain/token.ex @@ -201,16 +201,21 @@ defmodule Explorer.Chain.Token do Chain.paging_options() | {:sorting, SortingHelper.sorting_params()} | {:token_type, [String.t()]} + | {:necessity_by_association, map()} ]) :: [Token.t()] def list_top(filter, options \\ []) do paging_options = Keyword.get(options, :paging_options, Chain.default_paging_options()) token_type = Keyword.get(options, :token_type, nil) sorting = Keyword.get(options, :sorting, []) - query = from(t in Token, preload: [:contract_address]) + necessity_by_association = + Keyword.get(options, :necessity_by_association, %{ + :contract_address => :optional + }) sorted_paginated_query = - query + Token + |> Chain.join_associations(necessity_by_association) |> ExplorerHelper.maybe_hide_scam_addresses(:contract_address_hash) |> apply_filter(token_type) |> SortingHelper.apply_sorting(sorting, @default_sorting) From 94745592ac71bca96c3c44ac304fe20e237e1c9f Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Tue, 5 Nov 2024 07:40:41 -0600 Subject: [PATCH 297/363] chore: convinient way to manage known_hosts within devcontainer (#11091) * convinient way to manage known_hosts within container * ignore cursor.ai configs * readme improved --- .devcontainer/README.md | 3 ++- .devcontainer/devcontainer.json | 4 ++-- .gitignore | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.devcontainer/README.md b/.devcontainer/README.md index c32c5add62fd..f190587bc160 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -42,7 +42,6 @@ Key benefits include: c. Uncomment the `mounts` section: ```json "mounts": [ - "source=${localEnv:HOME}/.ssh/known_hosts,target=/home/vscode/.ssh/known_hosts,type=bind,consistency=cached", "source=${localEnv:HOME}/.ssh/config,target=/home/vscode/.ssh/config,type=bind,consistency=cached", "source=${localEnv:HOME}/.ssh/id_rsa,target=/home/vscode/.ssh/id_rsa,type=bind,consistency=cached" ], @@ -50,6 +49,8 @@ Key benefits include: d. Adjust the paths if your SSH keys are stored in a different location. + e. Use `git update-index --assume-unchanged .devcontainer/devcontainer.json` to prevent the changes to `devcontainer.json` from appearing in `git status` and VS Code's Source Control. To undo the changes, use `git update-index --no-assume-unchanged .devcontainer/devcontainer.json`. + 4. When prompted, click "Reopen in Container". If not prompted, press `F1`, type "Remote-Containers: Reopen in Container", and press Enter. 5. VS Code will build the devcontainer. This process includes: diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f89237d1ae0f..54eed1903062 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,7 +5,8 @@ "workspaceFolder": "/workspace", "postCreateCommand": { "safe-directory": "git config --global --add safe.directory ${containerWorkspaceFolder}", - "deps": "mix deps.get" + "deps": "mix deps.get", + "known_hosts": "sudo chown vscode:vscode /home/vscode/.ssh && ssh-keyscan github.com > /home/vscode/.ssh/known_hosts" }, "remoteEnv": { "PATH": "${containerEnv:PATH}:${containerWorkspaceFolder}/.devcontainer/bin" @@ -37,7 +38,6 @@ // Uncomment and adjust the private key path to the one you use to authenticate on GitHub // if you want to have ability to push to GitHub from the container. // "mounts": [ - // "source=${localEnv:HOME}/.ssh/known_hosts,target=/home/vscode/.ssh/known_hosts,type=bind,consistency=cached", // "source=${localEnv:HOME}/.ssh/config,target=/home/vscode/.ssh/config,type=bind,consistency=cached", // // Make sure that the private key can be used to authenticate on GitHub // "source=${localEnv:HOME}/.ssh/id_rsa,target=/home/vscode/.ssh/id_rsa,type=bind,consistency=cached" diff --git a/.gitignore b/.gitignore index db2acc80dd2e..911095c2c64a 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,8 @@ dump.rdb *.iml .vscode +.cursorignore +.cursorrules **.dec** From 0dfd4c6ca29455583648f0067640e25da4136b15 Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Wed, 6 Nov 2024 14:54:43 +0300 Subject: [PATCH 298/363] feat: zilliqa consensus data related to block (#10699) * refactor: DRY declaration of chain type and feature dependent repos * feat: add `:zilliqa` chain type * chore: add `zilliqa` to cspell.json * feat: import consensus data related to block * fix: cspell * fix: ethereum_jsonrpc tests * fix: format & credo * feat: render consensus data in `/api/v2/blocks/:block_hash_or_number` response * fix: add missing `view` field to block db schema * feat: add workflow to publish docker image * fix: cast `zilliqa_view` field of a block * refactor: view * refactor: define spec only once * refactor: DRY definition of other repos * fix: remove not null constraint for `zilliqa_view` * fix: dialyzer * fix: `@spec` for `chain_type_fields/2` * feat: github workflows * fix: debug * fix: add missing indexer and API workflows --- .github/workflows/config.yml | 19 +- .github/workflows/pre-release-zilliqa.yml | 167 ++++++++++++++ .../publish-docker-image-for-zilliqa.yml | 162 ++++++++++++++ .github/workflows/release-zilliqa.yml | 164 ++++++++++++++ .../controllers/api/v2/block_controller.ex | 8 + .../views/api/v2/block_view.ex | 6 + .../views/api/v2/zilliqa_view.ex | 88 ++++++++ .../lib/ethereum_jsonrpc/block.ex | 111 ++++++++-- .../lib/ethereum_jsonrpc/blocks.ex | 61 ++++- .../lib/ethereum_jsonrpc/zilliqa.ex | 20 ++ .../zilliqa/aggregate_quorum_certificate.ex | 186 ++++++++++++++++ .../lib/ethereum_jsonrpc/zilliqa/helper.ex | 111 ++++++++++ .../zilliqa/nested_quorum_certificates.ex | 187 ++++++++++++++++ .../zilliqa/quorum_certificate.ex | 100 +++++++++ apps/ethereum_jsonrpc/mix.exs | 2 +- .../test/ethereum_jsonrpc/block_test.exs | 4 + .../test/ethereum_jsonrpc/zilliqa_test.exs | 7 + apps/explorer/config/dev.exs | 74 +++---- apps/explorer/config/prod.exs | 128 +++-------- apps/explorer/config/test.exs | 21 +- apps/explorer/lib/explorer/application.ex | 19 +- apps/explorer/lib/explorer/chain/block.ex | 23 ++ .../zilliqa/aggregate_quorum_certificates.ex | 79 +++++++ .../zilliqa/nested_quorum_certificates.ex | 86 +++++++ .../runner/zilliqa/quorum_certificates.ex | 79 +++++++ .../chain/import/stage/chain_type_specific.ex | 5 + .../zilliqa/aggregate_quorum_certificate.ex | 64 ++++++ .../explorer/chain/zilliqa/hash/signature.ex | 60 +++++ .../zilliqa/nested_quorum_certificate.ex | 64 ++++++ .../chain/zilliqa/quorum_certificate.ex | 56 +++++ apps/explorer/lib/explorer/repo.ex | 209 +++--------------- ...240927123039_create_quorum_certificate.exs | 20 ++ ...01_create_aggregate_quorum_certificate.exs | 19 ++ ...23113_create_nested_quorum_certificate.exs | 21 ++ .../20241015095021_add_view_to_block.exs | 9 + ...anitize_duplicated_log_index_logs_test.exs | 4 + apps/indexer/lib/indexer/block/fetcher.ex | 49 ++-- config/config_helper.exs | 48 ++-- config/runtime/dev.exs | 155 +++---------- config/runtime/prod.exs | 141 +++--------- cspell.json | 5 + 41 files changed, 2218 insertions(+), 623 deletions(-) create mode 100644 .github/workflows/pre-release-zilliqa.yml create mode 100644 .github/workflows/publish-docker-image-for-zilliqa.yml create mode 100644 .github/workflows/release-zilliqa.yml create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/zilliqa_view.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/aggregate_quorum_certificate.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/helper.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/nested_quorum_certificates.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/quorum_certificate.ex create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/zilliqa_test.exs create mode 100644 apps/explorer/lib/explorer/chain/import/runner/zilliqa/aggregate_quorum_certificates.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/zilliqa/nested_quorum_certificates.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/zilliqa/quorum_certificates.ex create mode 100644 apps/explorer/lib/explorer/chain/zilliqa/aggregate_quorum_certificate.ex create mode 100644 apps/explorer/lib/explorer/chain/zilliqa/hash/signature.ex create mode 100644 apps/explorer/lib/explorer/chain/zilliqa/nested_quorum_certificate.ex create mode 100644 apps/explorer/lib/explorer/chain/zilliqa/quorum_certificate.ex create mode 100644 apps/explorer/priv/zilliqa/migrations/20240927123039_create_quorum_certificate.exs create mode 100644 apps/explorer/priv/zilliqa/migrations/20240927123101_create_aggregate_quorum_certificate.exs create mode 100644 apps/explorer/priv/zilliqa/migrations/20240927123113_create_nested_quorum_certificate.exs create mode 100644 apps/explorer/priv/zilliqa/migrations/20241015095021_add_view_to_block.exs diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml index 45f887f1d55f..9ec7ed351619 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/config.yml @@ -49,7 +49,24 @@ jobs: // Add/remove CI matrix chain types here const defaultChainTypes = ["default"]; - const chainTypes = ["ethereum", "polygon_zkevm", "rsk", "stability", "filecoin", "optimism", "arbitrum", "celo", "zetachain", "zksync", "shibarium", "blackfort", "scroll"]; + + const chainTypes = [ + "arbitrum", + "blackfort", + "celo", + "ethereum", + "filecoin", + "optimism", + "polygon_zkevm", + "rsk", + "scroll", + "shibarium", + "stability", + "zetachain", + "zilliqa", + "zksync" + ]; + const extraChainTypes = ["suave", "polygon_edge"]; // Chain type matrix we use in master branch diff --git a/.github/workflows/pre-release-zilliqa.yml b/.github/workflows/pre-release-zilliqa.yml new file mode 100644 index 000000000000..9b6192c64d5f --- /dev/null +++ b/.github/workflows/pre-release-zilliqa.yml @@ -0,0 +1,167 @@ +name: Pre-release for Zilliqa + +on: + workflow_dispatch: + inputs: + number: + type: number + required: true + +env: + OTP_VERSION: ${{ vars.OTP_VERSION }} + ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: 6.9.0 + steps: + - uses: actions/checkout@v4 + - name: Setup repo + uses: ./.github/actions/setup-repo + id: setup + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + docker-remote-multi-platform: true + docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} + docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} + + - name: Build and push Docker image (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zilliqa + + - name: Build and push Docker image (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zilliqa + + - name: Build and push Docker image (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zilliqa + + - name: Build and push Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zilliqa + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zilliqa + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zilliqa + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true diff --git a/.github/workflows/publish-docker-image-for-zilliqa.yml b/.github/workflows/publish-docker-image-for-zilliqa.yml new file mode 100644 index 000000000000..68dd5ce6e52b --- /dev/null +++ b/.github/workflows/publish-docker-image-for-zilliqa.yml @@ -0,0 +1,162 @@ +name: Zilliqa publish Docker image + +on: + workflow_dispatch: + push: + branches: + - production-zilliqa +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: 6.9.0 + DOCKER_CHAIN_NAME: zilliqa + steps: + - uses: actions/checkout@v4 + - name: Setup repo + uses: ./.github/actions/setup-repo + id: setup + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + docker-remote-multi-platform: true + docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} + docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} + + - name: Build and push Docker image (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }} + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + + - name: Build and push Docker image (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + + - name: Build and push Docker image (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + + - name: Build and push Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true diff --git a/.github/workflows/release-zilliqa.yml b/.github/workflows/release-zilliqa.yml new file mode 100644 index 000000000000..9c5d82d5bafb --- /dev/null +++ b/.github/workflows/release-zilliqa.yml @@ -0,0 +1,164 @@ +name: Release for Zilliqa + +on: + release: + types: [published] + +env: + OTP_VERSION: ${{ vars.OTP_VERSION }} + ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: 6.9.0 + steps: + - uses: actions/checkout@v4 + - name: Setup repo + uses: ./.github/actions/setup-repo + id: setup + with: + docker-username: ${{ secrets.DOCKER_USERNAME }} + docker-password: ${{ secrets.DOCKER_PASSWORD }} + docker-remote-multi-platform: true + docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }} + docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} + + - name: Build and push Docker image (indexer + API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zilliqa:latest, blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }} + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_WEBAPP=false + API_V1_READ_METHODS_DISABLED=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zilliqa + + - name: Build and push Docker image (indexer) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_API=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zilliqa + + - name: Build and push Docker image (API) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + DISABLE_INDEXER=true + DISABLE_WEBAPP=true + CACHE_EXCHANGE_RATES_PERIOD= + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + ADMIN_PANEL_ENABLED=false + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zilliqa + + - name: Build and push Docker image (indexer + API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-shrink-internal-txs + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + API_V1_READ_METHODS_DISABLED=false + DISABLE_WEBAPP=false + API_V1_WRITE_METHODS_DISABLED=false + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zilliqa + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (indexer + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-shrink-internal-txs-indexer + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_API=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zilliqa + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true + + - name: Build and push Docker image (API + shrink internal transactions) + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile + push: true + tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-shrink-internal-txs-api + labels: ${{ steps.setup.outputs.docker-labels }} + platforms: | + linux/amd64 + linux/arm64/v8 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_WEBAPP=true + DISABLE_INDEXER=true + CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta + RELEASE_VERSION=${{ env.RELEASE_VERSION }} + CHAIN_TYPE=zilliqa + SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex index ba6bcd1e3fed..d7a3164b476e 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex @@ -76,6 +76,14 @@ defmodule BlockScoutWeb.API.V2.BlockController do :arbitrum_confirmation_transaction => :optional } + :zilliqa -> + @chain_type_transaction_necessity_by_association %{} + @chain_type_block_necessity_by_association %{ + :zilliqa_quorum_certificate => :optional, + :zilliqa_aggregate_quorum_certificate => :optional, + [zilliqa_aggregate_quorum_certificate: [:nested_quorum_certificates]] => :optional + } + _ -> @chain_type_transaction_necessity_by_association %{} @chain_type_block_necessity_by_association %{} diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex index 6f9b2ccc48eb..79f3abdd6731 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex @@ -156,6 +156,12 @@ defmodule BlockScoutWeb.API.V2.BlockView do BlockScoutWeb.API.V2.CeloView.extend_block_json_response(result, block, single_block?) end + :zilliqa -> + defp chain_type_fields(result, block, single_block?) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + BlockScoutWeb.API.V2.ZilliqaView.extend_block_json_response(result, block, single_block?) + end + _ -> defp chain_type_fields(result, _block, _single_block?) do result diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/zilliqa_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/zilliqa_view.ex new file mode 100644 index 000000000000..bccc41f1eb7a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/zilliqa_view.ex @@ -0,0 +1,88 @@ +if Application.compile_env(:explorer, :chain_type) == :zilliqa do + defmodule BlockScoutWeb.API.V2.ZilliqaView do + @moduledoc """ + View functions for rendering Zilliqa-related data in JSON format. + """ + + alias Explorer.Chain.Block + alias Explorer.Chain.Zilliqa.{AggregateQuorumCertificate, QuorumCertificate} + + @doc """ + Extends the JSON output with a sub-map containing information related to Zilliqa, + such as the quorum certificate and aggregate quorum certificate. + + ## Parameters + - `out_json`: A map defining the output JSON which will be extended. + - `block`: The block structure containing Zilliqa-related data. + - `single_block?`: A boolean indicating if it is a single block. + + ## Returns + - A map extended with data related to Zilliqa. + """ + @spec extend_block_json_response(map(), Block.t(), boolean()) :: map() + def extend_block_json_response(out_json, %Block{}, false), + do: out_json + + def extend_block_json_response(out_json, %Block{zilliqa_view: zilliqa_view} = block, true) do + zilliqa_json = + %{view: zilliqa_view} + |> add_quorum_certificate(block) + |> add_aggregate_quorum_certificate(block) + + Map.put(out_json, :zilliqa, zilliqa_json) + end + + @spec add_quorum_certificate(map(), Block.t()) :: map() + defp add_quorum_certificate( + zilliqa_json, + %Block{ + zilliqa_quorum_certificate: %QuorumCertificate{ + view: view, + signature: signature, + signers: signers + } + } + ) do + zilliqa_json + |> Map.put(:quorum_certificate, %{ + view: view, + signature: signature, + signers: signers + }) + end + + defp add_quorum_certificate(zilliqa_json, _block), do: zilliqa_json + + @spec add_aggregate_quorum_certificate(map(), Block.t()) :: map() + defp add_aggregate_quorum_certificate(zilliqa_json, %Block{ + zilliqa_aggregate_quorum_certificate: %AggregateQuorumCertificate{ + view: view, + signature: signature, + nested_quorum_certificates: nested_quorum_certificates + } + }) + when is_list(nested_quorum_certificates) do + zilliqa_json + |> Map.put(:aggregate_quorum_certificate, %{ + view: view, + signature: signature, + signers: + Enum.map( + nested_quorum_certificates, + & &1.proposed_by_validator_index + ), + nested_quorum_certificates: + Enum.map( + nested_quorum_certificates, + &%{ + view: &1.view, + signature: &1.signature, + proposed_by_validator_index: &1.proposed_by_validator_index + } + ) + }) + end + + defp add_aggregate_quorum_certificate(zilliqa_json, _block), do: zilliqa_json + end +end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex index 9b3869256008..1024cb806238 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex @@ -8,6 +8,9 @@ defmodule EthereumJSONRPC.Block do alias EthereumJSONRPC.{Transactions, Uncles, Withdrawals} + alias EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate, as: ZilliqaAggregateQuorumCertificate + alias EthereumJSONRPC.Zilliqa.QuorumCertificate, as: ZilliqaQuorumCertificate + # Because proof of stake does not naturally produce uncles like proof of work, # the list of these in each block is empty, and the hash of this list # (sha3Uncles) is the RLP-encoded hash of an empty list. @@ -17,29 +20,36 @@ defmodule EthereumJSONRPC.Block do :rsk -> @chain_type_fields quote( do: [ - bitcoin_merged_mining_header: EthereumJSONRPC.data(), - bitcoin_merged_mining_coinbase_transaction: EthereumJSONRPC.data(), - bitcoin_merged_mining_merkle_proof: EthereumJSONRPC.data(), - hash_for_merged_mining: EthereumJSONRPC.data(), - minimum_gas_price: non_neg_integer() + {optional(:bitcoin_merged_mining_header), EthereumJSONRPC.data()}, + {optional(:bitcoin_merged_mining_coinbase_transaction), EthereumJSONRPC.data()}, + {optional(:bitcoin_merged_mining_merkle_proof), EthereumJSONRPC.data()}, + {optional(:hash_for_merged_mining), EthereumJSONRPC.data()}, + {optional(:minimum_gas_price), non_neg_integer()} ] ) :ethereum -> @chain_type_fields quote( do: [ - withdrawals_root: EthereumJSONRPC.hash(), - blob_gas_used: non_neg_integer(), - excess_blob_gas: non_neg_integer() + {optional(:withdrawals_root), EthereumJSONRPC.hash()}, + {optional(:blob_gas_used), non_neg_integer()}, + {optional(:excess_blob_gas), non_neg_integer()} ] ) :arbitrum -> @chain_type_fields quote( do: [ - send_count: non_neg_integer(), - send_root: EthereumJSONRPC.hash(), - l1_block_number: non_neg_integer() + {optional(:send_count), non_neg_integer()}, + {optional(:send_root), EthereumJSONRPC.hash()}, + {optional(:l1_block_number), non_neg_integer()} + ] + ) + + :zilliqa -> + @chain_type_fields quote( + do: [ + {optional(:zilliqa_view), non_neg_integer()} ] ) @@ -47,7 +57,14 @@ defmodule EthereumJSONRPC.Block do @chain_type_fields quote(do: []) end - @type elixir :: %{String.t() => non_neg_integer | DateTime.t() | String.t() | nil} + @type elixir :: %{ + String.t() => + non_neg_integer + | DateTime.t() + | String.t() + | map() + | nil + } @type params :: %{ unquote_splicing(@chain_type_fields), difficulty: pos_integer(), @@ -191,6 +208,15 @@ defmodule EthereumJSONRPC.Block do "sendCount" => 91,\ "l1BlockNumber" => 19828534,\ """ + :zilliqa -> """ + "view" => "0x115cca",\ + "quorumCertificate" => %{\ + "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa",\ + "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]",\ + "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",\ + "view" => "0x115cc7"\ + },\ + """ _ -> "" end} ...> "uncles" => [] @@ -233,6 +259,9 @@ defmodule EthereumJSONRPC.Block do send_count: 91,\ l1_block_number: 19828534,\ """ + :zilliqa -> """ + zilliqa_view: "0x115cca",\ + """ _ -> "" end} uncles: [] @@ -301,6 +330,9 @@ defmodule EthereumJSONRPC.Block do send_count: nil,\ l1_block_number: nil,\ """ + :zilliqa -> """ + zilliqa_view: nil,\ + """ _ -> "" end} uncles: [] @@ -490,6 +522,7 @@ defmodule EthereumJSONRPC.Block do } end + @spec chain_type_fields(params, elixir) :: params case Application.compile_env(:explorer, :chain_type) do :rsk -> defp chain_type_fields(params, elixir) do @@ -524,6 +557,14 @@ defmodule EthereumJSONRPC.Block do }) end + :zilliqa -> + defp chain_type_fields(params, elixir) do + params + |> Map.merge(%{ + zilliqa_view: Map.get(elixir, "view") + }) + end + _ -> defp chain_type_fields(params, _), do: params end @@ -733,6 +774,30 @@ defmodule EthereumJSONRPC.Block do def elixir_to_withdrawals(%{"withdrawals" => withdrawals}), do: withdrawals def elixir_to_withdrawals(_), do: [] + @doc """ + Get `t:EthereumJSONRPC.Zilliqa.QuorumCertificate.elixir/0` from `t:elixir/0`. + """ + @spec elixir_to_zilliqa_quorum_certificate(elixir()) :: ZilliqaQuorumCertificate.t() | nil + def elixir_to_zilliqa_quorum_certificate(%{"quorumCertificate" => quorum_certificate}), + do: quorum_certificate + + # WARN: This clause is introduced as a workaround to fix tests. HOWEVER, it + # allows the block with a `quorumCertificate` field to be successfully + # imported. This is a temporary solution and should be addressed in the future. + def elixir_to_zilliqa_quorum_certificate(_), do: nil + + @doc """ + Get `t:EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate.elixir/0` from `t:elixir/0`. + """ + @spec elixir_to_zilliqa_aggregate_quorum_certificate(elixir()) :: + ZilliqaAggregateQuorumCertificate.t() | nil + def elixir_to_zilliqa_aggregate_quorum_certificate(%{ + "aggregateQuorumCertificate" => aggregate_quorum_certificate + }), + do: aggregate_quorum_certificate + + def elixir_to_zilliqa_aggregate_quorum_certificate(_), do: nil + @doc """ Decodes the stringly typed numerical fields to `t:non_neg_integer/0` and the timestamps to `t:DateTime.t/0` @@ -834,7 +899,7 @@ defmodule EthereumJSONRPC.Block do defp entry_to_elixir({key, quantity}, _block) when key in ~w(difficulty gasLimit gasUsed minimumGasPrice baseFeePerGas number size - cumulativeDifficulty totalDifficulty paidFees minimumGasPrice blobGasUsed + cumulativeDifficulty totalDifficulty paidFees blobGasUsed excessBlobGas l1BlockNumber sendCount) and not is_nil(quantity) do {key, quantity_to_integer(quantity)} @@ -872,6 +937,26 @@ defmodule EthereumJSONRPC.Block do {key, Withdrawals.to_elixir(withdrawals, block_hash, quantity_to_integer(block_number))} end + case Application.compile_env(:explorer, :chain_type) do + :zilliqa -> + defp entry_to_elixir({"view" = key, quantity}, _block) when not is_nil(quantity) do + {key, quantity_to_integer(quantity)} + end + + defp entry_to_elixir({"quorumCertificate" = key, entry}, %{"hash" => block_hash}) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + {key, EthereumJSONRPC.Zilliqa.QuorumCertificate.new(entry, block_hash)} + end + + defp entry_to_elixir({"aggregateQuorumCertificate" = key, entry}, %{"hash" => block_hash}) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + {key, EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate.new(entry, block_hash)} + end + + _ -> + :ok + end + # bitcoinMergedMiningCoinbaseTransaction bitcoinMergedMiningHeader bitcoinMergedMiningMerkleProof hashForMergedMining - RSK https://github.com/blockscout/blockscout/pull/2934 # committedSeals committee pastCommittedSeals proposerSeal round - Autonity network https://github.com/blockscout/blockscout/pull/3480 # blockGasCost extDataGasUsed - sgb/ava https://github.com/blockscout/blockscout/pull/5301 diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex index eb2b14dd450b..9439089f6057 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex @@ -8,7 +8,44 @@ defmodule EthereumJSONRPC.Blocks do @type elixir :: [Block.elixir()] @type params :: [Block.params()] + + @default_struct_fields [ + blocks_params: [], + block_second_degree_relations_params: [], + transactions_params: [], + withdrawals_params: [], + errors: [] + ] + + case Application.compile_env(:explorer, :chain_type) do + :zilliqa -> + @chain_type_fields quote( + do: [ + zilliqa_quorum_certificates_params: [ + EthereumJSONRPC.Zilliqa.QuorumCertificate.params() + ], + zilliqa_aggregate_quorum_certificates_params: [ + EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate.params() + ], + zilliqa_nested_quorum_certificates_params: [ + EthereumJSONRPC.Zilliqa.NestedQuorumCertificates.params() + ] + ] + ) + + @chain_type_struct_fields [ + zilliqa_quorum_certificates_params: [], + zilliqa_aggregate_quorum_certificates_params: [], + zilliqa_nested_quorum_certificates_params: [] + ] + + _ -> + @chain_type_struct_fields [] + @chain_type_fields quote(do: []) + end + @type t :: %__MODULE__{ + unquote_splicing(@chain_type_fields), blocks_params: [map()], block_second_degree_relations_params: [map()], transactions_params: [map()], @@ -16,11 +53,7 @@ defmodule EthereumJSONRPC.Blocks do errors: [Transport.error()] } - defstruct blocks_params: [], - block_second_degree_relations_params: [], - transactions_params: [], - withdrawals_params: [], - errors: [] + defstruct @default_struct_fields ++ @chain_type_struct_fields def requests(id_to_params, request) when is_map(id_to_params) and is_function(request, 1) do Enum.map(id_to_params, fn {id, params} -> @@ -62,6 +95,21 @@ defmodule EthereumJSONRPC.Blocks do transactions_params: transactions_params, withdrawals_params: withdrawals_params } + |> extend_with_chain_type_fields(elixir_blocks) + end + + @spec extend_with_chain_type_fields(t(), elixir()) :: t() + case Application.compile_env(:explorer, :chain_type) do + :zilliqa -> + defp extend_with_chain_type_fields(%__MODULE__{} = blocks, elixir_blocks) do + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + EthereumJSONRPC.Zilliqa.Helper.extend_blocks_struct(blocks, elixir_blocks) + end + + _ -> + defp extend_with_chain_type_fields(%__MODULE__{} = blocks, _elixir_blocks) do + blocks + end end @doc """ @@ -134,6 +182,9 @@ defmodule EthereumJSONRPC.Blocks do send_count: nil,\ l1_block_number: nil,\ """ + :zilliqa -> """ + zilliqa_view: nil,\ + """ _ -> "" end} uncles: ["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273311"] diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa.ex new file mode 100644 index 000000000000..74e0d985d0cb --- /dev/null +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa.ex @@ -0,0 +1,20 @@ +defmodule EthereumJSONRPC.Zilliqa do + @moduledoc """ + Zilliqa type definitions. + """ + alias EthereumJSONRPC.Zilliqa.{ + AggregateQuorumCertificate, + NestedQuorumCertificates, + QuorumCertificate + } + + @type consensus_data_params :: %{ + zilliqa_quorum_certificates_params: [QuorumCertificate.params()], + zilliqa_aggregate_quorum_certificates_params: [AggregateQuorumCertificate.params()], + zilliqa_nested_quorum_certificates_params: [NestedQuorumCertificates.params()] + } + + @type bit_vector :: String.t() + @type validator_index :: non_neg_integer() + @type signers :: [validator_index()] +end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/aggregate_quorum_certificate.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/aggregate_quorum_certificate.ex new file mode 100644 index 000000000000..009dfc75e941 --- /dev/null +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/aggregate_quorum_certificate.ex @@ -0,0 +1,186 @@ +defmodule EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate do + @moduledoc """ + Represents an aggregate quorum certificate associated with the block. + """ + import EthereumJSONRPC, only: [quantity_to_integer: 1] + + alias EthereumJSONRPC.Zilliqa.NestedQuorumCertificates + + @type elixir :: %{ + String.t() => + EthereumJSONRPC.quantity() + | EthereumJSONRPC.hash() + | EthereumJSONRPC.Zilliqa.bit_vector() + | NestedQuorumCertificates.elixir() + } + + @type t :: %__MODULE__{ + block_hash: EthereumJSONRPC.hash(), + view: non_neg_integer(), + signature: EthereumJSONRPC.hash(), + quorum_certificates: NestedQuorumCertificates.t() + } + + @type params :: %{ + block_hash: EthereumJSONRPC.hash(), + view: non_neg_integer(), + signature: EthereumJSONRPC.hash() + } + + defstruct [:block_hash, :view, :signature, :quorum_certificates] + + @doc """ + Decodes the JSON object returned by JSONRPC node into the `t:t/0` format. + + ## Examples + + iex> aqc_json = %{ + ...> "cosigned" => "[0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + ...> "quorum_certificates" => [ + ...> %{ + ...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa", + ...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + ...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> "view" => "0x115cc7" + ...> }, + ...> %{ + ...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa", + ...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + ...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> "view" => "0x115cc7" + ...> }, + ...> %{ + ...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa", + ...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + ...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> "view" => "0x115cc7" + ...> }, + ...> %{ + ...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa", + ...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + ...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> "view" => "0x115cc7" + ...> } + ...> ], + ...> "signature" => "0x820f591cd78b29a69ba25bc85c4327fa3b0adb61a73a4f0bd943b4ab0b97e061eae9ac032d19fbfab7efb89fac2454ab0b89fea83185c0dac749ff55b0e2c21535a2b712872491577728db868d11939461a6bfde0d94d238f46b643bbe19767e", + ...> "view" => "0x115cca" + ...> } + iex> block_hash = "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d" + iex> EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate.new(aqc_json, block_hash) + %EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate{ + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137866, + signature: "0x820f591cd78b29a69ba25bc85c4327fa3b0adb61a73a4f0bd943b4ab0b97e061eae9ac032d19fbfab7efb89fac2454ab0b89fea83185c0dac749ff55b0e2c21535a2b712872491577728db868d11939461a6bfde0d94d238f46b643bbe19767e", + quorum_certificates: %EthereumJSONRPC.Zilliqa.NestedQuorumCertificates{ + signers: [1, 2, 3, 8], + items: [ + %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137863, + signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + signers: [0, 1, 3, 8] + }, + %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137863, + signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + signers: [0, 1, 3, 8] + }, + %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137863, + signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + signers: [0, 1, 3, 8] + }, + %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137863, + signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + signers: [0, 1, 3, 8] + } + ] + } + } + """ + @spec new(elixir(), EthereumJSONRPC.hash()) :: t() + def new( + %{ + "view" => view, + "signature" => signature, + "cosigned" => bit_vector, + "quorum_certificates" => quorum_certificates + }, + block_hash + ) do + %__MODULE__{ + block_hash: block_hash, + view: quantity_to_integer(view), + signature: signature, + quorum_certificates: + NestedQuorumCertificates.new( + quorum_certificates, + bit_vector, + block_hash + ) + } + end + + @doc """ + Converts `t:t/0` format to params used in `Explorer.Chain`. + + ## Examples + + iex> aggregate_quorum_certificate = %EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate{ + ...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + ...> view: 1137866, + ...> signature: "0x820f591cd78b29a69ba25bc85c4327fa3b0adb61a73a4f0bd943b4ab0b97e061eae9ac032d19fbfab7efb89fac2454ab0b89fea83185c0dac749ff55b0e2c21535a2b712872491577728db868d11939461a6bfde0d94d238f46b643bbe19767e", + ...> quorum_certificates: %EthereumJSONRPC.Zilliqa.NestedQuorumCertificates{ + ...> signers: [1, 2, 3, 8], + ...> items: [ + ...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + ...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + ...> view: 1137863, + ...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> signers: [0, 1, 3, 8] + ...> }, + ...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + ...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + ...> view: 1137863, + ...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> signers: [0, 1, 3, 8] + ...> }, + ...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + ...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + ...> view: 1137863, + ...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> signers: [0, 1, 3, 8] + ...> }, + ...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + ...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + ...> view: 1137863, + ...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> signers: [0, 1, 3, 8] + ...> } + ...> ] + ...> } + ...> } + iex> EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate.to_params(aggregate_quorum_certificate) + %{ + signature: "0x820f591cd78b29a69ba25bc85c4327fa3b0adb61a73a4f0bd943b4ab0b97e061eae9ac032d19fbfab7efb89fac2454ab0b89fea83185c0dac749ff55b0e2c21535a2b712872491577728db868d11939461a6bfde0d94d238f46b643bbe19767e", + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137866 + } + """ + @spec to_params(t()) :: params() + def to_params(%__MODULE__{ + block_hash: block_hash, + view: view, + signature: signature + }) do + %{ + block_hash: block_hash, + view: view, + signature: signature + } + end +end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/helper.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/helper.ex new file mode 100644 index 000000000000..24e5b5088a4b --- /dev/null +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/helper.ex @@ -0,0 +1,111 @@ +defmodule EthereumJSONRPC.Zilliqa.Helper do + @moduledoc """ + Helper functions for processing consensus data. + """ + alias EthereumJSONRPC.Zilliqa + alias EthereumJSONRPC.{Block, Blocks} + + alias EthereumJSONRPC.Zilliqa.{ + AggregateQuorumCertificate, + NestedQuorumCertificates, + QuorumCertificate + } + + @initial_acc %{ + zilliqa_quorum_certificates_params: [], + zilliqa_aggregate_quorum_certificates_params: [], + zilliqa_nested_quorum_certificates_params: [] + } + + @type consensus_data_params :: %{ + zilliqa_quorum_certificates_params: [QuorumCertificate.params()], + zilliqa_aggregate_quorum_certificates_params: [AggregateQuorumCertificate.params()], + zilliqa_nested_quorum_certificates_params: [NestedQuorumCertificates.params()] + } + + @spec extend_blocks_struct(Blocks.t(), Blocks.elixir()) :: Blocks.t() + def extend_blocks_struct(%Blocks{} = module, elixir_blocks) do + consensus_data_fields = + Enum.reduce( + elixir_blocks, + @initial_acc, + &reduce_to_consensus_data/2 + ) + + Map.merge(module, consensus_data_fields) + end + + @doc """ + Converts a bit vector string to a list of indexes where the bit corresponding + to signing validator is 1. + + ## Examples + + iex> bit_vector_to_signers("[1, 0, 1, 0]") + [0, 2] + + iex> bit_vector_to_signers("[1, 1, 1, 1]") + [0, 1, 2, 3] + """ + @spec bit_vector_to_signers(Zilliqa.bit_vector()) :: Zilliqa.signers() + def bit_vector_to_signers(bit_vector_string) do + bit_vector_string + |> Jason.decode!() + |> Enum.with_index() + |> Enum.filter(fn {bit, _} -> bit == 1 end) + |> Enum.map(fn {_, index} -> index end) + end + + @spec reduce_to_consensus_data( + Block.elixir(), + consensus_data_params() + ) :: consensus_data_params() + defp reduce_to_consensus_data( + elixir_block, + %{ + zilliqa_quorum_certificates_params: quorum_certificates_params_acc, + zilliqa_aggregate_quorum_certificates_params: aggregate_quorum_certificates_params_acc, + zilliqa_nested_quorum_certificates_params: aggregate_nested_quorum_certificates_params_acc + } + ) do + quorum_certificates_map = + elixir_block + |> Block.elixir_to_zilliqa_quorum_certificate() + |> case do + nil -> + %{zilliqa_quorum_certificates_params: quorum_certificates_params_acc} + + quorum_certificate -> + quorum_certificate_params = QuorumCertificate.to_params(quorum_certificate) + %{zilliqa_quorum_certificates_params: [quorum_certificate_params | quorum_certificates_params_acc]} + end + + aggregated_quorum_certificate_map = + elixir_block + |> Block.elixir_to_zilliqa_aggregate_quorum_certificate() + |> case do + nil -> + %{ + zilliqa_aggregate_quorum_certificates_params: aggregate_quorum_certificates_params_acc, + zilliqa_nested_quorum_certificates_params: aggregate_nested_quorum_certificates_params_acc + } + + aggregate_quorum_certificates -> + aggregate_quorum_certificate_params = + AggregateQuorumCertificate.to_params(aggregate_quorum_certificates) + + aggregate_nested_quorum_certificates_params = + NestedQuorumCertificates.to_params(aggregate_quorum_certificates.quorum_certificates) + + %{ + zilliqa_aggregate_quorum_certificates_params: [ + aggregate_quorum_certificate_params | aggregate_quorum_certificates_params_acc + ], + zilliqa_nested_quorum_certificates_params: + aggregate_nested_quorum_certificates_params ++ aggregate_nested_quorum_certificates_params_acc + } + end + + Map.merge(quorum_certificates_map, aggregated_quorum_certificate_map) + end +end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/nested_quorum_certificates.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/nested_quorum_certificates.ex new file mode 100644 index 000000000000..1dfb5192ebbb --- /dev/null +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/nested_quorum_certificates.ex @@ -0,0 +1,187 @@ +defmodule EthereumJSONRPC.Zilliqa.NestedQuorumCertificates do + @moduledoc """ + Represents a list of quorum certificates that were proposed by different + validators in the aggregate quorum certificate. + """ + + import EthereumJSONRPC.Zilliqa.Helper, + only: [bit_vector_to_signers: 1] + + alias EthereumJSONRPC.Zilliqa + alias EthereumJSONRPC.Zilliqa.QuorumCertificate + + defstruct [:signers, :items] + + @type elixir :: [QuorumCertificate.elixir()] + + @type t :: %__MODULE__{ + signers: [non_neg_integer()], + items: [QuorumCertificate.t()] + } + + @type params :: %{ + proposed_by_validator_index: Zilliqa.validator_index(), + block_hash: EthereumJSONRPC.hash(), + view: non_neg_integer(), + signature: EthereumJSONRPC.hash(), + signers: Zilliqa.signers() + } + + @doc """ + Decodes the JSON object returned by JSONRPC node into the `t:t/0` format. + + ## Examples + + iex> aqc_json = %{ + ...> "cosigned" => "[0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + ...> "quorum_certificates" => [ + ...> %{ + ...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa", + ...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + ...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> "view" => "0x115cc7" + ...> }, + ...> %{ + ...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa", + ...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + ...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> "view" => "0x115cc7" + ...> }, + ...> %{ + ...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa", + ...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + ...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> "view" => "0x115cc7" + ...> }, + ...> %{ + ...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa", + ...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + ...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> "view" => "0x115cc7" + ...> } + ...> ], + ...> "signature" => "0x820f591cd78b29a69ba25bc85c4327fa3b0adb61a73a4f0bd943b4ab0b97e061eae9ac032d19fbfab7efb89fac2454ab0b89fea83185c0dac749ff55b0e2c21535a2b712872491577728db868d11939461a6bfde0d94d238f46b643bbe19767e", + ...> "view" => "0x115cca" + ...> } + iex> quorum_certificates = aqc_json["quorum_certificates"] + iex> bit_vector = aqc_json["cosigned"] + iex> block_hash = "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d" + iex> EthereumJSONRPC.Zilliqa.NestedQuorumCertificates.new(quorum_certificates, bit_vector, block_hash) + %EthereumJSONRPC.Zilliqa.NestedQuorumCertificates{ + signers: [1, 2, 3, 8], + items: [ + %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137863, + signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + signers: [0, 1, 3, 8] + }, + %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137863, + signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + signers: [0, 1, 3, 8] + }, + %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137863, + signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + signers: [0, 1, 3, 8] + }, + %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137863, + signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + signers: [0, 1, 3, 8] + } + ] + } + """ + @spec new([QuorumCertificate.elixir()], Zilliqa.bit_vector(), EthereumJSONRPC.hash()) :: t() + def new(quorum_certificates, bit_vector, block_hash) do + signers = bit_vector_to_signers(bit_vector) + items = Enum.map(quorum_certificates, &QuorumCertificate.new(&1, block_hash)) + + %__MODULE__{ + signers: signers, + items: items + } + end + + @doc """ + Converts `t:t/0` format to params used in `Explorer.Chain`. + + ## Examples + + iex> nested_quorum_certificates = %EthereumJSONRPC.Zilliqa.NestedQuorumCertificates{ + ...> signers: [1, 2, 3, 8], + ...> items: [ + ...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + ...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + ...> view: 1137863, + ...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> signers: [0, 1, 3, 8] + ...> }, + ...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + ...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + ...> view: 1137863, + ...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> signers: [0, 1, 3, 8] + ...> }, + ...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + ...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + ...> view: 1137863, + ...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> signers: [0, 1, 3, 8] + ...> }, + ...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + ...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + ...> view: 1137863, + ...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> signers: [0, 1, 3, 8] + ...> } + ...> ] + ...> } + iex> EthereumJSONRPC.Zilliqa.NestedQuorumCertificates.to_params(nested_quorum_certificates) + [ + %{ + signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137863, + signers: [0, 1, 3, 8], + proposed_by_validator_index: 1 + }, + %{ + signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137863, + signers: [0, 1, 3, 8], + proposed_by_validator_index: 2 + }, + %{ + signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137863, + signers: [0, 1, 3, 8], + proposed_by_validator_index: 3 + }, + %{ + signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137863, + signers: [0, 1, 3, 8], + proposed_by_validator_index: 8 + } + ] + """ + @spec to_params(t()) :: [params()] + def to_params(%__MODULE__{signers: signers, items: items}) do + signers + |> Enum.zip(items) + |> Enum.map(fn {validator_index, cert} -> + cert + |> QuorumCertificate.to_params() + |> Map.put(:proposed_by_validator_index, validator_index) + end) + end +end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/quorum_certificate.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/quorum_certificate.ex new file mode 100644 index 000000000000..2d8523306422 --- /dev/null +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/quorum_certificate.ex @@ -0,0 +1,100 @@ +defmodule EthereumJSONRPC.Zilliqa.QuorumCertificate do + @moduledoc """ + Represents a quorum certificate associated with the block. + """ + import EthereumJSONRPC, only: [quantity_to_integer: 1] + + import EthereumJSONRPC.Zilliqa.Helper, + only: [bit_vector_to_signers: 1] + + @type elixir :: %{ + String.t() => EthereumJSONRPC.quantity() | EthereumJSONRPC.Zilliqa.bit_vector() | EthereumJSONRPC.hash() + } + + @type t :: %__MODULE__{ + block_hash: EthereumJSONRPC.hash(), + view: non_neg_integer(), + signature: EthereumJSONRPC.hash(), + signers: [non_neg_integer()] + } + + @type params :: %{ + block_hash: EthereumJSONRPC.hash(), + view: non_neg_integer(), + signature: EthereumJSONRPC.hash(), + signers: [non_neg_integer()] + } + + defstruct [:block_hash, :view, :signature, :signers] + + @doc """ + Decodes the JSON object returned by JSONRPC node into the `t:t/0` format. + + ## Examples + + iex> qc_json = %{ + ...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa", + ...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + ...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> "view" => "0x115cc7" + ...> } + iex> block_hash = "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d" + iex> EthereumJSONRPC.Zilliqa.QuorumCertificate.new(qc_json, block_hash) + %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137863, + signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + signers: [0, 1, 3, 8] + } + """ + @spec new(elixir(), EthereumJSONRPC.hash()) :: t() + def new( + %{ + "view" => view, + "cosigned" => bit_vector, + "signature" => signature + }, + block_hash + ) do + %__MODULE__{ + block_hash: block_hash, + view: quantity_to_integer(view), + signature: signature, + signers: bit_vector_to_signers(bit_vector) + } + end + + @doc """ + Converts `t:t/0` format to params used in `Explorer.Chain`. + + ## Examples + + iex> qc = %EthereumJSONRPC.Zilliqa.QuorumCertificate{ + ...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + ...> view: 1137863, + ...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + ...> signers: [0, 1, 3, 8] + ...> } + iex> EthereumJSONRPC.Zilliqa.QuorumCertificate.to_params(qc) + %{ + block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d", + view: 1137863, + signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6", + signers: [0, 1, 3, 8] + } + """ + @spec to_params(t()) :: params() + def to_params(%__MODULE__{ + block_hash: block_hash, + view: view, + signature: signature, + signers: signers + }) do + %{ + block_hash: block_hash, + view: view, + signature: signature, + signers: signers + } + end +end diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs index d965cdfe339a..ddf0dd970584 100644 --- a/apps/ethereum_jsonrpc/mix.exs +++ b/apps/ethereum_jsonrpc/mix.exs @@ -1,4 +1,4 @@ -defmodule EthereumJsonrpc.MixProject do +defmodule EthereumJSONRPC.MixProject do use Mix.Project def project do diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/block_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/block_test.exs index 7628b54da2bb..47f01c803b24 100644 --- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/block_test.exs +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/block_test.exs @@ -85,6 +85,10 @@ defmodule EthereumJSONRPC.BlockTest do l1_block_number: nil } + :zilliqa -> + defp chain_type_fields, + do: %{zilliqa_view: nil} + _ -> defp chain_type_fields, do: %{} end diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/zilliqa_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/zilliqa_test.exs new file mode 100644 index 000000000000..96d08c9101af --- /dev/null +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/zilliqa_test.exs @@ -0,0 +1,7 @@ +defmodule EthereumJSONRPC.ZilliqaTest do + use ExUnit.Case, async: true + + doctest EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate + doctest EthereumJSONRPC.Zilliqa.NestedQuorumCertificates + doctest EthereumJSONRPC.Zilliqa.QuorumCertificate +end diff --git a/apps/explorer/config/dev.exs b/apps/explorer/config/dev.exs index 7202a7f3ca6a..b95b70556aee 100644 --- a/apps/explorer/config/dev.exs +++ b/apps/explorer/config/dev.exs @@ -5,51 +5,35 @@ config :explorer, Explorer.Repo, timeout: :timer.seconds(80), migration_lock: nil -# Configure API database -config :explorer, Explorer.Repo.Replica1, timeout: :timer.seconds(80) - -# Configure Account database -config :explorer, Explorer.Repo.Account, timeout: :timer.seconds(80) - -# Configure Optimism database -config :explorer, Explorer.Repo.Optimism, timeout: :timer.seconds(80) - -# Configure Polygon Edge database -config :explorer, Explorer.Repo.PolygonEdge, timeout: :timer.seconds(80) - -# Configure Polygon zkEVM database -config :explorer, Explorer.Repo.PolygonZkevm, timeout: :timer.seconds(80) - -# Configure Scroll database -config :explorer, Explorer.Repo.Scroll, timeout: :timer.seconds(80) - -# Configure ZkSync database -config :explorer, Explorer.Repo.ZkSync, timeout: :timer.seconds(80) - -config :explorer, Explorer.Repo.Celo, timeout: :timer.seconds(80) - -config :explorer, Explorer.Repo.RSK, timeout: :timer.seconds(80) - -config :explorer, Explorer.Repo.Shibarium, timeout: :timer.seconds(80) - -config :explorer, Explorer.Repo.Suave, timeout: :timer.seconds(80) - -config :explorer, Explorer.Repo.Beacon, timeout: :timer.seconds(80) - -# Configure Arbitrum database -config :explorer, Explorer.Repo.Arbitrum, timeout: :timer.seconds(80) - -config :explorer, Explorer.Repo.BridgedTokens, timeout: :timer.seconds(80) - -config :explorer, Explorer.Repo.Filecoin, timeout: :timer.seconds(80) - -config :explorer, Explorer.Repo.Stability, timeout: :timer.seconds(80) - -config :explorer, Explorer.Repo.Mud, timeout: :timer.seconds(80) - -config :explorer, Explorer.Repo.ShrunkInternalTransactions, timeout: :timer.seconds(80) - -config :explorer, Explorer.Repo.Blackfort, timeout: :timer.seconds(80) +for repo <- [ + # Configure API database + Explorer.Repo.Replica1, + + # Feature dependent repos + Explorer.Repo.Account, + Explorer.Repo.BridgedTokens, + Explorer.Repo.ShrunkInternalTransactions, + + # Chain-type dependent repos + Explorer.Repo.Arbitrum, + Explorer.Repo.Beacon, + Explorer.Repo.Blackfort, + Explorer.Repo.Celo, + Explorer.Repo.Filecoin, + Explorer.Repo.Mud, + Explorer.Repo.Optimism, + Explorer.Repo.PolygonEdge, + Explorer.Repo.PolygonZkevm, + Explorer.Repo.RSK, + Explorer.Repo.Scroll, + Explorer.Repo.Shibarium, + Explorer.Repo.Stability, + Explorer.Repo.Suave, + Explorer.Repo.Zilliqa, + Explorer.Repo.ZkSync + ] do + config :explorer, repo, timeout: :timer.seconds(80) +end config :explorer, Explorer.Tracer, env: "dev", disabled?: true diff --git a/apps/explorer/config/prod.exs b/apps/explorer/config/prod.exs index 0f1f30cd9f78..89f52c6ad9f0 100644 --- a/apps/explorer/config/prod.exs +++ b/apps/explorer/config/prod.exs @@ -7,102 +7,38 @@ config :explorer, Explorer.Repo, migration_lock: nil, ssl_opts: [verify: :verify_none] -# Configures API the database -config :explorer, Explorer.Repo.Replica1, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -# Configures Account database -config :explorer, Explorer.Repo.Account, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.Optimism, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.PolygonEdge, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.PolygonZkevm, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.Scroll, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.ZkSync, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.Celo, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.RSK, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.Shibarium, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.Suave, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.Beacon, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.Arbitrum, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.BridgedTokens, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.Filecoin, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.Stability, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.Mud, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.ShrunkInternalTransactions, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] - -config :explorer, Explorer.Repo.Blackfort, - prepare: :unnamed, - timeout: :timer.seconds(60), - ssl_opts: [verify: :verify_none] +for repo <- [ + # Configures API the database + Explorer.Repo.Replica1, + + # Feature dependent repos + Explorer.Repo.Account, + Explorer.Repo.BridgedTokens, + Explorer.Repo.ShrunkInternalTransactions, + + # Chain-type dependent repos + Explorer.Repo.Arbitrum, + Explorer.Repo.Beacon, + Explorer.Repo.Blackfort, + Explorer.Repo.Celo, + Explorer.Repo.Filecoin, + Explorer.Repo.Mud, + Explorer.Repo.Optimism, + Explorer.Repo.PolygonEdge, + Explorer.Repo.PolygonZkevm, + Explorer.Repo.RSK, + Explorer.Repo.Scroll, + Explorer.Repo.Shibarium, + Explorer.Repo.Stability, + Explorer.Repo.Suave, + Explorer.Repo.Zilliqa, + Explorer.Repo.ZkSync + ] do + config :explorer, repo, + prepare: :unnamed, + timeout: :timer.seconds(60), + ssl_opts: [verify: :verify_none] +end config :explorer, Explorer.Tracer, env: "production", disabled?: true diff --git a/apps/explorer/config/test.exs b/apps/explorer/config/test.exs index f0f047be57bf..3ef8c7ab4034 100644 --- a/apps/explorer/config/test.exs +++ b/apps/explorer/config/test.exs @@ -54,23 +54,24 @@ config :explorer, Explorer.Repo.Account, log: false for repo <- [ + Explorer.Repo.Arbitrum, Explorer.Repo.Beacon, + Explorer.Repo.Blackfort, + Explorer.Repo.BridgedTokens, + Explorer.Repo.Celo, + Explorer.Repo.Filecoin, + Explorer.Repo.Mud, Explorer.Repo.Optimism, Explorer.Repo.PolygonEdge, Explorer.Repo.PolygonZkevm, - Explorer.Repo.Scroll, - Explorer.Repo.ZkSync, - Explorer.Repo.Celo, Explorer.Repo.RSK, + Explorer.Repo.Scroll, Explorer.Repo.Shibarium, - Explorer.Repo.Suave, - Explorer.Repo.Arbitrum, - Explorer.Repo.BridgedTokens, - Explorer.Repo.Filecoin, - Explorer.Repo.Stability, - Explorer.Repo.Mud, Explorer.Repo.ShrunkInternalTransactions, - Explorer.Repo.Blackfort + Explorer.Repo.Stability, + Explorer.Repo.Suave, + Explorer.Repo.Zilliqa, + Explorer.Repo.ZkSync ] do config :explorer, repo, database: database, diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index 33aaed879224..cfa4f10dfc5b 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -166,22 +166,23 @@ defmodule Explorer.Application do defp repos_by_chain_type do if Mix.env() == :test do [ + Explorer.Repo.Arbitrum, Explorer.Repo.Beacon, + Explorer.Repo.Blackfort, + Explorer.Repo.BridgedTokens, + Explorer.Repo.Celo, + Explorer.Repo.Filecoin, Explorer.Repo.Optimism, Explorer.Repo.PolygonEdge, Explorer.Repo.PolygonZkevm, - Explorer.Repo.Scroll, - Explorer.Repo.ZkSync, - Explorer.Repo.Celo, Explorer.Repo.RSK, + Explorer.Repo.Scroll, Explorer.Repo.Shibarium, - Explorer.Repo.Suave, - Explorer.Repo.Arbitrum, - Explorer.Repo.BridgedTokens, - Explorer.Repo.Filecoin, - Explorer.Repo.Stability, Explorer.Repo.ShrunkInternalTransactions, - Explorer.Repo.Blackfort + Explorer.Repo.Stability, + Explorer.Repo.Suave, + Explorer.Repo.Zilliqa, + Explorer.Repo.ZkSync ] else [] diff --git a/apps/explorer/lib/explorer/chain/block.ex b/apps/explorer/lib/explorer/chain/block.ex index 6cc8e049d39b..311242d57cdd 100644 --- a/apps/explorer/lib/explorer/chain/block.ex +++ b/apps/explorer/lib/explorer/chain/block.ex @@ -19,6 +19,8 @@ defmodule Explorer.Chain.Block.Schema do alias Explorer.Chain.Block.{Reward, SecondDegreeRelation} alias Explorer.Chain.Celo.EpochReward, as: CeloEpochReward alias Explorer.Chain.Optimism.TransactionBatch, as: OptimismTransactionBatch + alias Explorer.Chain.Zilliqa.AggregateQuorumCertificate, as: ZilliqaAggregateQuorumCertificate + alias Explorer.Chain.Zilliqa.QuorumCertificate, as: ZilliqaQuorumCertificate alias Explorer.Chain.ZkSync.BatchBlock, as: ZkSyncBatchBlock @chain_type_fields (case Application.compile_env(:explorer, :chain_type) do @@ -106,6 +108,24 @@ defmodule Explorer.Chain.Block.Schema do 2 ) + :zilliqa -> + elem( + quote do + field(:zilliqa_view, :integer) + + has_one(:zilliqa_quorum_certificate, ZilliqaQuorumCertificate, + foreign_key: :block_hash, + references: :hash + ) + + has_one(:zilliqa_aggregate_quorum_certificate, ZilliqaAggregateQuorumCertificate, + foreign_key: :block_hash, + references: :hash + ) + end, + 2 + ) + _ -> [] end) @@ -183,6 +203,9 @@ defmodule Explorer.Chain.Block do :arbitrum -> ~w(send_count send_root l1_block_number)a + :zilliqa -> + ~w(zilliqa_view)a + _ -> ~w()a end) diff --git a/apps/explorer/lib/explorer/chain/import/runner/zilliqa/aggregate_quorum_certificates.ex b/apps/explorer/lib/explorer/chain/import/runner/zilliqa/aggregate_quorum_certificates.ex new file mode 100644 index 000000000000..42a2b00357a3 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/zilliqa/aggregate_quorum_certificates.ex @@ -0,0 +1,79 @@ +defmodule Explorer.Chain.Import.Runner.Zilliqa.AggregateQuorumCertificates do + @moduledoc """ + Bulk imports `t:Explorer.Chain.Zilliqa.AggregateQuorumCertificate.t/0`. + """ + + require Ecto.Query + + alias Ecto.{Changeset, Multi, Repo} + alias Explorer.Chain.Import + alias Explorer.Chain.Zilliqa.AggregateQuorumCertificate + alias Explorer.Prometheus.Instrumenter + + @behaviour Import.Runner + + # milliseconds + @timeout 60_000 + + @type imported :: [AggregateQuorumCertificate.t()] + + @impl Import.Runner + def ecto_schema_module, do: AggregateQuorumCertificate + + @impl Import.Runner + def option_key, do: :zilliqa_aggregate_quorum_certificates + + @impl Import.Runner + @spec imported_table_row() :: %{:value_description => binary(), :value_type => binary()} + 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 + @spec run(Multi.t(), list(), map()) :: Multi.t() + 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, :insert_zilliqa_aggregate_quorum_certificates, fn repo, _ -> + Instrumenter.block_import_stage_runner( + fn -> insert(repo, changes_list, insert_options) end, + :block_referencing, + :zilliqa_aggregate_quorum_certificates, + :zilliqa_aggregate_quorum_certificates + ) + end) + end + + @impl Import.Runner + def timeout, do: @timeout + + @spec insert(Repo.t(), [map()], %{required(:timeout) => timeout(), required(:timestamps) => Import.timestamps()}) :: + {:ok, [AggregateQuorumCertificate.t()]} + | {:error, [Changeset.t()]} + def insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = _options) when is_list(changes_list) do + # Enforce Zilliqa.AggregateQuorumCertificate ShareLocks order (see docs: sharelock.md) + ordered_changes_list = Enum.sort_by(changes_list, & &1.block_hash) + + {:ok, inserted} = + Import.insert_changes_list( + repo, + ordered_changes_list, + for: AggregateQuorumCertificate, + returning: true, + timeout: timeout, + timestamps: timestamps, + conflict_target: :block_hash, + on_conflict: :nothing + ) + + {:ok, inserted} + end +end diff --git a/apps/explorer/lib/explorer/chain/import/runner/zilliqa/nested_quorum_certificates.ex b/apps/explorer/lib/explorer/chain/import/runner/zilliqa/nested_quorum_certificates.ex new file mode 100644 index 000000000000..78cc04979803 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/zilliqa/nested_quorum_certificates.ex @@ -0,0 +1,86 @@ +defmodule Explorer.Chain.Import.Runner.Zilliqa.NestedQuorumCertificates do + @moduledoc """ + Bulk imports `t:Explorer.Chain.Zilliqa.NestedQuorumCertificate.t/0`. + """ + + require Ecto.Query + + alias Ecto.{Changeset, Multi, Repo} + alias Explorer.Chain.Import + alias Explorer.Chain.Zilliqa.NestedQuorumCertificate + alias Explorer.Prometheus.Instrumenter + + @behaviour Import.Runner + + # milliseconds + @timeout 60_000 + + @type imported :: [NestedQuorumCertificate.t()] + + @impl Import.Runner + def ecto_schema_module, do: NestedQuorumCertificate + + @impl Import.Runner + def option_key, do: :zilliqa_nested_quorum_certificates + + @impl Import.Runner + @spec imported_table_row() :: %{:value_description => binary(), :value_type => binary()} + 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 + @spec run(Multi.t(), list(), map()) :: Multi.t() + 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, :insert_zilliqa_nested_quorum_certificates, fn repo, _ -> + Instrumenter.block_import_stage_runner( + fn -> insert(repo, changes_list, insert_options) end, + :block_referencing, + :zilliqa_nested_quorum_certificates, + :zilliqa_nested_quorum_certificates + ) + end) + end + + @impl Import.Runner + def timeout, do: @timeout + + @spec insert(Repo.t(), [map()], %{required(:timeout) => timeout(), required(:timestamps) => Import.timestamps()}) :: + {:ok, [NestedQuorumCertificate.t()]} + | {:error, [Changeset.t()]} + def insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = _options) when is_list(changes_list) do + # Enforce Zilliqa.NestedQuorumCertificate ShareLocks order (see docs: sharelock.md) + ordered_changes_list = + Enum.sort_by( + changes_list, + &{&1.block_hash, &1.proposed_by_validator_index} + ) + + {:ok, inserted} = + Import.insert_changes_list( + repo, + ordered_changes_list, + for: NestedQuorumCertificate, + returning: true, + timeout: timeout, + timestamps: timestamps, + conflict_target: [ + :block_hash, + :proposed_by_validator_index + ], + on_conflict: :nothing + ) + + {:ok, inserted} + end +end diff --git a/apps/explorer/lib/explorer/chain/import/runner/zilliqa/quorum_certificates.ex b/apps/explorer/lib/explorer/chain/import/runner/zilliqa/quorum_certificates.ex new file mode 100644 index 000000000000..e7b876910a8b --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/zilliqa/quorum_certificates.ex @@ -0,0 +1,79 @@ +defmodule Explorer.Chain.Import.Runner.Zilliqa.QuorumCertificates do + @moduledoc """ + Bulk imports `t:Explorer.Chain.Zilliqa.QuorumCertificate.t/0`. + """ + + require Ecto.Query + + alias Ecto.{Changeset, Multi, Repo} + alias Explorer.Chain.Import + alias Explorer.Chain.Zilliqa.QuorumCertificate + alias Explorer.Prometheus.Instrumenter + + @behaviour Import.Runner + + # milliseconds + @timeout 60_000 + + @type imported :: [QuorumCertificate.t()] + + @impl Import.Runner + def ecto_schema_module, do: QuorumCertificate + + @impl Import.Runner + def option_key, do: :zilliqa_quorum_certificates + + @impl Import.Runner + @spec imported_table_row() :: %{:value_description => binary(), :value_type => binary()} + 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 + @spec run(Multi.t(), list(), map()) :: Multi.t() + 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, :insert_zilliqa_quorum_certificates, fn repo, _ -> + Instrumenter.block_import_stage_runner( + fn -> insert(repo, changes_list, insert_options) end, + :block_referencing, + :zilliqa_quorum_certificates, + :zilliqa_quorum_certificates + ) + end) + end + + @impl Import.Runner + def timeout, do: @timeout + + @spec insert(Repo.t(), [map()], %{required(:timeout) => timeout(), required(:timestamps) => Import.timestamps()}) :: + {:ok, [QuorumCertificate.t()]} + | {:error, [Changeset.t()]} + def insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = _options) when is_list(changes_list) do + # Enforce Zilliqa.QuorumCertificate ShareLocks order (see docs: sharelock.md) + ordered_changes_list = Enum.sort_by(changes_list, & &1.block_hash) + + {:ok, inserted} = + Import.insert_changes_list( + repo, + ordered_changes_list, + for: QuorumCertificate, + returning: true, + timeout: timeout, + timestamps: timestamps, + conflict_target: :block_hash, + on_conflict: :nothing + ) + + {:ok, inserted} + end +end diff --git a/apps/explorer/lib/explorer/chain/import/stage/chain_type_specific.ex b/apps/explorer/lib/explorer/chain/import/stage/chain_type_specific.ex index f89781059882..567bd3f6b181 100644 --- a/apps/explorer/lib/explorer/chain/import/stage/chain_type_specific.ex +++ b/apps/explorer/lib/explorer/chain/import/stage/chain_type_specific.ex @@ -62,6 +62,11 @@ defmodule Explorer.Chain.Import.Stage.ChainTypeSpecific do Runner.Celo.ValidatorGroupVotes, Runner.Celo.ElectionRewards, Runner.Celo.EpochRewards + ], + zilliqa: [ + Runner.Zilliqa.AggregateQuorumCertificates, + Runner.Zilliqa.NestedQuorumCertificates, + Runner.Zilliqa.QuorumCertificates ] } diff --git a/apps/explorer/lib/explorer/chain/zilliqa/aggregate_quorum_certificate.ex b/apps/explorer/lib/explorer/chain/zilliqa/aggregate_quorum_certificate.ex new file mode 100644 index 000000000000..f26936009f65 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/zilliqa/aggregate_quorum_certificate.ex @@ -0,0 +1,64 @@ +defmodule Explorer.Chain.Zilliqa.AggregateQuorumCertificate do + @moduledoc """ + A stored representation of a Zilliqa aggregate quorum certificate in the + context of PBFT consensus. + + In PBFT (Practical Byzantine Fault Tolerance) consensus, an aggregate quorum + certificate combines multiple quorum certificates into one, providing proof + that a block has been approved across multiple consensus rounds or by multiple + subsets of validators. It includes aggregated signatures and references to + nested quorum certificates. + + Changes in the schema should be reflected in the bulk import module: + - `Explorer.Chain.Import.Runner.Zilliqa.AggregateQuorumCertificate` + """ + use Explorer.Schema + + alias Explorer.Chain.{Block, Hash} + alias Explorer.Chain.Zilliqa.Hash.Signature, as: SignatureHash + alias Explorer.Chain.Zilliqa.NestedQuorumCertificate + + @required_attrs ~w(block_hash view signature)a + + @typedoc """ + * `view` - the view number associated with the quorum certificate, indicating + the consensus round. + * `signature` - the aggregated BLS (Boneh–Lynn–Shacham) signature representing + the validators' agreement. + * `block_hash` - the hash of the block associated with this aggregate quorum + certificate. + * `nested_quorum_certificates` - the list of nested quorum certificates that + are part of this aggregate. + """ + @primary_key false + typed_schema "zilliqa_aggregate_quorum_certificates" do + field(:view, :integer) + field(:signature, SignatureHash) + + belongs_to(:block, Block, + foreign_key: :block_hash, + primary_key: true, + references: :hash, + type: Hash.Full, + null: false + ) + + has_many( + :nested_quorum_certificates, + NestedQuorumCertificate, + foreign_key: :block_hash, + references: :block_hash + ) + + timestamps() + end + + @spec changeset(Ecto.Schema.t(), map()) :: Ecto.Changeset.t() + def changeset(%__MODULE__{} = cert, attrs) do + cert + |> cast(attrs, @required_attrs) + |> validate_required(@required_attrs) + |> foreign_key_constraint(:block_hash) + |> unique_constraint(:block_hash, name: :aggregate_quorum_certificates_pkey) + end +end diff --git a/apps/explorer/lib/explorer/chain/zilliqa/hash/signature.ex b/apps/explorer/lib/explorer/chain/zilliqa/hash/signature.ex new file mode 100644 index 000000000000..44c4253f990b --- /dev/null +++ b/apps/explorer/lib/explorer/chain/zilliqa/hash/signature.ex @@ -0,0 +1,60 @@ +defmodule Explorer.Chain.Zilliqa.Hash.Signature do + @moduledoc """ + A 96-byte BLS signature of the supermajority of the validators. + """ + + alias Explorer.Chain.Hash + + use Ecto.Type + @behaviour Hash + + @byte_count 96 + + @typedoc """ + A #{@byte_count}-byte BLS signature hash of the + `t:Explorer.Chain.Zilliqa.QuorumCertificate.t/0` or + `t:Explorer.Chain.Zilliqa.AggregateQuorumCertificate.t/0` or + `t:Explorer.Chain.Zilliqa.NestedQuorumCertificate.t/0`. + """ + @type t :: %Hash{byte_count: unquote(@byte_count), bytes: <<_::unquote(@byte_count * Hash.bits_per_byte())>>} + + @doc """ + Casts a term to a `t`. + """ + @impl Ecto.Type + @spec cast(term()) :: {:ok, t()} | :error + def cast(term) do + Hash.cast(__MODULE__, term) + end + + @doc """ + Dumps a `t` to a binary. + """ + @impl Ecto.Type + @spec dump(term()) :: {:ok, binary} | :error + def dump(term) do + Hash.dump(__MODULE__, term) + end + + @doc """ + Loads a binary to a `t`. + """ + @impl Ecto.Type + @spec load(term()) :: {:ok, t()} | :error + def load(term) do + Hash.load(__MODULE__, term) + end + + @doc """ + Returns the type of the `t`. + """ + @impl Ecto.Type + @spec type() :: :binary + def type, do: :binary + + @doc """ + Returns the byte count of the `t`. + """ + @impl Hash + def byte_count, do: @byte_count +end diff --git a/apps/explorer/lib/explorer/chain/zilliqa/nested_quorum_certificate.ex b/apps/explorer/lib/explorer/chain/zilliqa/nested_quorum_certificate.ex new file mode 100644 index 000000000000..8edd55a14813 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/zilliqa/nested_quorum_certificate.ex @@ -0,0 +1,64 @@ +defmodule Explorer.Chain.Zilliqa.NestedQuorumCertificate do + @moduledoc """ + A stored representation of a nested quorum certificate in Zilliqa's PBFT + consensus. + + In Zilliqa's PBFT (Practical Byzantine Fault Tolerance) consensus, an + aggregate quorum certificate may include multiple nested quorum certificates. + Each nested quorum certificate represents a quorum certificate proposed by a + specific validator and contains its own aggregated signatures and participant + information. + + Changes in the schema should be reflected in the bulk import module: + - `Explorer.Chain.Import.Runner.Zilliqa.AggregateQuorumCertificate` + """ + use Explorer.Schema + + alias Explorer.Chain.Hash + alias Explorer.Chain.Zilliqa.AggregateQuorumCertificate + alias Explorer.Chain.Zilliqa.Hash.Signature, as: SignatureHash + + @required_attrs ~w(block_hash proposed_by_validator_index view signature signers)a + + @typedoc """ + * `proposed_by_validator_index` - the index of the validator who proposed this + nested quorum certificate. + * `view` - the view number associated with the quorum certificate, indicating + the consensus round. + * `signature` - the aggregated BLS (Boneh–Lynn–Shacham) signature representing + the validators' agreement. + * `signers` - the array of integers representing the indices of validators who + participated in the quorum (as indicated by the `cosigned` bit vector). + * `block_hash` - the hash of the block associated with the aggregate quorum + certificate to which this nested quorum certificate belongs. + """ + @primary_key false + typed_schema "zilliqa_nested_quorum_certificates" do + field(:proposed_by_validator_index, :integer, primary_key: true) + field(:view, :integer) + field(:signature, SignatureHash) + field(:signers, {:array, :integer}) + + belongs_to(:aggregate_quorum_certificate, AggregateQuorumCertificate, + foreign_key: :block_hash, + references: :block_hash, + primary_key: true, + type: Hash.Full, + null: false + ) + + timestamps() + end + + @spec changeset(Ecto.Schema.t(), map()) :: Ecto.Changeset.t() + def changeset(%__MODULE__{} = cert, attrs) do + cert + |> cast(attrs, @required_attrs) + |> validate_required(@required_attrs) + |> foreign_key_constraint(:block_hash) + |> unique_constraint( + [:proposed_by_validator_index, :block_hash], + name: :nested_quorum_certificates_pkey + ) + end +end diff --git a/apps/explorer/lib/explorer/chain/zilliqa/quorum_certificate.ex b/apps/explorer/lib/explorer/chain/zilliqa/quorum_certificate.ex new file mode 100644 index 000000000000..61235bae2699 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/zilliqa/quorum_certificate.ex @@ -0,0 +1,56 @@ +defmodule Explorer.Chain.Zilliqa.QuorumCertificate do + @moduledoc """ + A stored representation of a Zilliqa quorum certificate in the context of PBFT + consensus. + + In PBFT (Practical Byzantine Fault Tolerance) consensus, a quorum certificate + is a data structure that serves as proof that a block has been approved by a + supermajority of validators. It includes aggregated signatures and a bitmap + indicating which validators participated in the consensus. + + Changes in the schema should be reflected in the bulk import module: + - `Explorer.Chain.Import.Runner.Zilliqa.AggregateQuorumCertificate` + """ + use Explorer.Schema + + alias Explorer.Chain.{Block, Hash} + alias Explorer.Chain.Zilliqa.Hash.Signature, as: SignatureHash + + @required_attrs ~w(block_hash view signature signers)a + + @typedoc """ + * `view` - the view number associated with the quorum certificate, indicating + the consensus round. + * `signature` - the aggregated BLS (Boneh–Lynn–Shacham) signature representing + the validators' agreement. + * `signers` - the array of integers representing the indices of validators who + participated in the quorum (as indicated by the `cosigned` bit vector). + * `block_hash` - the hash of the block associated with this quorum + certificate. + """ + @primary_key false + typed_schema "zilliqa_quorum_certificates" do + field(:view, :integer) + field(:signature, SignatureHash) + field(:signers, {:array, :integer}) + + belongs_to(:block, Block, + foreign_key: :block_hash, + primary_key: true, + references: :hash, + type: Hash.Full, + null: false + ) + + timestamps() + end + + @spec changeset(Ecto.Schema.t(), map()) :: Ecto.Schema.t() + def changeset(%__MODULE__{} = cert, attrs) do + cert + |> cast(attrs, @required_attrs) + |> validate_required(@required_attrs) + |> foreign_key_constraint(:block_hash) + |> unique_constraint(:block_hash, name: :quorum_certificates_pkey) + end +end diff --git a/apps/explorer/lib/explorer/repo.ex b/apps/explorer/lib/explorer/repo.ex index 5f1ff6e174f1..7b159e263556 100644 --- a/apps/explorer/lib/explorer/repo.ex +++ b/apps/explorer/lib/explorer/repo.ex @@ -127,183 +127,38 @@ defmodule Explorer.Repo do end end - defmodule Account do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule Optimism do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule PolygonEdge do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule PolygonZkevm do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule Scroll do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule ZkSync do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule Celo do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule RSK do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule Shibarium do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule Suave do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule Beacon do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule Arbitrum do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule BridgedTokens do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule Filecoin do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule Stability do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule Mud do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule ShrunkInternalTransactions do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) - end - end - - defmodule Blackfort do - use Ecto.Repo, - otp_app: :explorer, - adapter: Ecto.Adapters.Postgres - - def init(_, opts) do - ConfigHelper.init_repo_module(__MODULE__, opts) + for repo <- [ + # Feature dependent repos + Explorer.Repo.Account, + Explorer.Repo.BridgedTokens, + Explorer.Repo.ShrunkInternalTransactions, + + # Chain-type dependent repos + Explorer.Repo.Arbitrum, + Explorer.Repo.Beacon, + Explorer.Repo.Blackfort, + Explorer.Repo.Celo, + Explorer.Repo.Filecoin, + Explorer.Repo.Mud, + Explorer.Repo.Optimism, + Explorer.Repo.PolygonEdge, + Explorer.Repo.PolygonZkevm, + Explorer.Repo.RSK, + Explorer.Repo.Scroll, + Explorer.Repo.Shibarium, + Explorer.Repo.Stability, + Explorer.Repo.Suave, + Explorer.Repo.Zilliqa, + Explorer.Repo.ZkSync + ] do + defmodule repo do + use Ecto.Repo, + otp_app: :explorer, + adapter: Ecto.Adapters.Postgres + + def init(_, opts) do + ConfigHelper.init_repo_module(__MODULE__, opts) + end end end end diff --git a/apps/explorer/priv/zilliqa/migrations/20240927123039_create_quorum_certificate.exs b/apps/explorer/priv/zilliqa/migrations/20240927123039_create_quorum_certificate.exs new file mode 100644 index 000000000000..f3156cae9bdb --- /dev/null +++ b/apps/explorer/priv/zilliqa/migrations/20240927123039_create_quorum_certificate.exs @@ -0,0 +1,20 @@ +defmodule Explorer.Repo.Zilliqa.Migrations.CreateQuorumCertificate do + use Ecto.Migration + + def change do + create table(:zilliqa_quorum_certificates, primary_key: false) do + add( + :block_hash, + references(:blocks, column: :hash, type: :bytea, on_delete: :delete_all), + null: false, + primary_key: true + ) + + add(:view, :integer, null: false) + add(:signature, :binary, null: false) + add(:signers, {:array, :smallint}, null: false) + + timestamps() + end + end +end diff --git a/apps/explorer/priv/zilliqa/migrations/20240927123101_create_aggregate_quorum_certificate.exs b/apps/explorer/priv/zilliqa/migrations/20240927123101_create_aggregate_quorum_certificate.exs new file mode 100644 index 000000000000..dd21c9cff3de --- /dev/null +++ b/apps/explorer/priv/zilliqa/migrations/20240927123101_create_aggregate_quorum_certificate.exs @@ -0,0 +1,19 @@ +defmodule Explorer.Repo.Zilliqa.Migrations.CreateAggregateQuorumCertificate do + use Ecto.Migration + + def change do + create table(:zilliqa_aggregate_quorum_certificates, primary_key: false) do + add( + :block_hash, + references(:blocks, column: :hash, type: :bytea, on_delete: :delete_all), + null: false, + primary_key: true + ) + + add(:view, :integer, null: false) + add(:signature, :binary, null: false) + + timestamps() + end + end +end diff --git a/apps/explorer/priv/zilliqa/migrations/20240927123113_create_nested_quorum_certificate.exs b/apps/explorer/priv/zilliqa/migrations/20240927123113_create_nested_quorum_certificate.exs new file mode 100644 index 000000000000..01bd2c042238 --- /dev/null +++ b/apps/explorer/priv/zilliqa/migrations/20240927123113_create_nested_quorum_certificate.exs @@ -0,0 +1,21 @@ +defmodule Explorer.Repo.Zilliqa.Migrations.CreateNestedQuorumCertificate do + use Ecto.Migration + + def change do + create table(:zilliqa_nested_quorum_certificates, primary_key: false) do + add( + :block_hash, + references(:blocks, column: :hash, type: :bytea, on_delete: :delete_all), + null: false, + primary_key: true + ) + + add(:proposed_by_validator_index, :smallint, primary_key: true) + add(:view, :integer, null: false) + add(:signature, :binary, null: false) + add(:signers, {:array, :smallint}, null: false) + + timestamps() + end + end +end diff --git a/apps/explorer/priv/zilliqa/migrations/20241015095021_add_view_to_block.exs b/apps/explorer/priv/zilliqa/migrations/20241015095021_add_view_to_block.exs new file mode 100644 index 000000000000..d988964daf89 --- /dev/null +++ b/apps/explorer/priv/zilliqa/migrations/20241015095021_add_view_to_block.exs @@ -0,0 +1,9 @@ +defmodule Explorer.Repo.Zilliqa.Migrations.AddViewToBlock do + use Ecto.Migration + + def change do + alter table(:blocks) do + add(:zilliqa_view, :integer) + end + end +end diff --git a/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs b/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs index 7d0c5cfb23f6..5fbdc25a1ad9 100644 --- a/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs +++ b/apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs @@ -32,6 +32,8 @@ defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogsTest do updated_logs = Repo.all(Log |> where([log], log.block_number == ^block.number) |> order_by([log], asc: log.index)) + Process.sleep(300) + assert match?( [ %{index: 0, data: %Explorer.Chain.Data{bytes: <<2>>}}, @@ -108,6 +110,8 @@ defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogsTest do assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true + Process.sleep(300) + updated_logs = Repo.all(Log |> where([log], log.block_number == ^block.number) |> order_by([log], asc: log.index)) diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 7672de69f34c..4105e4382520 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -145,7 +145,7 @@ defmodule Indexer.Block.Fetcher do additional_options \\ %{} ) when callback_module != nil do - {fetch_time, fetched_blocks} = + {fetch_time, fetch_result} = :timer.tc(fn -> EthereumJSONRPC.fetch_blocks_by_range(range, json_rpc_named_arguments) end) with {:blocks, @@ -156,7 +156,7 @@ defmodule Indexer.Block.Fetcher do withdrawals_params: withdrawals_params, block_second_degree_relations_params: block_second_degree_relations_params, errors: blocks_errors - }}} <- {:blocks, fetched_blocks}, + } = fetched_blocks}} <- {:blocks, fetch_result}, blocks = TransformBlocks.transform_blocks(blocks_params), {:receipts, {:ok, receipt_params}} <- {:receipts, Receipts.fetch(state, transactions_params_without_receipts)}, %{logs: receipt_logs, receipts: receipts} = receipt_params, @@ -247,17 +247,19 @@ defmodule Indexer.Block.Fetcher do token_instances: %{params: token_instances}, signed_authorizations: %{params: SignedAuthorizations.parse(transactions_with_receipts)} }, - chain_type_import_options = %{ - transactions_with_receipts: transactions_with_receipts, - optimism_withdrawals: optimism_withdrawals, - polygon_edge_withdrawals: polygon_edge_withdrawals, - polygon_edge_deposit_executes: polygon_edge_deposit_executes, - polygon_zkevm_bridge_operations: polygon_zkevm_bridge_operations, - scroll_l1_fee_params: scroll_l1_fee_params, - shibarium_bridge_operations: shibarium_bridge_operations, - celo_gas_tokens: celo_gas_tokens, - arbitrum_messages: arbitrum_xlevel_messages - }, + chain_type_import_options = + %{ + transactions_with_receipts: transactions_with_receipts, + optimism_withdrawals: optimism_withdrawals, + polygon_edge_withdrawals: polygon_edge_withdrawals, + polygon_edge_deposit_executes: polygon_edge_deposit_executes, + polygon_zkevm_bridge_operations: polygon_zkevm_bridge_operations, + scroll_l1_fee_params: scroll_l1_fee_params, + shibarium_bridge_operations: shibarium_bridge_operations, + celo_gas_tokens: celo_gas_tokens, + arbitrum_messages: arbitrum_xlevel_messages + } + |> extend_with_zilliqa_import_options(fetched_blocks), {:ok, inserted} <- __MODULE__.import( state, @@ -350,12 +352,33 @@ defmodule Indexer.Block.Fetcher do |> Map.put_new(:arbitrum_messages, %{params: arbitrum_xlevel_messages}) end + :zilliqa -> + defp import_options(basic_import_options, %{ + zilliqa_quorum_certificates: zilliqa_quorum_certificates, + zilliqa_aggregate_quorum_certificates: zilliqa_aggregate_quorum_certificates, + zilliqa_nested_quorum_certificates: zilliqa_nested_quorum_certificates + }) do + basic_import_options + |> Map.put_new(:zilliqa_quorum_certificates, %{params: zilliqa_quorum_certificates}) + |> Map.put_new(:zilliqa_aggregate_quorum_certificates, %{params: zilliqa_aggregate_quorum_certificates}) + |> Map.put_new(:zilliqa_nested_quorum_certificates, %{params: zilliqa_nested_quorum_certificates}) + end + _ -> defp import_options(basic_import_options, _) do basic_import_options end end + defp extend_with_zilliqa_import_options(chain_type_import_options, fetched_blocks) do + chain_type_import_options + |> Map.merge(%{ + zilliqa_quorum_certificates: Map.get(fetched_blocks, :zilliqa_quorum_certificates_params, []), + zilliqa_aggregate_quorum_certificates: Map.get(fetched_blocks, :zilliqa_aggregate_quorum_certificates_params, []), + zilliqa_nested_quorum_certificates: Map.get(fetched_blocks, :zilliqa_nested_quorum_certificates_params, []) + }) + end + defp update_block_cache([]), do: :ok defp update_block_cache(blocks) when is_list(blocks) do diff --git a/config/config_helper.exs b/config/config_helper.exs index 715cfbbdbeb1..850c3735d811 100644 --- a/config/config_helper.exs +++ b/config/config_helper.exs @@ -9,24 +9,27 @@ defmodule ConfigHelper do def repos do base_repos = [Explorer.Repo, Explorer.Repo.Account] - repos = - case chain_type() do - :ethereum -> base_repos ++ [Explorer.Repo.Beacon] - :optimism -> base_repos ++ [Explorer.Repo.Optimism] - :polygon_edge -> base_repos ++ [Explorer.Repo.PolygonEdge] - :polygon_zkevm -> base_repos ++ [Explorer.Repo.PolygonZkevm] - :rsk -> base_repos ++ [Explorer.Repo.RSK] - :scroll -> base_repos ++ [Explorer.Repo.Scroll] - :shibarium -> base_repos ++ [Explorer.Repo.Shibarium] - :suave -> base_repos ++ [Explorer.Repo.Suave] - :filecoin -> base_repos ++ [Explorer.Repo.Filecoin] - :stability -> base_repos ++ [Explorer.Repo.Stability] - :zksync -> base_repos ++ [Explorer.Repo.ZkSync] - :celo -> base_repos ++ [Explorer.Repo.Celo] - :arbitrum -> base_repos ++ [Explorer.Repo.Arbitrum] - :blackfort -> base_repos ++ [Explorer.Repo.Blackfort] - _ -> base_repos - end + chain_type_repo = + %{ + arbitrum: Explorer.Repo.Arbitrum, + blackfort: Explorer.Repo.Blackfort, + celo: Explorer.Repo.Celo, + ethereum: Explorer.Repo.Beacon, + filecoin: Explorer.Repo.Filecoin, + optimism: Explorer.Repo.Optimism, + polygon_edge: Explorer.Repo.PolygonEdge, + polygon_zkevm: Explorer.Repo.PolygonZkevm, + rsk: Explorer.Repo.RSK, + scroll: Explorer.Repo.Scroll, + shibarium: Explorer.Repo.Shibarium, + stability: Explorer.Repo.Stability, + suave: Explorer.Repo.Suave, + zilliqa: Explorer.Repo.Zilliqa, + zksync: Explorer.Repo.ZkSync + } + |> Map.get(chain_type()) + + chain_type_repos = (chain_type_repo && [chain_type_repo]) || [] ext_repos = [ @@ -37,7 +40,7 @@ defmodule ConfigHelper do |> Enum.filter(&elem(&1, 0)) |> Enum.map(&elem(&1, 1)) - repos ++ ext_repos + base_repos ++ chain_type_repos ++ ext_repos end @spec hackney_options() :: any() @@ -305,6 +308,8 @@ defmodule ConfigHelper do @supported_chain_types [ "default", "arbitrum", + "blackfort", + "celo", "ethereum", "filecoin", "optimism", @@ -316,9 +321,8 @@ defmodule ConfigHelper do "stability", "suave", "zetachain", - "zksync", - "celo", - "blackfort" + "zilliqa", + "zksync" ] @spec chain_type() :: atom() | nil diff --git a/config/runtime/dev.exs b/config/runtime/dev.exs index 198d0efbd330..3f2049a296ff 100644 --- a/config/runtime/dev.exs +++ b/config/runtime/dev.exs @@ -74,120 +74,6 @@ config :explorer, Explorer.Repo.Account, pool_size: ConfigHelper.parse_integer_env_var("ACCOUNT_POOL_SIZE", 10), queue_target: queue_target -# Configure Beacon Chain database -config :explorer, Explorer.Repo.Beacon, - database: database, - hostname: hostname, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type - pool_size: 1 - -# Configures BridgedTokens database -config :explorer, Explorer.Repo.BridgedTokens, - database: database, - hostname: hostname, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type - pool_size: 1 - -# Configure Optimism database -config :explorer, Explorer.Repo.Optimism, - database: database, - hostname: hostname, - url: System.get_env("DATABASE_URL"), - pool_size: 1 - -# Configure PolygonEdge database -config :explorer, Explorer.Repo.PolygonEdge, - database: database, - hostname: hostname, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type - pool_size: 1 - -# Configure PolygonZkevm database -config :explorer, Explorer.Repo.PolygonZkevm, - database: database, - hostname: hostname, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type - pool_size: 1 - -# Configure Scroll database -config :explorer, Explorer.Repo.Scroll, - database: database, - hostname: hostname, - url: System.get_env("DATABASE_URL"), - pool_size: 1 - -# Configure ZkSync database -config :explorer, Explorer.Repo.ZkSync, - database: database, - hostname: hostname, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type - pool_size: 1 - -# Configure Celo database -config :explorer, Explorer.Repo.Celo, - database: database, - hostname: hostname, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type - pool_size: 1 - -# Configure Rootstock database -config :explorer, Explorer.Repo.RSK, - database: database, - hostname: hostname, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type - pool_size: 1 - -# Configure Shibarium database -config :explorer, Explorer.Repo.Shibarium, - database: database, - hostname: hostname, - url: System.get_env("DATABASE_URL"), - pool_size: 1 - -# Configure Suave database -config :explorer, Explorer.Repo.Suave, - database: database, - hostname: hostname, - url: ExplorerConfigHelper.get_suave_db_url(), - pool_size: 1 - -# Configure Filecoin database -config :explorer, Explorer.Repo.Filecoin, - database: database, - hostname: hostname, - url: System.get_env("DATABASE_URL"), - pool_size: 1 - -# Configure Arbitrum database -config :explorer, Explorer.Repo.Arbitrum, - database: database, - hostname: hostname, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type - pool_size: 1 - -# Configures Stability database -config :explorer, Explorer.Repo.Stability, - database: database, - hostname: hostname, - url: System.get_env("DATABASE_URL"), - pool_size: 1 - database_mud = if System.get_env("MUD_DATABASE_URL"), do: nil, else: database hostname_mud = if System.get_env("MUD_DATABASE_URL"), do: nil, else: hostname @@ -199,19 +85,42 @@ config :explorer, Explorer.Repo.Mud, pool_size: ConfigHelper.parse_integer_env_var("MUD_POOL_SIZE", 10), queue_target: queue_target -# Configures ShrunkInternalTransactions database -config :explorer, Explorer.Repo.ShrunkInternalTransactions, +# Configure Suave database +config :explorer, Explorer.Repo.Suave, database: database, hostname: hostname, - url: System.get_env("DATABASE_URL"), + url: ExplorerConfigHelper.get_suave_db_url(), pool_size: 1 -# Configures Blackfort database -config :explorer, Explorer.Repo.Blackfort, - database: database, - hostname: hostname, - url: System.get_env("DATABASE_URL"), - pool_size: 1 +# Actually the following repos are not started, and its pool size remains +# unused. Separating repos for different CHAIN_TYPE is implemented only for the +# sake of keeping DB schema update relevant to the current chain type +for repo <- [ + # Chain-type dependent repos + Explorer.Repo.Arbitrum, + Explorer.Repo.Beacon, + Explorer.Repo.Blackfort, + Explorer.Repo.Celo, + Explorer.Repo.Filecoin, + Explorer.Repo.Optimism, + Explorer.Repo.PolygonEdge, + Explorer.Repo.PolygonZkevm, + Explorer.Repo.RSK, + Explorer.Repo.Scroll, + Explorer.Repo.Shibarium, + Explorer.Repo.Stability, + Explorer.Repo.Zilliqa, + Explorer.Repo.ZkSync, + # Feature dependent repos + Explorer.Repo.BridgedTokens, + Explorer.Repo.ShrunkInternalTransactions + ] do + config :explorer, repo, + database: database, + hostname: hostname, + url: System.get_env("DATABASE_URL"), + pool_size: 1 +end variant = Variant.get() diff --git a/config/runtime/prod.exs b/config/runtime/prod.exs index fbb3a50ad2f2..6225e9f09bf0 100644 --- a/config/runtime/prod.exs +++ b/config/runtime/prod.exs @@ -52,104 +52,6 @@ config :explorer, Explorer.Repo.Account, ssl: ExplorerConfigHelper.ssl_enabled?(), queue_target: queue_target -# Configure Beacon Chain database -config :explorer, Explorer.Repo.Beacon, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type - pool_size: 1, - ssl: ExplorerConfigHelper.ssl_enabled?() - -# Configures BridgedTokens database -config :explorer, Explorer.Repo.BridgedTokens, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type - pool_size: 1, - ssl: ExplorerConfigHelper.ssl_enabled?() - -# Configures Optimism database -config :explorer, Explorer.Repo.Optimism, - url: System.get_env("DATABASE_URL"), - pool_size: 1, - ssl: ExplorerConfigHelper.ssl_enabled?() - -# Configures PolygonEdge database -config :explorer, Explorer.Repo.PolygonEdge, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type - pool_size: 1, - ssl: ExplorerConfigHelper.ssl_enabled?() - -# Configures PolygonZkevm database -config :explorer, Explorer.Repo.PolygonZkevm, - url: System.get_env("DATABASE_URL"), - pool_size: 1, - ssl: ExplorerConfigHelper.ssl_enabled?() - -# Configures Scroll database -config :explorer, Explorer.Repo.Scroll, - url: System.get_env("DATABASE_URL"), - pool_size: 1, - ssl: ExplorerConfigHelper.ssl_enabled?() - -# Configures ZkSync database -config :explorer, Explorer.Repo.ZkSync, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type - pool_size: 1, - ssl: ExplorerConfigHelper.ssl_enabled?() - -# Configures Celo database -config :explorer, Explorer.Repo.Celo, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type - pool_size: 1, - ssl: ExplorerConfigHelper.ssl_enabled?() - -# Configures Rootstock database -config :explorer, Explorer.Repo.RSK, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type - pool_size: 1, - ssl: ExplorerConfigHelper.ssl_enabled?() - -# Configures Shibarium database -config :explorer, Explorer.Repo.Shibarium, - url: System.get_env("DATABASE_URL"), - pool_size: 1, - ssl: ExplorerConfigHelper.ssl_enabled?() - -# Configures Suave database -config :explorer, Explorer.Repo.Suave, - url: ExplorerConfigHelper.get_suave_db_url(), - pool_size: 1, - ssl: ExplorerConfigHelper.ssl_enabled?() - -# Configures Filecoin database -config :explorer, Explorer.Repo.Filecoin, - url: System.get_env("DATABASE_URL"), - pool_size: 1, - ssl: ExplorerConfigHelper.ssl_enabled?() - -# Configures Arbitrum database -config :explorer, Explorer.Repo.Arbitrum, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type - pool_size: 1, - ssl: ExplorerConfigHelper.ssl_enabled?() - -# Configures Stability database -config :explorer, Explorer.Repo.Stability, - url: System.get_env("DATABASE_URL"), - pool_size: 1, - ssl: ExplorerConfigHelper.ssl_enabled?() - # Configures Mud database config :explorer, Explorer.Repo.Mud, url: ExplorerConfigHelper.get_mud_db_url(), @@ -157,19 +59,42 @@ config :explorer, Explorer.Repo.Mud, ssl: ExplorerConfigHelper.ssl_enabled?(), queue_target: queue_target -# Configures ShrunkInternalTransactions database -config :explorer, Explorer.Repo.ShrunkInternalTransactions, - url: System.get_env("DATABASE_URL"), - # actually this repo is not started, and its pool size remains unused. - # separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type +# Configures Suave database +config :explorer, Explorer.Repo.Suave, + url: ExplorerConfigHelper.get_suave_db_url(), pool_size: 1, ssl: ExplorerConfigHelper.ssl_enabled?() -# Configures Blackfort database -config :explorer, Explorer.Repo.Blackfort, - url: System.get_env("DATABASE_URL"), - pool_size: 1, - ssl: ExplorerConfigHelper.ssl_enabled?() +# Actually the following repos are not started, and its pool size remains +# unused. Separating repos for different chain type or feature flag is +# implemented only for the sake of keeping DB schema update relevant to the +# current chain type +for repo <- [ + # Feature dependent repos + Explorer.Repo.BridgedTokens, + Explorer.Repo.ShrunkInternalTransactions, + + # Chain-type dependent repos + Explorer.Repo.Arbitrum, + Explorer.Repo.Beacon, + Explorer.Repo.Blackfort, + Explorer.Repo.Celo, + Explorer.Repo.Filecoin, + Explorer.Repo.Optimism, + Explorer.Repo.PolygonEdge, + Explorer.Repo.PolygonZkevm, + Explorer.Repo.RSK, + Explorer.Repo.Scroll, + Explorer.Repo.Shibarium, + Explorer.Repo.Stability, + Explorer.Repo.Zilliqa, + Explorer.Repo.ZkSync + ] do + config :explorer, repo, + url: System.get_env("DATABASE_URL"), + pool_size: 1, + ssl: ExplorerConfigHelper.ssl_enabled?() +end variant = Variant.get() diff --git a/cspell.json b/cspell.json index eb1d71f81c05..55cd21f2d189 100644 --- a/cspell.json +++ b/cspell.json @@ -64,6 +64,7 @@ "blockscout", "blockscoutuser", "bools", + "Boneh", "bridgedtokenlist", "brotli", "browserconfig", @@ -388,6 +389,7 @@ "pawesome", "paych", "pbcopy", + "PBFT", "peeker", "peekers", "pendingtxlist", @@ -477,6 +479,7 @@ "SENDGRID", "Sepolia", "Sérgio", + "Shacham", "sharelock", "sharelocks", "shibarium", @@ -521,6 +524,7 @@ "subtraces", "successa", "successb", + "supermajority", "supernet", "sushiswap", "swal", @@ -636,6 +640,7 @@ "zetachain", "zftv", "ziczr", + "zilliqa", "zindex", "zipcode", "zkatana", From 18eb3c6fb2559d4df16ae33e2f18a51064796610 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 6 Nov 2024 14:26:05 +0200 Subject: [PATCH 299/363] chore: Update Github Actions packages versions (#11144) * Update Github Actions packages versions * Update Github Actions packages version --- .devcontainer/Dockerfile | 2 +- .github/workflows/config.yml | 2 +- .github/workflows/pre-release-arbitrum.yml | 12 ++++++------ .github/workflows/pre-release-blackfort.yml | 6 +++--- .github/workflows/pre-release-celo.yml | 12 ++++++------ .github/workflows/pre-release-eth.yml | 6 +++--- .github/workflows/pre-release-filecoin.yml | 12 ++++++------ .github/workflows/pre-release-optimism.yml | 12 ++++++------ .github/workflows/pre-release-polygon-zkevm.yml | 12 ++++++------ .github/workflows/pre-release-redstone.yml | 6 +++--- .github/workflows/pre-release-scroll.yml | 12 ++++++------ .github/workflows/pre-release-shibarium.yml | 6 +++--- .github/workflows/pre-release-zilliqa.yml | 12 ++++++------ .github/workflows/pre-release-zksync.yml | 12 ++++++------ .github/workflows/pre-release.yml | 12 ++++++------ .../workflows/publish-docker-image-every-push.yml | 8 ++++---- .../publish-docker-image-for-arbitrum.yml | 12 ++++++------ .../publish-docker-image-for-blackfort.yml | 2 +- .../workflows/publish-docker-image-for-celo.yml | 12 ++++++------ .../workflows/publish-docker-image-for-core.yml | 2 +- .../publish-docker-image-for-eth-sepolia.yml | 12 ++++++------ .github/workflows/publish-docker-image-for-eth.yml | 6 +++--- .../publish-docker-image-for-filecoin.yml | 12 ++++++------ .../workflows/publish-docker-image-for-fuse.yml | 2 +- .../publish-docker-image-for-gnosis-chain.yml | 2 +- .../publish-docker-image-for-l2-staging.yml | 2 +- .../workflows/publish-docker-image-for-lukso.yml | 2 +- .../publish-docker-image-for-optimism.yml | 12 ++++++------ .../publish-docker-image-for-polygon-edge.yml | 2 +- .../publish-docker-image-for-redstone.yml | 2 +- .../publish-docker-image-for-rootstock.yml | 2 +- .../workflows/publish-docker-image-for-scroll.yml | 12 ++++++------ .../publish-docker-image-for-shibarium.yml | 6 +++--- .../publish-docker-image-for-stability.yml | 2 +- .../workflows/publish-docker-image-for-suave.yml | 2 +- .../publish-docker-image-for-zetachain.yml | 2 +- .../workflows/publish-docker-image-for-zilliqa.yml | 12 ++++++------ .../workflows/publish-docker-image-for-zkevm.yml | 12 ++++++------ .../workflows/publish-docker-image-for-zksync.yml | 12 ++++++------ .../publish-docker-image-staging-on-demand.yml | 2 +- .../publish-regular-docker-image-on-demand.yml | 6 +++--- .github/workflows/release-arbitrum.yml | 12 ++++++------ .github/workflows/release-blackfort.yml | 6 +++--- .github/workflows/release-celo.yml | 12 ++++++------ .github/workflows/release-eth.yml | 12 ++++++------ .github/workflows/release-filecoin.yml | 12 ++++++------ .github/workflows/release-fuse.yml | 6 +++--- .github/workflows/release-gnosis.yml | 6 +++--- .github/workflows/release-optimism.yml | 12 ++++++------ .github/workflows/release-polygon-edge.yml | 6 +++--- .github/workflows/release-polygon-zkevm.yml | 12 ++++++------ .github/workflows/release-redstone.yml | 6 +++--- .github/workflows/release-rootstock.yml | 6 +++--- .github/workflows/release-scroll.yml | 12 ++++++------ .github/workflows/release-shibarium.yml | 12 ++++++------ .github/workflows/release-stability.yml | 6 +++--- .github/workflows/release-suave.yml | 6 +++--- .github/workflows/release-zetachain.yml | 6 +++--- .github/workflows/release-zilliqa.yml | 12 ++++++------ .github/workflows/release-zksync.yml | 12 ++++++------ .github/workflows/release.yml | 14 +++++++------- .tool-versions | 2 +- apps/block_scout_web/assets/package-lock.json | 5 +++-- 63 files changed, 246 insertions(+), 245 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index a030c1694001..98f0627b5d8f 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -6,7 +6,7 @@ FROM hexpm/elixir:${VARIANT} # ARGs declared before FROM are not persisted beyond the FROM instruction. # They must be redeclared here to be available in the rest of the Dockerfile. ARG PHOENIX_VERSION="1.7.10" -ARG NODE_VERSION="18" +ARG NODE_VERSION="20" # This Dockerfile adds a non-root user with sudo access. Update the “remoteUser” property in # devcontainer.json to use it. More info: https://aka.ms/vscode-remote/containers/non-root-user. diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml index 9ec7ed351619..830d9c42ac82 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/config.yml @@ -364,7 +364,7 @@ jobs: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-blockscoutweb-npm- - name: Run cspell - uses: streetsidesoftware/cspell-action@v2 + uses: streetsidesoftware/cspell-action@v6 with: files: | **/*.ex* diff --git a/.github/workflows/pre-release-arbitrum.yml b/.github/workflows/pre-release-arbitrum.yml index 4d71a2da3469..ed4cf4b7abb7 100644 --- a/.github/workflows/pre-release-arbitrum.yml +++ b/.github/workflows/pre-release-arbitrum.yml @@ -30,7 +30,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Arbitrum (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -53,7 +53,7 @@ jobs: CHAIN_TYPE=arbitrum - name: Build and push Docker image for Arbitrum (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -75,7 +75,7 @@ jobs: CHAIN_TYPE=arbitrum - name: Build and push Docker image for Arbitrum (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -97,7 +97,7 @@ jobs: CHAIN_TYPE=arbitrum - name: Build and push Docker image for Arbitrum (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -121,7 +121,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Arbitrum (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -144,7 +144,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Arbitrum (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/pre-release-blackfort.yml b/.github/workflows/pre-release-blackfort.yml index f6c3347b4833..a11e4d0ea997 100644 --- a/.github/workflows/pre-release-blackfort.yml +++ b/.github/workflows/pre-release-blackfort.yml @@ -30,7 +30,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Blackfort (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -53,7 +53,7 @@ jobs: CHAIN_TYPE=blackfort - name: Build and push Docker image for Blackfort (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -75,7 +75,7 @@ jobs: CHAIN_TYPE=blackfort - name: Build and push Docker image for Blackfort (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/pre-release-celo.yml b/.github/workflows/pre-release-celo.yml index 2ee3acae19d7..6e2dc8a8c54d 100644 --- a/.github/workflows/pre-release-celo.yml +++ b/.github/workflows/pre-release-celo.yml @@ -31,7 +31,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for CELO (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -54,7 +54,7 @@ jobs: CHAIN_TYPE=celo - name: Build and push Docker image for CELO (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -76,7 +76,7 @@ jobs: CHAIN_TYPE=celo - name: Build and push Docker image for CELO (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -98,7 +98,7 @@ jobs: CHAIN_TYPE=celo - name: Build and push Docker image for CELO (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -123,7 +123,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for CELO (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -147,7 +147,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for CELO (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/pre-release-eth.yml b/.github/workflows/pre-release-eth.yml index cef696c93d08..b0a9a5b3c60a 100644 --- a/.github/workflows/pre-release-eth.yml +++ b/.github/workflows/pre-release-eth.yml @@ -30,7 +30,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Ethereum (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -53,7 +53,7 @@ jobs: CHAIN_TYPE=ethereum - name: Build and push Docker image for Ethereum (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -75,7 +75,7 @@ jobs: CHAIN_TYPE=ethereum - name: Build and push Docker image for Ethereum (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/pre-release-filecoin.yml b/.github/workflows/pre-release-filecoin.yml index 61c29d90a5f7..addb62d6422d 100644 --- a/.github/workflows/pre-release-filecoin.yml +++ b/.github/workflows/pre-release-filecoin.yml @@ -30,7 +30,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Filecoin (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -53,7 +53,7 @@ jobs: CHAIN_TYPE=filecoin - name: Build and push Docker image for Filecoin (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -75,7 +75,7 @@ jobs: CHAIN_TYPE=filecoin - name: Build and push Docker image for Filecoin (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -97,7 +97,7 @@ jobs: CHAIN_TYPE=filecoin - name: Build and push Docker image for Filecoin (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -121,7 +121,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Filecoin (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -144,7 +144,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Filecoin (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/pre-release-optimism.yml b/.github/workflows/pre-release-optimism.yml index e4e7e20ea165..65abb40145e1 100644 --- a/.github/workflows/pre-release-optimism.yml +++ b/.github/workflows/pre-release-optimism.yml @@ -30,7 +30,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Optimism (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -53,7 +53,7 @@ jobs: CHAIN_TYPE=optimism - name: Build and push Docker image for Optimism (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -75,7 +75,7 @@ jobs: CHAIN_TYPE=optimism - name: Build and push Docker image for Optimism (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -97,7 +97,7 @@ jobs: CHAIN_TYPE=optimism - name: Build and push Docker image for Optimism (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -121,7 +121,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Optimism (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -144,7 +144,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Optimism (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/pre-release-polygon-zkevm.yml b/.github/workflows/pre-release-polygon-zkevm.yml index 0b8b51db1de5..7f8f3c68b88e 100644 --- a/.github/workflows/pre-release-polygon-zkevm.yml +++ b/.github/workflows/pre-release-polygon-zkevm.yml @@ -30,7 +30,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Polygon ZkEVM (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -53,7 +53,7 @@ jobs: CHAIN_TYPE=polygon_zkevm - name: Build and push Docker image for Polygon ZkEVM (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -75,7 +75,7 @@ jobs: CHAIN_TYPE=polygon_zkevm - name: Build and push Docker image for Polygon ZkEVM (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -97,7 +97,7 @@ jobs: CHAIN_TYPE=polygon_zkevm - name: Build and push Docker image for Polygon ZkEVM (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -121,7 +121,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Polygon ZkEVM (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -144,7 +144,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Polygon ZkEVM (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/pre-release-redstone.yml b/.github/workflows/pre-release-redstone.yml index a6eafec90ffb..d790e7d5b11e 100644 --- a/.github/workflows/pre-release-redstone.yml +++ b/.github/workflows/pre-release-redstone.yml @@ -30,7 +30,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Redstone (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -54,7 +54,7 @@ jobs: MUD_INDEXER_ENABLED=true - name: Build and push Docker image for Redstone (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -77,7 +77,7 @@ jobs: MUD_INDEXER_ENABLED=true - name: Build and push Docker image for Redstone (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/pre-release-scroll.yml b/.github/workflows/pre-release-scroll.yml index e5143d843eda..fddbb5f6838f 100644 --- a/.github/workflows/pre-release-scroll.yml +++ b/.github/workflows/pre-release-scroll.yml @@ -30,7 +30,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Scroll (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -53,7 +53,7 @@ jobs: CHAIN_TYPE=scroll - name: Build and push Docker image for Scroll (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -75,7 +75,7 @@ jobs: CHAIN_TYPE=scroll - name: Build and push Docker image for Scroll (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -97,7 +97,7 @@ jobs: CHAIN_TYPE=scroll - name: Build and push Docker image for Scroll (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -121,7 +121,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Scroll (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -144,7 +144,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Scroll (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/pre-release-shibarium.yml b/.github/workflows/pre-release-shibarium.yml index fa9780ace761..0ad4d80ef610 100644 --- a/.github/workflows/pre-release-shibarium.yml +++ b/.github/workflows/pre-release-shibarium.yml @@ -30,7 +30,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Shibarium (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -53,7 +53,7 @@ jobs: CHAIN_TYPE=shibarium - name: Build and push Docker image for Shibarium (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -75,7 +75,7 @@ jobs: CHAIN_TYPE=shibarium - name: Build and push Docker image for Shibarium (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/pre-release-zilliqa.yml b/.github/workflows/pre-release-zilliqa.yml index 9b6192c64d5f..d466de1cd41d 100644 --- a/.github/workflows/pre-release-zilliqa.yml +++ b/.github/workflows/pre-release-zilliqa.yml @@ -30,7 +30,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -53,7 +53,7 @@ jobs: CHAIN_TYPE=zilliqa - name: Build and push Docker image (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -75,7 +75,7 @@ jobs: CHAIN_TYPE=zilliqa - name: Build and push Docker image (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -97,7 +97,7 @@ jobs: CHAIN_TYPE=zilliqa - name: Build and push Docker image (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -121,7 +121,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -144,7 +144,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/pre-release-zksync.yml b/.github/workflows/pre-release-zksync.yml index 4d4c9c660fbf..c9766e6ee45f 100644 --- a/.github/workflows/pre-release-zksync.yml +++ b/.github/workflows/pre-release-zksync.yml @@ -30,7 +30,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for ZkSync (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -53,7 +53,7 @@ jobs: CHAIN_TYPE=zksync - name: Build and push Docker image for ZkSync (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -75,7 +75,7 @@ jobs: CHAIN_TYPE=zksync - name: Build and push Docker image for ZkSync (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -97,7 +97,7 @@ jobs: CHAIN_TYPE=zksync - name: Build and push Docker image for ZkSync (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -121,7 +121,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for ZkSync (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -144,7 +144,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for ZkSync (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index f72a0ab5311b..5624427b48ec 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -30,7 +30,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build & Push Core Docker image (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -59,7 +59,7 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} - name: Build & Push Core Docker image (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -87,7 +87,7 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} - name: Build & Push Core Docker image (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -117,7 +117,7 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} - name: Build & Push Core Docker image (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -147,7 +147,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build & Push Core Docker image (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -176,7 +176,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build & Push Core Docker image (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-every-push.yml b/.github/workflows/publish-docker-image-every-push.yml index 4c12e57a85bc..705c51fa9648 100644 --- a/.github/workflows/publish-docker-image-every-push.yml +++ b/.github/workflows/publish-docker-image-every-push.yml @@ -30,7 +30,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -59,7 +59,7 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} - name: Build and push Docker image (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -85,7 +85,7 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} - name: Build and push Docker image (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -111,7 +111,7 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} - name: Build and push Docker image for frontend - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-arbitrum.yml b/.github/workflows/publish-docker-image-for-arbitrum.yml index b97299556dad..658253d747fe 100644 --- a/.github/workflows/publish-docker-image-for-arbitrum.yml +++ b/.github/workflows/publish-docker-image-for-arbitrum.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -48,7 +48,7 @@ jobs: CHAIN_TYPE=arbitrum - name: Build and push Docker image (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -70,7 +70,7 @@ jobs: CHAIN_TYPE=arbitrum - name: Build and push Docker image (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -92,7 +92,7 @@ jobs: CHAIN_TYPE=arbitrum - name: Build and push Docker image (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -116,7 +116,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -139,7 +139,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-blackfort.yml b/.github/workflows/publish-docker-image-for-blackfort.yml index 461ee2aac5b2..3c26abc3f262 100644 --- a/.github/workflows/publish-docker-image-for-blackfort.yml +++ b/.github/workflows/publish-docker-image-for-blackfort.yml @@ -28,7 +28,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-celo.yml b/.github/workflows/publish-docker-image-for-celo.yml index b2ed0aa80866..d8351c66186e 100644 --- a/.github/workflows/publish-docker-image-for-celo.yml +++ b/.github/workflows/publish-docker-image-for-celo.yml @@ -26,7 +26,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for CELO (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} - name: Build and push Docker image for CELO (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -73,7 +73,7 @@ jobs: CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} - name: Build and push Docker image for CELO (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -96,7 +96,7 @@ jobs: CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} - name: Build and push Docker image for CELO (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -121,7 +121,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for CELO (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -145,7 +145,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for CELO (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-core.yml b/.github/workflows/publish-docker-image-for-core.yml index 5348327efd10..49066e82665c 100644 --- a/.github/workflows/publish-docker-image-for-core.yml +++ b/.github/workflows/publish-docker-image-for-core.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-eth-sepolia.yml b/.github/workflows/publish-docker-image-for-eth-sepolia.yml index 84634dc355f2..fb7a5dc89faa 100644 --- a/.github/workflows/publish-docker-image-for-eth-sepolia.yml +++ b/.github/workflows/publish-docker-image-for-eth-sepolia.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -48,7 +48,7 @@ jobs: CHAIN_TYPE=ethereum - name: Build and push Docker image (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -70,7 +70,7 @@ jobs: CHAIN_TYPE=ethereum - name: Build and push Docker image (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -92,7 +92,7 @@ jobs: CHAIN_TYPE=ethereum - name: Build and push Docker image (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -116,7 +116,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -139,7 +139,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-eth.yml b/.github/workflows/publish-docker-image-for-eth.yml index 491e063c2e28..0bb766b34651 100644 --- a/.github/workflows/publish-docker-image-for-eth.yml +++ b/.github/workflows/publish-docker-image-for-eth.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -48,7 +48,7 @@ jobs: CHAIN_TYPE=ethereum - name: Build and push Docker image (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -70,7 +70,7 @@ jobs: CHAIN_TYPE=ethereum - name: Build and push Docker image (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-filecoin.yml b/.github/workflows/publish-docker-image-for-filecoin.yml index e3b8cabb4ed0..ed78cb005286 100644 --- a/.github/workflows/publish-docker-image-for-filecoin.yml +++ b/.github/workflows/publish-docker-image-for-filecoin.yml @@ -24,7 +24,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Filecoin (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -47,7 +47,7 @@ jobs: CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} - name: Build and push Docker image for Filecoin (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -69,7 +69,7 @@ jobs: CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} - name: Build and push Docker image for Filecoin (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -91,7 +91,7 @@ jobs: CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} - name: Build and push Docker image for Filecoin (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -115,7 +115,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Filecoin (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -138,7 +138,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Filecoin (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-fuse.yml b/.github/workflows/publish-docker-image-for-fuse.yml index 7502d674c06e..a7000d911486 100644 --- a/.github/workflows/publish-docker-image-for-fuse.yml +++ b/.github/workflows/publish-docker-image-for-fuse.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-gnosis-chain.yml b/.github/workflows/publish-docker-image-for-gnosis-chain.yml index c2d2a20bb513..156656e2217c 100644 --- a/.github/workflows/publish-docker-image-for-gnosis-chain.yml +++ b/.github/workflows/publish-docker-image-for-gnosis-chain.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-l2-staging.yml b/.github/workflows/publish-docker-image-for-l2-staging.yml index 84d0e23ca31c..5ada53ec4169 100644 --- a/.github/workflows/publish-docker-image-for-l2-staging.yml +++ b/.github/workflows/publish-docker-image-for-l2-staging.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-lukso.yml b/.github/workflows/publish-docker-image-for-lukso.yml index 59b033a73397..03cb63e2b283 100644 --- a/.github/workflows/publish-docker-image-for-lukso.yml +++ b/.github/workflows/publish-docker-image-for-lukso.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-optimism.yml b/.github/workflows/publish-docker-image-for-optimism.yml index 50789073db99..a48ffcba524b 100644 --- a/.github/workflows/publish-docker-image-for-optimism.yml +++ b/.github/workflows/publish-docker-image-for-optimism.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -48,7 +48,7 @@ jobs: CHAIN_TYPE=optimism - name: Build and push Docker image (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -70,7 +70,7 @@ jobs: CHAIN_TYPE=optimism - name: Build and push Docker image (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -92,7 +92,7 @@ jobs: CHAIN_TYPE=optimism - name: Build and push Docker image (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -116,7 +116,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -139,7 +139,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-polygon-edge.yml b/.github/workflows/publish-docker-image-for-polygon-edge.yml index 48ef51d4e781..99217b00860b 100644 --- a/.github/workflows/publish-docker-image-for-polygon-edge.yml +++ b/.github/workflows/publish-docker-image-for-polygon-edge.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-redstone.yml b/.github/workflows/publish-docker-image-for-redstone.yml index e84d908c7379..73fbd6ef0949 100644 --- a/.github/workflows/publish-docker-image-for-redstone.yml +++ b/.github/workflows/publish-docker-image-for-redstone.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-rootstock.yml b/.github/workflows/publish-docker-image-for-rootstock.yml index 0415956129ab..772ff3af8dcf 100644 --- a/.github/workflows/publish-docker-image-for-rootstock.yml +++ b/.github/workflows/publish-docker-image-for-rootstock.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-scroll.yml b/.github/workflows/publish-docker-image-for-scroll.yml index 87625b84d11f..58f29c8e1c02 100644 --- a/.github/workflows/publish-docker-image-for-scroll.yml +++ b/.github/workflows/publish-docker-image-for-scroll.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -48,7 +48,7 @@ jobs: CHAIN_TYPE=scroll - name: Build and push Docker image (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -70,7 +70,7 @@ jobs: CHAIN_TYPE=scroll - name: Build and push Docker image (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -92,7 +92,7 @@ jobs: CHAIN_TYPE=scroll - name: Build and push Docker image (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -116,7 +116,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -139,7 +139,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-shibarium.yml b/.github/workflows/publish-docker-image-for-shibarium.yml index df743fa7eeae..70f83d02c356 100644 --- a/.github/workflows/publish-docker-image-for-shibarium.yml +++ b/.github/workflows/publish-docker-image-for-shibarium.yml @@ -28,7 +28,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -51,7 +51,7 @@ jobs: CHAIN_TYPE=shibarium - name: Build and push Docker image (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -73,7 +73,7 @@ jobs: CHAIN_TYPE=shibarium - name: Build and push Docker image (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-stability.yml b/.github/workflows/publish-docker-image-for-stability.yml index 7a52c4840739..6ffbdb78cb92 100644 --- a/.github/workflows/publish-docker-image-for-stability.yml +++ b/.github/workflows/publish-docker-image-for-stability.yml @@ -28,7 +28,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-suave.yml b/.github/workflows/publish-docker-image-for-suave.yml index 0a31318104db..9e1fec6a1ae8 100644 --- a/.github/workflows/publish-docker-image-for-suave.yml +++ b/.github/workflows/publish-docker-image-for-suave.yml @@ -28,7 +28,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-zetachain.yml b/.github/workflows/publish-docker-image-for-zetachain.yml index 278f19c2060f..f93b780c1771 100644 --- a/.github/workflows/publish-docker-image-for-zetachain.yml +++ b/.github/workflows/publish-docker-image-for-zetachain.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-zilliqa.yml b/.github/workflows/publish-docker-image-for-zilliqa.yml index 68dd5ce6e52b..d08d19df17f7 100644 --- a/.github/workflows/publish-docker-image-for-zilliqa.yml +++ b/.github/workflows/publish-docker-image-for-zilliqa.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -48,7 +48,7 @@ jobs: CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} - name: Build and push Docker image (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -70,7 +70,7 @@ jobs: CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} - name: Build and push Docker image (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -92,7 +92,7 @@ jobs: CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }} - name: Build and push Docker image (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -116,7 +116,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -139,7 +139,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-zkevm.yml b/.github/workflows/publish-docker-image-for-zkevm.yml index ed3d6ecd89fe..aa37c36b7fac 100644 --- a/.github/workflows/publish-docker-image-for-zkevm.yml +++ b/.github/workflows/publish-docker-image-for-zkevm.yml @@ -25,7 +25,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -48,7 +48,7 @@ jobs: CHAIN_TYPE=polygon_zkevm - name: Build and push Docker image (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -70,7 +70,7 @@ jobs: CHAIN_TYPE=polygon_zkevm - name: Build and push Docker image (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -92,7 +92,7 @@ jobs: CHAIN_TYPE=polygon_zkevm - name: Build and push Docker image (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -116,7 +116,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -139,7 +139,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-for-zksync.yml b/.github/workflows/publish-docker-image-for-zksync.yml index 7d819c0c8dfe..4ea5cf08c8df 100644 --- a/.github/workflows/publish-docker-image-for-zksync.yml +++ b/.github/workflows/publish-docker-image-for-zksync.yml @@ -24,7 +24,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -47,7 +47,7 @@ jobs: CHAIN_TYPE=zksync - name: Build and push Docker image (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -69,7 +69,7 @@ jobs: CHAIN_TYPE=zksync - name: Build and push Docker image (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -91,7 +91,7 @@ jobs: CHAIN_TYPE=zksync - name: Build and push Docker image (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -114,7 +114,7 @@ jobs: CHAIN_TYPE=zksync - name: Build and push Docker image (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -136,7 +136,7 @@ jobs: CHAIN_TYPE=zksync - name: Build and push Docker image (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-docker-image-staging-on-demand.yml b/.github/workflows/publish-docker-image-staging-on-demand.yml index 9e184218ff89..a9ab37472f42 100644 --- a/.github/workflows/publish-docker-image-staging-on-demand.yml +++ b/.github/workflows/publish-docker-image-staging-on-demand.yml @@ -31,7 +31,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/publish-regular-docker-image-on-demand.yml b/.github/workflows/publish-regular-docker-image-on-demand.yml index 5a1b58379145..82cf59b281a1 100644 --- a/.github/workflows/publish-regular-docker-image-on-demand.yml +++ b/.github/workflows/publish-regular-docker-image-on-demand.yml @@ -24,7 +24,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -53,7 +53,7 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} - name: Build and push Docker image (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -79,7 +79,7 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} - name: Build and push Docker image (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-arbitrum.yml b/.github/workflows/release-arbitrum.yml index f6c6f6aa0aac..e96d7c7a40bb 100644 --- a/.github/workflows/release-arbitrum.yml +++ b/.github/workflows/release-arbitrum.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Arbitrum (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=arbitrum - name: Build and push Docker image for Arbitrum (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: CHAIN_TYPE=arbitrum - name: Build and push Docker image for Arbitrum (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -94,7 +94,7 @@ jobs: CHAIN_TYPE=arbitrum - name: Build and push Docker image for Arbitrum (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -118,7 +118,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Arbitrum (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -141,7 +141,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Arbitrum (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-blackfort.yml b/.github/workflows/release-blackfort.yml index b52db507da03..482d50ece741 100644 --- a/.github/workflows/release-blackfort.yml +++ b/.github/workflows/release-blackfort.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Blackfort (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=blackfort - name: Build and push Docker image for Blackfort (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: CHAIN_TYPE=blackfort - name: Build and push Docker image for Blackfort (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-celo.yml b/.github/workflows/release-celo.yml index e8d89a31a474..c7caa2a6b845 100644 --- a/.github/workflows/release-celo.yml +++ b/.github/workflows/release-celo.yml @@ -28,7 +28,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for CELO (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -52,7 +52,7 @@ jobs: CHAIN_TYPE=celo - name: Build and push Docker image for CELO (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -75,7 +75,7 @@ jobs: CHAIN_TYPE=celo - name: Build and push Docker image for CELO (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -98,7 +98,7 @@ jobs: CHAIN_TYPE=celo - name: Build and push Docker image for CELO (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -123,7 +123,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for CELO (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -147,7 +147,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for CELO (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-eth.yml b/.github/workflows/release-eth.yml index 6d4d4057a3f0..ba1328961b69 100644 --- a/.github/workflows/release-eth.yml +++ b/.github/workflows/release-eth.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Ethereum (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=ethereum - name: Build and push Docker image for Ethereum (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: CHAIN_TYPE=ethereum - name: Build and push Docker image for Ethereum (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -94,7 +94,7 @@ jobs: CHAIN_TYPE=ethereum - name: Build and push Docker image for Ethereum (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -118,7 +118,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Ethereum (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -141,7 +141,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Ethereum (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-filecoin.yml b/.github/workflows/release-filecoin.yml index 65b85b3486b7..3416ab6c977d 100644 --- a/.github/workflows/release-filecoin.yml +++ b/.github/workflows/release-filecoin.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Filecoin (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=filecoin - name: Build and push Docker image for Filecoin (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: CHAIN_TYPE=filecoin - name: Build and push Docker image for Filecoin (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -94,7 +94,7 @@ jobs: CHAIN_TYPE=filecoin - name: Build and push Docker image for Filecoin (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -118,7 +118,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Filecoin (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -141,7 +141,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Filecoin (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-fuse.yml b/.github/workflows/release-fuse.yml index 816ff059eb3d..749848d2e09a 100644 --- a/.github/workflows/release-fuse.yml +++ b/.github/workflows/release-fuse.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Fuse (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: BRIDGED_TOKENS_ENABLED=true - name: Build and push Docker image for Fuse (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: BRIDGED_TOKENS_ENABLED=true - name: Build and push Docker image for Fuse (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-gnosis.yml b/.github/workflows/release-gnosis.yml index 1a2e5d739388..b0fd96eb45bf 100644 --- a/.github/workflows/release-gnosis.yml +++ b/.github/workflows/release-gnosis.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Gnosis chain (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -51,7 +51,7 @@ jobs: CHAIN_TYPE=ethereum - name: Build and push Docker image for Gnosis chain (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -74,7 +74,7 @@ jobs: CHAIN_TYPE=ethereum - name: Build and push Docker image for Gnosis chain (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-optimism.yml b/.github/workflows/release-optimism.yml index 18308961eb7e..282d8c4acd70 100644 --- a/.github/workflows/release-optimism.yml +++ b/.github/workflows/release-optimism.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Optimism (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=optimism - name: Build and push Docker image for Optimism (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: CHAIN_TYPE=optimism - name: Build and push Docker image for Optimism (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -94,7 +94,7 @@ jobs: CHAIN_TYPE=optimism - name: Build and push Docker image for Optimism (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -118,7 +118,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Optimism (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -141,7 +141,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Optimism (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-polygon-edge.yml b/.github/workflows/release-polygon-edge.yml index f7cccd5c429b..c2b5d274cd28 100644 --- a/.github/workflows/release-polygon-edge.yml +++ b/.github/workflows/release-polygon-edge.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Polygon Edge (indexer + api) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=polygon_edge - name: Build and push Docker image for Polygon Edge (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: CHAIN_TYPE=polygon_edge - name: Build and push Docker image for Polygon Edge (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-polygon-zkevm.yml b/.github/workflows/release-polygon-zkevm.yml index f9f862d64184..677814d31300 100644 --- a/.github/workflows/release-polygon-zkevm.yml +++ b/.github/workflows/release-polygon-zkevm.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Polygon zkEVM (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=polygon_zkevm - name: Build and push Docker image for Polygon zkEVM (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: CHAIN_TYPE=polygon_zkevm - name: Build and push Docker image for Polygon zkEVM (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -94,7 +94,7 @@ jobs: CHAIN_TYPE=polygon_zkevm - name: Build and push Docker image for Polygon zkEVM (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -118,7 +118,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Polygon zkEVM (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -141,7 +141,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Polygon zkEVM (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-redstone.yml b/.github/workflows/release-redstone.yml index c204aca32dd4..e9c42a0f8908 100644 --- a/.github/workflows/release-redstone.yml +++ b/.github/workflows/release-redstone.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Redstone - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -51,7 +51,7 @@ jobs: MUD_INDEXER_ENABLED=true - name: Build and push Docker image for Redstone (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -74,7 +74,7 @@ jobs: MUD_INDEXER_ENABLED=true - name: Build and push Docker image for Redstone (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-rootstock.yml b/.github/workflows/release-rootstock.yml index d37a5d4f0d5a..789e99315e9b 100644 --- a/.github/workflows/release-rootstock.yml +++ b/.github/workflows/release-rootstock.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Rootstock (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=rsk - name: Build and push Docker image for Rootstock (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: CHAIN_TYPE=rsk - name: Build and push Docker image for Rootstock (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-scroll.yml b/.github/workflows/release-scroll.yml index ab2c08098d5c..a5926f44025f 100644 --- a/.github/workflows/release-scroll.yml +++ b/.github/workflows/release-scroll.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Scroll (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=scroll - name: Build and push Docker image for Scroll (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: CHAIN_TYPE=scroll - name: Build and push Docker image for Scroll (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -94,7 +94,7 @@ jobs: CHAIN_TYPE=scroll - name: Build and push Docker image for Scroll (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -118,7 +118,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Scroll (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -141,7 +141,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Scroll (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-shibarium.yml b/.github/workflows/release-shibarium.yml index 8f9d62c84240..443768afc601 100644 --- a/.github/workflows/release-shibarium.yml +++ b/.github/workflows/release-shibarium.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Shibarium (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=shibarium - name: Build and push Docker image for Shibarium (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: CHAIN_TYPE=shibarium - name: Build and push Docker image for Shibarium (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -94,7 +94,7 @@ jobs: CHAIN_TYPE=shibarium - name: Build and push Docker image for Shibarium (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -118,7 +118,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Shibarium (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -141,7 +141,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for Shibarium (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-stability.yml b/.github/workflows/release-stability.yml index fb51ebd42e89..ed682b749a60 100644 --- a/.github/workflows/release-stability.yml +++ b/.github/workflows/release-stability.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Stability (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=stability - name: Build and push Docker image for Stability (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: CHAIN_TYPE=stability - name: Build and push Docker image for Stability (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-suave.yml b/.github/workflows/release-suave.yml index 9d8c6e16cd4f..1f23138c96f6 100644 --- a/.github/workflows/release-suave.yml +++ b/.github/workflows/release-suave.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for SUAVE (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=suave - name: Build and push Docker image for SUAVE (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: CHAIN_TYPE=suave - name: Build and push Docker image for SUAVE (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-zetachain.yml b/.github/workflows/release-zetachain.yml index cb97a0f34379..9563e07d0173 100644 --- a/.github/workflows/release-zetachain.yml +++ b/.github/workflows/release-zetachain.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for Zetachain (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=zetachain - name: Build and push Docker image for Zetachain (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: CHAIN_TYPE=zetachain - name: Build and push Docker image for Zetachain (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-zilliqa.yml b/.github/workflows/release-zilliqa.yml index 9c5d82d5bafb..991c38db65f8 100644 --- a/.github/workflows/release-zilliqa.yml +++ b/.github/workflows/release-zilliqa.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=zilliqa - name: Build and push Docker image (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: CHAIN_TYPE=zilliqa - name: Build and push Docker image (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -94,7 +94,7 @@ jobs: CHAIN_TYPE=zilliqa - name: Build and push Docker image (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -118,7 +118,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -141,7 +141,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release-zksync.yml b/.github/workflows/release-zksync.yml index f967afac10cc..0d0c1b1bc05d 100644 --- a/.github/workflows/release-zksync.yml +++ b/.github/workflows/release-zksync.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build and push Docker image for ZkSync (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -50,7 +50,7 @@ jobs: CHAIN_TYPE=zksync - name: Build and push Docker image for ZkSync (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -72,7 +72,7 @@ jobs: CHAIN_TYPE=zksync - name: Build and push Docker image for ZkSync (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -94,7 +94,7 @@ jobs: CHAIN_TYPE=zksync - name: Build and push Docker image for ZkSync (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -118,7 +118,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for ZkSync (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -141,7 +141,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build and push Docker image for ZkSync (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b8dec9aff049..31a635c7edbb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }} - name: Build & Push Core Docker image (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -56,7 +56,7 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} - name: Build & Push Core Docker image (indexer) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -84,7 +84,7 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} - name: Build & Push Core Docker image (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -114,7 +114,7 @@ jobs: RELEASE_VERSION=${{ env.RELEASE_VERSION }} - name: Build & Push Core Docker image (indexer + API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -144,7 +144,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build & Push Core Docker image (indexer + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -173,7 +173,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build & Push Core Docker image (API + shrink internal transactions) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/Dockerfile @@ -204,7 +204,7 @@ jobs: SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true - name: Build & Push Docker image with an old UI (indexer + API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./docker/oldUI.Dockerfile diff --git a/.tool-versions b/.tool-versions index 270ab12e48f5..639a1a07cd2e 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,3 +1,3 @@ elixir 1.17.3-otp-27 erlang 27.1 -nodejs 18.17.1 +nodejs 20.17.0 diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 347eaea138ec..f32b03fb552c 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -101,10 +101,11 @@ } }, "../../../deps/phoenix": { - "version": "0.0.1" + "version": "1.5.14", + "license": "MIT" }, "../../../deps/phoenix_html": { - "version": "0.0.1" + "version": "3.0.4" }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", From 6dbdb26b35a886265f154811373a18707c9a64ac Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Wed, 6 Nov 2024 15:59:09 +0300 Subject: [PATCH 300/363] refactor: cspell configuration (#11146) * fix: cspell ignore weird IPFS url * fix: cspell ignore filecoin f410f addresses * chore: write ignore in single line * feat: configure everything in `cspell.json` * refactor: remove unnecessary cspell ignores * refactor: cspell run command in actions and devcontainer * chore: enable editor support * chore: add comment * fix: cspell action options * feat: run for all files --- .devcontainer/bin/bs | 2 +- .github/workflows/config.yml | 6 ++---- .../block_scout_web/views/nft_helper_test.exs | 1 + ...31151_drop_logs_address_hash_foreign_key.exs | 1 - ...ers_and_transactions_address_foreign_key.exs | 1 - ...20230817061317_drop_address_foreign_keys.exs | 1 - ...821120625_drop_rest_address_foreign_keys.exs | 1 - ...urrent_token_balances_tokens_foreign_key.exs | 1 - ...9_drop_token_balances_tokens_foreign_key.exs | 1 - cspell.json | 17 ++++++++++++++++- 10 files changed, 20 insertions(+), 12 deletions(-) diff --git a/.devcontainer/bin/bs b/.devcontainer/bin/bs index da9790bb4758..5a6e9a5121cc 100755 --- a/.devcontainer/bin/bs +++ b/.devcontainer/bin/bs @@ -146,7 +146,7 @@ recompile() { # Define the spellcheck subroutine spellcheck() { - cspell --config cspell.json "**/*.ex*" "**/*.eex" "**/*.js" --gitignore | less + cspell | less } # Define the dialyzer subroutine diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml index 830d9c42ac82..85c1efdcb249 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/config.yml @@ -366,10 +366,8 @@ jobs: - name: Run cspell uses: streetsidesoftware/cspell-action@v6 with: - files: | - **/*.ex* - **/*.eex - **/*.js" + use_cspell_files: true + incremental_files_only: false eslint: name: ESLint diff --git a/apps/block_scout_web/test/block_scout_web/views/nft_helper_test.exs b/apps/block_scout_web/test/block_scout_web/views/nft_helper_test.exs index 41f3ebbe02b0..3312ae4fe174 100644 --- a/apps/block_scout_web/test/block_scout_web/views/nft_helper_test.exs +++ b/apps/block_scout_web/test/block_scout_web/views/nft_helper_test.exs @@ -12,6 +12,7 @@ defmodule BlockScoutWeb.NFTHelperTest do end test "transforms ipfs link like ipfs://ipfs" do + # cspell:disable-next-line url = "ipfs://ipfs/Qmbgk4Ps5kiVdeYCHufMFgqzWLFuovFRtenY5P8m9vr9XW/animation.mp4" assert "https://ipfs.io/ipfs/Qmbgk4Ps5kiVdeYCHufMFgqzWLFuovFRtenY5P8m9vr9XW/animation.mp4" == diff --git a/apps/explorer/priv/repo/migrations/20230815131151_drop_logs_address_hash_foreign_key.exs b/apps/explorer/priv/repo/migrations/20230815131151_drop_logs_address_hash_foreign_key.exs index e847cb14d74f..350c8a3a85ca 100644 --- a/apps/explorer/priv/repo/migrations/20230815131151_drop_logs_address_hash_foreign_key.exs +++ b/apps/explorer/priv/repo/migrations/20230815131151_drop_logs_address_hash_foreign_key.exs @@ -1,4 +1,3 @@ -# cspell:ignore fkey defmodule Explorer.Repo.Migrations.DropLogsAddressHashForeignKey do use Ecto.Migration diff --git a/apps/explorer/priv/repo/migrations/20230816061723_drop_token_transfers_and_transactions_address_foreign_key.exs b/apps/explorer/priv/repo/migrations/20230816061723_drop_token_transfers_and_transactions_address_foreign_key.exs index 1cfe0a5fb3d7..f5a1ef465fc5 100644 --- a/apps/explorer/priv/repo/migrations/20230816061723_drop_token_transfers_and_transactions_address_foreign_key.exs +++ b/apps/explorer/priv/repo/migrations/20230816061723_drop_token_transfers_and_transactions_address_foreign_key.exs @@ -1,4 +1,3 @@ -# cspell:ignore fkey defmodule Explorer.Repo.Migrations.DropTokenTransfersAndTransactionsAddressForeignKey do use Ecto.Migration diff --git a/apps/explorer/priv/repo/migrations/20230817061317_drop_address_foreign_keys.exs b/apps/explorer/priv/repo/migrations/20230817061317_drop_address_foreign_keys.exs index f10c301b76e3..275e7172aa9a 100644 --- a/apps/explorer/priv/repo/migrations/20230817061317_drop_address_foreign_keys.exs +++ b/apps/explorer/priv/repo/migrations/20230817061317_drop_address_foreign_keys.exs @@ -1,4 +1,3 @@ -# cspell:ignore fkey defmodule Explorer.Repo.Migrations.DropAddressForeignKeys do use Ecto.Migration diff --git a/apps/explorer/priv/repo/migrations/20230821120625_drop_rest_address_foreign_keys.exs b/apps/explorer/priv/repo/migrations/20230821120625_drop_rest_address_foreign_keys.exs index 36cdd2353fa5..05d0afa05c28 100644 --- a/apps/explorer/priv/repo/migrations/20230821120625_drop_rest_address_foreign_keys.exs +++ b/apps/explorer/priv/repo/migrations/20230821120625_drop_rest_address_foreign_keys.exs @@ -1,4 +1,3 @@ -# cspell:ignore fkey defmodule Explorer.Repo.Migrations.DropRestAddressForeignKeys do use Ecto.Migration diff --git a/apps/explorer/priv/repo/migrations/20230831122819_drop_current_token_balances_tokens_foreign_key.exs b/apps/explorer/priv/repo/migrations/20230831122819_drop_current_token_balances_tokens_foreign_key.exs index 3befa9600c66..49f45e654f61 100644 --- a/apps/explorer/priv/repo/migrations/20230831122819_drop_current_token_balances_tokens_foreign_key.exs +++ b/apps/explorer/priv/repo/migrations/20230831122819_drop_current_token_balances_tokens_foreign_key.exs @@ -1,4 +1,3 @@ -# cspell:ignore fkey defmodule Explorer.Repo.Migrations.DropCurrentTokenBalancesTokensForeignKey do use Ecto.Migration diff --git a/apps/explorer/priv/repo/migrations/20230905085809_drop_token_balances_tokens_foreign_key.exs b/apps/explorer/priv/repo/migrations/20230905085809_drop_token_balances_tokens_foreign_key.exs index 265933ff4c23..6af0d319b4af 100644 --- a/apps/explorer/priv/repo/migrations/20230905085809_drop_token_balances_tokens_foreign_key.exs +++ b/apps/explorer/priv/repo/migrations/20230905085809_drop_token_balances_tokens_foreign_key.exs @@ -1,4 +1,3 @@ -# cspell:ignore fkey defmodule Explorer.Repo.Migrations.DropTokenBalancesTokensForeignKey do use Ecto.Migration diff --git a/cspell.json b/cspell.json index 55cd21f2d189..67dec66b99f9 100644 --- a/cspell.json +++ b/cspell.json @@ -1,13 +1,28 @@ // cSpell Settings { + "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", // Version of the setting file. Always 0.2 "version": "0.2", // language - current active spelling language "language": "en", + // enabled - enable code editor suggestions + "enabled": true, + // files - glob patterns of files to be checked + "files": [ + "**/*.ex*", + "**/*.eex", + "**/*.js" + ], + // useGitignore - use .gitignore to exclude files from checking + "useGitignore": true, // words - list of words to be always considered correct "ignorePaths": [ "apps/block_scout_web/assets/js/lib/ace/src-min/*.js" ], + "ignoreRegExpList": [ + // Ignore filecoin f410f-like native addresses + "f410f[a-z2-7]{39}" + ], "words": [ "aave", "absname", @@ -653,5 +668,5 @@ "dotenv", "html-eex", "makefile" - ] + ], } From eb94a4230fda75fd9a333912d4857cbc79f2a28d Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 7 Nov 2024 13:14:26 +0200 Subject: [PATCH 301/363] feat: Xname app proxy (#11010) * Xname app proxy * fix specs * Process reviewer comments --- .../api/v2/proxy/xname_controller.ex | 24 +++++++++ .../lib/block_scout_web/routers/api_router.ex | 4 ++ .../third_party_integrations/noves_fi.ex | 20 ++++++-- .../third_party_integrations/xname.ex | 50 +++++++++++++++++++ .../third_party_integrations/zerion.ex | 12 +++-- config/runtime.exs | 4 ++ cspell.json | 2 + docker-compose/envs/common-blockscout.env | 2 + 8 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/xname_controller.ex create mode 100644 apps/explorer/lib/explorer/third_party_integrations/xname.ex diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/xname_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/xname_controller.ex new file mode 100644 index 000000000000..75426a47d06a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/proxy/xname_controller.ex @@ -0,0 +1,24 @@ +defmodule BlockScoutWeb.API.V2.Proxy.XnameController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.API.V2.AddressController + + alias Explorer.ThirdPartyIntegrations.Xname + + action_fallback(BlockScoutWeb.API.V2.FallbackController) + + @doc """ + Function to handle GET requests to `/api/v2/proxy/xname/address/:address_hash_param` endpoint. + """ + @spec address(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()} + def address(conn, %{"address_hash_param" => address_hash_string} = params) do + with {:ok, _address_hash, _address} <- AddressController.validate_address(address_hash_string, params), + url = Xname.address_url(address_hash_string), + {response, status} <- Xname.api_request(url, conn), + {:is_empty_response, false} <- {:is_empty_response, is_nil(response)} do + conn + |> put_status(status) + |> json(response) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index 533131077c63..80038e0b103a 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -353,6 +353,10 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/wallets/:address_hash_param/portfolio", V2.Proxy.ZerionController, :wallet_portfolio) end + scope "/xname" do + get("/addresses/:address_hash_param", V2.Proxy.XnameController, :address) + end + scope "/metadata" do get("/addresses", V2.Proxy.MetadataController, :addresses) end diff --git a/apps/explorer/lib/explorer/third_party_integrations/noves_fi.ex b/apps/explorer/lib/explorer/third_party_integrations/noves_fi.ex index d0fda30068f9..c51f590982d3 100644 --- a/apps/explorer/lib/explorer/third_party_integrations/noves_fi.ex +++ b/apps/explorer/lib/explorer/third_party_integrations/noves_fi.ex @@ -3,6 +3,8 @@ defmodule Explorer.ThirdPartyIntegrations.NovesFi do Module for Noves.Fi API integration https://blockscout.noves.fi/swagger/index.html """ + require Logger + alias Explorer.Helper alias Explorer.Utility.Microservice @@ -33,7 +35,11 @@ defmodule Explorer.ThirdPartyIntegrations.NovesFi do {:ok, %HTTPoison.Response{status_code: status, body: body}} -> {Helper.decode_json(body), status} - _ -> + {:error, reason} -> + Logger.error(fn -> + ["Error while requesting Noves.Fi API endpoint #{url}. The reason is: ", inspect(reason)] + end) + {nil, 500} end end @@ -47,13 +53,17 @@ defmodule Explorer.ThirdPartyIntegrations.NovesFi do {:ok, %HTTPoison.Response{status_code: status, body: body}} -> {Helper.decode_json(body), status} - _ -> + {:error, reason} -> + Logger.error(fn -> + ["Error while requesting Noves.Fi API endpoint #{url}. The reason is: ", inspect(reason)] + end) + {nil, 500} end end @doc """ - Noves.fi /evm/{chain}/tx/{txHash} endpoint + Noves.fi /evm/:chain/tx/:transaction_hash endpoint """ @spec transaction_url(String.t()) :: String.t() def transaction_url(transaction_hash_string) do @@ -61,7 +71,7 @@ defmodule Explorer.ThirdPartyIntegrations.NovesFi do end @doc """ - Noves.fi /evm/{chain}/describeTxs endpoint + Noves.fi /evm/:chain/describeTxs endpoint """ @spec describe_transactions_url() :: String.t() def describe_transactions_url do @@ -69,7 +79,7 @@ defmodule Explorer.ThirdPartyIntegrations.NovesFi do end @doc """ - Noves.fi /evm/{chain}/transactions/{accountAddress} endpoint + Noves.fi /evm/:chain/transactions/:address_hash endpoint """ @spec address_transactions_url(String.t()) :: String.t() def address_transactions_url(address_hash_string) do diff --git a/apps/explorer/lib/explorer/third_party_integrations/xname.ex b/apps/explorer/lib/explorer/third_party_integrations/xname.ex new file mode 100644 index 000000000000..aa86995cbe6f --- /dev/null +++ b/apps/explorer/lib/explorer/third_party_integrations/xname.ex @@ -0,0 +1,50 @@ +defmodule Explorer.ThirdPartyIntegrations.Xname do + @moduledoc """ + Module for proxying xname https://xname.app/ API endpoints + """ + + require Logger + + alias Explorer.Helper + alias Explorer.Utility.Microservice + + @recv_timeout 60_000 + + @doc """ + Proxy request to XName API endpoints + """ + @spec api_request(String.t(), Plug.Conn.t(), atom()) :: {any(), integer()} + def api_request(url, conn, method \\ :get) + + def api_request(url, _conn, :get) do + headers = [{"x-api-key", api_key()}] + + case HTTPoison.get(url, headers, recv_timeout: @recv_timeout) do + {:ok, %HTTPoison.Response{status_code: status, body: body}} -> + {Helper.decode_json(body), status} + + {:error, reason} -> + Logger.error(fn -> + ["Error while requesting XName app API endpoint #{url}. The reason is: ", inspect(reason)] + end) + + {nil, 500} + end + end + + @doc """ + https://gateway.xname.app/xhs/level/:address_hash endpoint + """ + @spec address_url(String.t()) :: String.t() + def address_url(address_hash_string) do + "#{base_url()}/xhs/level/#{address_hash_string}" + end + + defp base_url do + Microservice.base_url(__MODULE__) + end + + defp api_key do + Application.get_env(:explorer, __MODULE__)[:api_key] + end +end diff --git a/apps/explorer/lib/explorer/third_party_integrations/zerion.ex b/apps/explorer/lib/explorer/third_party_integrations/zerion.ex index 2822e1608af7..7d129c319a3e 100644 --- a/apps/explorer/lib/explorer/third_party_integrations/zerion.ex +++ b/apps/explorer/lib/explorer/third_party_integrations/zerion.ex @@ -3,6 +3,8 @@ defmodule Explorer.ThirdPartyIntegrations.Zerion do Module for Zerion API integration https://developers.zerion.io/reference """ + require Logger + alias Explorer.Helper alias Explorer.Utility.Microservice @@ -11,7 +13,7 @@ defmodule Explorer.ThirdPartyIntegrations.Zerion do @doc """ Proxy request to Zerion API endpoints """ - @spec api_request(String.t(), Plug.Conn.t(), :get | :post_transactions) :: {any(), integer()} + @spec api_request(String.t(), Plug.Conn.t(), atom()) :: {any(), integer()} def api_request(url, conn, method \\ :get) def api_request(url, _conn, :get) do @@ -22,13 +24,17 @@ defmodule Explorer.ThirdPartyIntegrations.Zerion do {:ok, %HTTPoison.Response{status_code: status, body: body}} -> {Helper.decode_json(body), status} - _ -> + {:error, reason} -> + Logger.error(fn -> + ["Error while requesting Zerion API endpoint #{url}. The reason is: ", inspect(reason)] + end) + {nil, 500} end end @doc """ - Zerion /wallets/{accountAddress}/portfolio endpoint + Zerion /wallets/:address_hash/portfolio endpoint """ @spec wallet_portfolio_url(String.t()) :: String.t() def wallet_portfolio_url(address_hash_string) do diff --git a/config/runtime.exs b/config/runtime.exs index a675e1d33338..5a329391249b 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -499,6 +499,10 @@ config :explorer, Explorer.ThirdPartyIntegrations.Zerion, service_url: System.get_env("ZERION_BASE_API_URL", "https://api.zerion.io/v1"), api_key: System.get_env("ZERION_API_TOKEN") +config :explorer, Explorer.ThirdPartyIntegrations.Xname, + service_url: System.get_env("XNAME_BASE_API_URL", "https://gateway.xname.app"), + api_key: System.get_env("XNAME_API_TOKEN") + enabled? = ConfigHelper.parse_bool_env_var("MICROSERVICE_SC_VERIFIER_ENABLED", "true") # or "eth_bytecode_db" type = System.get_env("MICROSERVICE_SC_VERIFIER_TYPE", "sc_verifier") diff --git a/cspell.json b/cspell.json index 67dec66b99f9..e939b1a69404 100644 --- a/cspell.json +++ b/cspell.json @@ -435,6 +435,7 @@ "prederived", "progressbar", "proxiable", + "proxying", "psql", "pubkey", "pubkeys", @@ -641,6 +642,7 @@ "xakgj", "xbaddress", "xdai", + "Xname", "xffff", "xlevel", "xlink", diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 35872c4e7506..21d50b33e415 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -447,6 +447,8 @@ TENDERLY_CHAIN_PATH= # NOVES_FI_API_TOKEN= # ZERION_BASE_API_URL= # ZERION_API_TOKEN= +# XNAME_BASE_API_URL= +# XNAME_API_TOKEN= # BRIDGED_TOKENS_ENABLED= # BRIDGED_TOKENS_ETH_OMNI_BRIDGE_MEDIATOR= # BRIDGED_TOKENS_BSC_OMNI_BRIDGE_MEDIATOR= From b9115d9945940cfd9d053427c3545a779f250c75 Mon Sep 17 00:00:00 2001 From: GimluCom <79271880+GimluCom@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:12:03 +0100 Subject: [PATCH 302/363] fix: Update overview.html.eex (#11094) --- .../templates/block/overview.html.eex | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex index feee728643a1..fc1553d0c27e 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex @@ -161,15 +161,17 @@
<%= @block.difficulty |> Decimal.to_integer() |> BlockScoutWeb.Cldr.Number.to_string! %>
<%= if block_type(@block) == "Block" do %> - -
-
- <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", - text: gettext("Total difficulty of the chain until this block.") %> - <%= gettext("Total Difficulty") %> -
-
<%= @block.total_difficulty |> Decimal.to_integer() |> BlockScoutWeb.Cldr.Number.to_string! %>
-
+ <%= if !is_nil(@block.total_difficulty) do %> + +
+
+ <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Total difficulty of the chain until this block.") %> + <%= gettext("Total Difficulty") %> +
+
<%= @block.total_difficulty |> Decimal.to_integer() |> BlockScoutWeb.Cldr.Number.to_string! %>
+
+ <% end %>
From b87b06bf865c35609e50bc694ff5001fddab930a Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Thu, 7 Nov 2024 22:06:16 +0300 Subject: [PATCH 303/363] feat: gas prices with base fee if no transactions (#11132) * feat: gas prices with base fee if no transactions * Replace wildcard with exact error Co-authored-by: Victor Baranov --------- Co-authored-by: Victor Baranov --- .../explorer/chain/cache/gas_price_oracle.ex | 65 ++++++++++++++----- .../chain/cache/gas_price_oracle_test.exs | 47 ++++++++++++++ 2 files changed, 94 insertions(+), 18 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex b/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex index bc9b590538b1..6e52a045d55a 100644 --- a/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex +++ b/apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex @@ -92,11 +92,7 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do from_block_query = max(from_block_acc, from_block_actual) - average_block_time = - case AverageBlockTime.average_block_time() do - {:error, _} -> nil - average_block_time -> average_block_time |> Duration.to_milliseconds() - end + average_block_time = get_average_block_time() fee_query = from( @@ -186,11 +182,26 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do defp merge_gas_prices(new, acc, from_block), do: Enum.take_while(new ++ acc, &(&1.block_number > from_block)) defp process_fee_data_from_db([]) do - %{ - slow: nil, - average: nil, - fast: nil - } + case Block.next_block_base_fee_per_gas() do + %Decimal{} = base_fee -> + base_fee_wei = base_fee |> Wei.from(:wei) + exchange_rate = Market.get_coin_exchange_rate() + + average_block_time = get_average_block_time() + + %{ + slow: compose_gas_price(base_fee_wei, average_block_time, exchange_rate, base_fee_wei, 0), + average: compose_gas_price(base_fee_wei, average_block_time, exchange_rate, base_fee_wei, 0), + fast: compose_gas_price(base_fee_wei, average_block_time, exchange_rate, base_fee_wei, 0) + } + + _ -> + %{ + slow: nil, + average: nil, + fast: nil + } + end end defp process_fee_data_from_db(fees) do @@ -223,13 +234,12 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do {gas_price(slow_gas_price), gas_price(average_gas_price), gas_price(fast_gas_price), nil} end - exchange_rate_from_db = Market.get_coin_exchange_rate() + exchange_rate = Market.get_coin_exchange_rate() %{ - slow: compose_gas_price(slow_fee, slow_time, exchange_rate_from_db, base_fee_wei, slow_priority_fee_per_gas), - average: - compose_gas_price(average_fee, average_time, exchange_rate_from_db, base_fee_wei, average_priority_fee_per_gas), - fast: compose_gas_price(fast_fee, fast_time, exchange_rate_from_db, base_fee_wei, fast_priority_fee_per_gas) + slow: compose_gas_price(slow_fee, slow_time, exchange_rate, base_fee_wei, slow_priority_fee_per_gas), + average: compose_gas_price(average_fee, average_time, exchange_rate, base_fee_wei, average_priority_fee_per_gas), + fast: compose_gas_price(fast_fee, fast_time, exchange_rate, base_fee_wei, fast_priority_fee_per_gas) } end @@ -251,15 +261,27 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do {key, value} -> value = if is_list(value), do: value, else: [value] count = Enum.count(value) - {key, value |> Enum.reduce(Decimal.new(0), &Decimal.add/2) |> Decimal.div(count)} + + value = + value + |> Enum.reduce(Decimal.new(0), fn + fee, sum when is_float(fee) -> + fee |> Decimal.from_float() |> Decimal.add(sum) + + fee, sum -> + Decimal.add(sum, fee) + end) + |> Decimal.div(count) + + {key, value} end) end - defp compose_gas_price(fee, time, exchange_rate_from_db, base_fee, priority_fee) do + defp compose_gas_price(fee, time, exchange_rate, base_fee, priority_fee) do %{ price: fee |> format_wei(), time: time && time |> Decimal.to_float(), - fiat_price: fiat_fee(fee, exchange_rate_from_db), + fiat_price: fiat_fee(fee, exchange_rate), base_fee: base_fee |> format_wei(), priority_fee: base_fee && priority_fee && priority_fee |> Decimal.new() |> Wei.from(:wei) |> format_wei(), priority_fee_wei: base_fee && priority_fee && priority_fee |> Decimal.new() |> Decimal.round(), @@ -358,4 +380,11 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do end defp async_task_on_deletion(_data), do: nil + + defp get_average_block_time do + case AverageBlockTime.average_block_time() do + {:error, :disabled} -> nil + average_block_time -> average_block_time |> Duration.to_milliseconds() + end + end end diff --git a/apps/explorer/test/explorer/chain/cache/gas_price_oracle_test.exs b/apps/explorer/test/explorer/chain/cache/gas_price_oracle_test.exs index 3fc1493ed2ad..d048d056c2b4 100644 --- a/apps/explorer/test/explorer/chain/cache/gas_price_oracle_test.exs +++ b/apps/explorer/test/explorer/chain/cache/gas_price_oracle_test.exs @@ -90,6 +90,53 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do }}, []} = GasPriceOracle.get_average_gas_price(2, 35, 60, 90) end + test "returns base fee only gas estimation if there is no recent transactions with non-zero gas price" do + block1 = + insert(:block, + number: 100, + hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391", + base_fee_per_gas: 100 + ) + + block2 = + insert(:block, + number: 101, + hash: "0x76c3da57334fffdc66c0d954dce1a910fcff13ec889a13b2d8b0b6e9440ce729", + base_fee_per_gas: 100 + ) + + :transaction + |> insert( + status: :ok, + block_hash: block1.hash, + block_number: block1.number, + cumulative_gas_used: 884_322, + gas_used: 106_025, + index: 0, + gas_price: 0, + hash: "0xac2a7dab94d965893199e7ee01649e2d66f0787a4c558b3118c09e80d4df8269" + ) + + :transaction + |> insert( + status: :ok, + block_hash: block2.hash, + block_number: block2.number, + cumulative_gas_used: 884_322, + gas_used: 106_025, + index: 0, + gas_price: 0, + hash: "0x5d5c2776f96704e7845f7d3c1fbba6685ab6efd6f82b6cd11d549f3b3a46bd03" + ) + + assert {{:ok, + %{ + average: %{base_fee: 0.01, priority_fee: +0.0, price: 0.01}, + fast: %{base_fee: 0.01, priority_fee: +0.0, price: 0.01}, + slow: %{base_fee: 0.01, priority_fee: +0.0, price: 0.01} + }}, []} = GasPriceOracle.get_average_gas_price(2, 35, 60, 90) + end + test "returns the same percentile values if gas price is the same over transactions" do block1 = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391") block2 = insert(:block, number: 101, hash: "0x76c3da57334fffdc66c0d954dce1a910fcff13ec889a13b2d8b0b6e9440ce729") From 3d627c5b763dfbc51286185f97279d1032b44a8f Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Fri, 8 Nov 2024 13:25:33 +0400 Subject: [PATCH 304/363] fix: Omit pbo for blocks lower than trace first block for indexing status (#11053) --- apps/explorer/lib/explorer/chain.ex | 3 ++- .../lib/explorer/chain/pending_block_operation.ex | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 3065e3ada2de..24c19b1d39e1 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -1473,7 +1473,6 @@ defmodule Explorer.Chain do def indexed_ratio_internal_transactions do if indexer_running?() and internal_transactions_fetcher_running?() do %{max: max_saved_block_number} = BlockNumber.get_all() - pbo_count = PendingBlockOperationCache.estimated_count() min_blockchain_trace_block_number = Application.get_env(:indexer, :trace_first_block) @@ -1485,6 +1484,8 @@ defmodule Explorer.Chain do full_blocks_range = max_saved_block_number - min_blockchain_trace_block_number - BlockNumberHelper.null_rounds_count() + 1 + pbo_count = PendingBlockOperation.count_in_range(min_blockchain_trace_block_number, max_saved_block_number) + processed_int_transactions_for_blocks_count = max(0, full_blocks_range - pbo_count) ratio = get_ratio(processed_int_transactions_for_blocks_count, full_blocks_range) diff --git a/apps/explorer/lib/explorer/chain/pending_block_operation.ex b/apps/explorer/lib/explorer/chain/pending_block_operation.ex index 9a1b5b48941c..918b213c0243 100644 --- a/apps/explorer/lib/explorer/chain/pending_block_operation.ex +++ b/apps/explorer/lib/explorer/chain/pending_block_operation.ex @@ -6,6 +6,7 @@ defmodule Explorer.Chain.PendingBlockOperation do use Explorer.Schema alias Explorer.Chain.{Block, Hash} + alias Explorer.Repo @required_attrs ~w(block_hash block_number)a @@ -41,4 +42,17 @@ defmodule Explorer.Chain.PendingBlockOperation do select: pending_ops.block_hash ) end + + @doc """ + Returns the count of pending block operations in provided blocks range + (between `from_block_number` and `to_block_number`). + """ + @spec count_in_range(integer(), integer()) :: integer() + def count_in_range(from_block_number, to_block_number) when from_block_number <= to_block_number do + __MODULE__ + |> where([pbo], pbo.block_number >= ^from_block_number) + |> where([pbo], pbo.block_number <= ^to_block_number) + |> select([pbo], count(pbo.block_number)) + |> Repo.one() + end end From 489995971b7992449e302deb534c527a3c61ce98 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Tue, 12 Nov 2024 17:56:48 +0700 Subject: [PATCH 305/363] fix: Return `l1_tx_hashes` in the response of /batches/da/celestia/... API endpoint (#11184) --- apps/explorer/lib/explorer/chain/optimism/frame_sequence.ex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/explorer/lib/explorer/chain/optimism/frame_sequence.ex b/apps/explorer/lib/explorer/chain/optimism/frame_sequence.ex index d78646c5a177..66dbe4cd0da5 100644 --- a/apps/explorer/lib/explorer/chain/optimism/frame_sequence.ex +++ b/apps/explorer/lib/explorer/chain/optimism/frame_sequence.ex @@ -119,6 +119,8 @@ defmodule Explorer.Chain.Optimism.FrameSequence do # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_count` property "tx_count" => transaction_count, "l1_transaction_hashes" => batch.l1_transaction_hashes, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l1_transaction_hashes` property + "l1_tx_hashes" => batch.l1_transaction_hashes, "batch_data_container" => batch_data_container } From 14d2d240c6d234f806bc0c328ec04b83492b5d3d Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Tue, 12 Nov 2024 16:02:13 +0300 Subject: [PATCH 306/363] Fix missing `signers` field in nested quorum certificate (#11185) --- .../lib/block_scout_web/views/api/v2/zilliqa_view.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/zilliqa_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/zilliqa_view.ex index bccc41f1eb7a..d3f7c4768776 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/zilliqa_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/zilliqa_view.ex @@ -77,7 +77,8 @@ if Application.compile_env(:explorer, :chain_type) == :zilliqa do &%{ view: &1.view, signature: &1.signature, - proposed_by_validator_index: &1.proposed_by_validator_index + proposed_by_validator_index: &1.proposed_by_validator_index, + signers: &1.signers } ) }) From 632c6d44dfa1e19b076172cef101ac5f570f602d Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 12 Nov 2024 20:28:23 +0700 Subject: [PATCH 307/363] Update release workflow --- .github/workflows/release-arbitrum.yml | 1 + .github/workflows/release-blackfort.yml | 1 + .github/workflows/release-celo.yml | 1 + .github/workflows/release-eth.yml | 1 + .github/workflows/release-filecoin.yml | 1 + .github/workflows/release-fuse.yml | 1 + .github/workflows/release-gnosis.yml | 1 + .github/workflows/release-optimism.yml | 1 + .github/workflows/release-polygon-edge.yml | 1 + .github/workflows/release-polygon-zkevm.yml | 1 + .github/workflows/release-redstone.yml | 1 + .github/workflows/release-rootstock.yml | 1 + .github/workflows/release-scroll.yml | 1 + .github/workflows/release-shibarium.yml | 1 + .github/workflows/release-stability.yml | 1 + .github/workflows/release-suave.yml | 1 + .github/workflows/release-zetachain.yml | 1 + .github/workflows/release-zksync.yml | 1 + .github/workflows/release.yml | 1 + 19 files changed, 19 insertions(+) diff --git a/.github/workflows/release-arbitrum.yml b/.github/workflows/release-arbitrum.yml index e96d7c7a40bb..dcae4d8473d0 100644 --- a/.github/workflows/release-arbitrum.yml +++ b/.github/workflows/release-arbitrum.yml @@ -1,6 +1,7 @@ name: Release for Arbitrum on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-blackfort.yml b/.github/workflows/release-blackfort.yml index 482d50ece741..d8e72c5174fa 100644 --- a/.github/workflows/release-blackfort.yml +++ b/.github/workflows/release-blackfort.yml @@ -1,6 +1,7 @@ name: Release for Blackfort on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-celo.yml b/.github/workflows/release-celo.yml index c7caa2a6b845..b18f83ce7a3c 100644 --- a/.github/workflows/release-celo.yml +++ b/.github/workflows/release-celo.yml @@ -1,6 +1,7 @@ name: Release for Celo on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-eth.yml b/.github/workflows/release-eth.yml index ba1328961b69..f17b4e73a1ab 100644 --- a/.github/workflows/release-eth.yml +++ b/.github/workflows/release-eth.yml @@ -1,6 +1,7 @@ name: Release for Ethereum on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-filecoin.yml b/.github/workflows/release-filecoin.yml index 3416ab6c977d..1e9226909e37 100644 --- a/.github/workflows/release-filecoin.yml +++ b/.github/workflows/release-filecoin.yml @@ -1,6 +1,7 @@ name: Release for Filecoin on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-fuse.yml b/.github/workflows/release-fuse.yml index 749848d2e09a..dc66bc5752be 100644 --- a/.github/workflows/release-fuse.yml +++ b/.github/workflows/release-fuse.yml @@ -1,6 +1,7 @@ name: Release for Fuse on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-gnosis.yml b/.github/workflows/release-gnosis.yml index b0fd96eb45bf..54ec2c489950 100644 --- a/.github/workflows/release-gnosis.yml +++ b/.github/workflows/release-gnosis.yml @@ -1,6 +1,7 @@ name: Release for Gnosis Chain on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-optimism.yml b/.github/workflows/release-optimism.yml index 282d8c4acd70..f9b90637dc85 100644 --- a/.github/workflows/release-optimism.yml +++ b/.github/workflows/release-optimism.yml @@ -1,6 +1,7 @@ name: Release for Optimism on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-polygon-edge.yml b/.github/workflows/release-polygon-edge.yml index c2b5d274cd28..3d911dcb1cb7 100644 --- a/.github/workflows/release-polygon-edge.yml +++ b/.github/workflows/release-polygon-edge.yml @@ -1,6 +1,7 @@ name: Release for Polygon Edge on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-polygon-zkevm.yml b/.github/workflows/release-polygon-zkevm.yml index 677814d31300..d9d093b06ae6 100644 --- a/.github/workflows/release-polygon-zkevm.yml +++ b/.github/workflows/release-polygon-zkevm.yml @@ -1,6 +1,7 @@ name: Release for Polygon zkEVM on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-redstone.yml b/.github/workflows/release-redstone.yml index e9c42a0f8908..78fcef603f2b 100644 --- a/.github/workflows/release-redstone.yml +++ b/.github/workflows/release-redstone.yml @@ -1,6 +1,7 @@ name: Release for Redstone on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-rootstock.yml b/.github/workflows/release-rootstock.yml index 789e99315e9b..73396bc8e77c 100644 --- a/.github/workflows/release-rootstock.yml +++ b/.github/workflows/release-rootstock.yml @@ -1,6 +1,7 @@ name: Release for Rootstock on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-scroll.yml b/.github/workflows/release-scroll.yml index a5926f44025f..ba1ed72ef4f9 100644 --- a/.github/workflows/release-scroll.yml +++ b/.github/workflows/release-scroll.yml @@ -1,6 +1,7 @@ name: Release for Scroll on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-shibarium.yml b/.github/workflows/release-shibarium.yml index 443768afc601..84816989dd6c 100644 --- a/.github/workflows/release-shibarium.yml +++ b/.github/workflows/release-shibarium.yml @@ -1,6 +1,7 @@ name: Release for Shibarium on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-stability.yml b/.github/workflows/release-stability.yml index ed682b749a60..eb4aef50af5a 100644 --- a/.github/workflows/release-stability.yml +++ b/.github/workflows/release-stability.yml @@ -1,6 +1,7 @@ name: Release for Stability on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-suave.yml b/.github/workflows/release-suave.yml index 1f23138c96f6..91b63e386d61 100644 --- a/.github/workflows/release-suave.yml +++ b/.github/workflows/release-suave.yml @@ -1,6 +1,7 @@ name: Release for SUAVE on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-zetachain.yml b/.github/workflows/release-zetachain.yml index 9563e07d0173..74713ff9f829 100644 --- a/.github/workflows/release-zetachain.yml +++ b/.github/workflows/release-zetachain.yml @@ -1,6 +1,7 @@ name: Release for Zetachain on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release-zksync.yml b/.github/workflows/release-zksync.yml index 0d0c1b1bc05d..ae98f0bac505 100644 --- a/.github/workflows/release-zksync.yml +++ b/.github/workflows/release-zksync.yml @@ -1,6 +1,7 @@ name: Release for ZkSync on: + workflow_dispatch: release: types: [published] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 31a635c7edbb..716b27254243 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,7 @@ name: Release on: + workflow_dispatch: release: types: [published] From 9c86a43829ab78d91a7dc2697e15ed7fae809569 Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Tue, 12 Nov 2024 20:30:17 +0700 Subject: [PATCH 308/363] Temp update release workflow --- .github/workflows/release-zilliqa.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-zilliqa.yml b/.github/workflows/release-zilliqa.yml index 991c38db65f8..515bfa8a143d 100644 --- a/.github/workflows/release-zilliqa.yml +++ b/.github/workflows/release-zilliqa.yml @@ -1,6 +1,7 @@ name: Release for Zilliqa on: + workflow_dispatch: release: types: [published] From e8aa655d0cddef634e56e2b0d276f435c4f9c2f4 Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:56:42 +0300 Subject: [PATCH 309/363] fix: add `auth0-forwarded-for` header in auth0 (#11178) --- .../account/api/v2/authenticate_controller.ex | 2 +- .../account/api/v2/email_controller.ex | 3 ++- .../third_party_integrations/auth0.ex | 25 +++++++++++-------- config/runtime.exs | 3 ++- docker-compose/envs/common-blockscout.env | 1 + 5 files changed, 21 insertions(+), 13 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/authenticate_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/authenticate_controller.ex index 5fd57e7da6d3..13686c01aa49 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/authenticate_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/authenticate_controller.ex @@ -133,7 +133,7 @@ defmodule BlockScoutWeb.Account.API.V2.AuthenticateController do """ @spec confirm_otp(Conn.t(), map()) :: :error | {:error, any()} | Conn.t() def confirm_otp(conn, %{"email" => email, "otp" => otp}) do - with {:ok, auth} <- Auth0.confirm_otp_and_get_auth(email, otp) do + with {:ok, auth} <- Auth0.confirm_otp_and_get_auth(email, otp, AccessHelper.conn_to_ip_string(conn)) do put_auth_to_session(conn, auth) end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/email_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/email_controller.ex index 88c8e1fd72c9..829b914220f3 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/email_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/email_controller.ex @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.Account.API.V2.EmailController do import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + alias BlockScoutWeb.AccessHelper alias BlockScoutWeb.Account.API.V2.AuthenticateController alias Explorer.Account.Identity alias Explorer.{Helper, Repo} @@ -97,7 +98,7 @@ defmodule BlockScoutWeb.Account.API.V2.EmailController do | Plug.Conn.t() def link_email(conn, %{"email" => email, "otp" => otp}) do with {:auth, %{} = user} <- {:auth, current_user(conn)}, - {:ok, auth} <- Auth0.link_email(user, email, otp) do + {:ok, auth} <- Auth0.link_email(user, email, otp, AccessHelper.conn_to_ip_string(conn)) do AuthenticateController.put_auth_to_session(conn, auth) end end diff --git a/apps/explorer/lib/explorer/third_party_integrations/auth0.ex b/apps/explorer/lib/explorer/third_party_integrations/auth0.ex index 939f14909458..51c59806d75d 100644 --- a/apps/explorer/lib/explorer/third_party_integrations/auth0.ex +++ b/apps/explorer/lib/explorer/third_party_integrations/auth0.ex @@ -159,17 +159,19 @@ defmodule Explorer.ThirdPartyIntegrations.Auth0 do - `primary_user_id`: The ID of the existing user account - `email`: The email address to be linked - `otp`: The one-time password for verification + - `ip`: The IP address of the requester ## Returns - `{:ok, Auth.t()}` if the email was successfully linked - `{:error, String.t()}` error with the description - `:error` if there was an unexpected error """ - @spec link_email(Identity.session(), String.t(), String.t()) :: :error | {:ok, Auth.t()} | {:error, String.t()} - def link_email(%{uid: primary_user_id, email: nil}, email, otp) do + @spec link_email(Identity.session(), String.t(), String.t(), String.t()) :: + :error | {:ok, Auth.t()} | {:error, String.t()} + def link_email(%{uid: primary_user_id, email: nil}, email, otp, ip) do case find_users_by_email(email) do {:ok, []} -> - with {:ok, token} <- confirm_otp(email, otp), + with {:ok, token} <- confirm_otp(email, otp, ip), {:ok, %{"sub" => "email|" <> identity_id}} <- get_user_from_token(token), :ok <- link_users(primary_user_id, identity_id, "email"), {:ok, user} <- update_user_email(primary_user_id, email) do @@ -184,7 +186,7 @@ defmodule Explorer.ThirdPartyIntegrations.Auth0 do end end - def link_email(_account_with_email, _, _), do: {:error, "This account already has an email"} + def link_email(_account_with_email, _, _, _), do: {:error, "This account already has an email"} @doc """ Confirms a one-time password (OTP) and retrieves authentication information. @@ -195,15 +197,16 @@ defmodule Explorer.ThirdPartyIntegrations.Auth0 do ## Parameters - `email`: The email address associated with the OTP - `otp`: The one-time password to be confirmed + - `ip`: The IP address of the requester ## Returns - `{:ok, Auth.t()}` if the OTP is confirmed and authentication is successful - `{:error, String.t()}` error with the description - `:error` if there was an unexpected error """ - @spec confirm_otp_and_get_auth(String.t(), String.t()) :: :error | {:error, String.t()} | {:ok, Auth.t()} - def confirm_otp_and_get_auth(email, otp) do - with {:ok, token} <- confirm_otp(email, otp), + @spec confirm_otp_and_get_auth(String.t(), String.t(), String.t()) :: :error | {:error, String.t()} | {:ok, Auth.t()} + def confirm_otp_and_get_auth(email, otp, ip) do + with {:ok, token} <- confirm_otp(email, otp, ip), {:ok, %{"sub" => user_id} = user} <- get_user_from_token(token), {:search, _user_from_token, {:ok, user}} <- {:search, user, get_user_by_id(user_id)} do maybe_link_email_and_get_auth(user) @@ -265,7 +268,7 @@ defmodule Explorer.ThirdPartyIntegrations.Auth0 do message = %Siwe.Message{ domain: Helper.get_app_host(), address: address, - statement: "Sign in to Blockscout Account V2 via Ethereum account", + statement: Application.get_env(:explorer, Account)[:siwe_message], uri: Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:scheme] <> "://" <> Helper.get_app_host(), @@ -440,7 +443,7 @@ defmodule Explorer.ThirdPartyIntegrations.Auth0 do {:error, @misconfiguration_detected} end - defp confirm_otp(email, otp) do + defp confirm_otp(email, otp, ip) do client = OAuth.client() body = @@ -452,7 +455,9 @@ defmodule Explorer.ThirdPartyIntegrations.Auth0 do } |> put_client_id_and_secret() - case Client.post(client, "/oauth/token", body, @json_content_type) do + headers = [{"auth0-forwarded-for", ip} | @json_content_type] + + case Client.post(client, "/oauth/token", body, headers) do {:ok, %OAuth2.Response{status_code: 200, body: body}} -> {:ok, AccessToken.new(body)} diff --git a/config/runtime.exs b/config/runtime.exs index 5a329391249b..399394b782f0 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -564,7 +564,8 @@ config :explorer, Explorer.Account, private_tags_limit: ConfigHelper.parse_integer_env_var("ACCOUNT_PRIVATE_TAGS_LIMIT", 2000), watchlist_addresses_limit: ConfigHelper.parse_integer_env_var("ACCOUNT_WATCHLIST_ADDRESSES_LIMIT", 15), notifications_limit_for_30_days: - ConfigHelper.parse_integer_env_var("ACCOUNT_WATCHLIST_NOTIFICATIONS_LIMIT_FOR_30_DAYS", 1000) + ConfigHelper.parse_integer_env_var("ACCOUNT_WATCHLIST_NOTIFICATIONS_LIMIT_FOR_30_DAYS", 1000), + siwe_message: System.get_env("ACCOUNT_SIWE_MESSAGE", "Sign in to Blockscout Account V2") config :explorer, :token_id_migration, first_block: ConfigHelper.parse_integer_env_var("TOKEN_ID_MIGRATION_FIRST_BLOCK", 0), diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 21d50b33e415..80166a833236 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -407,6 +407,7 @@ DECODE_NOT_A_CONTRACT_CALLS=true # ACCOUNT_OTP_RESEND_INTERVAL= # ACCOUNT_PRIVATE_TAGS_LIMIT=2000 # ACCOUNT_WATCHLIST_ADDRESSES_LIMIT=15 +# ACCOUNT_SIWE_MESSAGE= ACCOUNT_CLOAK_KEY= ACCOUNT_ENABLED=false ACCOUNT_REDIS_URL=redis://redis-db:6379 From 8a1d83b7adcef923cde70b519a1066c7f49852dc Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:57:36 +0300 Subject: [PATCH 310/363] chore: extend recaptcha logging (#11182) --- .../lib/block_scout_web/captcha_helper.ex | 27 ++++++++++++++++--- config/runtime.exs | 3 ++- docker-compose/envs/common-blockscout.env | 1 + 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/captcha_helper.ex b/apps/block_scout_web/lib/block_scout_web/captcha_helper.ex index b05f39d696c0..810652e2171d 100644 --- a/apps/block_scout_web/lib/block_scout_web/captcha_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/captcha_helper.ex @@ -2,6 +2,7 @@ defmodule BlockScoutWeb.CaptchaHelper do @moduledoc """ A helper for CAPTCHA """ + require Logger alias Explorer.Helper @@ -49,26 +50,42 @@ defmodule BlockScoutWeb.CaptchaHelper do false -> true - _ -> + error -> + Logger.error("Failed to verify reCAPTCHA: #{inspect(error)}") false end end # v3 case defp success?(%{"success" => true, "score" => score, "hostname" => hostname}) do + unless Helper.get_app_host() == hostname do + Logger.warning("reCAPTCHA v3 Hostname mismatch: #{inspect(hostname)} != #{inspect(Helper.get_app_host())}") + end + + if Helper.get_app_host() == hostname and not check_recaptcha_v3_score(score) do + Logger.warning("reCAPTCHA v3 low score: #{inspect(score)} < #{inspect(score_threshold())}") + end + (!check_hostname?() || Helper.get_app_host() == hostname) && check_recaptcha_v3_score(score) end # v2 case defp success?(%{"success" => true, "hostname" => hostname}) do + unless Helper.get_app_host() == hostname do + Logger.warning("reCAPTCHA v2 Hostname mismatch: #{inspect(hostname)} != #{inspect(Helper.get_app_host())}") + end + !check_hostname?() || Helper.get_app_host() == hostname end - defp success?(_resp), do: false + defp success?(resp) do + Logger.error("Failed to verify reCAPTCHA, unexpected response: #{inspect(resp)}") + false + end defp check_recaptcha_v3_score(score) do - if score >= 0.5 do + if score >= score_threshold() do true else false @@ -78,4 +95,8 @@ defmodule BlockScoutWeb.CaptchaHelper do defp check_hostname? do Application.get_env(:block_scout_web, :recaptcha)[:check_hostname?] end + + defp score_threshold do + Application.get_env(:block_scout_web, :recaptcha)[:score_threshold] + end end diff --git a/config/runtime.exs b/config/runtime.exs index 399394b782f0..2ad540901979 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -44,7 +44,8 @@ config :block_scout_web, :recaptcha, v3_client_key: System.get_env("RE_CAPTCHA_V3_CLIENT_KEY"), v3_secret_key: System.get_env("RE_CAPTCHA_V3_SECRET_KEY"), is_disabled: ConfigHelper.parse_bool_env_var("RE_CAPTCHA_DISABLED"), - check_hostname?: ConfigHelper.parse_bool_env_var("RE_CAPTCHA_CHECK_HOSTNAME", "true") + check_hostname?: ConfigHelper.parse_bool_env_var("RE_CAPTCHA_CHECK_HOSTNAME", "true"), + score_threshold: ConfigHelper.parse_float_env_var("RE_CAPTCHA_SCORE_THRESHOLD", "0.5") network_path = "NETWORK_PATH" diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 80166a833236..f31d65b6ff15 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -369,6 +369,7 @@ RE_CAPTCHA_V3_SECRET_KEY= RE_CAPTCHA_V3_CLIENT_KEY= RE_CAPTCHA_DISABLED=false # RE_CAPTCHA_CHECK_HOSTNAME +# RE_CAPTCHA_SCORE_THRESHOLD JSON_RPC= # API_RATE_LIMIT_HAMMER_REDIS_URL=redis://redis-db:6379/1 # API_RATE_LIMIT_IS_BLOCKSCOUT_BEHIND_PROXY=false From 473d43333fdd6f7f645c0f035bdb0d6fd49443a7 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:32:19 +0300 Subject: [PATCH 311/363] fix: Fix failed filecoin tests (#11187) --- .../api/v2/smart_contract_controller_test.exs | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs index 5d4c0222de0c..c7a25a5d2dc7 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/smart_contract_controller_test.exs @@ -179,7 +179,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do result_props = correct_response |> Map.keys() for prop <- result_props do - assert correct_response[prop] == response[prop] + assert prepare_implementation(correct_response[prop]) == response[prop] end end @@ -495,7 +495,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do result_props = correct_response |> Map.keys() for prop <- result_props do - assert correct_response[prop] == response[prop] + assert prepare_implementation(correct_response[prop]) == response[prop] end end @@ -657,7 +657,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do result_props = correct_response |> Map.keys() for prop <- result_props do - assert correct_response[prop] == response[prop] + assert prepare_implementation(correct_response[prop]) == response[prop] end end @@ -1160,7 +1160,9 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ "proxy_type" => "eip1967", - "implementations" => [%{"address" => formatted_implementation_address_hash_string, "name" => nil}], + "implementations" => [ + prepare_implementation(%{"address" => formatted_implementation_address_hash_string, "name" => nil}) + ], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -1285,7 +1287,9 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ "proxy_type" => "eip1967", - "implementations" => [%{"address" => formatted_implementation_address_hash_string, "name" => nil}], + "implementations" => [ + prepare_implementation(%{"address" => formatted_implementation_address_hash_string, "name" => nil}) + ], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -1410,7 +1414,9 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do assert response == %{ "proxy_type" => "eip1967", - "implementations" => [%{"address" => formatted_implementation_address_hash_string, "name" => nil}], + "implementations" => [ + prepare_implementation(%{"address" => formatted_implementation_address_hash_string, "name" => nil}) + ], "has_custom_methods_read" => false, "has_custom_methods_write" => false, "is_self_destructed" => false, @@ -3685,4 +3691,20 @@ defmodule BlockScoutWeb.API.V2.SmartContractControllerTest do EthereumJSONRPC.Mox |> TestHelper.mock_logic_storage_pointer_request(error?, response) end + + defp prepare_implementation(items) when is_list(items) do + Enum.map(items, &prepare_implementation/1) + end + + defp prepare_implementation(%{"address" => _, "name" => _} = implementation) do + case Application.get_env(:explorer, :chain_type) do + :filecoin -> + Map.put(implementation, "filecoin_robust_address", nil) + + _ -> + implementation + end + end + + defp prepare_implementation(other), do: other end From b7221bdb5c52bf05585aaa7428c17afe2bf3bda3 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Sat, 16 Nov 2024 11:41:23 +0400 Subject: [PATCH 312/363] feat: Multiple json rpc urls (#10934) * feat: Multiple json rpc urls * Fix archive balances request in case of trace url is missing * Add docs + specs for EndpointAvailabilityObserver public functions * Update tests for internal transactions fetcher * Fix archive balances request * Fix adding unavailable endpoint --- .../views/smart_contract_view.ex | 15 ++- apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex | 2 +- .../lib/ethereum_jsonrpc/http.ex | 36 ++++-- .../ethereum_jsonrpc/utility/common_helper.ex | 11 ++ .../utility/endpoint_availability_checker.ex | 14 +-- .../utility/endpoint_availability_observer.ex | 111 +++++++++++++++--- apps/explorer/config/dev/besu.exs | 33 ++++-- apps/explorer/config/dev/erigon.exs | 33 ++++-- apps/explorer/config/dev/filecoin.exs | 35 +++++- apps/explorer/config/dev/ganache.exs | 20 +++- apps/explorer/config/dev/geth.exs | 33 ++++-- apps/explorer/config/dev/nethermind.exs | 33 ++++-- apps/explorer/config/dev/rsk.exs | 33 ++++-- apps/explorer/config/prod/besu.exs | 22 ++-- apps/explorer/config/prod/erigon.exs | 22 ++-- apps/explorer/config/prod/filecoin.exs | 20 +++- apps/explorer/config/prod/ganache.exs | 14 ++- apps/explorer/config/prod/geth.exs | 22 ++-- apps/explorer/config/prod/nethermind.exs | 22 ++-- apps/explorer/config/prod/rsk.exs | 22 ++-- .../cache/optimism_finalization_period.ex | 2 +- apps/explorer/test/explorer/chain_test.exs | 10 ++ apps/indexer/config/dev/besu.exs | 37 ++++-- apps/indexer/config/dev/erigon.exs | 37 ++++-- apps/indexer/config/dev/filecoin.exs | 35 +++++- apps/indexer/config/dev/ganache.exs | 20 +++- apps/indexer/config/dev/geth.exs | 33 ++++-- apps/indexer/config/dev/nethermind.exs | 37 ++++-- apps/indexer/config/dev/rsk.exs | 37 ++++-- apps/indexer/config/prod/besu.exs | 26 ++-- apps/indexer/config/prod/erigon.exs | 26 ++-- apps/indexer/config/prod/filecoin.exs | 20 +++- apps/indexer/config/prod/ganache.exs | 20 +++- apps/indexer/config/prod/geth.exs | 22 ++-- apps/indexer/config/prod/nethermind.exs | 26 ++-- apps/indexer/config/prod/rsk.exs | 26 ++-- .../test/indexer/block/fetcher_test.exs | 7 ++ config/config_helper.exs | 9 ++ config/runtime.exs | 7 +- 39 files changed, 746 insertions(+), 244 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex index e6459593ba5e..86fc1795ba7c 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex @@ -226,11 +226,14 @@ defmodule BlockScoutWeb.SmartContractView do def cut_rpc_url(error) do transport_options = Application.get_env(:explorer, :json_rpc_named_arguments)[:transport_options] - error - |> String.replace(transport_options[:url], "rpc_url") - |> (&if(transport_options[:fallback_url], - do: String.replace(&1, transport_options[:fallback_url], "rpc_url"), - else: &1 - )).() + all_urls = + (transport_options[:urls] || []) ++ + (transport_options[:trace_urls] || []) ++ + (transport_options[:eth_call_urls] || []) ++ + (transport_options[:fallback_urls] || []) ++ + (transport_options[:fallback_trace_urls] || []) ++ + (transport_options[:fallback_eth_call_urls] || []) + + String.replace(error, Enum.reject(all_urls, &(&1 in [nil, ""])), "rpc_url") end end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex index 5c4d4a396e26..69012ce9f1be 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex @@ -552,7 +552,7 @@ defmodule EthereumJSONRPC do CommonHelper.put_in_keyword_nested( json_rpc_named_arguments, [:transport_options, :method_to_url, :eth_getBalance], - System.get_env("ETHEREUM_JSONRPC_TRACE_URL") + :trace ) end diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex index a411ab921b99..7d3543f75e09 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex @@ -3,7 +3,8 @@ defmodule EthereumJSONRPC.HTTP do JSONRPC over HTTP """ - alias EthereumJSONRPC.{DecodeError, Transport, Utility.EndpointAvailabilityObserver} + alias EthereumJSONRPC.{DecodeError, Transport} + alias EthereumJSONRPC.Utility.{CommonHelper, EndpointAvailabilityObserver} require Logger @@ -238,25 +239,36 @@ defmodule EthereumJSONRPC.HTTP do defp url(options, method) when is_list(options) and is_binary(method) do with {:ok, method_to_url} <- Keyword.fetch(options, :method_to_url), {:ok, method_atom} <- to_existing_atom(method), - {:ok, url} <- Keyword.fetch(method_to_url, method_atom) do - {url_type, fallback_url} = - case method_atom do - :eth_call -> {:eth_call, options[:fallback_eth_call_url]} - _ -> {:trace, options[:fallback_trace_url]} - end + {:ok, url_type} <- Keyword.fetch(method_to_url, method_atom) do + fallback_urls = CommonHelper.url_type_to_urls(url_type, options, :fallback) + + url = + url_type + |> CommonHelper.url_type_to_urls(options) + |> EndpointAvailabilityObserver.maybe_replace_urls(fallback_urls, url_type) + |> select_single_url() - {url_type, EndpointAvailabilityObserver.maybe_replace_url(url, fallback_url, url_type)} + {url_type, url} else _ -> + url_type = :http + url = - options - |> Keyword.fetch!(:url) - |> EndpointAvailabilityObserver.maybe_replace_url(options[:fallback_url], :http) + url_type + |> CommonHelper.url_type_to_urls(options) + |> EndpointAvailabilityObserver.maybe_replace_urls(options[:fallback_urls], url_type) + |> select_single_url() - {:http, url} + {url_type, url} end end + defp select_single_url([]), do: nil + + defp select_single_url(urls) do + Enum.random(urls) + end + defp to_existing_atom(string) do {:ok, String.to_existing_atom(string)} rescue diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/common_helper.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/common_helper.ex index a6d0d55f2d7b..615d9f1ddaad 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/common_helper.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/common_helper.ex @@ -30,6 +30,17 @@ defmodule EthereumJSONRPC.Utility.CommonHelper do Keyword.put(keyword || [], nearest_path, put_in_keyword_nested(keyword[nearest_path], rest_path, value)) end + @doc """ + Extracts urls corresponding to `url_type` from json rpc transport options + """ + @spec url_type_to_urls(atom(), Keyword.t(), atom() | String.t()) :: [String.t()] + def url_type_to_urls(url_type, json_rpc_transport_options, subtype \\ nil) do + key_prefix = (subtype && "#{subtype}_") || "" + url_prefix = (url_type == :http && "") || "#{url_type}_" + urls_key = String.to_existing_atom("#{key_prefix}#{url_prefix}urls") + json_rpc_transport_options[urls_key] + end + defp convert_to_ms(number, "s"), do: :timer.seconds(number) defp convert_to_ms(number, "m"), do: :timer.minutes(number) defp convert_to_ms(number, "h"), do: :timer.hours(number) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/endpoint_availability_checker.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/endpoint_availability_checker.ex index 2f1c1fdb3f77..78242fe679bf 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/endpoint_availability_checker.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/endpoint_availability_checker.ex @@ -40,8 +40,7 @@ defmodule EthereumJSONRPC.Utility.EndpointAvailabilityChecker do {:ok, _number} -> url = json_rpc_named_arguments[:transport_options][:url] - EndpointAvailabilityObserver.enable_endpoint(url, url_type) - log_url_available(url, url_type, json_rpc_named_arguments) + EndpointAvailabilityObserver.enable_endpoint(url, url_type, json_rpc_named_arguments) acc _ -> @@ -54,17 +53,8 @@ defmodule EthereumJSONRPC.Utility.EndpointAvailabilityChecker do {:noreply, %{state | unavailable_endpoints_arguments: new_unavailable_endpoints}} end - defp log_url_available(url, url_type, json_rpc_named_arguments) do - message_extra = - if EndpointAvailabilityObserver.fallback_url_set?(url_type, json_rpc_named_arguments), - do: ", switching back to it", - else: "" - - Logger.info("URL #{inspect(url)} is available now#{message_extra}") - end - defp fetch_latest_block_number(json_rpc_named_arguments) do - {_, arguments_without_fallback} = pop_in(json_rpc_named_arguments, [:transport_options, :fallback_url]) + {_, arguments_without_fallback} = pop_in(json_rpc_named_arguments, [:transport_options, :fallback_urls]) %{id: 0, method: "eth_blockNumber", params: []} |> EthereumJSONRPC.request() diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/endpoint_availability_observer.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/endpoint_availability_observer.ex index fe768190b144..6d274f9016e2 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/endpoint_availability_observer.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/endpoint_availability_observer.ex @@ -7,12 +7,14 @@ defmodule EthereumJSONRPC.Utility.EndpointAvailabilityObserver do require Logger - alias EthereumJSONRPC.Utility.EndpointAvailabilityChecker + alias EthereumJSONRPC.Utility.{CommonHelper, EndpointAvailabilityChecker} @max_error_count 3 @window_duration 3 @cleaning_interval :timer.seconds(1) + @type url_type :: :ws | :trace | :http | :eth_call + def start_link(_) do GenServer.start_link(__MODULE__, :ok, name: __MODULE__) end @@ -23,14 +25,34 @@ defmodule EthereumJSONRPC.Utility.EndpointAvailabilityObserver do {:ok, %{error_counts: %{}, unavailable_endpoints: %{ws: [], trace: [], http: [], eth_call: []}}} end + @doc """ + Increases `url` of `url_type` type error count asynchronously. + """ + @spec inc_error_count(binary(), Keyword.t(), url_type()) :: :ok def inc_error_count(url, json_rpc_named_arguments, url_type) do GenServer.cast(__MODULE__, {:inc_error_count, url, json_rpc_named_arguments, url_type}) end + @doc """ + Checks if `url` of `url_type` type is available. + """ + @spec check_endpoint(binary(), url_type()) :: :ok | :unavailable def check_endpoint(url, url_type) do GenServer.call(__MODULE__, {:check_endpoint, url, url_type}) end + @doc """ + Filters out unavailable urls from `urls` of `url_type` type. + """ + @spec filter_unavailable_urls([binary()], url_type()) :: [binary()] + def filter_unavailable_urls(urls, url_type) do + GenServer.call(__MODULE__, {:filter_unavailable_urls, urls, url_type}) + end + + @doc """ + Checks if `url` of `url_type` type is unavailable and replaces it with `replace_url` if it's not `nil`. + """ + @spec maybe_replace_url(binary(), binary() | nil, url_type()) :: binary() def maybe_replace_url(url, replace_url, url_type) do case check_endpoint(url, url_type) do :ok -> url @@ -38,8 +60,23 @@ defmodule EthereumJSONRPC.Utility.EndpointAvailabilityObserver do end end - def enable_endpoint(url, url_type) do - GenServer.cast(__MODULE__, {:enable_endpoint, url, url_type}) + @doc """ + Analogue of `maybe_replace_url/3` for multiple urls. + """ + @spec maybe_replace_urls([binary()] | nil, [binary()] | nil, url_type()) :: [binary()] + def maybe_replace_urls(urls, replace_urls, url_type) do + case filter_unavailable_urls(urls, url_type) do + [] -> replace_urls || urls || [] + available_urls -> available_urls + end + end + + @doc """ + Marks `url` of `url_type` type as available. + """ + @spec enable_endpoint(binary(), url_type(), Keyword.t()) :: :ok + def enable_endpoint(url, url_type, json_rpc_named_arguments) do + GenServer.cast(__MODULE__, {:enable_endpoint, url, url_type, json_rpc_named_arguments}) end def handle_call({:check_endpoint, url, url_type}, _from, %{unavailable_endpoints: unavailable_endpoints} = state) do @@ -48,6 +85,14 @@ defmodule EthereumJSONRPC.Utility.EndpointAvailabilityObserver do {:reply, result, state} end + def handle_call( + {:filter_unavailable_urls, urls, url_type}, + _from, + %{unavailable_endpoints: unavailable_endpoints} = state + ) do + {:reply, do_filter_unavailable_urls(urls, unavailable_endpoints[url_type]), state} + end + def handle_cast({:inc_error_count, url, json_rpc_named_arguments, url_type}, state) do new_state = if json_rpc_named_arguments[:api?], @@ -57,7 +102,12 @@ defmodule EthereumJSONRPC.Utility.EndpointAvailabilityObserver do {:noreply, new_state} end - def handle_cast({:enable_endpoint, url, url_type}, %{unavailable_endpoints: unavailable_endpoints} = state) do + def handle_cast( + {:enable_endpoint, url, url_type, json_rpc_named_arguments}, + %{unavailable_endpoints: unavailable_endpoints} = state + ) do + log_url_available(url, url_type, unavailable_endpoints, json_rpc_named_arguments) + {:noreply, %{state | unavailable_endpoints: %{unavailable_endpoints | url_type => unavailable_endpoints[url_type] -- [url]}}} end @@ -81,6 +131,10 @@ defmodule EthereumJSONRPC.Utility.EndpointAvailabilityObserver do end end + defp do_filter_unavailable_urls(urls, unavailable_urls) do + Enum.reject(urls || [], fn url -> url in unavailable_urls end) + end + defp do_increase_error_counts(url, json_rpc_named_arguments, url_type, %{error_counts: error_counts} = state) do current_count = error_counts[url][url_type][:count] unavailable_endpoints = state.unavailable_endpoints[url_type] @@ -94,11 +148,11 @@ defmodule EthereumJSONRPC.Utility.EndpointAvailabilityObserver do current_count + 1 >= @max_error_count -> EndpointAvailabilityChecker.add_endpoint( - put_in(json_rpc_named_arguments[:transport_options][:url], url), + put_in(json_rpc_named_arguments[:transport_options][:urls], [url]), url_type ) - log_url_unavailable(url, url_type, json_rpc_named_arguments) + log_url_unavailable(url, url_type, unavailable_endpoints, json_rpc_named_arguments) %{ state @@ -114,20 +168,49 @@ defmodule EthereumJSONRPC.Utility.EndpointAvailabilityObserver do end end - defp log_url_unavailable(url, url_type, json_rpc_named_arguments) do + defp log_url_unavailable(url, :ws, _unavailable_endpoints, _json_rpc_named_arguments) do + Logger.warning("URL #{inspect(url)} is unavailable") + end + + defp log_url_unavailable(url, url_type, unavailable_endpoints, json_rpc_named_arguments) do + available_urls = + url_type + |> available_urls(unavailable_endpoints, json_rpc_named_arguments) + |> Kernel.--([url]) + fallback_url_message = - if fallback_url_set?(url_type, json_rpc_named_arguments), - do: "switching to fallback #{url_type} url", - else: "and no fallback is set" + case {available_urls, fallback_url_set?(url_type, json_rpc_named_arguments)} do + {[], true} -> "and there is no other #{url_type} url available, switching to fallback #{url_type} url" + {[], false} -> "there is no other #{url_type} url available, and no fallback is set" + _ -> "switching to another #{url_type} url" + end Logger.warning("URL #{inspect(url)} is unavailable, #{fallback_url_message}") end - def fallback_url_set?(url_type, json_rpc_named_arguments) do + defp log_url_available(url, url_type, unavailable_endpoints, json_rpc_named_arguments) do + available_urls = available_urls(url_type, unavailable_endpoints, json_rpc_named_arguments) + + message_extra = + case {available_urls, fallback_url_set?(url_type, json_rpc_named_arguments)} do + {[], true} -> ", switching back from fallback urls" + _ -> "" + end + + Logger.info("URL #{inspect(url)} of #{url_type} type is available now#{message_extra}") + end + + defp available_urls(url_type, unavailable_endpoints, json_rpc_named_arguments) do + url_type + |> CommonHelper.url_type_to_urls(json_rpc_named_arguments[:transport_options]) + |> do_filter_unavailable_urls(unavailable_endpoints) + end + + defp fallback_url_set?(url_type, json_rpc_named_arguments) do case url_type do - :http -> not is_nil(json_rpc_named_arguments[:transport_options][:fallback_url]) - :trace -> not is_nil(json_rpc_named_arguments[:transport_options][:fallback_trace_url]) - :eth_call -> not is_nil(json_rpc_named_arguments[:transport_options][:fallback_eth_call_url]) + :http -> not is_nil(json_rpc_named_arguments[:transport_options][:fallback_urls]) + :trace -> not is_nil(json_rpc_named_arguments[:transport_options][:fallback_trace_urls]) + :eth_call -> not is_nil(json_rpc_named_arguments[:transport_options][:fallback_eth_call_urls]) _ -> false end end diff --git a/apps/explorer/config/dev/besu.exs b/apps/explorer/config/dev/besu.exs index e19ee16351b8..598ce1459b19 100644 --- a/apps/explorer/config/dev/besu.exs +++ b/apps/explorer/config/dev/besu.exs @@ -12,14 +12,33 @@ config :explorer, transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545", - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL", "http://localhost:8545"), + trace_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_TRACE_URLS", + "ETHEREUM_JSONRPC_TRACE_URL", + "http://localhost:8545" + ), + eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_ETH_CALL_URL", + "http://localhost:8545" + ), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url("http://localhost:8545"), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545" + eth_call: :eth_call, + eth_getBalance: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/explorer/config/dev/erigon.exs b/apps/explorer/config/dev/erigon.exs index 4c766d8247fc..ce6108e4543f 100644 --- a/apps/explorer/config/dev/erigon.exs +++ b/apps/explorer/config/dev/erigon.exs @@ -12,14 +12,33 @@ config :explorer, transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545", - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL", "http://localhost:8545"), + trace_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_TRACE_URLS", + "ETHEREUM_JSONRPC_TRACE_URL", + "http://localhost:8545" + ), + eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_ETH_CALL_URL", + "http://localhost:8545" + ), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url("http://localhost:8545"), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545" + eth_call: :eth_call, + eth_getBalance: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/explorer/config/dev/filecoin.exs b/apps/explorer/config/dev/filecoin.exs index f234e5b57000..2ebd3941ec36 100644 --- a/apps/explorer/config/dev/filecoin.exs +++ b/apps/explorer/config/dev/filecoin.exs @@ -12,13 +12,36 @@ config :explorer, transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:1234/rpc/v1", - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_HTTP_URLS", + "ETHEREUM_JSONRPC_HTTP_URL", + "http://localhost:1234/rpc/v1" + ), + trace_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_TRACE_URLS", + "ETHEREUM_JSONRPC_TRACE_URL", + "http://localhost:1234/rpc/v1" + ), + eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_ETH_CALL_URL", + "http://localhost:1234/rpc/v1" + ), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url("http://localhost:1234/rpc/v1"), - trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:1234/rpc/v1" + eth_call: :eth_call, + trace_block: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/explorer/config/dev/ganache.exs b/apps/explorer/config/dev/ganache.exs index 6ca442c81e69..8c3c678311eb 100644 --- a/apps/explorer/config/dev/ganache.exs +++ b/apps/explorer/config/dev/ganache.exs @@ -12,11 +12,23 @@ config :explorer, transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:7545", - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL", "http://localhost:7545"), + eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_ETH_CALL_URL", + "http://localhost:7545" + ), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url("http://localhost:7545") + eth_call: :eth_call ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/explorer/config/dev/geth.exs b/apps/explorer/config/dev/geth.exs index 41d6eb7a16ff..6210a08d583c 100644 --- a/apps/explorer/config/dev/geth.exs +++ b/apps/explorer/config/dev/geth.exs @@ -12,14 +12,33 @@ config :explorer, transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545", - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL", "http://localhost:8545"), + trace_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_TRACE_URLS", + "ETHEREUM_JSONRPC_TRACE_URL", + "http://localhost:8545" + ), + eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_ETH_CALL_URL", + "http://localhost:8545" + ), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url("http://localhost:8545"), - debug_traceTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - debug_traceBlockByNumber: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545" + eth_call: :eth_call, + debug_traceTransaction: :trace, + debug_traceBlockByNumber: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/explorer/config/dev/nethermind.exs b/apps/explorer/config/dev/nethermind.exs index 9e44aa811ab5..83bdbc3a899c 100644 --- a/apps/explorer/config/dev/nethermind.exs +++ b/apps/explorer/config/dev/nethermind.exs @@ -12,14 +12,33 @@ config :explorer, transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545", - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL", "http://localhost:8545"), + trace_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_TRACE_URLS", + "ETHEREUM_JSONRPC_TRACE_URL", + "http://localhost:8545" + ), + eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_ETH_CALL_URL", + "http://localhost:8545" + ), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url("http://localhost:8545"), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545" + eth_call: :eth_call, + eth_getBalance: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/explorer/config/dev/rsk.exs b/apps/explorer/config/dev/rsk.exs index 0f8e4444e994..6e2d4f391222 100644 --- a/apps/explorer/config/dev/rsk.exs +++ b/apps/explorer/config/dev/rsk.exs @@ -12,14 +12,33 @@ config :explorer, transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545", - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL", "http://localhost:8545"), + trace_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_TRACE_URLS", + "ETHEREUM_JSONRPC_TRACE_URL", + "http://localhost:8545" + ), + eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_ETH_CALL_URL", + "http://localhost:8545" + ), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url("http://localhost:8545"), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545" + eth_call: :eth_call, + eth_getBalance: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/explorer/config/prod/besu.exs b/apps/explorer/config/prod/besu.exs index cafb89d2b0af..a7b8c6ff6e73 100644 --- a/apps/explorer/config/prod/besu.exs +++ b/apps/explorer/config/prod/besu.exs @@ -12,14 +12,22 @@ config :explorer, transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"), - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL"), + trace_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_TRACE_URLS", "ETHEREUM_JSONRPC_TRACE_URL"), + eth_call_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_ETH_CALL_URLS", "ETHEREUM_JSONRPC_ETH_CALL_URL"), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url(), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") + eth_call: :eth_call, + eth_getBalance: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/explorer/config/prod/erigon.exs b/apps/explorer/config/prod/erigon.exs index 616a135b32f9..d2a8a51e188c 100644 --- a/apps/explorer/config/prod/erigon.exs +++ b/apps/explorer/config/prod/erigon.exs @@ -12,14 +12,22 @@ config :explorer, transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"), - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL"), + trace_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_TRACE_URLS", "ETHEREUM_JSONRPC_TRACE_URL"), + eth_call_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_ETH_CALL_URLS", "ETHEREUM_JSONRPC_ETH_CALL_URL"), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url(), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") + eth_call: :eth_call, + eth_getBalance: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/explorer/config/prod/filecoin.exs b/apps/explorer/config/prod/filecoin.exs index bbad83ed26b4..a47c57517a1a 100644 --- a/apps/explorer/config/prod/filecoin.exs +++ b/apps/explorer/config/prod/filecoin.exs @@ -12,13 +12,21 @@ config :explorer, transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"), - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL"), + trace_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_TRACE_URLS", "ETHEREUM_JSONRPC_TRACE_URL"), + eth_call_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_ETH_CALL_URLS", "ETHEREUM_JSONRPC_ETH_CALL_URL"), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url(), - trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") + eth_call: :eth_call, + trace_block: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/explorer/config/prod/ganache.exs b/apps/explorer/config/prod/ganache.exs index 0fd0385cada8..d4a0aff64a78 100644 --- a/apps/explorer/config/prod/ganache.exs +++ b/apps/explorer/config/prod/ganache.exs @@ -12,11 +12,17 @@ config :explorer, transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"), - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL"), + eth_call_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_ETH_CALL_URLS", "ETHEREUM_JSONRPC_ETH_CALL_URL"), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url() + eth_call: :eth_call ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/explorer/config/prod/geth.exs b/apps/explorer/config/prod/geth.exs index 9cf3bd32abbd..2630943455df 100644 --- a/apps/explorer/config/prod/geth.exs +++ b/apps/explorer/config/prod/geth.exs @@ -12,14 +12,22 @@ config :explorer, transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"), - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL"), + trace_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_TRACE_URLS", "ETHEREUM_JSONRPC_TRACE_URL"), + eth_call_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_ETH_CALL_URLS", "ETHEREUM_JSONRPC_ETH_CALL_URL"), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url(), - debug_traceTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - debug_traceBlockByNumber: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") + eth_call: :eth_call, + debug_traceTransaction: :trace, + debug_traceBlockByNumber: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/explorer/config/prod/nethermind.exs b/apps/explorer/config/prod/nethermind.exs index d7cf3f3ecc0f..7232bad8c2c7 100644 --- a/apps/explorer/config/prod/nethermind.exs +++ b/apps/explorer/config/prod/nethermind.exs @@ -12,14 +12,22 @@ config :explorer, transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"), - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL"), + trace_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_TRACE_URLS", "ETHEREUM_JSONRPC_TRACE_URL"), + eth_call_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_ETH_CALL_URLS", "ETHEREUM_JSONRPC_ETH_CALL_URL"), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url(), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") + eth_call: :eth_call, + eth_getBalance: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/explorer/config/prod/rsk.exs b/apps/explorer/config/prod/rsk.exs index 1046aafb0dc3..4f63fa6d4dee 100644 --- a/apps/explorer/config/prod/rsk.exs +++ b/apps/explorer/config/prod/rsk.exs @@ -12,14 +12,22 @@ config :explorer, transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"), - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL"), + trace_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_TRACE_URLS", "ETHEREUM_JSONRPC_TRACE_URL"), + eth_call_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_ETH_CALL_URLS", "ETHEREUM_JSONRPC_ETH_CALL_URL"), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url(), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") + eth_call: :eth_call, + eth_getBalance: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/explorer/lib/explorer/chain/cache/optimism_finalization_period.ex b/apps/explorer/lib/explorer/chain/cache/optimism_finalization_period.ex index aa09cf2148ce..7ce8d834368f 100644 --- a/apps/explorer/lib/explorer/chain/cache/optimism_finalization_period.ex +++ b/apps/explorer/lib/explorer/chain/cache/optimism_finalization_period.ex @@ -42,7 +42,7 @@ defmodule Explorer.Chain.Cache.OptimismFinalizationPeriod do transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: optimism_l1_rpc, + urls: [optimism_l1_rpc], http_options: [ recv_timeout: :timer.minutes(10), timeout: :timer.minutes(10), diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 1fe83e8f1e3b..8aef4e7aec12 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -756,6 +756,13 @@ defmodule Explorer.ChainTest do insert(:pending_block_operation, block: block, block_number: block.number) + configuration = Application.get_env(:indexer, Indexer.Fetcher.InternalTransaction.Supervisor) + Application.put_env(:indexer, Indexer.Fetcher.InternalTransaction.Supervisor, disabled?: false) + + on_exit(fn -> + Application.put_env(:indexer, Indexer.Fetcher.InternalTransaction.Supervisor, configuration) + end) + refute Chain.finished_indexing_internal_transactions?() end end @@ -1066,9 +1073,12 @@ defmodule Explorer.ChainTest do setup do Supervisor.terminate_child(Explorer.Supervisor, PendingBlockOperationCache.child_id()) Supervisor.restart_child(Explorer.Supervisor, PendingBlockOperationCache.child_id()) + configuration = Application.get_env(:indexer, Indexer.Fetcher.InternalTransaction.Supervisor) + Application.put_env(:indexer, Indexer.Fetcher.InternalTransaction.Supervisor, disabled?: false) on_exit(fn -> Application.put_env(:indexer, :trace_first_block, 0) + Application.put_env(:indexer, Indexer.Fetcher.InternalTransaction.Supervisor, configuration) Supervisor.terminate_child(Explorer.Supervisor, PendingBlockOperationCache.child_id()) end) end diff --git a/apps/indexer/config/dev/besu.exs b/apps/indexer/config/dev/besu.exs index bcac1a5640ee..0efe225df124 100644 --- a/apps/indexer/config/dev/besu.exs +++ b/apps/indexer/config/dev/besu.exs @@ -18,16 +18,35 @@ config :indexer, else: EthereumJSONRPC.IPC, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545", - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL", "http://localhost:8545"), + trace_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_TRACE_URLS", + "ETHEREUM_JSONRPC_TRACE_URL", + "http://localhost:8545" + ), + eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_ETH_CALL_URL", + "http://localhost:8545" + ), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url("http://localhost:8545"), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_replayBlockTransactions: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545" + eth_call: :eth_call, + eth_getBalance: :trace, + trace_block: :trace, + trace_replayBlockTransactions: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/indexer/config/dev/erigon.exs b/apps/indexer/config/dev/erigon.exs index 8a0d81d49891..a819283d3ce9 100644 --- a/apps/indexer/config/dev/erigon.exs +++ b/apps/indexer/config/dev/erigon.exs @@ -18,16 +18,35 @@ config :indexer, else: EthereumJSONRPC.IPC, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545", - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL", "http://localhost:8545"), + trace_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_TRACE_URLS", + "ETHEREUM_JSONRPC_TRACE_URL", + "http://localhost:8545" + ), + eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_ETH_CALL_URL", + "http://localhost:8545" + ), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url("http://localhost:8545"), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_replayBlockTransactions: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545" + eth_call: :eth_call, + eth_getBalance: :trace, + trace_block: :trace, + trace_replayBlockTransactions: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/indexer/config/dev/filecoin.exs b/apps/indexer/config/dev/filecoin.exs index 4f3c58b53846..bed9eea600de 100644 --- a/apps/indexer/config/dev/filecoin.exs +++ b/apps/indexer/config/dev/filecoin.exs @@ -17,13 +17,36 @@ config :indexer, ), transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:1234/rpc/v1", - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_HTTP_URLS", + "ETHEREUM_JSONRPC_HTTP_URL", + "http://localhost:1234/rpc/v1" + ), + trace_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_TRACE_URLS", + "ETHEREUM_JSONRPC_TRACE_URL", + "http://localhost:1234/rpc/v1" + ), + eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_ETH_CALL_URL", + "http://localhost:1234/rpc/v1" + ), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url("http://localhost:1234/rpc/v1"), - trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:1234/rpc/v1" + eth_call: :eth_call, + trace_block: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/indexer/config/dev/ganache.exs b/apps/indexer/config/dev/ganache.exs index c2c40947da9e..d66ded540fd9 100644 --- a/apps/indexer/config/dev/ganache.exs +++ b/apps/indexer/config/dev/ganache.exs @@ -17,11 +17,23 @@ config :indexer, ), transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:7545", - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL", "http://localhost:7545"), + eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_ETH_CALL_URL", + "http://localhost:7545" + ), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url("http://localhost:7545") + eth_call: :eth_call ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/indexer/config/dev/geth.exs b/apps/indexer/config/dev/geth.exs index d12f24dc1a19..82fde60069cf 100644 --- a/apps/indexer/config/dev/geth.exs +++ b/apps/indexer/config/dev/geth.exs @@ -17,14 +17,33 @@ config :indexer, ), transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545", - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL", "http://localhost:8545"), + trace_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_TRACE_URLS", + "ETHEREUM_JSONRPC_TRACE_URL", + "http://localhost:8545" + ), + eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_ETH_CALL_URL", + "http://localhost:8545" + ), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url("http://localhost:8545"), - debug_traceTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - debug_traceBlockByNumber: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545" + eth_call: :eth_call, + debug_traceTransaction: :trace, + debug_traceBlockByNumber: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/indexer/config/dev/nethermind.exs b/apps/indexer/config/dev/nethermind.exs index 0e212345294a..0f59e2c9658b 100644 --- a/apps/indexer/config/dev/nethermind.exs +++ b/apps/indexer/config/dev/nethermind.exs @@ -18,16 +18,35 @@ config :indexer, else: EthereumJSONRPC.IPC, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545", - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL", "http://localhost:8545"), + trace_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_TRACE_URLS", + "ETHEREUM_JSONRPC_TRACE_URL", + "http://localhost:8545" + ), + eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_ETH_CALL_URL", + "http://localhost:8545" + ), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url("http://localhost:8545"), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_replayBlockTransactions: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545" + eth_call: :eth_call, + eth_getBalance: :trace, + trace_block: :trace, + trace_replayBlockTransactions: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/indexer/config/dev/rsk.exs b/apps/indexer/config/dev/rsk.exs index c31b0dfdf4b2..c4a0d8434815 100644 --- a/apps/indexer/config/dev/rsk.exs +++ b/apps/indexer/config/dev/rsk.exs @@ -19,16 +19,35 @@ config :indexer, ), transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545", - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL", "http://localhost:8545"), + trace_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_TRACE_URLS", + "ETHEREUM_JSONRPC_TRACE_URL", + "http://localhost:8545" + ), + eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_ETH_CALL_URL", + "http://localhost:8545" + ), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url("http://localhost:8545"), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_replayBlockTransactions: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545", - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545" + eth_call: :eth_call, + eth_getBalance: :trace, + trace_block: :trace, + trace_replayBlockTransactions: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/indexer/config/prod/besu.exs b/apps/indexer/config/prod/besu.exs index e87d210895c9..b78e3c611cda 100644 --- a/apps/indexer/config/prod/besu.exs +++ b/apps/indexer/config/prod/besu.exs @@ -17,16 +17,24 @@ config :indexer, ), transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"), - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL"), + trace_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_TRACE_URLS", "ETHEREUM_JSONRPC_TRACE_URL"), + eth_call_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_ETH_CALL_URLS", "ETHEREUM_JSONRPC_ETH_CALL_URL"), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url(), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_replayBlockTransactions: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") + eth_call: :eth_call, + eth_getBalance: :trace, + trace_block: :trace, + trace_replayBlockTransactions: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/indexer/config/prod/erigon.exs b/apps/indexer/config/prod/erigon.exs index 1574567cf7f4..0686e62d3ef8 100644 --- a/apps/indexer/config/prod/erigon.exs +++ b/apps/indexer/config/prod/erigon.exs @@ -17,16 +17,24 @@ config :indexer, ), transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"), - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL"), + trace_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_TRACE_URLS", "ETHEREUM_JSONRPC_TRACE_URL"), + eth_call_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_ETH_CALL_URLS", "ETHEREUM_JSONRPC_ETH_CALL_URL"), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url(), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_replayBlockTransactions: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") + eth_call: :eth_call, + eth_getBalance: :trace, + trace_block: :trace, + trace_replayBlockTransactions: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/indexer/config/prod/filecoin.exs b/apps/indexer/config/prod/filecoin.exs index 82aff88a0865..749c6a8fbbaa 100644 --- a/apps/indexer/config/prod/filecoin.exs +++ b/apps/indexer/config/prod/filecoin.exs @@ -17,13 +17,21 @@ config :indexer, ), transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"), - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL"), + trace_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_TRACE_URLS", "ETHEREUM_JSONRPC_TRACE_URL"), + eth_call_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_ETH_CALL_URLS", "ETHEREUM_JSONRPC_ETH_CALL_URL"), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url(), - trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") + eth_call: :eth_call, + trace_block: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/indexer/config/prod/ganache.exs b/apps/indexer/config/prod/ganache.exs index c2c40947da9e..d66ded540fd9 100644 --- a/apps/indexer/config/prod/ganache.exs +++ b/apps/indexer/config/prod/ganache.exs @@ -17,11 +17,23 @@ config :indexer, ), transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:7545", - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL", "http://localhost:7545"), + eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_ETH_CALL_URL", + "http://localhost:7545" + ), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url("http://localhost:7545") + eth_call: :eth_call ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/indexer/config/prod/geth.exs b/apps/indexer/config/prod/geth.exs index b690a4409462..22a2e84365f4 100644 --- a/apps/indexer/config/prod/geth.exs +++ b/apps/indexer/config/prod/geth.exs @@ -17,14 +17,22 @@ config :indexer, ), transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"), - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL"), + trace_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_TRACE_URLS", "ETHEREUM_JSONRPC_TRACE_URL"), + eth_call_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_ETH_CALL_URLS", "ETHEREUM_JSONRPC_ETH_CALL_URL"), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url(), - debug_traceTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - debug_traceBlockByNumber: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") + eth_call: :eth_call, + debug_traceTransaction: :trace, + debug_traceBlockByNumber: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/indexer/config/prod/nethermind.exs b/apps/indexer/config/prod/nethermind.exs index 3b751ff56fab..43f71bbae02a 100644 --- a/apps/indexer/config/prod/nethermind.exs +++ b/apps/indexer/config/prod/nethermind.exs @@ -17,16 +17,24 @@ config :indexer, ), transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"), - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL"), + trace_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_TRACE_URLS", "ETHEREUM_JSONRPC_TRACE_URL"), + eth_call_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_ETH_CALL_URLS", "ETHEREUM_JSONRPC_ETH_CALL_URL"), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url(), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_replayBlockTransactions: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") + eth_call: :eth_call, + eth_getBalance: :trace, + trace_block: :trace, + trace_replayBlockTransactions: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/indexer/config/prod/rsk.exs b/apps/indexer/config/prod/rsk.exs index 434344b39772..b710d1347416 100644 --- a/apps/indexer/config/prod/rsk.exs +++ b/apps/indexer/config/prod/rsk.exs @@ -19,16 +19,24 @@ config :indexer, ), transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"), - fallback_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), - fallback_trace_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), - fallback_eth_call_url: System.get_env("ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL"), + urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_HTTP_URLS", "ETHEREUM_JSONRPC_HTTP_URL"), + trace_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_TRACE_URLS", "ETHEREUM_JSONRPC_TRACE_URL"), + eth_call_urls: ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_ETH_CALL_URLS", "ETHEREUM_JSONRPC_ETH_CALL_URL"), + fallback_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS", "ETHEREUM_JSONRPC_FALLBACK_HTTP_URL"), + fallback_trace_urls: + ConfigHelper.parse_urls_list("ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS", "ETHEREUM_JSONRPC_FALLBACK_TRACE_URL"), + fallback_eth_call_urls: + ConfigHelper.parse_urls_list( + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS", + "ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL" + ), method_to_url: [ - eth_call: ConfigHelper.eth_call_url(), - eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_replayBlockTransactions: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"), - trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") + eth_call: :eth_call, + eth_getBalance: :trace, + trace_block: :trace, + trace_replayBlockTransactions: :trace, + trace_replayTransaction: :trace ], http_options: [recv_timeout: timeout, timeout: timeout, hackney: hackney_opts] ], diff --git a/apps/indexer/test/indexer/block/fetcher_test.exs b/apps/indexer/test/indexer/block/fetcher_test.exs index dc337b5e28c4..023affce464a 100644 --- a/apps/indexer/test/indexer/block/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/fetcher_test.exs @@ -54,6 +54,13 @@ defmodule Indexer.Block.FetcherTest do poll: false ) + configuration = Application.get_env(:indexer, Indexer.Fetcher.InternalTransaction.Supervisor) + Application.put_env(:indexer, Indexer.Fetcher.InternalTransaction.Supervisor, disabled?: false) + + on_exit(fn -> + Application.put_env(:indexer, Indexer.Fetcher.InternalTransaction.Supervisor, configuration) + end) + ContractCode.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) InternalTransaction.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) Token.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) diff --git a/config/config_helper.exs b/config/config_helper.exs index 850c3735d811..512291e8b04f 100644 --- a/config/config_helper.exs +++ b/config/config_helper.exs @@ -337,4 +337,13 @@ defmodule ConfigHelper do def eth_call_url(default \\ nil) do System.get_env("ETHEREUM_JSONRPC_ETH_CALL_URL") || System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || default end + + def parse_urls_list(urls_var, url_var, default_url \\ nil) do + default = default_url || System.get_env("ETHEREUM_JSONRPC_HTTP_URL") + + case parse_list_env_var(urls_var) do + [] -> [safe_get_env(url_var, default)] + urls -> urls + end + end end diff --git a/config/runtime.exs b/config/runtime.exs index 2ad540901979..b440a741fc22 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -176,10 +176,13 @@ config :ueberauth, Ueberauth, logout_url: "https://#{System.get_env("ACCOUNT_AUT ### Ethereum JSONRPC ### ######################## +trace_url_missing? = System.get_env("ETHEREUM_JSONRPC_TRACE_URL") in ["", nil] + config :ethereum_jsonrpc, rpc_transport: if(System.get_env("ETHEREUM_JSONRPC_TRANSPORT", "http") == "http", do: :http, else: :ipc), ipc_path: System.get_env("IPC_PATH"), - disable_archive_balances?: ConfigHelper.parse_bool_env_var("ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES"), + disable_archive_balances?: + trace_url_missing? or ConfigHelper.parse_bool_env_var("ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES"), archive_balances_window: ConfigHelper.parse_integer_env_var("ETHEREUM_JSONRPC_ARCHIVE_BALANCES_WINDOW", 200) config :ethereum_jsonrpc, EthereumJSONRPC.HTTP, @@ -763,7 +766,7 @@ config :indexer, Indexer.Fetcher.BlockReward.Supervisor, disabled?: ConfigHelper.parse_bool_env_var("INDEXER_DISABLE_BLOCK_REWARD_FETCHER") config :indexer, Indexer.Fetcher.InternalTransaction.Supervisor, - disabled?: ConfigHelper.parse_bool_env_var("INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER") + disabled?: trace_url_missing? or ConfigHelper.parse_bool_env_var("INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER") disable_coin_balances_fetcher? = ConfigHelper.parse_bool_env_var("INDEXER_DISABLE_ADDRESS_COIN_BALANCE_FETCHER") From 9fb366aea363006b616a544012e46cf29858ff9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 16 Nov 2024 14:47:33 +0700 Subject: [PATCH 313/363] chore(deps-dev): bump credo from 1.7.9 to 1.7.10 (#11175) Bumps [credo](https://github.com/rrrene/credo) from 1.7.9 to 1.7.10. - [Release notes](https://github.com/rrrene/credo/releases) - [Changelog](https://github.com/rrrene/credo/blob/master/CHANGELOG.md) - [Commits](https://github.com/rrrene/credo/compare/v1.7.9...v1.7.10) --- updated-dependencies: - dependency-name: credo dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 39297c43cd03..6b3f1ca2b41f 100644 --- a/mix.lock +++ b/mix.lock @@ -29,7 +29,7 @@ "cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"}, - "credo": {:hex, :credo, "1.7.9", "07bb31907746ae2b5e569197c9e16c0d75c8578a22f01bee63f212047efb2647", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f87c11c34ba579f7c5044f02b2a807e1ed2fa5fdbb24dc7eb4ad59c1904887f3"}, + "credo": {:hex, :credo, "1.7.10", "6e64fe59be8da5e30a1b96273b247b5cf1cc9e336b5fd66302a64b25749ad44d", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "71fbc9a6b8be21d993deca85bf151df023a3097b01e09a2809d460348561d8cd"}, "csv": {:hex, :csv, "2.5.0", "c47b5a5221bf2e56d6e8eb79e77884046d7fd516280dc7d9b674251e0ae46246", [:mix], [{:parallel_stream, "~> 1.0.4 or ~> 1.1.0", [hex: :parallel_stream, repo: "hexpm", optional: false]}], "hexpm", "e821f541487045c7591a1963eeb42afff0dfa99bdcdbeb3410795a2f59c77d34"}, "dataloader": {:hex, :dataloader, "2.0.1", "fa06b057b432b993203003fbff5ff040b7f6483a77e732b7dfc18f34ded2634f", [:mix], [{:ecto, ">= 3.4.3 and < 4.0.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:opentelemetry_process_propagator, "~> 0.2.1 or ~> 0.3", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "da7ff00890e1b14f7457419b9508605a8e66ae2cc2d08c5db6a9f344550efa11"}, "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, From ddac917e6136b93eb16ffd918f8539cd88d0aab7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 12:23:18 +0400 Subject: [PATCH 314/363] chore(deps): bump decimal from 2.1.1 to 2.2.0 (#11224) Bumps [decimal](https://github.com/ericmj/decimal) from 2.1.1 to 2.2.0. - [Release notes](https://github.com/ericmj/decimal/releases) - [Changelog](https://github.com/ericmj/decimal/blob/main/CHANGELOG.md) - [Commits](https://github.com/ericmj/decimal/compare/v2.1.1...v2.2.0) --- updated-dependencies: - dependency-name: decimal dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 6b3f1ca2b41f..fd4b9d10b213 100644 --- a/mix.lock +++ b/mix.lock @@ -33,7 +33,7 @@ "csv": {:hex, :csv, "2.5.0", "c47b5a5221bf2e56d6e8eb79e77884046d7fd516280dc7d9b674251e0ae46246", [:mix], [{:parallel_stream, "~> 1.0.4 or ~> 1.1.0", [hex: :parallel_stream, repo: "hexpm", optional: false]}], "hexpm", "e821f541487045c7591a1963eeb42afff0dfa99bdcdbeb3410795a2f59c77d34"}, "dataloader": {:hex, :dataloader, "2.0.1", "fa06b057b432b993203003fbff5ff040b7f6483a77e732b7dfc18f34ded2634f", [:mix], [{:ecto, ">= 3.4.3 and < 4.0.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:opentelemetry_process_propagator, "~> 0.2.1 or ~> 0.3", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "da7ff00890e1b14f7457419b9508605a8e66ae2cc2d08c5db6a9f344550efa11"}, "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, - "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, + "decimal": {:hex, :decimal, "2.2.0", "df3d06bb9517e302b1bd265c1e7f16cda51547ad9d99892049340841f3e15836", [:mix], [], "hexpm", "af8daf87384b51b7e611fb1a1f2c4d4876b65ef968fa8bd3adf44cff401c7f21"}, "decorator": {:hex, :decorator, "1.4.0", "a57ac32c823ea7e4e67f5af56412d12b33274661bb7640ec7fc882f8d23ac419", [:mix], [], "hexpm", "0a07cedd9083da875c7418dea95b78361197cf2bf3211d743f6f7ce39656597f"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, From df352b158e0e387e9109aee58d8c3df148f542b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 12:23:41 +0400 Subject: [PATCH 315/363] chore(deps): bump postgrex from 0.19.2 to 0.19.3 (#11222) Bumps [postgrex](https://github.com/elixir-ecto/postgrex) from 0.19.2 to 0.19.3. - [Release notes](https://github.com/elixir-ecto/postgrex/releases) - [Changelog](https://github.com/elixir-ecto/postgrex/blob/master/CHANGELOG.md) - [Commits](https://github.com/elixir-ecto/postgrex/commits) --- updated-dependencies: - dependency-name: postgrex dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index fd4b9d10b213..54d5ba4cce54 100644 --- a/mix.lock +++ b/mix.lock @@ -117,7 +117,7 @@ "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, - "postgrex": {:hex, :postgrex, "0.19.2", "34d6884a332c7bf1e367fc8b9a849d23b43f7da5c6e263def92784d03f9da468", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "618988886ab7ae8561ebed9a3c7469034bf6a88b8995785a3378746a4b9835ec"}, + "postgrex": {:hex, :postgrex, "0.19.3", "a0bda6e3bc75ec07fca5b0a89bffd242ca209a4822a9533e7d3e84ee80707e19", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "d31c28053655b78f47f948c85bb1cf86a9c1f8ead346ba1aa0d0df017fa05b61"}, "prometheus": {:hex, :prometheus, "4.11.0", "b95f8de8530f541bd95951e18e355a840003672e5eda4788c5fa6183406ba29a", [:mix, :rebar3], [{:quantile_estimator, "~> 0.2.1", [hex: :quantile_estimator, repo: "hexpm", optional: false]}], "hexpm", "719862351aabf4df7079b05dc085d2bbcbe3ac0ac3009e956671b1d5ab88247d"}, "prometheus_ecto": {:hex, :prometheus_ecto, "1.4.3", "3dd4da1812b8e0dbee81ea58bb3b62ed7588f2eae0c9e97e434c46807ff82311", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm", "8d66289f77f913b37eda81fd287340c17e61a447549deb28efc254532b2bed82"}, "prometheus_ex": {:git, "https://github.com/lanodan/prometheus.ex", "31f7fbe4b71b79ba27efc2a5085746c4011ceb8f", [branch: "fix/elixir-1.14"]}, From c8a28c31bad586c353073743fc025b7317964d07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 14:20:16 +0400 Subject: [PATCH 316/363] chore(deps): bump ex_cldr_numbers from 2.33.3 to 2.33.4 (#11223) Bumps [ex_cldr_numbers](https://github.com/elixir-cldr/cldr_numbers) from 2.33.3 to 2.33.4. - [Release notes](https://github.com/elixir-cldr/cldr_numbers/releases) - [Changelog](https://github.com/elixir-cldr/cldr_numbers/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-cldr/cldr_numbers/compare/v2.33.3...2.33.4) --- updated-dependencies: - dependency-name: ex_cldr_numbers dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mix.lock b/mix.lock index 54d5ba4cce54..28e5ddb353e7 100644 --- a/mix.lock +++ b/mix.lock @@ -14,7 +14,7 @@ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "bureaucrat": {:hex, :bureaucrat, "0.2.10", "b0de157dad540e40007b663b683f716ced21f85ff0591093aadb209ad0d967e1", [: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 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "bc7e5162b911c29c8ebefee87a2c16fbf13821a58f448a8fd024eb6c17fae15c"}, "bypass": {:hex, :bypass, "2.1.0", "909782781bf8e20ee86a9cabde36b259d44af8b9f38756173e8f5e2e1fabb9b1", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "d9b5df8fa5b7a6efa08384e9bbecfe4ce61c77d28a4282f79e02f1ef78d96b80"}, - "castore": {:hex, :castore, "1.0.9", "5cc77474afadf02c7c017823f460a17daa7908e991b0cc917febc90e466a375c", [:mix], [], "hexpm", "5ea956504f1ba6f2b4eb707061d8e17870de2bee95fb59d512872c2ef06925e7"}, + "castore": {:hex, :castore, "1.0.10", "43bbeeac820f16c89f79721af1b3e092399b3a1ecc8df1a472738fd853574911", [:mix], [], "hexpm", "1b0b7ea14d889d9ea21202c43a4fa015eb913021cb535e8ed91946f4b77a8848"}, "cbor": {:hex, :cbor, "1.0.1", "39511158e8ea5a57c1fcb9639aaa7efde67129678fee49ebbda780f6f24959b0", [:mix], [], "hexpm", "5431acbe7a7908f17f6a9cd43311002836a34a8ab01876918d8cfb709cd8b6a2"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, "cldr_utils": {:hex, :cldr_utils, "2.28.2", "f500667164a9043369071e4f9dcef31f88b8589b2e2c07a1eb9f9fa53cb1dce9", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "c506eb1a170ba7cdca59b304ba02a56795ed119856662f6b1a420af80ec42551"}, @@ -37,7 +37,7 @@ "decorator": {:hex, :decorator, "1.4.0", "a57ac32c823ea7e4e67f5af56412d12b33274661bb7640ec7fc882f8d23ac419", [:mix], [], "hexpm", "0a07cedd9083da875c7418dea95b78361197cf2bf3211d743f6f7ce39656597f"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, - "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"}, + "digital_token": {:hex, :digital_token, "1.0.0", "454a4444061943f7349a51ef74b7fb1ebd19e6a94f43ef711f7dae88c09347df", [:mix], [{:cldr_utils, "~> 2.17", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "8ed6f5a8c2fa7b07147b9963db506a1b4c7475d9afca6492136535b064c9e9e6"}, "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "ecto": {:hex, :ecto, "3.12.4", "267c94d9f2969e6acc4dd5e3e3af5b05cdae89a4d549925f3008b2b7eb0b93c3", [:mix], [{:decimal, "~> 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", "ef04e4101688a67d061e1b10d7bc1fbf00d1d13c17eef08b71d070ff9188f747"}, "ecto_sql": {:hex, :ecto_sql, "3.12.1", "c0d0d60e85d9ff4631f12bafa454bc392ce8b9ec83531a412c12a0d415a3a4d0", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 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", "aff5b958a899762c5f09028c847569f7dfb9cc9d63bdb8133bff8a5546de6bf5"}, @@ -45,9 +45,9 @@ "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_abi": {:hex, :ex_abi, "0.8.1", "451fa960ddc4dfbb350e13509f3dd64ca586b8484a77aad9f7d778161b5eab79", [:mix], [{:ex_keccak, "~> 0.7.5", [hex: :ex_keccak, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "abcf53d556c2948e5c1241340afd4a72cdf93ab6daef16fc200c16ca1183cdca"}, "ex_cldr": {:hex, :ex_cldr, "2.40.1", "c1fcb0cd9d2a70d28f4540a99f32127e7f1813e0db109d65ab29dea5337ae266", [:mix], [{:cldr_utils, "~> 2.28", [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", "509810702e8e81991851d9426ffe6b34b48b7b9baa12922e7b3fb8f6368606f3"}, - "ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.16.2", "670d96cc4fb18cfebd82488ed687742683be2d0725d66ec051578d4b13539aa8", [:mix], [{:ex_cldr, "~> 2.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "2ccfac2838f4df8c8e5424dbc68eb2f3ac9eeb45e10365050901f7ac7a914ce1"}, + "ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.16.3", "1ec6444b5d0c0aabba5a3bc321d73f1c9c751c6add92e7fb7775ccc071d96bd8", [:mix], [{:ex_cldr, "~> 2.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "4d1b5f8449fdf0ece6a2e5c7401ad8fcfde77ee6ea480bddc16e266dfa2b570c"}, "ex_cldr_lists": {:hex, :ex_cldr_lists, "2.11.1", "ad18f861d7c5ca82aac6d173469c6a2339645c96790172ab0aa255b64fb7303b", [: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", "00161c04510ccb3f18b19a6b8562e50c21f1e9c15b8ff4c934bea5aad0b4ade2"}, - "ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.33.3", "9fedcf279a17d19abdf8872738472326e82378d90ec2dd9756a0c84558c86b36", [: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.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, "~> 2.16", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "4a0d90d06710c1499528d5f536c539379a73a68d4679c55375198a798d138442"}, + "ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.33.4", "ecb06f40fc63f484a53d4ea80e1bdd6860ec44d3032f2b10b17340d34c0a13d5", [: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.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, "~> 2.16", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "d15b7e217e9e60c328e73045e51dc67d7ac5d2997247b833efab2c69b2ed06f5"}, "ex_cldr_units": {:hex, :ex_cldr_units, "3.17.2", "b0483d5c61c6c8649aafdcafc7372dd71a7a30f52dd4c9b072576467bf721454", [:mix], [{:cldr_utils, "~> 2.25", [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.33.0", [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", "457d76c6e3b548bd7aba3c7b5d157213be2842d1162c2283abf81d9e2f1e1fc7"}, "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, "ex_json_schema": {:hex, :ex_json_schema, "0.10.2", "7c4b8c1481fdeb1741e2ce66223976edfb9bccebc8014f6aec35d4efe964fb71", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "37f43be60f8407659d4d0155a7e45e7f406dab1f827051d3d35858a709baf6a6"}, @@ -58,7 +58,7 @@ "ex_utils": {:hex, :ex_utils, "0.1.7", "2c133e0bcdc49a858cf8dacf893308ebc05bc5fba501dc3d2935e65365ec0bf3", [:mix], [], "hexpm", "66d4fe75285948f2d1e69c2a5ddd651c398c813574f8d36a9eef11dc20356ef6"}, "exactor": {:hex, :exactor, "2.2.4", "5efb4ddeb2c48d9a1d7c9b465a6fffdd82300eb9618ece5d34c3334d5d7245b1", [:mix], [], "hexpm", "1222419f706e01bfa1095aec9acf6421367dcfab798a6f67c54cf784733cd6b5"}, "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm", "32e95820a97cffea67830e91514a2ad53b888850442d6d395f53a1ac60c82e07"}, - "expo": {:hex, :expo, "1.0.1", "f9e2f984f5b8d195815d52d0ba264798c12c8d2f2606f76fa4c60e8ebe39474d", [:mix], [], "hexpm", "f250b33274e3e56513644858c116f255d35c767c2b8e96a512fe7839ef9306a1"}, + "expo": {:hex, :expo, "1.1.0", "f7b9ed7fb5745ebe1eeedf3d6f29226c5dd52897ac67c0f8af62a07e661e5c75", [:mix], [], "hexpm", "fbadf93f4700fb44c331362177bdca9eeb8097e8b0ef525c9cc501cb9917c960"}, "exvcr": {:hex, :exvcr, "0.15.2", "2216c8605b5c3e300160c2a5bd896b4928fa51fc3fb3420d3e792ad833ac89ba", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:finch, "~> 0.16", [hex: :finch, repo: "hexpm", optional: true]}, {:httpoison, "~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "2bd4125889bd3953d7fbb7b388c34190c31e292f12896da56ecf0743d40439ed"}, "file_info": {:hex, :file_info, "0.0.4", "2e0e77f211e833f38ead22cb29ce53761d457d80b3ffe0ffe0eb93880b0963b2", [:mix], [{:mimetype_parser, "~> 0.1.2", [hex: :mimetype_parser, repo: "hexpm", optional: false]}], "hexpm", "50e7ad01c2c8b9339010675fe4dc4a113b8d6ca7eddce24d1d74fd0e762781a5"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, @@ -66,7 +66,7 @@ "floki": {:hex, :floki, "0.36.3", "1102f93b16a55bc5383b85ae3ec470f82dee056eaeff9195e8afdf0ef2a43c30", [:mix], [], "hexpm", "fe0158bff509e407735f6d40b3ee0d7deb47f3f3ee7c6c182ad28599f9f6b27a"}, "flow": {:hex, :flow, "1.2.4", "1dd58918287eb286656008777cb32714b5123d3855956f29aa141ebae456922d", [:mix], [{:gen_stage, "~> 1.0", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm", "874adde96368e71870f3510b91e35bc31652291858c86c0e75359cbdd35eb211"}, "gen_stage": {:hex, :gen_stage, "1.2.1", "19d8b5e9a5996d813b8245338a28246307fd8b9c99d1237de199d21efc4c76a1", [:mix], [], "hexpm", "83e8be657fa05b992ffa6ac1e3af6d57aa50aace8f691fcf696ff02f8335b001"}, - "gettext": {:hex, :gettext, "0.26.1", "38e14ea5dcf962d1fc9f361b63ea07c0ce715a8ef1f9e82d3dfb8e67e0416715", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "01ce56f188b9dc28780a52783d6529ad2bc7124f9744e571e1ee4ea88bf08734"}, + "gettext": {:hex, :gettext, "0.26.2", "5978aa7b21fada6deabf1f6341ddba50bc69c999e812211903b169799208f2a8", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "aa978504bcf76511efdc22d580ba08e2279caab1066b76bb9aa81c4a1e0a32a5"}, "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~>2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, "hammer": {:hex, :hammer, "6.2.1", "5ae9c33e3dceaeb42de0db46bf505bd9c35f259c8defb03390cd7556fea67ee2", [:mix], [{:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm", "b9476d0c13883d2dc0cc72e786bac6ac28911fba7cc2e04b70ce6a6d9c4b2bdc"}, "hammer_backend_redis": {:hex, :hammer_backend_redis, "6.1.2", "eb296bb4924928e24135308b2afc189201fd09411c870c6bbadea444a49b2f2c", [:mix], [{:hammer, "~> 6.0", [hex: :hammer, repo: "hexpm", optional: false]}, {:redix, "~> 1.1", [hex: :redix, repo: "hexpm", optional: false]}], "hexpm", "217ea066278910543a5e9b577d5bf2425419446b94fe76bdd9f255f39feec9fa"}, From 50da37f33601b1b8192570497a594f6d0bebb17f Mon Sep 17 00:00:00 2001 From: kustrun <9192608+kustrun@users.noreply.github.com> Date: Wed, 20 Nov 2024 11:43:20 +0100 Subject: [PATCH 317/363] fix(nginx-conf): Redirect `/api-docs` to frontend. (#11202) Previously, when navigating to `/api-docs`, the nginx configuration mistakenly routed the request to the backend because it matched the `api` regex pattern. We've now refined the regex to match requests to `/api` only if not immediately followed by `-docs`. Examples: - **Matches**: `"api/v1"`, `"api-test"`, `"apixyz"`, `"apiDocs"` (anything without `"-docs"` directly after `"api"`) - **Doesn't Match**: `"api-docs"`, `"api-docs-v2"` (where `"api"` is directly followed by `"-docs"`) --- docker-compose/proxy/default.conf.template | 2 +- docker-compose/proxy/explorer.conf.template | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose/proxy/default.conf.template b/docker-compose/proxy/default.conf.template index dbd5180d10b1..cebe8a837b3d 100644 --- a/docker-compose/proxy/default.conf.template +++ b/docker-compose/proxy/default.conf.template @@ -9,7 +9,7 @@ server { server_name localhost; proxy_http_version 1.1; - location ~ ^/(api|socket|sitemap.xml|auth/auth0|auth/auth0/callback|auth/logout) { + location ~ ^/(api(?!-docs$)|socket|sitemap.xml|auth/auth0|auth/auth0/callback|auth/logout) { proxy_pass ${BACK_PROXY_PASS}; proxy_http_version 1.1; proxy_set_header Host "$host"; diff --git a/docker-compose/proxy/explorer.conf.template b/docker-compose/proxy/explorer.conf.template index f63e979a0d8d..7cc794ede6a6 100644 --- a/docker-compose/proxy/explorer.conf.template +++ b/docker-compose/proxy/explorer.conf.template @@ -9,7 +9,7 @@ server { server_name localhost; proxy_http_version 1.1; - location ~ ^/(api|socket|sitemap.xml|auth/auth0|auth/auth0/callback|auth/logout) { + location ~ ^/(api(?!-docs$)|socket|sitemap.xml|auth/auth0|auth/auth0/callback|auth/logout) { proxy_pass ${BACK_PROXY_PASS}; proxy_http_version 1.1; proxy_set_header Host "$host"; From 4100e959a51998950beb00e276d712682bdb7b67 Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:23:42 +0300 Subject: [PATCH 318/363] chore: invalid association `token_transfers` (#11204) --- apps/explorer/lib/explorer/chain/token_transfer.ex | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index e5b5efe51a36..74e0287c5c1c 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -136,10 +136,10 @@ defmodule Explorer.Chain.TokenTransfer do require Explorer.Chain.TokenTransfer.Schema import Ecto.Changeset - import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] alias Explorer.Chain alias Explorer.Chain.{DenormalizationHelper, Hash, Log, TokenTransfer} + alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.{PagingOptions, Repo} @default_paging_options %PagingOptions{page_size: 50} @@ -240,8 +240,8 @@ defmodule Explorer.Chain.TokenTransfer do DenormalizationHelper.extend_transaction_preload([ :transaction, :token, - [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]], - [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] + [from_address: [:scam_badge, :names, :smart_contract, Implementation.proxy_implementations_association()]], + [to_address: [:scam_badge, :names, :smart_contract, Implementation.proxy_implementations_association()]] ]) only_consensus_transfers_query() @@ -267,8 +267,8 @@ defmodule Explorer.Chain.TokenTransfer do DenormalizationHelper.extend_transaction_preload([ :transaction, :token, - [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]], - [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] + [from_address: [:scam_badge, :names, :smart_contract, Implementation.proxy_implementations_association()]], + [to_address: [:scam_badge, :names, :smart_contract, Implementation.proxy_implementations_association()]] ]) only_consensus_transfers_query() @@ -300,8 +300,8 @@ defmodule Explorer.Chain.TokenTransfer do DenormalizationHelper.extend_transaction_preload([ :transaction, :token, - [from_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]], - [to_address: [:scam_badge, :names, :smart_contract, proxy_implementations_association()]] + [from_address: [:scam_badge, :names, :smart_contract, Implementation.proxy_implementations_association()]], + [to_address: [:scam_badge, :names, :smart_contract, Implementation.proxy_implementations_association()]] ]) only_consensus_transfers_query() From b7458900c755eca48478a877999ec59883e49ef3 Mon Sep 17 00:00:00 2001 From: varasev <33550681+varasev@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:51:37 +0400 Subject: [PATCH 319/363] chore: OP modules improvements (#11073) * Add new envs for OP stack * Fix updating logs filter in OP Deposits fetcher * Add fallback envs for OP * Add socket notifier for OP batches * Update common-blockscout.env * Set infinity timeout for select db queries * Support transactions without `to` field * Add some docs * mix format * Restore OP fetcher after reorg and restart * Add specs and docs * Fix spelling * Refactoring and hardcode INDEXER_BEACON_BLOB_FETCHER_* envs * mix format * Update spelling * Small fix for Indexer.Fetcher.Optimism.Deposit * Rewrite Indexer.Fetcher.Optimism.Deposit * Update common-blockscout.env * Add todo comments for deprecated socket topic * Fix for the new websocket channel * Add todo comment --------- Co-authored-by: POA <33550681+poa@users.noreply.github.com> --- .../channels/optimism_channel.ex | 20 + .../channels/optimism_deposit_channel.ex | 22 - .../block_scout_web/channels/user_socket.ex | 1 - .../channels/user_socket_v2.ex | 3 +- .../lib/block_scout_web/notifier.ex | 16 +- .../lib/block_scout_web/notifiers/optimism.ex | 40 ++ .../block_scout_web/realtime_event_handler.ex | 7 +- .../views/api/v2/optimism_view.ex | 62 +- .../lib/ethereum_jsonrpc/transaction.ex | 175 +++--- .../lib/explorer/chain/events/publisher.ex | 5 +- .../lib/explorer/chain/events/subscriber.ex | 5 +- .../lib/explorer/chain/optimism/deposit.ex | 18 +- .../explorer/chain/optimism/dispute_game.ex | 2 +- .../explorer/chain/optimism/frame_sequence.ex | 101 +++- .../chain/optimism/frame_sequence_blob.ex | 2 +- .../explorer/chain/optimism/output_root.ex | 34 +- .../chain/optimism/transaction_batch.ex | 4 +- .../lib/explorer/chain/optimism/withdrawal.ex | 36 +- .../chain/optimism/withdrawal_event.ex | 32 + apps/indexer/lib/indexer/fetcher/optimism.ex | 123 +++- .../lib/indexer/fetcher/optimism/deposit.ex | 559 +++++------------- .../indexer/fetcher/optimism/output_root.ex | 42 +- .../fetcher/optimism/transaction_batch.ex | 228 +++++-- .../indexer/fetcher/optimism/withdrawal.ex | 105 ++-- .../fetcher/optimism/withdrawal_event.ex | 44 +- .../fetcher/rollup_l1_reorg_monitor.ex | 1 + apps/indexer/lib/indexer/helper.ex | 15 + config/runtime.exs | 12 +- cspell.json | 1 + docker-compose/envs/common-blockscout.env | 8 +- 30 files changed, 1020 insertions(+), 703 deletions(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/channels/optimism_channel.ex delete mode 100644 apps/block_scout_web/lib/block_scout_web/channels/optimism_deposit_channel.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/notifiers/optimism.ex diff --git a/apps/block_scout_web/lib/block_scout_web/channels/optimism_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/optimism_channel.ex new file mode 100644 index 000000000000..6fa69e4ebb3b --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/channels/optimism_channel.ex @@ -0,0 +1,20 @@ +defmodule BlockScoutWeb.OptimismChannel do + @moduledoc """ + Establishes pub/sub channel for live updates of OP related events. + """ + use BlockScoutWeb, :channel + + def join("optimism:new_batch", _params, socket) do + {:ok, %{}, socket} + end + + def join("optimism:new_deposits", _params, socket) do + {:ok, %{}, socket} + end + + # todo: the `optimism_deposits:new_deposits` socket topic is for backward compatibility + # for the frontend and should be removed after the frontend starts to use the `optimism:new_deposits` + def join("optimism_deposits:new_deposits", _params, socket) do + {:ok, %{}, socket} + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/channels/optimism_deposit_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/optimism_deposit_channel.ex deleted file mode 100644 index 3f2c513f9b1c..000000000000 --- a/apps/block_scout_web/lib/block_scout_web/channels/optimism_deposit_channel.ex +++ /dev/null @@ -1,22 +0,0 @@ -defmodule BlockScoutWeb.OptimismDepositChannel do - @moduledoc """ - Establishes pub/sub channel for live updates of Optimism deposit events. - """ - use BlockScoutWeb, :channel - - intercept(["deposits"]) - - def join("optimism_deposits:new_deposits", _params, socket) do - {:ok, %{}, socket} - end - - def handle_out( - "deposits", - %{deposits: deposits}, - %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket - ) do - push(socket, "deposits", %{deposits: Enum.count(deposits)}) - - {:noreply, socket} - end -end diff --git a/apps/block_scout_web/lib/block_scout_web/channels/user_socket.ex b/apps/block_scout_web/lib/block_scout_web/channels/user_socket.ex index 5d51597e359e..3badb9d1b783 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/user_socket.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/user_socket.ex @@ -5,7 +5,6 @@ defmodule BlockScoutWeb.UserSocket do channel("addresses:*", BlockScoutWeb.AddressChannel) channel("blocks:*", BlockScoutWeb.BlockChannel) channel("exchange_rate:*", BlockScoutWeb.ExchangeRateChannel) - channel("optimism_deposits:*", BlockScoutWeb.OptimismDepositChannel) channel("rewards:*", BlockScoutWeb.RewardChannel) channel("transactions:*", BlockScoutWeb.TransactionChannel) channel("tokens:*", BlockScoutWeb.TokenChannel) diff --git a/apps/block_scout_web/lib/block_scout_web/channels/user_socket_v2.ex b/apps/block_scout_web/lib/block_scout_web/channels/user_socket_v2.ex index 57cdf442c95a..696b3b0de4a2 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/user_socket_v2.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/user_socket_v2.ex @@ -7,7 +7,6 @@ defmodule BlockScoutWeb.UserSocketV2 do channel("addresses:*", BlockScoutWeb.AddressChannel) channel("blocks:*", BlockScoutWeb.BlockChannel) channel("exchange_rate:*", BlockScoutWeb.ExchangeRateChannel) - channel("optimism_deposits:*", BlockScoutWeb.OptimismDepositChannel) channel("rewards:*", BlockScoutWeb.RewardChannel) channel("transactions:*", BlockScoutWeb.TransactionChannel) channel("tokens:*", BlockScoutWeb.TokenChannel) @@ -16,6 +15,8 @@ defmodule BlockScoutWeb.UserSocketV2 do case Application.compile_env(:explorer, :chain_type) do :arbitrum -> channel("arbitrum:*", BlockScoutWeb.ArbitrumChannel) + # todo: change `optimism*"` to `optimism:*` after the deprecated `optimism_deposits:new_deposits` topic is removed + :optimism -> channel("optimism*", BlockScoutWeb.OptimismChannel) _ -> nil end diff --git a/apps/block_scout_web/lib/block_scout_web/notifier.ex b/apps/block_scout_web/lib/block_scout_web/notifier.ex index dead3bb875b5..9ed9e000273d 100644 --- a/apps/block_scout_web/lib/block_scout_web/notifier.ex +++ b/apps/block_scout_web/lib/block_scout_web/notifier.ex @@ -35,6 +35,9 @@ defmodule BlockScoutWeb.Notifier do :arbitrum -> @chain_type_specific_events ~w(new_arbitrum_batches new_messages_to_arbitrum_amount)a + :optimism -> + @chain_type_specific_events ~w(new_optimism_batches new_optimism_deposits)a + _ -> nil end @@ -275,10 +278,6 @@ defmodule BlockScoutWeb.Notifier do Endpoint.broadcast("addresses:#{to_string(address_hash)}", "changed_bytecode", %{}) end - def handle_event({:chain_event, :optimism_deposits, :realtime, deposits}) do - broadcast_optimism_deposits(deposits, "optimism_deposits:new_deposits", "deposits") - end - def handle_event({:chain_event, :smart_contract_was_verified = event, :on_demand, [address_hash]}) do broadcast_automatic_verification_events(event, address_hash) end @@ -303,6 +302,11 @@ defmodule BlockScoutWeb.Notifier do # credo:disable-for-next-line Credo.Check.Design.AliasUsage do: BlockScoutWeb.Notifiers.Arbitrum.handle_event(event) + :optimism -> + def handle_event({:chain_event, topic, _, _} = event) when topic in @chain_type_specific_events, + # credo:disable-for-next-line Credo.Check.Design.AliasUsage + do: BlockScoutWeb.Notifiers.Optimism.handle_event(event) + _ -> nil end @@ -457,10 +461,6 @@ defmodule BlockScoutWeb.Notifier do end end - defp broadcast_optimism_deposits(deposits, deposit_channel, event) do - Endpoint.broadcast(deposit_channel, event, %{deposits: deposits}) - end - defp broadcast_transactions_websocket_v2(transactions) do pending_transactions = Enum.filter(transactions, fn diff --git a/apps/block_scout_web/lib/block_scout_web/notifiers/optimism.ex b/apps/block_scout_web/lib/block_scout_web/notifiers/optimism.ex new file mode 100644 index 000000000000..7d9151c9eae7 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/notifiers/optimism.ex @@ -0,0 +1,40 @@ +defmodule BlockScoutWeb.Notifiers.Optimism do + @moduledoc """ + Module to handle and broadcast OP related events. + """ + + alias BlockScoutWeb.Endpoint + + require Logger + + def handle_event({:chain_event, :new_optimism_batches, :realtime, batches}) do + batches + |> Enum.sort_by(& &1.internal_id, :asc) + |> Enum.each(fn batch -> + Endpoint.broadcast("optimism:new_batch", "new_optimism_batch", %{ + batch: batch + }) + end) + end + + def handle_event({:chain_event, :new_optimism_deposits, :realtime, deposits}) do + deposits_count = Enum.count(deposits) + + if deposits_count > 0 do + Endpoint.broadcast("optimism:new_deposits", "new_optimism_deposits", %{ + deposits: deposits_count + }) + + # todo: the `optimism_deposits:new_deposits` socket topic is for backward compatibility + # for the frontend and should be removed after the frontend starts to use the `optimism:new_deposits` + Endpoint.broadcast("optimism_deposits:new_deposits", "deposits", %{ + deposits: deposits_count + }) + end + end + + def handle_event(event) do + Logger.warning("Unknown broadcasted event #{inspect(event)}.") + nil + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex b/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex index f77ae623f836..c0fb18915f34 100644 --- a/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex +++ b/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex @@ -19,6 +19,12 @@ defmodule BlockScoutWeb.RealtimeEventHandler do Subscriber.to(:new_messages_to_arbitrum_amount, :realtime) end + :optimism -> + def chain_type_specific_subscriptions do + Subscriber.to(:new_optimism_batches, :realtime) + Subscriber.to(:new_optimism_deposits, :realtime) + end + _ -> def chain_type_specific_subscriptions do nil @@ -32,7 +38,6 @@ defmodule BlockScoutWeb.RealtimeEventHandler do Subscriber.to(:block_rewards, :realtime) Subscriber.to(:internal_transactions, :realtime) Subscriber.to(:internal_transactions, :on_demand) - Subscriber.to(:optimism_deposits, :realtime) Subscriber.to(:token_transfers, :realtime) Subscriber.to(:addresses, :on_demand) Subscriber.to(:address_coin_balances, :on_demand) diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex index f0217f268cad..2c68d4425abe 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/optimism_view.ex @@ -7,7 +7,7 @@ defmodule BlockScoutWeb.API.V2.OptimismView do alias Explorer.{Chain, Repo} alias Explorer.Helper, as: ExplorerHelper alias Explorer.Chain.{Block, Transaction} - alias Explorer.Chain.Optimism.{FrameSequenceBlob, Withdrawal} + alias Explorer.Chain.Optimism.{FrameSequence, FrameSequenceBlob, Withdrawal} @doc """ Function to render GET requests to `/api/v2/optimism/txn-batches` endpoint. @@ -66,19 +66,7 @@ defmodule BlockScoutWeb.API.V2.OptimismView do |> Enum.map(fn batch -> from..to//_ = batch.l2_block_range - %{ - "internal_id" => batch.id, - "l1_timestamp" => batch.l1_timestamp, - "l2_block_start" => from, - "l2_block_end" => to, - "transaction_count" => batch.transaction_count, - # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_count` property - "tx_count" => batch.transaction_count, - "l1_transaction_hashes" => batch.l1_transaction_hashes, - # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l1_transaction_hashes` property - "l1_tx_hashes" => batch.l1_transaction_hashes, - "batch_data_container" => batch.batch_data_container - } + render_base_info_for_batch(batch.id, from, to, batch.transaction_count, batch) end) %{ @@ -272,6 +260,52 @@ defmodule BlockScoutWeb.API.V2.OptimismView do count end + # Transforms an L1 batch into a map format for HTTP response. + # + # This function processes an Optimism L1 batch and converts it into a map that + # includes basic batch information. + # + # ## Parameters + # - `internal_id`: The internal ID of the batch. + # - `l2_block_number_from`: Start L2 block number of the batch block range. + # - `l2_block_number_to`: End L2 block number of the batch block range. + # - `transaction_count`: The L2 transaction count included into the blocks of the range. + # - `batch`: Either an `Explorer.Chain.Optimism.FrameSequence` entry or a map with + # the corresponding fields. + # + # ## Returns + # - A map with detailed information about the batch formatted for use in JSON HTTP responses. + @spec render_base_info_for_batch( + non_neg_integer(), + non_neg_integer(), + non_neg_integer(), + non_neg_integer(), + FrameSequence.t() + | %{:l1_timestamp => DateTime.t(), :l1_transaction_hashes => list(), optional(any()) => any()} + ) :: %{ + :internal_id => non_neg_integer(), + :l1_timestamp => DateTime.t(), + :l2_block_start => non_neg_integer(), + :l2_block_end => non_neg_integer(), + :transaction_count => non_neg_integer(), + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_count` property + :tx_count => non_neg_integer(), + :l1_transaction_hashes => list(), + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l1_transaction_hashes` property + :l1_tx_hashes => list(), + :batch_data_container => :in_blob4844 | :in_celestia | :in_calldata | nil + } + defp render_base_info_for_batch(internal_id, l2_block_number_from, l2_block_number_to, transaction_count, batch) do + FrameSequence.prepare_base_info_for_batch( + internal_id, + l2_block_number_from, + l2_block_number_to, + transaction_count, + batch.batch_data_container, + batch + ) + end + @doc """ Extends the json output for a block using Optimism frame sequence (bound with the provided L2 block) - adds info about L1 batch to the output. diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex index 8611bdb696c6..a584ae1d5043 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex @@ -307,24 +307,31 @@ defmodule EthereumJSONRPC.Transaction do |> chain_type_fields(elixir) end - def do_elixir_to_params( - %{ - "blockHash" => block_hash, - "blockNumber" => block_number, - "from" => from_address_hash, - "gas" => gas, - "gasPrice" => gas_price, - "hash" => hash, - "input" => input, - "nonce" => nonce, - "to" => to_address_hash, - "transactionIndex" => index, - "value" => value, - "type" => type, - "maxPriorityFeePerGas" => max_priority_fee_per_gas, - "maxFeePerGas" => max_fee_per_gas - } = transaction - ) do + # Converts a map of the transaction parameters to the map with the corresponding atom parameters. + # + # ## Parameters + # - `transaction`: The input map. + # + # ## Returns + # - The resulting map. + @spec do_elixir_to_params(%{String.t() => any()}) :: %{atom() => any()} + defp do_elixir_to_params( + %{ + "blockHash" => block_hash, + "blockNumber" => block_number, + "from" => from_address_hash, + "gas" => gas, + "gasPrice" => gas_price, + "hash" => hash, + "input" => input, + "nonce" => nonce, + "transactionIndex" => index, + "value" => value, + "type" => type, + "maxPriorityFeePerGas" => max_priority_fee_per_gas, + "maxFeePerGas" => max_fee_per_gas + } = transaction + ) do result = %{ block_hash: block_hash, block_number: block_number, @@ -335,7 +342,7 @@ defmodule EthereumJSONRPC.Transaction do index: index, input: input, nonce: nonce, - to_address_hash: to_address_hash, + to_address_hash: Map.get(transaction, "to"), value: value, transaction_index: index, type: type, @@ -355,23 +362,22 @@ defmodule EthereumJSONRPC.Transaction do # txpool_content method on Erigon node returns transaction data # without gas price - def do_elixir_to_params( - %{ - "blockHash" => block_hash, - "blockNumber" => block_number, - "from" => from_address_hash, - "gas" => gas, - "hash" => hash, - "input" => input, - "nonce" => nonce, - "to" => to_address_hash, - "transactionIndex" => index, - "value" => value, - "type" => type, - "maxPriorityFeePerGas" => max_priority_fee_per_gas, - "maxFeePerGas" => max_fee_per_gas - } = transaction - ) do + defp do_elixir_to_params( + %{ + "blockHash" => block_hash, + "blockNumber" => block_number, + "from" => from_address_hash, + "gas" => gas, + "hash" => hash, + "input" => input, + "nonce" => nonce, + "transactionIndex" => index, + "value" => value, + "type" => type, + "maxPriorityFeePerGas" => max_priority_fee_per_gas, + "maxFeePerGas" => max_fee_per_gas + } = transaction + ) do result = %{ block_hash: block_hash, block_number: block_number, @@ -382,7 +388,7 @@ defmodule EthereumJSONRPC.Transaction do index: index, input: input, nonce: nonce, - to_address_hash: to_address_hash, + to_address_hash: Map.get(transaction, "to"), value: value, transaction_index: index, type: type, @@ -401,22 +407,21 @@ defmodule EthereumJSONRPC.Transaction do end # for legacy transactions without maxPriorityFeePerGas and maxFeePerGas - def do_elixir_to_params( - %{ - "blockHash" => block_hash, - "blockNumber" => block_number, - "from" => from_address_hash, - "gas" => gas, - "gasPrice" => gas_price, - "hash" => hash, - "input" => input, - "nonce" => nonce, - "to" => to_address_hash, - "transactionIndex" => index, - "value" => value, - "type" => type - } = transaction - ) do + defp do_elixir_to_params( + %{ + "blockHash" => block_hash, + "blockNumber" => block_number, + "from" => from_address_hash, + "gas" => gas, + "gasPrice" => gas_price, + "hash" => hash, + "input" => input, + "nonce" => nonce, + "transactionIndex" => index, + "value" => value, + "type" => type + } = transaction + ) do result = %{ block_hash: block_hash, block_number: block_number, @@ -427,7 +432,7 @@ defmodule EthereumJSONRPC.Transaction do index: index, input: input, nonce: nonce, - to_address_hash: to_address_hash, + to_address_hash: Map.get(transaction, "to"), value: value, transaction_index: index, type: type @@ -443,21 +448,20 @@ defmodule EthereumJSONRPC.Transaction do end # for legacy transactions without type, maxPriorityFeePerGas and maxFeePerGas - def do_elixir_to_params( - %{ - "blockHash" => block_hash, - "blockNumber" => block_number, - "from" => from_address_hash, - "gas" => gas, - "gasPrice" => gas_price, - "hash" => hash, - "input" => input, - "nonce" => nonce, - "to" => to_address_hash, - "transactionIndex" => index, - "value" => value - } = transaction - ) do + defp do_elixir_to_params( + %{ + "blockHash" => block_hash, + "blockNumber" => block_number, + "from" => from_address_hash, + "gas" => gas, + "gasPrice" => gas_price, + "hash" => hash, + "input" => input, + "nonce" => nonce, + "transactionIndex" => index, + "value" => value + } = transaction + ) do result = %{ block_hash: block_hash, block_number: block_number, @@ -468,7 +472,7 @@ defmodule EthereumJSONRPC.Transaction do index: index, input: input, nonce: nonce, - to_address_hash: to_address_hash, + to_address_hash: Map.get(transaction, "to"), value: value, transaction_index: index } @@ -483,21 +487,20 @@ defmodule EthereumJSONRPC.Transaction do end # for transactions without gasPrice, maxPriorityFeePerGas and maxFeePerGas - def do_elixir_to_params( - %{ - "blockHash" => block_hash, - "blockNumber" => block_number, - "from" => from_address_hash, - "gas" => gas, - "hash" => hash, - "input" => input, - "nonce" => nonce, - "to" => to_address_hash, - "transactionIndex" => index, - "type" => type, - "value" => value - } = transaction - ) do + defp do_elixir_to_params( + %{ + "blockHash" => block_hash, + "blockNumber" => block_number, + "from" => from_address_hash, + "gas" => gas, + "hash" => hash, + "input" => input, + "nonce" => nonce, + "transactionIndex" => index, + "type" => type, + "value" => value + } = transaction + ) do result = %{ block_hash: block_hash, block_number: block_number, @@ -508,7 +511,7 @@ defmodule EthereumJSONRPC.Transaction do index: index, input: input, nonce: nonce, - to_address_hash: to_address_hash, + to_address_hash: Map.get(transaction, "to"), value: value, transaction_index: index, type: type diff --git a/apps/explorer/lib/explorer/chain/events/publisher.ex b/apps/explorer/lib/explorer/chain/events/publisher.ex index adea72f104f0..a0048248cf73 100644 --- a/apps/explorer/lib/explorer/chain/events/publisher.ex +++ b/apps/explorer/lib/explorer/chain/events/publisher.ex @@ -5,7 +5,7 @@ defmodule Explorer.Chain.Events.Publisher do @common_allowed_events ~w(addresses address_coin_balances address_token_balances address_current_token_balances blocks block_rewards internal_transactions - last_block_number optimism_deposits token_transfers transactions contract_verification_result + last_block_number token_transfers transactions contract_verification_result token_total_supply changed_bytecode fetched_bytecode fetched_token_instance_metadata smart_contract_was_verified zkevm_confirmed_batches eth_bytecode_db_lookup_started smart_contract_was_not_verified)a @@ -14,6 +14,9 @@ defmodule Explorer.Chain.Events.Publisher do :arbitrum -> @chain_type_specific_allowed_events ~w(new_arbitrum_batches new_messages_to_arbitrum_amount)a + :optimism -> + @chain_type_specific_allowed_events ~w(new_optimism_batches new_optimism_deposits)a + _ -> @chain_type_specific_allowed_events ~w()a end diff --git a/apps/explorer/lib/explorer/chain/events/subscriber.ex b/apps/explorer/lib/explorer/chain/events/subscriber.ex index 3e76f6579615..741422fac3f4 100644 --- a/apps/explorer/lib/explorer/chain/events/subscriber.ex +++ b/apps/explorer/lib/explorer/chain/events/subscriber.ex @@ -5,7 +5,7 @@ defmodule Explorer.Chain.Events.Subscriber do @common_allowed_broadcast_events ~w(addresses address_coin_balances address_token_balances address_current_token_balances blocks block_rewards internal_transactions - last_block_number optimism_deposits token_transfers transactions contract_verification_result + last_block_number token_transfers transactions contract_verification_result token_total_supply changed_bytecode fetched_bytecode fetched_token_instance_metadata smart_contract_was_verified zkevm_confirmed_batches eth_bytecode_db_lookup_started smart_contract_was_not_verified)a @@ -14,6 +14,9 @@ defmodule Explorer.Chain.Events.Subscriber do :arbitrum -> @chain_type_specific_allowed_broadcast_events ~w(new_arbitrum_batches new_messages_to_arbitrum_amount)a + :optimism -> + @chain_type_specific_allowed_broadcast_events ~w(new_optimism_batches new_optimism_deposits)a + _ -> @chain_type_specific_allowed_broadcast_events ~w()a end diff --git a/apps/explorer/lib/explorer/chain/optimism/deposit.ex b/apps/explorer/lib/explorer/chain/optimism/deposit.ex index 36543fb59d84..021c4a11d463 100644 --- a/apps/explorer/lib/explorer/chain/optimism/deposit.ex +++ b/apps/explorer/lib/explorer/chain/optimism/deposit.ex @@ -52,6 +52,22 @@ defmodule Explorer.Chain.Optimism.Deposit do ) end + @doc """ + Forms a query to remove all Deposits with the specified L1 block number. + Used by the `Indexer.Fetcher.Optimism.Deposit` module. + + ## Parameters + - `l1_block_number`: The L1 block number for which the Deposits should be removed + from the `op_deposits` database table. + + ## Returns + - A query which can be used by the `delete_all` function. + """ + @spec remove_deposits_query(non_neg_integer()) :: Ecto.Queryable.t() + def remove_deposits_query(l1_block_number) do + from(d in __MODULE__, where: d.l1_block_number == ^l1_block_number) + end + @doc """ Lists `t:Explorer.Chain.Optimism.Deposit.t/0`'s' in descending order based on l1_block_number and l2_transaction_hash. @@ -74,7 +90,7 @@ defmodule Explorer.Chain.Optimism.Deposit do |> join_association(:l2_transaction, :required) |> page_deposits(paging_options) |> limit(^paging_options.page_size) - |> select_repo(options).all() + |> select_repo(options).all(timeout: :infinity) end end diff --git a/apps/explorer/lib/explorer/chain/optimism/dispute_game.ex b/apps/explorer/lib/explorer/chain/optimism/dispute_game.ex index 1ef62c4cde84..4ad8318b9f9b 100644 --- a/apps/explorer/lib/explorer/chain/optimism/dispute_game.ex +++ b/apps/explorer/lib/explorer/chain/optimism/dispute_game.ex @@ -78,7 +78,7 @@ defmodule Explorer.Chain.Optimism.DisputeGame do base_query |> page_dispute_games(paging_options) |> limit(^paging_options.page_size) - |> select_repo(options).all() + |> select_repo(options).all(timeout: :infinity) end defp page_dispute_games(query, %PagingOptions{key: nil}), do: query diff --git a/apps/explorer/lib/explorer/chain/optimism/frame_sequence.ex b/apps/explorer/lib/explorer/chain/optimism/frame_sequence.ex index 66dbe4cd0da5..3ec97e2b7ab5 100644 --- a/apps/explorer/lib/explorer/chain/optimism/frame_sequence.ex +++ b/apps/explorer/lib/explorer/chain/optimism/frame_sequence.ex @@ -88,7 +88,8 @@ defmodule Explorer.Chain.Optimism.FrameSequence do ## Parameters - `internal_id`: Batch'es internal id. - - `options`: A keyword list of options that may include whether to use a replica database. + - `options`: A keyword list of options that may include whether to use a replica database + and/or whether to include blobs (true by default). ## Returns - A map with info about L1 batch having the specified id. @@ -108,30 +109,94 @@ defmodule Explorer.Chain.Optimism.FrameSequence do l2_block_number_to = TransactionBatch.edge_l2_block_number(internal_id, :max) transaction_count = Transaction.transaction_count_for_block_range(l2_block_number_from..l2_block_number_to) - {batch_data_container, blobs} = FrameSequenceBlob.list(internal_id, options) - - result = %{ - "internal_id" => internal_id, - "l1_timestamp" => batch.l1_timestamp, - "l2_block_start" => l2_block_number_from, - "l2_block_end" => l2_block_number_to, - "transaction_count" => transaction_count, - # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_count` property - "tx_count" => transaction_count, - "l1_transaction_hashes" => batch.l1_transaction_hashes, - # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l1_transaction_hashes` property - "l1_tx_hashes" => batch.l1_transaction_hashes, - "batch_data_container" => batch_data_container - } + {batch_data_container, blobs} = + if Keyword.get(options, :include_blobs?, true) do + FrameSequenceBlob.list(internal_id, options) + else + {nil, []} + end + + result = + prepare_base_info_for_batch( + internal_id, + l2_block_number_from, + l2_block_number_to, + transaction_count, + batch_data_container, + batch + ) if Enum.empty?(blobs) do result else - Map.put(result, "blobs", blobs) + Map.put(result, :blobs, blobs) end end end + @doc """ + Transforms an L1 batch into a map format for HTTP response. + + This function processes an Optimism L1 batch and converts it into a map that + includes basic batch information. + + ## Parameters + - `internal_id`: The internal ID of the batch. + - `l2_block_number_from`: Start L2 block number of the batch block range. + - `l2_block_number_to`: End L2 block number of the batch block range. + - `transaction_count`: The L2 transaction count included into the blocks of the range. + - `batch_data_container`: Designates where the batch info is stored: :in_blob4844, :in_celestia, or :in_calldata. + Can be `nil` if the container is unknown. + - `batch`: Either an `Explorer.Chain.Optimism.FrameSequence` entry or a map with + the corresponding fields. + + ## Returns + - A map with detailed information about the batch formatted for use in JSON HTTP responses. + """ + @spec prepare_base_info_for_batch( + non_neg_integer(), + non_neg_integer(), + non_neg_integer(), + non_neg_integer(), + :in_blob4844 | :in_celestia | :in_calldata | nil, + __MODULE__.t() + | %{:l1_timestamp => DateTime.t(), :l1_transaction_hashes => list(), optional(any()) => any()} + ) :: %{ + :internal_id => non_neg_integer(), + :l1_timestamp => DateTime.t(), + :l2_block_start => non_neg_integer(), + :l2_block_end => non_neg_integer(), + :transaction_count => non_neg_integer(), + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_count` property + :tx_count => non_neg_integer(), + :l1_transaction_hashes => list(), + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l1_transaction_hashes` property + :l1_tx_hashes => list(), + :batch_data_container => :in_blob4844 | :in_celestia | :in_calldata | nil + } + def prepare_base_info_for_batch( + internal_id, + l2_block_number_from, + l2_block_number_to, + transaction_count, + batch_data_container, + batch + ) do + %{ + :internal_id => internal_id, + :l1_timestamp => batch.l1_timestamp, + :l2_block_start => l2_block_number_from, + :l2_block_end => l2_block_number_to, + :transaction_count => transaction_count, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `transaction_count` property + :tx_count => transaction_count, + :l1_transaction_hashes => batch.l1_transaction_hashes, + # todo: keep next line for compatibility with frontend and remove when new frontend is bound to `l1_transaction_hashes` property + :l1_tx_hashes => batch.l1_transaction_hashes, + :batch_data_container => batch_data_container + } + end + @doc """ Lists `t:Explorer.Chain.Optimism.FrameSequence.t/0`'s' in descending order based on id. @@ -167,7 +232,7 @@ defmodule Explorer.Chain.Optimism.FrameSequence do base_query |> page_frame_sequences(paging_options) |> limit(^paging_options.page_size) - |> select_repo(options).all() + |> select_repo(options).all(timeout: :infinity) end end diff --git a/apps/explorer/lib/explorer/chain/optimism/frame_sequence_blob.ex b/apps/explorer/lib/explorer/chain/optimism/frame_sequence_blob.ex index 66d65984fa76..001c3c2f318d 100644 --- a/apps/explorer/lib/explorer/chain/optimism/frame_sequence_blob.ex +++ b/apps/explorer/lib/explorer/chain/optimism/frame_sequence_blob.ex @@ -73,7 +73,7 @@ defmodule Explorer.Chain.Optimism.FrameSequenceBlob do ) query - |> repo.all() + |> repo.all(timeout: :infinity) |> filter_blobs_by_type() end diff --git a/apps/explorer/lib/explorer/chain/optimism/output_root.ex b/apps/explorer/lib/explorer/chain/optimism/output_root.ex index e32b4a7f3508..0d68cb7c3b51 100644 --- a/apps/explorer/lib/explorer/chain/optimism/output_root.ex +++ b/apps/explorer/lib/explorer/chain/optimism/output_root.ex @@ -58,7 +58,7 @@ defmodule Explorer.Chain.Optimism.OutputRoot do base_query |> page_output_roots(paging_options) |> limit(^paging_options.page_size) - |> select_repo(options).all() + |> select_repo(options).all(timeout: :infinity) end end @@ -67,4 +67,36 @@ defmodule Explorer.Chain.Optimism.OutputRoot do defp page_output_roots(query, %PagingOptions{key: {index}}) do from(r in query, where: r.l2_output_index < ^index) end + + @doc """ + Forms a query to find the last Output Root's L1 block number and transaction hash. + Used by the `Indexer.Fetcher.Optimism.OutputRoot` module. + + ## Returns + - A query which can be used by the `Repo.one` function. + """ + @spec last_root_l1_block_number_query() :: Ecto.Queryable.t() + def last_root_l1_block_number_query do + from(root in __MODULE__, + select: {root.l1_block_number, root.l1_transaction_hash}, + order_by: [desc: root.l2_output_index], + limit: 1 + ) + end + + @doc """ + Forms a query to remove all Output Roots related to the specified L1 block number. + Used by the `Indexer.Fetcher.Optimism.OutputRoot` module. + + ## Parameters + - `l1_block_number`: The L1 block number for which the Output Roots should be removed + from the `op_output_roots` database table. + + ## Returns + - A query which can be used by the `delete_all` function. + """ + @spec remove_roots_query(non_neg_integer()) :: Ecto.Queryable.t() + def remove_roots_query(l1_block_number) do + from(root in __MODULE__, where: root.l1_block_number == ^l1_block_number) + end end diff --git a/apps/explorer/lib/explorer/chain/optimism/transaction_batch.ex b/apps/explorer/lib/explorer/chain/optimism/transaction_batch.ex index 2ce8032da299..a1853e01169f 100644 --- a/apps/explorer/lib/explorer/chain/optimism/transaction_batch.ex +++ b/apps/explorer/lib/explorer/chain/optimism/transaction_batch.ex @@ -133,7 +133,7 @@ defmodule Explorer.Chain.Optimism.TransactionBatch do |> join_association(:frame_sequence, :required) |> page_transaction_batches(paging_options) |> limit(^paging_options.page_size) - |> select_repo(options).all() + |> select_repo(options).all(timeout: :infinity) end end @@ -174,7 +174,7 @@ defmodule Explorer.Chain.Optimism.TransactionBatch do |> limit(^paging_options.page_size) |> order_by(desc: :number) |> join_associations(necessity_by_association) - |> select_repo(options).all() + |> select_repo(options).all(timeout: :infinity) end defp page_blocks(query, %PagingOptions{key: nil}), do: query diff --git a/apps/explorer/lib/explorer/chain/optimism/withdrawal.ex b/apps/explorer/lib/explorer/chain/optimism/withdrawal.ex index 011273f092fc..917e14e4a1e1 100644 --- a/apps/explorer/lib/explorer/chain/optimism/withdrawal.ex +++ b/apps/explorer/lib/explorer/chain/optimism/withdrawal.ex @@ -84,7 +84,7 @@ defmodule Explorer.Chain.Optimism.Withdrawal do base_query |> page_optimism_withdrawals(paging_options) |> limit(^paging_options.page_size) - |> select_repo(options).all() + |> select_repo(options).all(timeout: :infinity) end end @@ -94,6 +94,38 @@ defmodule Explorer.Chain.Optimism.Withdrawal do from(w in query, where: w.msg_nonce < ^nonce) end + @doc """ + Forms a query to find the last Withdrawal's L2 block number and transaction hash. + Used by the `Indexer.Fetcher.Optimism.Withdrawal` module. + + ## Returns + - A query which can be used by the `Repo.one` function. + """ + @spec last_withdrawal_l2_block_number_query() :: Ecto.Queryable.t() + def last_withdrawal_l2_block_number_query do + from(w in __MODULE__, + select: {w.l2_block_number, w.l2_transaction_hash}, + order_by: [desc: w.msg_nonce], + limit: 1 + ) + end + + @doc """ + Forms a query to remove all Withdrawals related to the specified L2 block number. + Used by the `Indexer.Fetcher.Optimism.Withdrawal` module. + + ## Parameters + - `l2_block_number`: The L2 block number for which the Withdrawals should be removed + from the `op_withdrawals` database table. + + ## Returns + - A query which can be used by the `delete_all` function. + """ + @spec remove_withdrawals_query(non_neg_integer()) :: Ecto.Queryable.t() + def remove_withdrawals_query(l2_block_number) do + from(w in __MODULE__, where: w.l2_block_number == ^l2_block_number) + end + @doc """ Gets withdrawal statuses for Optimism Withdrawal transaction. For each withdrawal associated with this transaction, @@ -117,7 +149,7 @@ defmodule Explorer.Chain.Optimism.Withdrawal do ) query - |> Repo.replica().all() + |> Repo.replica().all(timeout: :infinity) |> Enum.map(fn w -> msg_nonce = Bitwise.band( diff --git a/apps/explorer/lib/explorer/chain/optimism/withdrawal_event.ex b/apps/explorer/lib/explorer/chain/optimism/withdrawal_event.ex index bac79ac951c2..aebc4ad4eb66 100644 --- a/apps/explorer/lib/explorer/chain/optimism/withdrawal_event.ex +++ b/apps/explorer/lib/explorer/chain/optimism/withdrawal_event.ex @@ -34,4 +34,36 @@ defmodule Explorer.Chain.Optimism.WithdrawalEvent do |> cast(attrs, @required_attrs ++ @optional_attrs) |> validate_required(@required_attrs) end + + @doc """ + Forms a query to find the last Withdrawal L1 event's block number and transaction hash. + Used by the `Indexer.Fetcher.Optimism.WithdrawalEvent` module. + + ## Returns + - A query which can be used by the `Repo.one` function. + """ + @spec last_event_l1_block_number_query() :: Ecto.Queryable.t() + def last_event_l1_block_number_query do + from(event in __MODULE__, + select: {event.l1_block_number, event.l1_transaction_hash}, + order_by: [desc: event.l1_timestamp], + limit: 1 + ) + end + + @doc """ + Forms a query to remove all Withdrawal L1 events related to the specified L1 block number. + Used by the `Indexer.Fetcher.Optimism.WithdrawalEvent` module. + + ## Parameters + - `l1_block_number`: The L1 block number for which the events should be removed + from the `op_withdrawal_events` database table. + + ## Returns + - A query which can be used by the `delete_all` function. + """ + @spec remove_events_query(non_neg_integer()) :: Ecto.Queryable.t() + def remove_events_query(l1_block_number) do + from(event in __MODULE__, where: event.l1_block_number == ^l1_block_number) + end end diff --git a/apps/indexer/lib/indexer/fetcher/optimism.ex b/apps/indexer/lib/indexer/fetcher/optimism.ex index 9e4cc382c8b6..11ba4aa5f1d6 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism.ex @@ -19,11 +19,11 @@ defmodule Indexer.Fetcher.Optimism do alias EthereumJSONRPC.Block.ByNumber alias EthereumJSONRPC.Contract + alias Explorer.Repo alias Indexer.Helper @fetcher_name :optimism @block_check_interval_range_size 100 - @eth_get_logs_range_size 250 @finite_retries_number 3 def child_spec(start_link_arguments) do @@ -183,10 +183,6 @@ defmodule Indexer.Fetcher.Optimism do Helper.repeated_call(&json_rpc/2, [req, json_rpc_named_arguments], error_message, retries) end - def get_logs_range_size do - @eth_get_logs_range_size - end - @doc """ Forms JSON RPC named arguments for the given RPC URL. """ @@ -207,24 +203,34 @@ defmodule Indexer.Fetcher.Optimism do end @doc """ - Does initializations for `Indexer.Fetcher.Optimism.WithdrawalEvent` or `Indexer.Fetcher.Optimism.OutputRoot` module. - Contains common code used by both modules. + Does initializations for `Indexer.Fetcher.Optimism.WithdrawalEvent`, `Indexer.Fetcher.Optimism.OutputRoot`, or + `Indexer.Fetcher.Optimism.Deposit` module. Contains common code used by these modules. ## Parameters - - `output_oracle`: An address of L2OutputOracle contract on L1. Must be `nil` if the `caller` is not `OutputRoot` module. + - `output_oracle`: An address of L2OutputOracle contract on L1. + Must be `nil` if the `caller` is not `Indexer.Fetcher.Optimism.OutputRoot` module. - `caller`: The module that called this function. ## Returns - - A map for the `handle_continue` handler of the calling module. + - A resulting map for the `handle_continue` handler of the calling module. """ @spec init_continue(binary() | nil, module()) :: {:noreply, map()} | {:stop, :normal, %{}} def init_continue(output_oracle, caller) - when caller in [Indexer.Fetcher.Optimism.WithdrawalEvent, Indexer.Fetcher.Optimism.OutputRoot] do + when caller in [ + Indexer.Fetcher.Optimism.Deposit, + Indexer.Fetcher.Optimism.WithdrawalEvent, + Indexer.Fetcher.Optimism.OutputRoot + ] do {contract_name, table_name, start_block_note} = - if caller == Indexer.Fetcher.Optimism.WithdrawalEvent do - {"Optimism Portal", "op_withdrawal_events", "Withdrawals L1"} - else - {"Output Oracle", "op_output_roots", "Output Roots"} + case caller do + Indexer.Fetcher.Optimism.Deposit -> + {"Optimism Portal", "op_deposits", "Deposits"} + + Indexer.Fetcher.Optimism.WithdrawalEvent -> + {"Optimism Portal", "op_withdrawal_events", "Withdrawals L1"} + + _ -> + {"Output Oracle", "op_output_roots", "Output Roots"} end optimism_env = Application.get_all_env(:indexer)[__MODULE__] @@ -238,21 +244,20 @@ defmodule Indexer.Fetcher.Optimism do json_rpc_named_arguments = json_rpc_named_arguments(optimism_l1_rpc), {optimism_portal, start_block_l1} <- read_system_config(system_config, json_rpc_named_arguments), {:contract_is_valid, true} <- - {:contract_is_valid, - caller == Indexer.Fetcher.Optimism.WithdrawalEvent or Helper.address_correct?(output_oracle)}, + {:contract_is_valid, caller != Indexer.Fetcher.Optimism.OutputRoot or Helper.address_correct?(output_oracle)}, true <- start_block_l1 > 0, - {last_l1_block_number, last_l1_transaction_hash} <- caller.get_last_l1_item(), + {last_l1_block_number, last_l1_transaction_hash, last_l1_transaction} <- + caller.get_last_l1_item(json_rpc_named_arguments), {:start_block_l1_valid, true} <- {:start_block_l1_valid, start_block_l1 <= last_l1_block_number || last_l1_block_number == 0}, - {:ok, last_l1_transaction} <- get_transaction_by_hash(last_l1_transaction_hash, json_rpc_named_arguments), {:l1_transaction_not_found, false} <- {:l1_transaction_not_found, !is_nil(last_l1_transaction_hash) && is_nil(last_l1_transaction)}, {:ok, block_check_interval, last_safe_block} <- get_block_check_interval(json_rpc_named_arguments) do contract_address = - if caller == Indexer.Fetcher.Optimism.WithdrawalEvent do - optimism_portal - else + if caller == Indexer.Fetcher.Optimism.OutputRoot do output_oracle + else + optimism_portal end start_block = max(start_block_l1, last_l1_block_number) @@ -266,6 +271,7 @@ defmodule Indexer.Fetcher.Optimism do start_block: start_block, end_block: last_safe_block, json_rpc_named_arguments: json_rpc_named_arguments, + eth_get_logs_range_size: optimism_env[:l1_eth_get_logs_range_size], stop: false }} else @@ -307,7 +313,7 @@ defmodule Indexer.Fetcher.Optimism do {:stop, :normal, %{}} nil -> - Logger.error("Cannot read SystemConfig contract.") + Logger.error("Cannot read SystemConfig contract and fallback envs are not correctly defined.") {:stop, :normal, %{}} _ -> @@ -325,6 +331,9 @@ defmodule Indexer.Fetcher.Optimism do Gets `OptimismPortal` contract address from the `SystemConfig` contract and the number of a start block (from which all Optimism fetchers should start). + If SystemConfig has obsolete implementation, the values are fallen back from the corresponding + env variables (INDEXER_OPTIMISM_L1_PORTAL_CONTRACT and INDEXER_OPTIMISM_L1_START_BLOCK). + ## Parameters - `contract_address`: An address of SystemConfig contract. - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. @@ -348,7 +357,7 @@ defmodule Indexer.Fetcher.Optimism do &json_rpc/2, [requests, json_rpc_named_arguments], error_message, - Helper.infinite_retries_number() + Helper.finite_retries_number() ) do {:ok, responses} -> "0x000000000000000000000000" <> optimism_portal = Enum.at(responses, 0).result @@ -356,7 +365,11 @@ defmodule Indexer.Fetcher.Optimism do {"0x" <> optimism_portal, start_block} _ -> - nil + env = Application.get_all_env(:indexer)[__MODULE__] + + if Helper.address_correct?(env[:portal]) and not is_nil(env[:start_block_l1]) do + {env[:portal], env[:start_block_l1]} + end end end @@ -380,4 +393,66 @@ defmodule Indexer.Fetcher.Optimism do optimism_config = Application.get_all_env(:indexer)[__MODULE__] not is_nil(optimism_config[:optimism_l1_system_config]) end + + @doc """ + Determines the last saved block number, the last saved transaction hash, and the transaction info for + a certain entity defined by the passed functions. + + Used by the OP fetcher modules to start fetching from a correct block number + after reorg has occurred. + + ## Parameters + - `layer`: Just for logging purposes. Can be `:L1` or `:L2` depending on the layer of the entity. + - `last_block_number_query_fun`: A function which will be called to form database query + to get the latest item in the corresponding database table. + - `remove_query_fun`: A function which will be called to form database query to remove the entity rows + created due to reorg from the corresponding table. + - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. + Used to get transaction info by its hash from the RPC node. + Can be `nil` if the transaction info is not needed. + + ## Returns + - A tuple `{last_block_number, last_transaction_hash, last_transaction}` where + `last_block_number` is the last block number found in the corresponding table (0 if not found), + `last_transaction_hash` is the last transaction hash found in the corresponding table (nil if not found), + `last_transaction` is the transaction info got from the RPC (nil if not found or not needed). + - A tuple `{:error, message}` in case the `eth_getTransactionByHash` RPC request failed. + """ + @spec get_last_item(:L1 | :L2, function(), function(), EthereumJSONRPC.json_rpc_named_arguments() | nil) :: + {non_neg_integer(), binary() | nil, map() | nil} | {:error, any()} + def get_last_item(layer, last_block_number_query_fun, remove_query_fun, json_rpc_named_arguments \\ nil) + when is_function(last_block_number_query_fun, 0) and is_function(remove_query_fun, 1) do + {last_block_number, last_transaction_hash} = + last_block_number_query_fun.() + |> Repo.one() + |> Kernel.||({0, nil}) + + with {:empty_hash, false} <- {:empty_hash, is_nil(last_transaction_hash)}, + {:empty_json_rpc_named_arguments, false} <- + {:empty_json_rpc_named_arguments, is_nil(json_rpc_named_arguments)}, + {:ok, last_transaction} <- get_transaction_by_hash(last_transaction_hash, json_rpc_named_arguments), + {:empty_transaction, false} <- {:empty_transaction, is_nil(last_transaction)} do + {last_block_number, last_transaction_hash, last_transaction} + else + {:empty_hash, true} -> + {last_block_number, nil, nil} + + {:empty_json_rpc_named_arguments, true} -> + {last_block_number, last_transaction_hash, nil} + + {:error, _} = error -> + error + + {:empty_transaction, true} -> + Logger.error( + "Cannot find last #{layer} transaction from RPC by its hash (#{last_transaction_hash}). Probably, there was a reorg on #{layer} chain. Trying to check preceding transaction..." + ) + + last_block_number + |> remove_query_fun.() + |> Repo.delete_all() + + get_last_item(layer, last_block_number_query_fun, remove_query_fun, json_rpc_named_arguments) + end + end end diff --git a/apps/indexer/lib/indexer/fetcher/optimism/deposit.ex b/apps/indexer/lib/indexer/fetcher/optimism/deposit.ex index 8e7155d5a0b6..5adb4a0e0b0d 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/deposit.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/deposit.ex @@ -10,37 +10,23 @@ defmodule Indexer.Fetcher.Optimism.Deposit do import Ecto.Query - import EthereumJSONRPC, only: [integer_to_quantity: 1, quantity_to_integer: 1, request: 1] - import Explorer.Helper, only: [decode_data: 2, parse_integer: 1] + import EthereumJSONRPC, only: [quantity_to_integer: 1] + import Explorer.Helper, only: [decode_data: 2] alias EthereumJSONRPC.Block.ByNumber alias EthereumJSONRPC.Blocks alias Explorer.{Chain, Repo} alias Explorer.Chain.Events.Publisher alias Explorer.Chain.Optimism.Deposit + alias Explorer.Chain.RollupReorgMonitorQueue alias Indexer.Fetcher.Optimism alias Indexer.Helper - defstruct [ - :batch_size, - :start_block, - :from_block, - :safe_block, - :optimism_portal, - :json_rpc_named_arguments, - :transaction_type, - mode: :catch_up, - filter_id: nil, - check_interval: nil - ] - # 32-byte signature of the event TransactionDeposited(address indexed from, address indexed to, uint256 indexed version, bytes opaqueData) @transaction_deposited_event "0xb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32" - @retry_interval_minutes 3 - @retry_interval :timer.minutes(@retry_interval_minutes) - @address_prefix "0x000000000000000000000000" - @batch_size 500 + @fetcher_name :optimism_deposits + @address_prefix "0x000000000000000000000000" def child_spec(start_link_arguments) do spec = %{ @@ -63,266 +49,100 @@ defmodule Indexer.Fetcher.Optimism.Deposit do end @impl GenServer - def handle_continue(:ok, state) do + def handle_continue(:ok, _state) do Logger.metadata(fetcher: @fetcher_name) - env = Application.get_all_env(:indexer)[__MODULE__] - optimism_env = Application.get_all_env(:indexer)[Optimism] - system_config = optimism_env[:optimism_l1_system_config] - optimism_l1_rpc = optimism_env[:optimism_l1_rpc] - - with {:system_config_valid, true} <- {:system_config_valid, Helper.address_correct?(system_config)}, - {:rpc_l1_undefined, false} <- {:rpc_l1_undefined, is_nil(optimism_l1_rpc)}, - json_rpc_named_arguments = Optimism.json_rpc_named_arguments(optimism_l1_rpc), - {optimism_portal, start_block_l1} <- Optimism.read_system_config(system_config, json_rpc_named_arguments), - true <- start_block_l1 > 0, - {last_l1_block_number, last_l1_transaction_hash} <- get_last_l1_item(), - {:ok, last_l1_transaction} <- - Optimism.get_transaction_by_hash(last_l1_transaction_hash, json_rpc_named_arguments), - {:l1_transaction_not_found, false} <- - {:l1_transaction_not_found, !is_nil(last_l1_transaction_hash) && is_nil(last_l1_transaction)}, - {safe_block, _} = Helper.get_safe_block(json_rpc_named_arguments), - {:start_block_l1_valid, true} <- - {:start_block_l1_valid, - (start_block_l1 <= last_l1_block_number || last_l1_block_number == 0) && start_block_l1 <= safe_block} do - start_block = max(start_block_l1, last_l1_block_number) - - if start_block > safe_block do - Process.send(self(), :switch_to_realtime, []) - else - Process.send(self(), :fetch, []) - end - - {:noreply, - %__MODULE__{ - start_block: start_block, - from_block: start_block, - safe_block: safe_block, - optimism_portal: optimism_portal, - json_rpc_named_arguments: json_rpc_named_arguments, - batch_size: parse_integer(env[:batch_size]) || @batch_size, - transaction_type: env[:transaction_type] - }} - else - {:start_block_l1_valid, false} -> - Logger.error("Invalid L1 Start Block value. Please, check the value and op_deposits table.") - {:stop, :normal, state} - - {:rpc_l1_undefined, true} -> - Logger.error("L1 RPC URL is not defined.") - {:stop, :normal, state} - - {:system_config_valid, false} -> - Logger.error("SystemConfig contract address is invalid or undefined.") - {:stop, :normal, state} - - {:error, error_data} -> - Logger.error("Cannot get last L1 transaction from RPC by its hash due to the RPC error: #{inspect(error_data)}") - - {:stop, :normal, state} - - {:l1_transaction_not_found, true} -> - Logger.error( - "Cannot find last L1 transaction from RPC by its hash. Probably, there was a reorg on L1 chain. Please, check op_deposits table." - ) - - {:stop, :normal, state} - - nil -> - Logger.error("Cannot read SystemConfig contract.") - {:stop, :normal, state} - - _ -> - Logger.error("Optimism deposits L1 Start Block is invalid or zero.") - {:stop, :normal, state} - end + Optimism.init_continue(nil, __MODULE__) end @impl GenServer def handle_info( - :fetch, - %__MODULE__{ + :continue, + %{ + contract_address: optimism_portal, + block_check_interval: block_check_interval, start_block: start_block, - from_block: from_block, - safe_block: safe_block, - optimism_portal: optimism_portal, + end_block: end_block, json_rpc_named_arguments: json_rpc_named_arguments, - mode: :catch_up, - batch_size: batch_size, - transaction_type: transaction_type + eth_get_logs_range_size: eth_get_logs_range_size } = state ) do - to_block = min(from_block + batch_size, safe_block) + # credo:disable-for-next-line + time_before = Timex.now() + + transaction_type = Application.get_all_env(:indexer)[__MODULE__][:transaction_type] + + chunks_number = ceil((end_block - start_block + 1) / eth_get_logs_range_size) + chunk_range = Range.new(0, max(chunks_number - 1, 0), 1) + + last_written_block = + chunk_range + |> Enum.reduce_while(start_block - 1, fn current_chunk, _ -> + chunk_start = start_block + eth_get_logs_range_size * current_chunk + chunk_end = min(chunk_start + eth_get_logs_range_size - 1, end_block) + + if chunk_end >= chunk_start do + Helper.log_blocks_chunk_handling(chunk_start, chunk_end, start_block, end_block, nil, :L1) - with {:logs, {:ok, logs}} <- - {:logs, + {:ok, result} = Optimism.get_logs( - from_block, - to_block, + chunk_start, + chunk_end, optimism_portal, @transaction_deposited_event, json_rpc_named_arguments, - 3 - )}, - _ = Helper.log_blocks_chunk_handling(from_block, to_block, start_block, safe_block, nil, :L1), - deposits = events_to_deposits(logs, transaction_type, json_rpc_named_arguments), - {:import, {:ok, _imported}} <- - {:import, Chain.import(%{optimism_deposits: %{params: deposits}, timeout: :infinity})} do - Publisher.broadcast(%{optimism_deposits: deposits}, :realtime) - - Helper.log_blocks_chunk_handling( - from_block, - to_block, - start_block, - safe_block, - "#{Enum.count(deposits)} TransactionDeposited event(s)", - :L1 - ) + Helper.infinite_retries_number() + ) + + deposit_events = prepare_events(result, transaction_type, json_rpc_named_arguments) + + {:ok, _} = + Chain.import(%{ + optimism_deposits: %{params: deposit_events}, + timeout: :infinity + }) + + Publisher.broadcast(%{new_optimism_deposits: deposit_events}, :realtime) + + Helper.log_blocks_chunk_handling( + chunk_start, + chunk_end, + start_block, + end_block, + "#{Enum.count(deposit_events)} TransactionDeposited event(s)", + :L1 + ) + end + + reorg_block = RollupReorgMonitorQueue.reorg_block_pop(__MODULE__) - if to_block == safe_block do - Logger.info("Fetched all L1 blocks (#{start_block}..#{safe_block}), switching to realtime mode.") - Process.send(self(), :switch_to_realtime, []) - {:noreply, state} + if !is_nil(reorg_block) && reorg_block > 0 do + {deleted_count, _} = Repo.delete_all(from(d in Deposit, where: d.l1_block_number >= ^reorg_block)) + + log_deleted_rows_count(reorg_block, deleted_count) + + {:halt, if(reorg_block <= chunk_end, do: reorg_block - 1, else: chunk_end)} + else + {:cont, chunk_end} + end + end) + + new_start_block = last_written_block + 1 + + {:ok, new_end_block} = + Optimism.get_block_number_by_tag("latest", json_rpc_named_arguments, Helper.infinite_retries_number()) + + delay = + if new_end_block == last_written_block do + # there is no new block, so wait for some time to let the chain issue the new block + max(block_check_interval - Timex.diff(Timex.now(), time_before, :milliseconds), 0) else - Process.send(self(), :fetch, []) - {:noreply, %{state | from_block: to_block + 1}} + 0 end - else - {:logs, {:error, _error}} -> - Logger.error("Cannot fetch logs. Retrying in #{@retry_interval_minutes} minutes...") - Process.send_after(self(), :fetch, @retry_interval) - {:noreply, state} - - {:import, {:error, error}} -> - Logger.error("Cannot import logs due to #{inspect(error)}. Retrying in #{@retry_interval_minutes} minutes...") - Process.send_after(self(), :fetch, @retry_interval) - {:noreply, state} - - {:import, {:error, step, failed_value, _changes_so_far}} -> - Logger.error( - "Failed to import #{inspect(failed_value)} during #{step}. Retrying in #{@retry_interval_minutes} minutes..." - ) - - Process.send_after(self(), :fetch, @retry_interval) - {:noreply, state} - end - end - @impl GenServer - def handle_info( - :switch_to_realtime, - %__MODULE__{ - from_block: from_block, - safe_block: safe_block, - optimism_portal: optimism_portal, - json_rpc_named_arguments: json_rpc_named_arguments, - batch_size: batch_size, - mode: :catch_up, - transaction_type: transaction_type - } = state - ) do - with {:check_interval, {:ok, check_interval, new_safe}} <- - {:check_interval, Optimism.get_block_check_interval(json_rpc_named_arguments)}, - {:catch_up, _, false} <- {:catch_up, new_safe, new_safe - safe_block + 1 > batch_size}, - {:logs, {:ok, logs}} <- - {:logs, - Optimism.get_logs( - max(safe_block, from_block), - "latest", - optimism_portal, - @transaction_deposited_event, - json_rpc_named_arguments, - 3 - )}, - {:ok, filter_id} <- - get_new_filter( - max(safe_block, from_block), - "latest", - optimism_portal, - @transaction_deposited_event, - json_rpc_named_arguments - ) do - handle_new_logs(logs, transaction_type, json_rpc_named_arguments) - Process.send(self(), :fetch, []) - {:noreply, %{state | mode: :realtime, filter_id: filter_id, check_interval: check_interval}} - else - {:catch_up, new_safe, true} -> - Process.send(self(), :fetch, []) - {:noreply, %{state | safe_block: new_safe}} - - {:logs, {:error, error}} -> - Logger.error("Failed to get logs while switching to realtime mode, reason: #{inspect(error)}") - Process.send_after(self(), :switch_to_realtime, @retry_interval) - {:noreply, state} - - {:error, _error} -> - Logger.error("Failed to set logs filter. Retrying in #{@retry_interval_minutes} minutes...") - Process.send_after(self(), :switch_to_realtime, @retry_interval) - {:noreply, state} - - {:check_interval, {:error, _error}} -> - Logger.error("Failed to calculate check_interval. Retrying in #{@retry_interval_minutes} minutes...") - Process.send_after(self(), :switch_to_realtime, @retry_interval) - {:noreply, state} - end - end + Process.send_after(self(), :continue, delay) - @impl GenServer - def handle_info( - :fetch, - %__MODULE__{ - json_rpc_named_arguments: json_rpc_named_arguments, - mode: :realtime, - filter_id: filter_id, - check_interval: check_interval, - transaction_type: transaction_type - } = state - ) do - case get_filter_changes(filter_id, json_rpc_named_arguments) do - {:ok, logs} -> - handle_new_logs(logs, transaction_type, json_rpc_named_arguments) - Process.send_after(self(), :fetch, check_interval) - {:noreply, state} - - {:error, :filter_not_found} -> - Logger.error("The old filter not found on the node. Creating new filter...") - Process.send(self(), :update_filter, []) - {:noreply, state} - - {:error, _error} -> - Logger.error("Failed to set logs filter. Retrying in #{@retry_interval_minutes} minutes...") - Process.send_after(self(), :fetch, @retry_interval) - {:noreply, state} - end - end - - @impl GenServer - def handle_info( - :update_filter, - %__MODULE__{ - optimism_portal: optimism_portal, - json_rpc_named_arguments: json_rpc_named_arguments, - mode: :realtime - } = state - ) do - {last_l1_block_number, _} = get_last_l1_item() - - case get_new_filter( - last_l1_block_number + 1, - "latest", - optimism_portal, - @transaction_deposited_event, - json_rpc_named_arguments - ) do - {:ok, filter_id} -> - Process.send(self(), :fetch, []) - {:noreply, %{state | filter_id: filter_id}} - - {:error, _error} -> - Logger.error("Failed to set logs filter. Retrying in #{@retry_interval_minutes} minutes...") - Process.send_after(self(), :update_filter, @retry_interval) - {:noreply, state} - end + {:noreply, %{state | start_block: new_start_block, end_block: new_end_block}} end @impl GenServer @@ -331,82 +151,25 @@ defmodule Indexer.Fetcher.Optimism.Deposit do {:noreply, state} end - @impl GenServer - def terminate( - _reason, - %__MODULE__{ - json_rpc_named_arguments: json_rpc_named_arguments - } = state - ) do - if state.filter_id do - Logger.info("Optimism deposits fetcher is terminating, uninstalling filter") - uninstall_filter(state.filter_id, json_rpc_named_arguments) - end - end - - @impl GenServer - def terminate(:normal, _state) do - :ok - end - - defp handle_new_logs(logs, transaction_type, json_rpc_named_arguments) do - {reorgs, logs_to_parse, min_block, max_block, cnt} = - logs - |> Enum.reduce({MapSet.new(), [], nil, 0, 0}, fn - %{"removed" => true, "blockNumber" => block_number}, {reorgs, logs_to_parse, min_block, max_block, cnt} -> - {MapSet.put(reorgs, block_number), logs_to_parse, min_block, max_block, cnt} - - %{"blockNumber" => block_number} = log, {reorgs, logs_to_parse, min_block, max_block, cnt} -> - { - reorgs, - [log | logs_to_parse], - min(min_block, quantity_to_integer(block_number)), - max(max_block, quantity_to_integer(block_number)), - cnt + 1 - } - end) - - handle_reorgs(reorgs) - - unless Enum.empty?(logs_to_parse) do - deposits = events_to_deposits(logs_to_parse, transaction_type, json_rpc_named_arguments) - {:ok, _imported} = Chain.import(%{optimism_deposits: %{params: deposits}, timeout: :infinity}) - - Publisher.broadcast(%{optimism_deposits: deposits}, :realtime) - - Helper.log_blocks_chunk_handling( - min_block, - max_block, - min_block, - max_block, - "#{cnt} TransactionDeposited event(s)", - :L1 + defp log_deleted_rows_count(reorg_block, count) do + if count > 0 do + Logger.warning( + "As L1 reorg was detected, all rows with l1_block_number >= #{reorg_block} were removed from the op_deposits table. Number of removed rows: #{count}." ) end end - defp events_to_deposits(logs, transaction_type, json_rpc_named_arguments) do + defp prepare_events(events, transaction_type, json_rpc_named_arguments) do timestamps = - logs - |> Enum.reduce(MapSet.new(), fn %{"blockNumber" => block_number_quantity}, acc -> - block_number = quantity_to_integer(block_number_quantity) - MapSet.put(acc, block_number) + events + |> get_blocks_by_events(json_rpc_named_arguments, Helper.infinite_retries_number()) + |> Enum.reduce(%{}, fn block, acc -> + block_number = quantity_to_integer(Map.get(block, "number")) + {:ok, timestamp} = DateTime.from_unix(quantity_to_integer(Map.get(block, "timestamp"))) + Map.put(acc, block_number, timestamp) end) - |> MapSet.to_list() - |> get_block_timestamps_by_numbers(json_rpc_named_arguments) - |> case do - {:ok, timestamps} -> - timestamps - - {:error, error} -> - Logger.error( - "Failed to get L1 block timestamps for deposits due to #{inspect(error)}. Timestamps will be set to null." - ) - - %{} - end - Enum.map(logs, &event_to_deposit(&1, timestamps, transaction_type)) + Enum.map(events, &event_to_deposit(&1, timestamps, transaction_type)) end defp event_to_deposit( @@ -440,11 +203,13 @@ defmodule Indexer.Fetcher.Optimism.Deposit do msg_value::binary-size(32), value::binary-size(32), gas_limit::binary-size(8), - is_creation::binary-size(1), + _is_creation::binary-size(1), data::binary >> ] = decode_data(opaque_data, [:bytes]) + is_system = <<0>> + rlp_encoded = ExRLP.encode( [ @@ -454,7 +219,7 @@ defmodule Indexer.Fetcher.Optimism.Deposit do msg_value |> String.replace_leading(<<0>>, <<>>), value |> String.replace_leading(<<0>>, <<>>), gas_limit |> String.replace_leading(<<0>>, <<>>), - is_creation |> String.replace_leading(<<0>>, <<>>), + is_system |> String.replace_leading(<<0>>, <<>>), data ], encoding: :hex @@ -483,98 +248,70 @@ defmodule Indexer.Fetcher.Optimism.Deposit do } end - defp handle_reorgs(reorgs) do - if MapSet.size(reorgs) > 0 do - Logger.warning("L1 reorg detected. The following L1 blocks were removed: #{inspect(MapSet.to_list(reorgs))}") - - {deleted_count, _} = Repo.delete_all(from(d in Deposit, where: d.l1_block_number in ^reorgs)) - - if deleted_count > 0 do - Logger.warning( - "As L1 reorg was detected, all affected rows were removed from the op_deposits table. Number of removed rows: #{deleted_count}." - ) - end - end - end - - defp get_block_timestamps_by_numbers(numbers, json_rpc_named_arguments, retries \\ 3) do - id_to_params = - numbers - |> Stream.map(fn number -> %{number: number} end) - |> Stream.with_index() - |> Enum.into(%{}, fn {params, id} -> {id, params} end) - - request = Blocks.requests(id_to_params, &ByNumber.request(&1, false)) - error_message = &"Cannot fetch timestamps for blocks #{numbers}. Error: #{inspect(&1)}" + @doc """ + Determines the last saved L1 block number, the last saved transaction hash, and the transaction info for L1 Deposit events. - case Optimism.repeated_request(request, error_message, json_rpc_named_arguments, retries) do - {:ok, response} -> - %Blocks{blocks_params: blocks_params} = Blocks.from_responses(response, id_to_params) + Used by the `Indexer.Fetcher.Optimism` module to start fetching from a correct block number + after reorg has occurred. - {:ok, - blocks_params - |> Enum.reduce(%{}, fn %{number: number, timestamp: timestamp}, acc -> Map.put_new(acc, number, timestamp) end)} + ## Parameters + - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. + Used to get transaction info by its hash from the RPC node. - err -> - err - end + ## Returns + - A tuple `{last_block_number, last_transaction_hash, last_transaction}` where + `last_block_number` is the last block number found in the corresponding table (0 if not found), + `last_transaction_hash` is the last transaction hash found in the corresponding table (nil if not found), + `last_transaction` is the transaction info got from the RPC (nil if not found). + - A tuple `{:error, message}` in case the `eth_getTransactionByHash` RPC request failed. + """ + @spec get_last_l1_item(EthereumJSONRPC.json_rpc_named_arguments()) :: + {non_neg_integer(), binary() | nil, map() | nil} | {:error, any()} + def get_last_l1_item(json_rpc_named_arguments) do + Optimism.get_last_item( + :L1, + &Deposit.last_deposit_l1_block_number_query/0, + &Deposit.remove_deposits_query/1, + json_rpc_named_arguments + ) end - defp get_new_filter(from_block, to_block, address, topic0, json_rpc_named_arguments, retries \\ 3) do - processed_from_block = if is_integer(from_block), do: integer_to_quantity(from_block), else: from_block - processed_to_block = if is_integer(to_block), do: integer_to_quantity(to_block), else: to_block - - req = - request(%{ - id: 0, - method: "eth_newFilter", - params: [ - %{ - fromBlock: processed_from_block, - toBlock: processed_to_block, - address: address, - topics: [topic0] - } - ] - }) - - error_message = &"Cannot create new log filter. Error: #{inspect(&1)}" - - Optimism.repeated_request(req, error_message, json_rpc_named_arguments, retries) + @doc """ + Returns L1 RPC URL for this module. + """ + @spec l1_rpc_url() :: binary() | nil + def l1_rpc_url do + Optimism.l1_rpc_url() end - defp get_filter_changes(filter_id, json_rpc_named_arguments, retries \\ 3) do - req = - request(%{ - id: 0, - method: "eth_getFilterChanges", - params: [filter_id] - }) - - error_message = &"Cannot fetch filter changes. Error: #{inspect(&1)}" + @doc """ + Determines if `Indexer.Fetcher.RollupL1ReorgMonitor` module must be up + before this fetcher starts. - case Optimism.repeated_request(req, error_message, json_rpc_named_arguments, retries) do - {:error, %{code: _, message: "filter not found"}} -> {:error, :filter_not_found} - response -> response - end + ## Returns + - `true` if the reorg monitor must be active, `false` otherwise. + """ + @spec requires_l1_reorg_monitor?() :: boolean() + def requires_l1_reorg_monitor? do + Optimism.requires_l1_reorg_monitor?() end - defp uninstall_filter(filter_id, json_rpc_named_arguments, retries \\ 1) do - req = - request(%{ - id: 0, - method: "eth_getFilterChanges", - params: [filter_id] - }) - - error_message = &"Cannot uninstall filter. Error: #{inspect(&1)}" + defp get_blocks_by_events(events, json_rpc_named_arguments, retries) do + request = + events + |> Enum.reduce(%{}, fn event, acc -> + Map.put(acc, event["blockNumber"], 0) + end) + |> Stream.map(fn {block_number, _} -> %{number: block_number} end) + |> Stream.with_index() + |> Enum.into(%{}, fn {params, id} -> {id, params} end) + |> Blocks.requests(&ByNumber.request(&1, false, false)) - Optimism.repeated_request(req, error_message, json_rpc_named_arguments, retries) - end + error_message = &"Cannot fetch blocks with batch request. Error: #{inspect(&1)}. Request: #{inspect(request)}" - defp get_last_l1_item do - Deposit.last_deposit_l1_block_number_query() - |> Repo.one() - |> Kernel.||({0, nil}) + case Optimism.repeated_request(request, error_message, json_rpc_named_arguments, retries) do + {:ok, results} -> Enum.map(results, fn %{result: result} -> result end) + {:error, _} -> [] + end end end diff --git a/apps/indexer/lib/indexer/fetcher/optimism/output_root.ex b/apps/indexer/lib/indexer/fetcher/optimism/output_root.ex index c032a878d290..6223d2f731a2 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/output_root.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/output_root.ex @@ -67,20 +67,21 @@ defmodule Indexer.Fetcher.Optimism.OutputRoot do start_block: start_block, end_block: end_block, json_rpc_named_arguments: json_rpc_named_arguments, + eth_get_logs_range_size: eth_get_logs_range_size, stop: stop } = state ) do # credo:disable-for-next-line time_before = Timex.now() - chunks_number = ceil((end_block - start_block + 1) / Optimism.get_logs_range_size()) + chunks_number = ceil((end_block - start_block + 1) / eth_get_logs_range_size) chunk_range = Range.new(0, max(chunks_number - 1, 0), 1) last_written_block = chunk_range |> Enum.reduce_while(start_block - 1, fn current_chunk, _ -> - chunk_start = start_block + Optimism.get_logs_range_size() * current_chunk - chunk_end = min(chunk_start + Optimism.get_logs_range_size() - 1, end_block) + chunk_start = start_block + eth_get_logs_range_size * current_chunk + chunk_end = min(chunk_start + eth_get_logs_range_size - 1, end_block) if chunk_end >= chunk_start do IndexerHelper.log_blocks_chunk_handling(chunk_start, chunk_end, start_block, end_block, nil, :L1) @@ -184,17 +185,32 @@ defmodule Indexer.Fetcher.Optimism.OutputRoot do end end - def get_last_l1_item do - query = - from(root in OutputRoot, - select: {root.l1_block_number, root.l1_transaction_hash}, - order_by: [desc: root.l2_output_index], - limit: 1 - ) + @doc """ + Determines the last saved L1 block number, the last saved transaction hash, and the transaction info for Output Roots. + + Used by the `Indexer.Fetcher.Optimism` module to start fetching from a correct block number + after reorg has occurred. - query - |> Repo.one() - |> Kernel.||({0, nil}) + ## Parameters + - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. + Used to get transaction info by its hash from the RPC node. + + ## Returns + - A tuple `{last_block_number, last_transaction_hash, last_transaction}` where + `last_block_number` is the last block number found in the corresponding table (0 if not found), + `last_transaction_hash` is the last transaction hash found in the corresponding table (nil if not found), + `last_transaction` is the transaction info got from the RPC (nil if not found). + - A tuple `{:error, message}` in case the `eth_getTransactionByHash` RPC request failed. + """ + @spec get_last_l1_item(EthereumJSONRPC.json_rpc_named_arguments()) :: + {non_neg_integer(), binary() | nil, map() | nil} | {:error, any()} + def get_last_l1_item(json_rpc_named_arguments) do + Optimism.get_last_item( + :L1, + &OutputRoot.last_root_l1_block_number_query/0, + &OutputRoot.remove_roots_query/1, + json_rpc_named_arguments + ) end @doc """ diff --git a/apps/indexer/lib/indexer/fetcher/optimism/transaction_batch.ex b/apps/indexer/lib/indexer/fetcher/optimism/transaction_batch.ex index 8185dec52f80..aa0623e05997 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/transaction_batch.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/transaction_batch.ex @@ -28,11 +28,13 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do import Explorer.Helper, only: [parse_integer: 1] + alias Ecto.Multi alias EthereumJSONRPC.Block.ByHash alias EthereumJSONRPC.{Blocks, Contract} alias Explorer.{Chain, Repo} alias Explorer.Chain.Beacon.Blob, as: BeaconBlob alias Explorer.Chain.{Block, Hash, RollupReorgMonitorQueue} + alias Explorer.Chain.Events.Publisher alias Explorer.Chain.Optimism.{FrameSequence, FrameSequenceBlob} alias Explorer.Chain.Optimism.TransactionBatch, as: OptimismTransactionBatch alias HTTPoison.Response @@ -42,10 +44,18 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do alias Indexer.Helper alias Varint.LEB128 - @fetcher_name :optimism_transaction_batches + @beacon_blob_fetcher_reference_slot_eth 8_500_000 + @beacon_blob_fetcher_reference_timestamp_eth 1_708_824_023 + @beacon_blob_fetcher_reference_slot_sepolia 4_400_000 + @beacon_blob_fetcher_reference_timestamp_sepolia 1_708_533_600 + @beacon_blob_fetcher_reference_slot_holesky 1_000_000 + @beacon_blob_fetcher_reference_timestamp_holesky 1_707_902_400 + @beacon_blob_fetcher_slot_duration 12 + @chain_id_eth 1 + @chain_id_sepolia 11_155_111 + @chain_id_holesky 17000 - # Optimism chain block time is a constant (2 seconds) - @op_chain_block_time 2 + @fetcher_name :optimism_transaction_batches @compressor_brotli 1 @@ -101,7 +111,6 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do {:batch_inbox_valid, true} <- {:batch_inbox_valid, Helper.address_correct?(batch_inbox)}, {:batch_submitter_valid, true} <- {:batch_submitter_valid, Helper.address_correct?(batch_submitter)}, - false <- is_nil(start_block_l1), true <- start_block_l1 > 0, chunk_size = parse_integer(env[:blocks_chunk_size]), {:chunk_size_valid, true} <- {:chunk_size_valid, !is_nil(chunk_size) && chunk_size > 0}, @@ -115,6 +124,14 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do Optimism.get_block_check_interval(json_rpc_named_arguments) do start_block = max(start_block_l1, last_l1_block_number) + chain_id_l1 = fetch_chain_id(json_rpc_named_arguments) + + if is_nil(chain_id_l1) do + Logger.warning( + "Cannot get Chain ID from the L1 RPC. The module will use fallback values from INDEXER_BEACON_BLOB_FETCHER_* env variables." + ) + end + Process.send(self(), :continue, []) {:noreply, @@ -129,8 +146,10 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do chunk_size: chunk_size, incomplete_channels: %{}, genesis_block_l2: env[:genesis_block_l2], + block_duration: optimism_env[:block_duration], json_rpc_named_arguments: json_rpc_named_arguments, - json_rpc_named_arguments_l2: json_rpc_named_arguments_l2 + json_rpc_named_arguments_l2: json_rpc_named_arguments_l2, + chain_id_l1: chain_id_l1 }} else {:system_config_valid, false} -> @@ -182,7 +201,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do {:stop, :normal, state} {:system_config_read, nil} -> - Logger.error("Cannot read SystemConfig contract.") + Logger.error("Cannot read SystemConfig contract and fallback envs are not correctly defined.") {:stop, :normal, state} _ -> @@ -213,8 +232,10 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do # - `chunk_size`: max number of L1 blocks in one chunk # - `incomplete_channels`: intermediate map of channels (incomplete frame sequences) in memory # - `genesis_block_l2`: Optimism BedRock upgrade L2 block number (used when parsing span batches) + # - `block_duration`: L2 block duration in seconds (used when parsing span batches) # - `json_rpc_named_arguments`: data to connect to L1 RPC server # - `json_rpc_named_arguments_l2`: data to connect to L2 RPC server + # - `chain_id_l1`: chain ID of L1 layer. @impl GenServer def handle_info( :continue, @@ -229,8 +250,10 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do chunk_size: chunk_size, incomplete_channels: incomplete_channels, genesis_block_l2: genesis_block_l2, + block_duration: block_duration, json_rpc_named_arguments: json_rpc_named_arguments, - json_rpc_named_arguments_l2: json_rpc_named_arguments_l2 + json_rpc_named_arguments_l2: json_rpc_named_arguments_l2, + chain_id_l1: chain_id_l1 } = state ) do time_before = Timex.now() @@ -260,10 +283,10 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do Range.new(chunk_start, chunk_end), batch_inbox, batch_submitter, - genesis_block_l2, + {genesis_block_l2, block_duration}, incomplete_channels_acc, {json_rpc_named_arguments, json_rpc_named_arguments_l2}, - {eip4844_blobs_api_url, celestia_blobs_api_url}, + {eip4844_blobs_api_url, celestia_blobs_api_url, chain_id_l1}, Helper.infinite_retries_number() ) @@ -285,6 +308,14 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do remove_prev_frame_sequences(inserted) set_frame_sequences_view_ready(sequences) + Publisher.broadcast( + %{ + new_optimism_batches: + Enum.map(sequences, &FrameSequence.batch_by_internal_id(&1.id, include_blobs?: false)) + }, + :realtime + ) + Helper.log_blocks_chunk_handling( chunk_start, chunk_end, @@ -402,28 +433,62 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do block.timestamp end + # Determines the last saved L1 block number, the last saved transaction hash, and the transaction info for batches. + # + # Utilized to start fetching from a correct block number after reorg has occurred. + # + # ## Parameters + # - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. + # Used to get transaction info by its hash from the RPC node. + # + # ## Returns + # - A tuple `{last_block_number, last_transaction_hash, last_transaction}` where + # `last_block_number` is the last block number found in the corresponding table (0 if not found), + # `last_transaction_hash` is the last transaction hash found in the corresponding table (nil if not found), + # `last_transaction` is the transaction info got from the RPC (nil if not found). + @spec get_last_l1_item(EthereumJSONRPC.json_rpc_named_arguments()) :: {non_neg_integer(), binary() | nil, map() | nil} defp get_last_l1_item(json_rpc_named_arguments) do - l1_transaction_hashes = + result = Repo.one( from( tb in OptimismTransactionBatch, inner_join: fs in FrameSequence, on: fs.id == tb.frame_sequence_id, - select: fs.l1_transaction_hashes, + select: {fs.id, fs.l1_transaction_hashes}, order_by: [desc: tb.l2_block_number], limit: 1 ) ) - if is_nil(l1_transaction_hashes) do - {0, nil, nil} + with {:empty_hashes, false} <- {:empty_hashes, is_nil(result)}, + l1_transaction_hashes = elem(result, 1), + last_l1_transaction_hash = List.last(l1_transaction_hashes), + {:ok, last_l1_transaction} = + Optimism.get_transaction_by_hash(last_l1_transaction_hash, json_rpc_named_arguments), + {:empty_transaction, false, last_l1_transaction_hash} <- + {:empty_transaction, is_nil(last_l1_transaction), last_l1_transaction_hash} do + last_l1_block_number = quantity_to_integer(Map.get(last_l1_transaction, "blockNumber", 0)) + {last_l1_block_number, last_l1_transaction_hash, last_l1_transaction} else - last_l1_transaction_hash = List.last(l1_transaction_hashes) + {:empty_hashes, true} -> + {0, nil, nil} - {:ok, last_l1_transaction} = Optimism.get_transaction_by_hash(last_l1_transaction_hash, json_rpc_named_arguments) + {:empty_transaction, true, last_l1_transaction_hash} -> + Logger.error( + "Cannot find last L1 transaction from RPC by its hash (#{last_l1_transaction_hash}). Probably, there was a reorg on L1 chain. Trying to check preceding frame sequence..." + ) - last_l1_block_number = quantity_to_integer(Map.get(last_l1_transaction || %{}, "blockNumber", 0)) - {last_l1_block_number, last_l1_transaction_hash, last_l1_transaction} + id = elem(result, 0) + + Multi.new() + |> Multi.delete_all( + :delete_transaction_batches, + from(tb in OptimismTransactionBatch, where: tb.frame_sequence_id == ^id) + ) + |> Multi.delete_all(:delete_frame_sequence, from(fs in FrameSequence, where: fs.id == ^id)) + |> Repo.transaction() + + get_last_l1_item(json_rpc_named_arguments) end end @@ -431,7 +496,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do block_range, batch_inbox, batch_submitter, - genesis_block_l2, + {genesis_block_l2, block_duration}, incomplete_channels, {json_rpc_named_arguments, json_rpc_named_arguments_l2}, blobs_api_url, @@ -443,7 +508,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do |> transactions_filter(batch_submitter, batch_inbox) |> get_transaction_batches_inner( blocks_params, - genesis_block_l2, + {genesis_block_l2, block_duration}, incomplete_channels, json_rpc_named_arguments_l2, blobs_api_url @@ -471,7 +536,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do block_range, batch_inbox, batch_submitter, - genesis_block_l2, + {genesis_block_l2, block_duration}, incomplete_channels, {json_rpc_named_arguments, json_rpc_named_arguments_l2}, blobs_api_url, @@ -481,7 +546,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do end end - defp eip4844_blobs_to_inputs(_transaction_hash, _blob_versioned_hashes, _block_timestamp, "") do + defp eip4844_blobs_to_inputs(_transaction_hash, _blob_versioned_hashes, _block_timestamp, "", _chain_id_l1) do Logger.error( "Cannot read EIP-4844 blobs from the Blockscout Blobs API as the API URL is not defined. Please, check INDEXER_OPTIMISM_L1_BATCH_BLOCKSCOUT_BLOBS_API_URL env variable." ) @@ -493,7 +558,8 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do transaction_hash, blob_versioned_hashes, block_timestamp, - blobs_api_url + blobs_api_url, + chain_id_l1 ) do blob_versioned_hashes |> Enum.reduce([], fn blob_hash, inputs_acc -> @@ -530,7 +596,8 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do transaction_hash, blob_hash, block_timestamp, - inputs_acc + inputs_acc, + chain_id_l1 ) end end) @@ -541,13 +608,38 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do transaction_hash, blob_hash, block_timestamp, - inputs_acc + inputs_acc, + chain_id_l1 ) do beacon_config = - :indexer - |> Application.get_env(Blob) - |> Keyword.take([:reference_slot, :reference_timestamp, :slot_duration]) - |> Enum.into(%{}) + case chain_id_l1 do + @chain_id_eth -> + %{ + reference_slot: @beacon_blob_fetcher_reference_slot_eth, + reference_timestamp: @beacon_blob_fetcher_reference_timestamp_eth, + slot_duration: @beacon_blob_fetcher_slot_duration + } + + @chain_id_sepolia -> + %{ + reference_slot: @beacon_blob_fetcher_reference_slot_sepolia, + reference_timestamp: @beacon_blob_fetcher_reference_timestamp_sepolia, + slot_duration: @beacon_blob_fetcher_slot_duration + } + + @chain_id_holesky -> + %{ + reference_slot: @beacon_blob_fetcher_reference_slot_holesky, + reference_timestamp: @beacon_blob_fetcher_reference_timestamp_holesky, + slot_duration: @beacon_blob_fetcher_slot_duration + } + + _ -> + :indexer + |> Application.get_env(Blob) + |> Keyword.take([:reference_slot, :reference_timestamp, :slot_duration]) + |> Enum.into(%{}) + end {:ok, fetched_blobs} = block_timestamp @@ -665,10 +757,10 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do defp get_transaction_batches_inner( transactions_filtered, blocks_params, - genesis_block_l2, + {genesis_block_l2, block_duration}, incomplete_channels, json_rpc_named_arguments_l2, - {eip4844_blobs_api_url, celestia_blobs_api_url} + {eip4844_blobs_api_url, celestia_blobs_api_url, chain_id_l1} ) do transactions_filtered |> Enum.reduce({:ok, incomplete_channels, [], [], []}, fn transaction, @@ -684,7 +776,8 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do transaction.hash, transaction.blob_versioned_hashes, block_timestamp, - eip4844_blobs_api_url + eip4844_blobs_api_url, + chain_id_l1 ) first_byte(transaction.input) == 0xCE -> @@ -706,7 +799,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do blocks_params, new_incomplete_channels_acc, {new_batches_acc, new_sequences_acc, new_blobs_acc}, - genesis_block_l2, + {genesis_block_l2, block_duration}, json_rpc_named_arguments_l2 ) end @@ -720,7 +813,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do blocks_params, incomplete_channels_acc, {batches_acc, sequences_acc, blobs_acc}, - genesis_block_l2, + {genesis_block_l2, block_duration}, json_rpc_named_arguments_l2 ) do frame = input_to_frame(input.bytes) @@ -766,7 +859,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do batches_acc, sequences_acc, blobs_acc, - genesis_block_l2, + {genesis_block_l2, block_duration}, json_rpc_named_arguments_l2 ) else @@ -782,7 +875,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do batches_acc, sequences_acc, blobs_acc, - genesis_block_l2, + {genesis_block_l2, block_duration}, json_rpc_named_arguments_l2 ) do frame_sequence_last = List.first(sequences_acc) @@ -849,7 +942,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do bytes, frame_sequence_id, channel.l1_timestamp, - genesis_block_l2, + {genesis_block_l2, block_duration}, json_rpc_named_arguments_l2 ) @@ -1119,7 +1212,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do bytes, id, l1_timestamp, - genesis_block_l2, + {genesis_block_l2, block_duration}, json_rpc_named_arguments_l2 ) do uncompressed_bytes = @@ -1144,7 +1237,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do version <= 2 -> # parsing the span batch - handle_v1_batch(content, id, l1_timestamp, genesis_block_l2, batch_acc) + handle_v1_batch(content, id, l1_timestamp, genesis_block_l2, block_duration, batch_acc) true -> Logger.error("Unsupported batch version ##{version}") @@ -1188,7 +1281,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do [batch | batch_acc] end - defp handle_v1_batch(content, frame_sequence_id, l1_timestamp, genesis_block_l2, batch_acc) do + defp handle_v1_batch(content, frame_sequence_id, l1_timestamp, genesis_block_l2, block_duration, batch_acc) do {rel_timestamp, content_remainder} = LEB128.decode(content) # skip l1_origin_num @@ -1202,12 +1295,12 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do |> LEB128.decode() # the first and last L2 blocks in the span - span_start = div(rel_timestamp, @op_chain_block_time) + genesis_block_l2 + span_start = div(rel_timestamp, block_duration) + genesis_block_l2 span_end = span_start + block_count - 1 cond do - rem(rel_timestamp, @op_chain_block_time) != 0 -> - Logger.error("rel_timestamp is not divisible by #{@op_chain_block_time}. We ignore the span batch.") + rem(rel_timestamp, block_duration) != 0 -> + Logger.error("rel_timestamp is not divisible by #{block_duration}. We ignore the span batch.") batch_acc @@ -1326,6 +1419,22 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do end) end + # Reads some public getters of SystemConfig contract and returns retrieved values. + # Gets the number of a start block (from which this fetcher should start), + # the inbox address, and the batcher (batch submitter) address. + # + # If SystemConfig has obsolete implementation, the values are fallen back from the corresponding + # env variables (INDEXER_OPTIMISM_L1_START_BLOCK, INDEXER_OPTIMISM_L1_BATCH_INBOX, INDEXER_OPTIMISM_L1_BATCH_SUBMITTER). + # + # ## Parameters + # - `contract_address`: An address of SystemConfig contract. + # - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. + # + # ## Returns + # - A tuple: {start_block, inbox, submitter}. + # - `nil` in case of error. + @spec read_system_config(String.t(), EthereumJSONRPC.json_rpc_named_arguments()) :: + {non_neg_integer(), String.t(), String.t()} | nil defp read_system_config(contract_address, json_rpc_named_arguments) do requests = [ # startBlock() public getter @@ -1342,7 +1451,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do &json_rpc/2, [requests, json_rpc_named_arguments], error_message, - Helper.infinite_retries_number() + Helper.finite_retries_number() ) do {:ok, responses} -> start_block = quantity_to_integer(Enum.at(responses, 0).result) @@ -1350,6 +1459,39 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do "0x000000000000000000000000" <> batch_submitter = Enum.at(responses, 2).result {start_block, String.downcase("0x" <> batch_inbox), String.downcase("0x" <> batch_submitter)} + _ -> + start_block = Application.get_all_env(:indexer)[Indexer.Fetcher.Optimism][:start_block_l1] + env = Application.get_all_env(:indexer)[__MODULE__] + + if not is_nil(start_block) and Helper.address_correct?(env[:inbox]) and Helper.address_correct?(env[:submitter]) do + {start_block, String.downcase(env[:inbox]), String.downcase(env[:submitter])} + end + end + end + + # Fetches the chain id from the RPC. + # + # ## Parameters + # - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. + # + # ## Returns + # - The chain id as unsigned integer. + # - `nil` if the request failed. + @spec fetch_chain_id(EthereumJSONRPC.json_rpc_named_arguments()) :: non_neg_integer() | nil + defp fetch_chain_id(json_rpc_named_arguments) do + error_message = &"Cannot read `eth_chainId`. Error: #{inspect(&1)}" + + request = EthereumJSONRPC.request(%{id: 0, method: "eth_chainId", params: []}) + + case Helper.repeated_call( + &json_rpc/2, + [request, json_rpc_named_arguments], + error_message, + Helper.infinite_retries_number() + ) do + {:ok, response} -> + quantity_to_integer(response) + _ -> nil end diff --git a/apps/indexer/lib/indexer/fetcher/optimism/withdrawal.ex b/apps/indexer/lib/indexer/fetcher/optimism/withdrawal.ex index 113a56fbce1f..0c64506ba452 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/withdrawal.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/withdrawal.ex @@ -56,13 +56,12 @@ defmodule Indexer.Fetcher.Optimism.Withdrawal do start_block_l2 = parse_integer(env[:start_block_l2]), false <- is_nil(start_block_l2), true <- start_block_l2 > 0, - {last_l2_block_number, last_l2_transaction_hash} <- get_last_l2_item(), + {last_l2_block_number, last_l2_transaction_hash, last_l2_transaction} <- + get_last_l2_item(json_rpc_named_arguments), {safe_block, safe_block_is_latest} = Helper.get_safe_block(json_rpc_named_arguments), {:start_block_l2_valid, true} <- {:start_block_l2_valid, (start_block_l2 <= last_l2_block_number || last_l2_block_number == 0) && start_block_l2 <= safe_block}, - {:ok, last_l2_transaction} <- - Optimism.get_transaction_by_hash(last_l2_transaction_hash, json_rpc_named_arguments), {:l2_transaction_not_found, false} <- {:l2_transaction_not_found, !is_nil(last_l2_transaction_hash) && is_nil(last_l2_transaction)} do Process.send(self(), :continue, []) @@ -74,7 +73,9 @@ defmodule Indexer.Fetcher.Optimism.Withdrawal do safe_block: safe_block, safe_block_is_latest: safe_block_is_latest, message_passer: env[:message_passer], - json_rpc_named_arguments: json_rpc_named_arguments + json_rpc_named_arguments: json_rpc_named_arguments, + eth_get_logs_range_size: + Application.get_all_env(:indexer)[Indexer.Fetcher.Optimism][:l2_eth_get_logs_range_size] }} else {:start_block_l2_undefined, true} -> @@ -113,10 +114,11 @@ defmodule Indexer.Fetcher.Optimism.Withdrawal do %{ start_block_l2: start_block_l2, message_passer: message_passer, - json_rpc_named_arguments: json_rpc_named_arguments + json_rpc_named_arguments: json_rpc_named_arguments, + eth_get_logs_range_size: eth_get_logs_range_size } = state ) do - fill_msg_nonce_gaps(start_block_l2, message_passer, json_rpc_named_arguments) + fill_msg_nonce_gaps(start_block_l2, message_passer, json_rpc_named_arguments, eth_get_logs_range_size) Process.send(self(), :find_new_events, []) {:noreply, state} end @@ -129,17 +131,18 @@ defmodule Indexer.Fetcher.Optimism.Withdrawal do safe_block: safe_block, safe_block_is_latest: safe_block_is_latest, message_passer: message_passer, - json_rpc_named_arguments: json_rpc_named_arguments + json_rpc_named_arguments: json_rpc_named_arguments, + eth_get_logs_range_size: eth_get_logs_range_size } = state ) do # find and fill all events between start_block and "safe" block # the "safe" block can be "latest" (when safe_block_is_latest == true) - fill_block_range(start_block, safe_block, message_passer, json_rpc_named_arguments) + fill_block_range(start_block, safe_block, message_passer, json_rpc_named_arguments, eth_get_logs_range_size) if not safe_block_is_latest do # find and fill all events between "safe" and "latest" block (excluding "safe") {:ok, latest_block} = Optimism.get_block_number_by_tag("latest", json_rpc_named_arguments) - fill_block_range(safe_block + 1, latest_block, message_passer, json_rpc_named_arguments) + fill_block_range(safe_block + 1, latest_block, message_passer, json_rpc_named_arguments, eth_get_logs_range_size) end {:stop, :normal, state} @@ -183,7 +186,8 @@ defmodule Indexer.Fetcher.Optimism.Withdrawal do w.msg_nonce, ^nonce_max ) - ) + ), + timeout: :infinity ) end @@ -198,7 +202,8 @@ defmodule Indexer.Fetcher.Optimism.Withdrawal do w.msg_nonce, ^nonce_min ) - ) + ), + timeout: :infinity ) end @@ -254,24 +259,31 @@ defmodule Indexer.Fetcher.Optimism.Withdrawal do Enum.count(withdrawals) end - defp fill_block_range(l2_block_start, l2_block_end, message_passer, json_rpc_named_arguments, scan_db) do + defp fill_block_range( + l2_block_start, + l2_block_end, + message_passer, + json_rpc_named_arguments, + eth_get_logs_range_size, + scan_db + ) do chunks_number = if scan_db do 1 else - ceil((l2_block_end - l2_block_start + 1) / Optimism.get_logs_range_size()) + ceil((l2_block_end - l2_block_start + 1) / eth_get_logs_range_size) end chunk_range = Range.new(0, max(chunks_number - 1, 0), 1) Enum.reduce(chunk_range, 0, fn current_chunk, withdrawals_count_acc -> - chunk_start = l2_block_start + Optimism.get_logs_range_size() * current_chunk + chunk_start = l2_block_start + eth_get_logs_range_size * current_chunk chunk_end = if scan_db do l2_block_end else - min(chunk_start + Optimism.get_logs_range_size() - 1, l2_block_end) + min(chunk_start + eth_get_logs_range_size - 1, l2_block_end) end Helper.log_blocks_chunk_handling(chunk_start, chunk_end, l2_block_start, l2_block_end, nil, :L2) @@ -298,23 +310,30 @@ defmodule Indexer.Fetcher.Optimism.Withdrawal do end) end - defp fill_block_range(start_block, end_block, message_passer, json_rpc_named_arguments) do + defp fill_block_range(start_block, end_block, message_passer, json_rpc_named_arguments, eth_get_logs_range_size) do if start_block <= end_block do - fill_block_range(start_block, end_block, message_passer, json_rpc_named_arguments, true) - fill_msg_nonce_gaps(start_block, message_passer, json_rpc_named_arguments, false) - {last_l2_block_number, _} = get_last_l2_item() + fill_block_range(start_block, end_block, message_passer, json_rpc_named_arguments, eth_get_logs_range_size, true) + fill_msg_nonce_gaps(start_block, message_passer, json_rpc_named_arguments, eth_get_logs_range_size, false) + {last_l2_block_number, _, _} = get_last_l2_item() fill_block_range( max(start_block, last_l2_block_number), end_block, message_passer, json_rpc_named_arguments, + eth_get_logs_range_size, false ) end end - defp fill_msg_nonce_gaps(start_block_l2, message_passer, json_rpc_named_arguments, scan_db \\ true) do + defp fill_msg_nonce_gaps( + start_block_l2, + message_passer, + json_rpc_named_arguments, + eth_get_logs_range_size, + scan_db \\ true + ) do nonce_min = Repo.aggregate(OptimismWithdrawal, :min, :msg_nonce) nonce_max = Repo.aggregate(OptimismWithdrawal, :max, :msg_nonce) @@ -332,7 +351,14 @@ defmodule Indexer.Fetcher.Optimism.Withdrawal do |> Enum.zip(new_ends) |> Enum.each(fn {l2_block_start, l2_block_end} -> withdrawals_count = - fill_block_range(l2_block_start, l2_block_end, message_passer, json_rpc_named_arguments, scan_db) + fill_block_range( + l2_block_start, + l2_block_end, + message_passer, + json_rpc_named_arguments, + eth_get_logs_range_size, + scan_db + ) if withdrawals_count > 0 do log_fill_msg_nonce_gaps(scan_db, l2_block_start, l2_block_end, withdrawals_count) @@ -340,22 +366,35 @@ defmodule Indexer.Fetcher.Optimism.Withdrawal do end) if scan_db do - fill_msg_nonce_gaps(start_block_l2, message_passer, json_rpc_named_arguments, false) + fill_msg_nonce_gaps(start_block_l2, message_passer, json_rpc_named_arguments, eth_get_logs_range_size, false) end end end - defp get_last_l2_item do - query = - from(w in OptimismWithdrawal, - select: {w.l2_block_number, w.l2_transaction_hash}, - order_by: [desc: w.msg_nonce], - limit: 1 - ) - - query - |> Repo.one() - |> Kernel.||({0, nil}) + # Determines the last saved L2 block number, the last saved transaction hash, and the transaction info for withdrawals. + # + # Utilized to start fetching from a correct block number after reorg has occurred. + # + # ## Parameters + # - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. + # Used to get transaction info by its hash from the RPC node. + # Can be `nil` if the transaction info is not needed. + # + # ## Returns + # - A tuple `{last_block_number, last_transaction_hash, last_transaction}` where + # `last_block_number` is the last block number found in the corresponding table (0 if not found), + # `last_transaction_hash` is the last transaction hash found in the corresponding table (nil if not found), + # `last_transaction` is the transaction info got from the RPC (nil if not found or not needed). + # - A tuple `{:error, message}` in case the `eth_getTransactionByHash` RPC request failed. + @spec get_last_l2_item(EthereumJSONRPC.json_rpc_named_arguments() | nil) :: + {non_neg_integer(), binary() | nil, map() | nil} | {:error, any()} + defp get_last_l2_item(json_rpc_named_arguments \\ nil) do + Optimism.get_last_item( + :L2, + &OptimismWithdrawal.last_withdrawal_l2_block_number_query/0, + &OptimismWithdrawal.remove_withdrawals_query/1, + json_rpc_named_arguments + ) end defp log_fill_msg_nonce_gaps(scan_db, l2_block_start, l2_block_end, withdrawals_count) do diff --git a/apps/indexer/lib/indexer/fetcher/optimism/withdrawal_event.ex b/apps/indexer/lib/indexer/fetcher/optimism/withdrawal_event.ex index 3cb2a55d4a08..5efb32390669 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/withdrawal_event.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/withdrawal_event.ex @@ -69,20 +69,21 @@ defmodule Indexer.Fetcher.Optimism.WithdrawalEvent do block_check_interval: block_check_interval, start_block: start_block, end_block: end_block, - json_rpc_named_arguments: json_rpc_named_arguments + json_rpc_named_arguments: json_rpc_named_arguments, + eth_get_logs_range_size: eth_get_logs_range_size } = state ) do # credo:disable-for-next-line time_before = Timex.now() - chunks_number = ceil((end_block - start_block + 1) / Optimism.get_logs_range_size()) + chunks_number = ceil((end_block - start_block + 1) / eth_get_logs_range_size) chunk_range = Range.new(0, max(chunks_number - 1, 0), 1) last_written_block = chunk_range |> Enum.reduce_while(start_block - 1, fn current_chunk, _ -> - chunk_start = start_block + Optimism.get_logs_range_size() * current_chunk - chunk_end = min(chunk_start + Optimism.get_logs_range_size() - 1, end_block) + chunk_start = start_block + eth_get_logs_range_size * current_chunk + chunk_end = min(chunk_start + eth_get_logs_range_size - 1, end_block) if chunk_end >= chunk_start do Helper.log_blocks_chunk_handling(chunk_start, chunk_end, start_block, end_block, nil, :L1) @@ -245,17 +246,32 @@ defmodule Indexer.Fetcher.Optimism.WithdrawalEvent do |> Map.values() end - def get_last_l1_item do - query = - from(we in WithdrawalEvent, - select: {we.l1_block_number, we.l1_transaction_hash}, - order_by: [desc: we.l1_timestamp], - limit: 1 - ) + @doc """ + Determines the last saved L1 block number, the last saved transaction hash, and the transaction info for L1 Withdrawal events. + + Used by the `Indexer.Fetcher.Optimism` module to start fetching from a correct block number + after reorg has occurred. - query - |> Repo.one() - |> Kernel.||({0, nil}) + ## Parameters + - `json_rpc_named_arguments`: Configuration parameters for the JSON RPC connection. + Used to get transaction info by its hash from the RPC node. + + ## Returns + - A tuple `{last_block_number, last_transaction_hash, last_transaction}` where + `last_block_number` is the last block number found in the corresponding table (0 if not found), + `last_transaction_hash` is the last transaction hash found in the corresponding table (nil if not found), + `last_transaction` is the transaction info got from the RPC (nil if not found). + - A tuple `{:error, message}` in case the `eth_getTransactionByHash` RPC request failed. + """ + @spec get_last_l1_item(EthereumJSONRPC.json_rpc_named_arguments()) :: + {non_neg_integer(), binary() | nil, map() | nil} | {:error, any()} + def get_last_l1_item(json_rpc_named_arguments) do + Optimism.get_last_item( + :L1, + &WithdrawalEvent.last_event_l1_block_number_query/0, + &WithdrawalEvent.remove_events_query/1, + json_rpc_named_arguments + ) end @doc """ diff --git a/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex b/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex index 825dec00da9b..b2f9541b2b5d 100644 --- a/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex +++ b/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex @@ -20,6 +20,7 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do @modules_can_use_reorg_monitor (case Application.compile_env(:explorer, :chain_type) do :optimism -> [ + Indexer.Fetcher.Optimism.Deposit, Indexer.Fetcher.Optimism.OutputRoot, Indexer.Fetcher.Optimism.TransactionBatch, Indexer.Fetcher.Optimism.WithdrawalEvent diff --git a/apps/indexer/lib/indexer/helper.ex b/apps/indexer/lib/indexer/helper.ex index d01e030d9981..60c634ad7d3e 100644 --- a/apps/indexer/lib/indexer/helper.ex +++ b/apps/indexer/lib/indexer/helper.ex @@ -178,6 +178,21 @@ defmodule Indexer.Helper do repeated_call(&json_rpc/2, [req, json_rpc_named_arguments], error_message, retries) end + @doc """ + Returns a number of attempts for RPC requests sending by indexer modules. + The number is defined by @finite_retries_number attribute. + """ + @spec finite_retries_number() :: non_neg_integer() + def finite_retries_number do + @finite_retries_number + end + + @doc """ + Returns a big number of attempts for RPC requests sending by indexer modules + (simulating an infinite number of attempts). The number is defined by + @infinite_retries_number attribute. + """ + @spec infinite_retries_number() :: non_neg_integer() def infinite_retries_number do @infinite_retries_number end diff --git a/config/runtime.exs b/config/runtime.exs index b440a741fc22..1c6944773238 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -884,10 +884,14 @@ config :indexer, Indexer.Fetcher.Optimism.WithdrawalEvent.Supervisor, enabled: C config :indexer, Indexer.Fetcher.Optimism, optimism_l1_rpc: System.get_env("INDEXER_OPTIMISM_L1_RPC"), - optimism_l1_system_config: System.get_env("INDEXER_OPTIMISM_L1_SYSTEM_CONFIG_CONTRACT") + optimism_l1_system_config: System.get_env("INDEXER_OPTIMISM_L1_SYSTEM_CONFIG_CONTRACT"), + l1_eth_get_logs_range_size: ConfigHelper.parse_integer_env_var("INDEXER_OPTIMISM_L1_ETH_GET_LOGS_RANGE_SIZE", 250), + l2_eth_get_logs_range_size: ConfigHelper.parse_integer_env_var("INDEXER_OPTIMISM_L2_ETH_GET_LOGS_RANGE_SIZE", 250), + block_duration: ConfigHelper.parse_integer_env_var("INDEXER_OPTIMISM_BLOCK_DURATION", 2), + start_block_l1: ConfigHelper.parse_integer_or_nil_env_var("INDEXER_OPTIMISM_L1_START_BLOCK"), + portal: System.get_env("INDEXER_OPTIMISM_L1_PORTAL_CONTRACT") config :indexer, Indexer.Fetcher.Optimism.Deposit, - batch_size: System.get_env("INDEXER_OPTIMISM_L1_DEPOSITS_BATCH_SIZE"), transaction_type: ConfigHelper.parse_integer_env_var("INDEXER_OPTIMISM_L1_DEPOSITS_TRANSACTION_TYPE", 126) config :indexer, Indexer.Fetcher.Optimism.OutputRoot, @@ -902,7 +906,9 @@ config :indexer, Indexer.Fetcher.Optimism.TransactionBatch, blocks_chunk_size: System.get_env("INDEXER_OPTIMISM_L1_BATCH_BLOCKS_CHUNK_SIZE", "4"), eip4844_blobs_api_url: System.get_env("INDEXER_OPTIMISM_L1_BATCH_BLOCKSCOUT_BLOBS_API_URL", ""), celestia_blobs_api_url: System.get_env("INDEXER_OPTIMISM_L1_BATCH_CELESTIA_BLOBS_API_URL", ""), - genesis_block_l2: ConfigHelper.parse_integer_or_nil_env_var("INDEXER_OPTIMISM_L2_BATCH_GENESIS_BLOCK_NUMBER") + genesis_block_l2: ConfigHelper.parse_integer_or_nil_env_var("INDEXER_OPTIMISM_L2_BATCH_GENESIS_BLOCK_NUMBER"), + inbox: System.get_env("INDEXER_OPTIMISM_L1_BATCH_INBOX"), + submitter: System.get_env("INDEXER_OPTIMISM_L1_BATCH_SUBMITTER") config :indexer, Indexer.Fetcher.Withdrawal.Supervisor, disabled?: System.get_env("INDEXER_DISABLE_WITHDRAWALS_FETCHER", "true") == "true" diff --git a/cspell.json b/cspell.json index e939b1a69404..0d89d8063545 100644 --- a/cspell.json +++ b/cspell.json @@ -261,6 +261,7 @@ "histoday", "hljs", "Hodl", + "holesky", "HOPR", "httpoison", "hyperledger", diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index f31d65b6ff15..c3608980e277 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -284,13 +284,19 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # WITHDRAWALS_FIRST_BLOCK= # INDEXER_OPTIMISM_L1_RPC= # INDEXER_OPTIMISM_L1_SYSTEM_CONFIG_CONTRACT= +# INDEXER_OPTIMISM_L1_PORTAL_CONTRACT= +# INDEXER_OPTIMISM_L1_START_BLOCK= +# INDEXER_OPTIMISM_L1_BATCH_INBOX= +# INDEXER_OPTIMISM_L1_BATCH_SUBMITTER= # INDEXER_OPTIMISM_L1_BATCH_BLOCKS_CHUNK_SIZE= # INDEXER_OPTIMISM_L2_BATCH_GENESIS_BLOCK_NUMBER= +# INDEXER_OPTIMISM_BLOCK_DURATION= # INDEXER_OPTIMISM_L1_OUTPUT_ORACLE_CONTRACT= # INDEXER_OPTIMISM_L2_WITHDRAWALS_START_BLOCK= # INDEXER_OPTIMISM_L2_MESSAGE_PASSER_CONTRACT= -# INDEXER_OPTIMISM_L1_DEPOSITS_BATCH_SIZE= # INDEXER_OPTIMISM_L1_DEPOSITS_TRANSACTION_TYPE= +# INDEXER_OPTIMISM_L1_ETH_GET_LOGS_RANGE_SIZE= +# INDEXER_OPTIMISM_L2_ETH_GET_LOGS_RANGE_SIZE= # INDEXER_SCROLL_L1_RPC= # INDEXER_SCROLL_L1_MESSENGER_CONTRACT= # INDEXER_SCROLL_L1_MESSENGER_START_BLOCK= From b899b64d43d3496e93f7a88ed59a1462ff11a14d Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:40:40 +0300 Subject: [PATCH 320/363] fix: invalid metadata requests (#11210) * fix: prevent empty requests to metadata service * fix: remove empty hashes from request parameters * Small fix * Add docs --------- Co-authored-by: Leonid Tyurin --- .../chain/address/metadata_preloader.ex | 5 ++-- .../microservice_interfaces/metadata.ex | 24 ++++++++++++++++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/address/metadata_preloader.ex b/apps/explorer/lib/explorer/chain/address/metadata_preloader.ex index 01595915310a..85cdebe5e2cd 100644 --- a/apps/explorer/lib/explorer/chain/address/metadata_preloader.ex +++ b/apps/explorer/lib/explorer/chain/address/metadata_preloader.ex @@ -86,9 +86,8 @@ defmodule Explorer.Chain.Address.MetadataPreloader do def preload_metadata_to_list(items) do address_hash_strings = items - |> Enum.reduce([], fn item, acc -> - item_to_address_hash_strings(item) ++ acc - end) + |> Enum.flat_map(&item_to_address_hash_strings/1) + |> Enum.filter(&(&1 != "")) |> Enum.uniq() case Metadata.get_addresses_tags(address_hash_strings) do diff --git a/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex b/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex index ac3b1f78898b..cf7aa99203b6 100644 --- a/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex +++ b/apps/explorer/lib/explorer/microservice_interfaces/metadata.ex @@ -18,16 +18,34 @@ defmodule Explorer.MicroserviceInterfaces.Metadata do @page_size 50 @request_error_msg "Error while sending request to Metadata microservice" - @spec get_addresses_tags([String.t()]) :: {:error, :disabled | <<_::416>> | Jason.DecodeError.t()} | {:ok, any()} + @doc """ + Retrieves tags for a list of addresses. + + ## Parameters + - `addresses`: A list of addresses for which tags need to be fetched. + + ## Returns + - A map with metadata tags from microservice. Returns `:ignore` when the input list is empty. + + ## Examples + + iex> get_addresses_tags([]) + :ignore + + """ + @spec get_addresses_tags([String.t()]) :: + {:error, :disabled | <<_::416>> | Jason.DecodeError.t()} | {:ok, any()} | :ignore + def get_addresses_tags([]), do: :ignore + def get_addresses_tags(addresses) do with :ok <- Microservice.check_enabled(__MODULE__) do - body = %{ + params = %{ addresses: Enum.join(addresses, ","), tags_limit: @tags_per_address_limit, chain_id: Application.get_env(:block_scout_web, :chain_id) } - http_get_request(addresses_metadata_url(), body) + http_get_request(addresses_metadata_url(), params) end end From c4e6f9c384ceb5b98070c43b1e72ae373e5573df Mon Sep 17 00:00:00 2001 From: Viktor Baranov Date: Thu, 21 Nov 2024 16:45:16 +0400 Subject: [PATCH 321/363] Fixate 6.9.1 as the latest release in master branch --- .github/workflows/pre-release-arbitrum.yml | 2 +- .github/workflows/pre-release-blackfort.yml | 2 +- .github/workflows/pre-release-celo.yml | 2 +- .github/workflows/pre-release-eth.yml | 2 +- .github/workflows/pre-release-filecoin.yml | 2 +- .github/workflows/pre-release-optimism.yml | 2 +- .github/workflows/pre-release-polygon-zkevm.yml | 2 +- .github/workflows/pre-release-redstone.yml | 2 +- .github/workflows/pre-release-scroll.yml | 2 +- .github/workflows/pre-release-shibarium.yml | 2 +- .github/workflows/pre-release-zilliqa.yml | 2 +- .github/workflows/pre-release-zksync.yml | 2 +- .github/workflows/pre-release.yml | 2 +- .github/workflows/publish-docker-image-every-push.yml | 2 +- .github/workflows/publish-docker-image-for-arbitrum.yml | 2 +- .github/workflows/publish-docker-image-for-blackfort.yml | 2 +- .github/workflows/publish-docker-image-for-celo.yml | 2 +- .github/workflows/publish-docker-image-for-core.yml | 2 +- .github/workflows/publish-docker-image-for-eth-sepolia.yml | 2 +- .github/workflows/publish-docker-image-for-eth.yml | 2 +- .github/workflows/publish-docker-image-for-filecoin.yml | 2 +- .github/workflows/publish-docker-image-for-fuse.yml | 2 +- .github/workflows/publish-docker-image-for-gnosis-chain.yml | 2 +- .github/workflows/publish-docker-image-for-l2-staging.yml | 2 +- .github/workflows/publish-docker-image-for-lukso.yml | 2 +- .github/workflows/publish-docker-image-for-optimism.yml | 2 +- .github/workflows/publish-docker-image-for-polygon-edge.yml | 2 +- .github/workflows/publish-docker-image-for-redstone.yml | 2 +- .github/workflows/publish-docker-image-for-rootstock.yml | 2 +- .github/workflows/publish-docker-image-for-scroll.yml | 2 +- .github/workflows/publish-docker-image-for-shibarium.yml | 2 +- .github/workflows/publish-docker-image-for-stability.yml | 2 +- .github/workflows/publish-docker-image-for-suave.yml | 2 +- .github/workflows/publish-docker-image-for-zetachain.yml | 2 +- .github/workflows/publish-docker-image-for-zilliqa.yml | 2 +- .github/workflows/publish-docker-image-for-zkevm.yml | 2 +- .github/workflows/publish-docker-image-for-zksync.yml | 2 +- .github/workflows/publish-docker-image-staging-on-demand.yml | 2 +- .github/workflows/publish-regular-docker-image-on-demand.yml | 2 +- .github/workflows/release-arbitrum.yml | 2 +- .github/workflows/release-blackfort.yml | 2 +- .github/workflows/release-celo.yml | 2 +- .github/workflows/release-eth.yml | 2 +- .github/workflows/release-filecoin.yml | 2 +- .github/workflows/release-fuse.yml | 2 +- .github/workflows/release-gnosis.yml | 2 +- .github/workflows/release-optimism.yml | 2 +- .github/workflows/release-polygon-edge.yml | 2 +- .github/workflows/release-polygon-zkevm.yml | 2 +- .github/workflows/release-redstone.yml | 2 +- .github/workflows/release-rootstock.yml | 2 +- .github/workflows/release-scroll.yml | 2 +- .github/workflows/release-shibarium.yml | 2 +- .github/workflows/release-stability.yml | 2 +- .github/workflows/release-suave.yml | 2 +- .github/workflows/release-zetachain.yml | 2 +- .github/workflows/release-zilliqa.yml | 2 +- .github/workflows/release-zksync.yml | 2 +- .github/workflows/release.yml | 2 +- apps/block_scout_web/mix.exs | 2 +- apps/ethereum_jsonrpc/mix.exs | 2 +- apps/explorer/lib/explorer/token/metadata_retriever.ex | 2 +- apps/explorer/mix.exs | 2 +- apps/indexer/mix.exs | 2 +- docker-compose/docker-compose.yml | 2 +- docker/Makefile | 2 +- mix.exs | 2 +- rel/config.exs | 2 +- 68 files changed, 68 insertions(+), 68 deletions(-) diff --git a/.github/workflows/pre-release-arbitrum.yml b/.github/workflows/pre-release-arbitrum.yml index ed4cf4b7abb7..6a5de3655a59 100644 --- a/.github/workflows/pre-release-arbitrum.yml +++ b/.github/workflows/pre-release-arbitrum.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-blackfort.yml b/.github/workflows/pre-release-blackfort.yml index a11e4d0ea997..2ffe9133bce9 100644 --- a/.github/workflows/pre-release-blackfort.yml +++ b/.github/workflows/pre-release-blackfort.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-celo.yml b/.github/workflows/pre-release-celo.yml index 6e2dc8a8c54d..b441005d4041 100644 --- a/.github/workflows/pre-release-celo.yml +++ b/.github/workflows/pre-release-celo.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 API_GRAPHQL_MAX_COMPLEXITY: 10400 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/pre-release-eth.yml b/.github/workflows/pre-release-eth.yml index b0a9a5b3c60a..593f3c4f2109 100644 --- a/.github/workflows/pre-release-eth.yml +++ b/.github/workflows/pre-release-eth.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-filecoin.yml b/.github/workflows/pre-release-filecoin.yml index addb62d6422d..d7c9a5a6acf5 100644 --- a/.github/workflows/pre-release-filecoin.yml +++ b/.github/workflows/pre-release-filecoin.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-optimism.yml b/.github/workflows/pre-release-optimism.yml index 65abb40145e1..673fa9304cf4 100644 --- a/.github/workflows/pre-release-optimism.yml +++ b/.github/workflows/pre-release-optimism.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-polygon-zkevm.yml b/.github/workflows/pre-release-polygon-zkevm.yml index 7f8f3c68b88e..59a8dd38e95d 100644 --- a/.github/workflows/pre-release-polygon-zkevm.yml +++ b/.github/workflows/pre-release-polygon-zkevm.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-redstone.yml b/.github/workflows/pre-release-redstone.yml index d790e7d5b11e..09358ed202b1 100644 --- a/.github/workflows/pre-release-redstone.yml +++ b/.github/workflows/pre-release-redstone.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-scroll.yml b/.github/workflows/pre-release-scroll.yml index fddbb5f6838f..84d479593dfb 100644 --- a/.github/workflows/pre-release-scroll.yml +++ b/.github/workflows/pre-release-scroll.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-shibarium.yml b/.github/workflows/pre-release-shibarium.yml index 0ad4d80ef610..e34c7f0f3fe2 100644 --- a/.github/workflows/pre-release-shibarium.yml +++ b/.github/workflows/pre-release-shibarium.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-zilliqa.yml b/.github/workflows/pre-release-zilliqa.yml index d466de1cd41d..4a40958eed00 100644 --- a/.github/workflows/pre-release-zilliqa.yml +++ b/.github/workflows/pre-release-zilliqa.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-zksync.yml b/.github/workflows/pre-release-zksync.yml index c9766e6ee45f..f8b504a1f731 100644 --- a/.github/workflows/pre-release-zksync.yml +++ b/.github/workflows/pre-release-zksync.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index 5624427b48ec..027f7a12562a 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/publish-docker-image-every-push.yml b/.github/workflows/publish-docker-image-every-push.yml index 705c51fa9648..6ae1b0e4ce6d 100644 --- a/.github/workflows/publish-docker-image-every-push.yml +++ b/.github/workflows/publish-docker-image-every-push.yml @@ -11,7 +11,7 @@ on: env: OTP_VERSION: ${{ vars.OTP_VERSION }} ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 jobs: push_to_registry: diff --git a/.github/workflows/publish-docker-image-for-arbitrum.yml b/.github/workflows/publish-docker-image-for-arbitrum.yml index 658253d747fe..b8be835c7dfe 100644 --- a/.github/workflows/publish-docker-image-for-arbitrum.yml +++ b/.github/workflows/publish-docker-image-for-arbitrum.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: arbitrum steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-blackfort.yml b/.github/workflows/publish-docker-image-for-blackfort.yml index 3c26abc3f262..41ded9361071 100644 --- a/.github/workflows/publish-docker-image-for-blackfort.yml +++ b/.github/workflows/publish-docker-image-for-blackfort.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: blackfort steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-celo.yml b/.github/workflows/publish-docker-image-for-celo.yml index d8351c66186e..61b324cb414b 100644 --- a/.github/workflows/publish-docker-image-for-celo.yml +++ b/.github/workflows/publish-docker-image-for-celo.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: celo API_GRAPHQL_MAX_COMPLEXITY: 10400 steps: diff --git a/.github/workflows/publish-docker-image-for-core.yml b/.github/workflows/publish-docker-image-for-core.yml index 49066e82665c..1e6f4fb38a04 100644 --- a/.github/workflows/publish-docker-image-for-core.yml +++ b/.github/workflows/publish-docker-image-for-core.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: poa steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-eth-sepolia.yml b/.github/workflows/publish-docker-image-for-eth-sepolia.yml index fb7a5dc89faa..a94e8bd3936b 100644 --- a/.github/workflows/publish-docker-image-for-eth-sepolia.yml +++ b/.github/workflows/publish-docker-image-for-eth-sepolia.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: eth-sepolia steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-eth.yml b/.github/workflows/publish-docker-image-for-eth.yml index 0bb766b34651..6d7248b707d1 100644 --- a/.github/workflows/publish-docker-image-for-eth.yml +++ b/.github/workflows/publish-docker-image-for-eth.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: mainnet steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-filecoin.yml b/.github/workflows/publish-docker-image-for-filecoin.yml index ed78cb005286..9dce15aeca19 100644 --- a/.github/workflows/publish-docker-image-for-filecoin.yml +++ b/.github/workflows/publish-docker-image-for-filecoin.yml @@ -9,7 +9,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: filecoin steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-fuse.yml b/.github/workflows/publish-docker-image-for-fuse.yml index a7000d911486..7b14ecda818e 100644 --- a/.github/workflows/publish-docker-image-for-fuse.yml +++ b/.github/workflows/publish-docker-image-for-fuse.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: fuse steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-gnosis-chain.yml b/.github/workflows/publish-docker-image-for-gnosis-chain.yml index 156656e2217c..9d73227b52ea 100644 --- a/.github/workflows/publish-docker-image-for-gnosis-chain.yml +++ b/.github/workflows/publish-docker-image-for-gnosis-chain.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: xdai steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-l2-staging.yml b/.github/workflows/publish-docker-image-for-l2-staging.yml index 5ada53ec4169..7c03c5599f42 100644 --- a/.github/workflows/publish-docker-image-for-l2-staging.yml +++ b/.github/workflows/publish-docker-image-for-l2-staging.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: optimism-l2-advanced steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-lukso.yml b/.github/workflows/publish-docker-image-for-lukso.yml index 03cb63e2b283..8b910865b2fd 100644 --- a/.github/workflows/publish-docker-image-for-lukso.yml +++ b/.github/workflows/publish-docker-image-for-lukso.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: lukso steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-optimism.yml b/.github/workflows/publish-docker-image-for-optimism.yml index a48ffcba524b..b31b4422f2c6 100644 --- a/.github/workflows/publish-docker-image-for-optimism.yml +++ b/.github/workflows/publish-docker-image-for-optimism.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: optimism steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-polygon-edge.yml b/.github/workflows/publish-docker-image-for-polygon-edge.yml index 99217b00860b..ada5c6f34600 100644 --- a/.github/workflows/publish-docker-image-for-polygon-edge.yml +++ b/.github/workflows/publish-docker-image-for-polygon-edge.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: polygon-edge steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-redstone.yml b/.github/workflows/publish-docker-image-for-redstone.yml index 73fbd6ef0949..0b80420c4e4a 100644 --- a/.github/workflows/publish-docker-image-for-redstone.yml +++ b/.github/workflows/publish-docker-image-for-redstone.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: redstone steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-rootstock.yml b/.github/workflows/publish-docker-image-for-rootstock.yml index 772ff3af8dcf..117d4e7423ca 100644 --- a/.github/workflows/publish-docker-image-for-rootstock.yml +++ b/.github/workflows/publish-docker-image-for-rootstock.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: rsk steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-scroll.yml b/.github/workflows/publish-docker-image-for-scroll.yml index 58f29c8e1c02..3a6d2eea110d 100644 --- a/.github/workflows/publish-docker-image-for-scroll.yml +++ b/.github/workflows/publish-docker-image-for-scroll.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: scroll steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-shibarium.yml b/.github/workflows/publish-docker-image-for-shibarium.yml index 70f83d02c356..db54268d4b71 100644 --- a/.github/workflows/publish-docker-image-for-shibarium.yml +++ b/.github/workflows/publish-docker-image-for-shibarium.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: shibarium steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-stability.yml b/.github/workflows/publish-docker-image-for-stability.yml index 6ffbdb78cb92..578fb0a0e924 100644 --- a/.github/workflows/publish-docker-image-for-stability.yml +++ b/.github/workflows/publish-docker-image-for-stability.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: stability steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-suave.yml b/.github/workflows/publish-docker-image-for-suave.yml index 9e1fec6a1ae8..b5b496061ebf 100644 --- a/.github/workflows/publish-docker-image-for-suave.yml +++ b/.github/workflows/publish-docker-image-for-suave.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: suave steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-zetachain.yml b/.github/workflows/publish-docker-image-for-zetachain.yml index f93b780c1771..adb84c5ddb34 100644 --- a/.github/workflows/publish-docker-image-for-zetachain.yml +++ b/.github/workflows/publish-docker-image-for-zetachain.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: zetachain steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-zilliqa.yml b/.github/workflows/publish-docker-image-for-zilliqa.yml index d08d19df17f7..5051b844ad2b 100644 --- a/.github/workflows/publish-docker-image-for-zilliqa.yml +++ b/.github/workflows/publish-docker-image-for-zilliqa.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: zilliqa steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-zkevm.yml b/.github/workflows/publish-docker-image-for-zkevm.yml index aa37c36b7fac..463515911056 100644 --- a/.github/workflows/publish-docker-image-for-zkevm.yml +++ b/.github/workflows/publish-docker-image-for-zkevm.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: zkevm steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-zksync.yml b/.github/workflows/publish-docker-image-for-zksync.yml index 4ea5cf08c8df..56dbd127097e 100644 --- a/.github/workflows/publish-docker-image-for-zksync.yml +++ b/.github/workflows/publish-docker-image-for-zksync.yml @@ -9,7 +9,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 DOCKER_CHAIN_NAME: zksync steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-staging-on-demand.yml b/.github/workflows/publish-docker-image-staging-on-demand.yml index a9ab37472f42..7cd48280eb5f 100644 --- a/.github/workflows/publish-docker-image-staging-on-demand.yml +++ b/.github/workflows/publish-docker-image-staging-on-demand.yml @@ -12,7 +12,7 @@ on: env: OTP_VERSION: ${{ vars.OTP_VERSION }} ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 jobs: push_to_registry: diff --git a/.github/workflows/publish-regular-docker-image-on-demand.yml b/.github/workflows/publish-regular-docker-image-on-demand.yml index 82cf59b281a1..100cb8472d46 100644 --- a/.github/workflows/publish-regular-docker-image-on-demand.yml +++ b/.github/workflows/publish-regular-docker-image-on-demand.yml @@ -5,7 +5,7 @@ on: env: OTP_VERSION: ${{ vars.OTP_VERSION }} ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 jobs: push_to_registry: diff --git a/.github/workflows/release-arbitrum.yml b/.github/workflows/release-arbitrum.yml index dcae4d8473d0..d0a7eadf5aca 100644 --- a/.github/workflows/release-arbitrum.yml +++ b/.github/workflows/release-arbitrum.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-blackfort.yml b/.github/workflows/release-blackfort.yml index d8e72c5174fa..f0b67e28b8c6 100644 --- a/.github/workflows/release-blackfort.yml +++ b/.github/workflows/release-blackfort.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-celo.yml b/.github/workflows/release-celo.yml index b18f83ce7a3c..1d3f419dffd3 100644 --- a/.github/workflows/release-celo.yml +++ b/.github/workflows/release-celo.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 API_GRAPHQL_MAX_COMPLEXITY: 10400 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release-eth.yml b/.github/workflows/release-eth.yml index f17b4e73a1ab..eb5c1fb089e9 100644 --- a/.github/workflows/release-eth.yml +++ b/.github/workflows/release-eth.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-filecoin.yml b/.github/workflows/release-filecoin.yml index 1e9226909e37..9a7508059077 100644 --- a/.github/workflows/release-filecoin.yml +++ b/.github/workflows/release-filecoin.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-fuse.yml b/.github/workflows/release-fuse.yml index dc66bc5752be..14fc591eba69 100644 --- a/.github/workflows/release-fuse.yml +++ b/.github/workflows/release-fuse.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-gnosis.yml b/.github/workflows/release-gnosis.yml index 54ec2c489950..e42d92f30c4a 100644 --- a/.github/workflows/release-gnosis.yml +++ b/.github/workflows/release-gnosis.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-optimism.yml b/.github/workflows/release-optimism.yml index f9b90637dc85..2fccba0ccecf 100644 --- a/.github/workflows/release-optimism.yml +++ b/.github/workflows/release-optimism.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-polygon-edge.yml b/.github/workflows/release-polygon-edge.yml index 3d911dcb1cb7..17142d6fa71a 100644 --- a/.github/workflows/release-polygon-edge.yml +++ b/.github/workflows/release-polygon-edge.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-polygon-zkevm.yml b/.github/workflows/release-polygon-zkevm.yml index d9d093b06ae6..be273e8330be 100644 --- a/.github/workflows/release-polygon-zkevm.yml +++ b/.github/workflows/release-polygon-zkevm.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-redstone.yml b/.github/workflows/release-redstone.yml index 78fcef603f2b..bfa2fc510245 100644 --- a/.github/workflows/release-redstone.yml +++ b/.github/workflows/release-redstone.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-rootstock.yml b/.github/workflows/release-rootstock.yml index 73396bc8e77c..dbf2ac5d79b4 100644 --- a/.github/workflows/release-rootstock.yml +++ b/.github/workflows/release-rootstock.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-scroll.yml b/.github/workflows/release-scroll.yml index ba1ed72ef4f9..a7c9c2e9cc6f 100644 --- a/.github/workflows/release-scroll.yml +++ b/.github/workflows/release-scroll.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-shibarium.yml b/.github/workflows/release-shibarium.yml index 84816989dd6c..d3ca78c811b1 100644 --- a/.github/workflows/release-shibarium.yml +++ b/.github/workflows/release-shibarium.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-stability.yml b/.github/workflows/release-stability.yml index eb4aef50af5a..6cda8ed056a7 100644 --- a/.github/workflows/release-stability.yml +++ b/.github/workflows/release-stability.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-suave.yml b/.github/workflows/release-suave.yml index 91b63e386d61..fdb07ec963a8 100644 --- a/.github/workflows/release-suave.yml +++ b/.github/workflows/release-suave.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-zetachain.yml b/.github/workflows/release-zetachain.yml index 74713ff9f829..85b77055ac9b 100644 --- a/.github/workflows/release-zetachain.yml +++ b/.github/workflows/release-zetachain.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-zilliqa.yml b/.github/workflows/release-zilliqa.yml index 515bfa8a143d..e855f5971447 100644 --- a/.github/workflows/release-zilliqa.yml +++ b/.github/workflows/release-zilliqa.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-zksync.yml b/.github/workflows/release-zksync.yml index ae98f0bac505..48160b844b72 100644 --- a/.github/workflows/release-zksync.yml +++ b/.github/workflows/release-zksync.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 716b27254243..66f70c1e22eb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs index 1db8dbb12457..f50c75e3e23f 100644 --- a/apps/block_scout_web/mix.exs +++ b/apps/block_scout_web/mix.exs @@ -23,7 +23,7 @@ defmodule BlockScoutWeb.Mixfile do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "6.9.0", + version: "6.9.1", xref: [ exclude: [ Explorer.Chain.PolygonZkevm.Reader, diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs index ddf0dd970584..153b2c9d3ec4 100644 --- a/apps/ethereum_jsonrpc/mix.exs +++ b/apps/ethereum_jsonrpc/mix.exs @@ -23,7 +23,7 @@ defmodule EthereumJSONRPC.MixProject do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "6.9.0" + version: "6.9.1" ] end diff --git a/apps/explorer/lib/explorer/token/metadata_retriever.ex b/apps/explorer/lib/explorer/token/metadata_retriever.ex index 22e958ecb573..f1b6b3489ce7 100644 --- a/apps/explorer/lib/explorer/token/metadata_retriever.ex +++ b/apps/explorer/lib/explorer/token/metadata_retriever.ex @@ -14,7 +14,7 @@ defmodule Explorer.Token.MetadataRetriever do @no_uri_error "no uri" @vm_execution_error "VM execution error" @invalid_base64_data "invalid data:application/json;base64" - @default_headers [{"User-Agent", "blockscout-6.9.0"}] + @default_headers [{"User-Agent", "blockscout-6.9.1"}] # https://eips.ethereum.org/EIPS/eip-1155#metadata @erc1155_token_id_placeholder "{id}" diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index c07e1b385212..f9bec33effef 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -24,7 +24,7 @@ defmodule Explorer.Mixfile do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "6.9.0", + version: "6.9.1", xref: [exclude: [BlockScoutWeb.Routers.WebRouter.Helpers, Indexer.Helper]] ] end diff --git a/apps/indexer/mix.exs b/apps/indexer/mix.exs index 13c68614732d..eca20bcbc880 100644 --- a/apps/indexer/mix.exs +++ b/apps/indexer/mix.exs @@ -14,7 +14,7 @@ defmodule Indexer.MixProject do elixirc_paths: elixirc_paths(Mix.env()), lockfile: "../../mix.lock", start_permanent: Mix.env() == :prod, - version: "6.9.0", + version: "6.9.1", xref: [ exclude: [ Explorer.Chain.Optimism.Deposit, diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index 43134d888572..d91d67adf356 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -37,7 +37,7 @@ services: CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED: "" CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL: "" ADMIN_PANEL_ENABLED: "" - RELEASE_VERSION: 6.9.0 + RELEASE_VERSION: 6.9.1 links: - db:database environment: diff --git a/docker/Makefile b/docker/Makefile index 6b4eecee6861..4735936b4117 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -10,7 +10,7 @@ STATS_CONTAINER_NAME := stats STATS_DB_CONTAINER_NAME := stats-db PROXY_CONTAINER_NAME := proxy PG_CONTAINER_NAME := postgres -RELEASE_VERSION ?= '6.9.0' +RELEASE_VERSION ?= '6.9.1' TAG := $(RELEASE_VERSION)-commit-$(shell git log -1 --pretty=format:"%h") STABLE_TAG := $(RELEASE_VERSION) diff --git a/mix.exs b/mix.exs index 52e7aa844cb6..40eb4eaba8ab 100644 --- a/mix.exs +++ b/mix.exs @@ -7,7 +7,7 @@ defmodule BlockScout.Mixfile do [ # app: :block_scout, # aliases: aliases(config_env()), - version: "6.9.0", + version: "6.9.1", apps_path: "apps", deps: deps(), dialyzer: dialyzer(), diff --git a/rel/config.exs b/rel/config.exs index d7141e3c2c67..ec27a9a3e3f0 100644 --- a/rel/config.exs +++ b/rel/config.exs @@ -71,7 +71,7 @@ end # will be used by default release :blockscout do - set version: "6.9.0-beta" + set version: "6.9.1-beta" set applications: [ :runtime_tools, block_scout_web: :permanent, From d5709dfda9e5b8184b192cbafafb890d5891a93e Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:40:28 +0300 Subject: [PATCH 322/363] feat: Add Stylus verificaiton support (#11183) * feat: Add Stylus verificaiton support * Fix tests * Apply suggestions from code review Co-authored-by: Alexander Kolotov * Refactoring + handle snake case * Add env to docker-compose/envs/common-blockscout.env * Apply suggestions from code review Co-authored-by: Alexander Kolotov * Fix cspell * Drop MICROSERVICE_STYLUS_VERIFIER_ENABLED * Fix tests * Fix tests * Apply suggestions from code review Co-authored-by: Alexander Kolotov * Process review comments * Fix dialyzer --------- Co-authored-by: Alexander Kolotov --- .dialyzer-ignore | 2 + .../api/v2/verification_controller.ex | 71 +++++- .../routers/smart_contracts_api_v2_router.ex | 4 + .../views/api/v2/filecoin_view.ex | 9 +- .../views/api/v2/smart_contract_view.ex | 37 ++- .../lib/explorer/chain/smart_contract.ex | 20 +- .../smart_contract/compiler_version.ex | 81 +++--- .../smart_contract/solidity/publisher.ex | 3 +- .../solidity/publisher_worker.ex | 7 +- .../smart_contract/stylus/publisher.ex | 235 ++++++++++++++++++ .../smart_contract/stylus/publisher_worker.ex | 72 ++++++ .../smart_contract/stylus/verifier.ex | 108 ++++++++ .../stylus_verifier_interface.ex | 165 ++++++++++++ .../smart_contract/vyper/publisher.ex | 3 +- .../smart_contract/vyper/publisher_worker.ex | 7 +- .../20241111195112_add_stylus_fields.exs | 10 + .../20241111200520_add_language_field.exs | 9 + config/config_helper.exs | 38 +++ config/runtime.exs | 3 + docker-compose/envs/common-blockscout.env | 1 + 20 files changed, 833 insertions(+), 52 deletions(-) create mode 100644 apps/explorer/lib/explorer/smart_contract/stylus/publisher.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/stylus/publisher_worker.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/stylus/verifier.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/stylus_verifier_interface.ex create mode 100644 apps/explorer/priv/arbitrum/migrations/20241111195112_add_stylus_fields.exs create mode 100644 apps/explorer/priv/repo/migrations/20241111200520_add_language_field.exs diff --git a/.dialyzer-ignore b/.dialyzer-ignore index 056ffa415ce2..941f77a1f158 100644 --- a/.dialyzer-ignore +++ b/.dialyzer-ignore @@ -1,6 +1,8 @@ lib/ethereum_jsonrpc/rolling_window.ex:171 lib/explorer/smart_contract/solidity/publisher_worker.ex:1 lib/explorer/smart_contract/vyper/publisher_worker.ex:1 +lib/explorer/smart_contract/stylus/publisher_worker.ex:1 lib/explorer/smart_contract/solidity/publisher_worker.ex:8 lib/explorer/smart_contract/vyper/publisher_worker.ex:8 +lib/explorer/smart_contract/stylus/publisher_worker.ex:8 lib/phoenix/router.ex:402 diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex index 3176bde5a8ee..5450b97173e4 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex @@ -11,8 +11,9 @@ defmodule BlockScoutWeb.API.V2.VerificationController do alias Explorer.Chain.SmartContract alias Explorer.SmartContract.Solidity.PublisherWorker, as: SolidityPublisherWorker alias Explorer.SmartContract.Solidity.PublishHelper + alias Explorer.SmartContract.Stylus.PublisherWorker, as: StylusPublisherWorker alias Explorer.SmartContract.Vyper.PublisherWorker, as: VyperPublisherWorker - alias Explorer.SmartContract.{CompilerVersion, RustVerifierInterface, Solidity.CodeCompiler} + alias Explorer.SmartContract.{CompilerVersion, RustVerifierInterface, Solidity.CodeCompiler, StylusVerifierInterface} action_fallback(BlockScoutWeb.API.V2.FallbackController) @@ -46,6 +47,7 @@ defmodule BlockScoutWeb.API.V2.VerificationController do config = base_config |> maybe_add_zk_options() + |> maybe_add_stylus_options() conn |> json(config) @@ -64,6 +66,10 @@ defmodule BlockScoutWeb.API.V2.VerificationController do do: ["multi-part", "vyper-multi-part", "vyper-standard-input"] ++ &1, else: &1 )).() + |> (&if(StylusVerifierInterface.enabled?(), + do: ["stylus-github-repository" | &1], + else: &1 + )).() end end @@ -79,6 +85,16 @@ defmodule BlockScoutWeb.API.V2.VerificationController do end end + # Adds Stylus compiler versions to config if Stylus verification is enabled + defp maybe_add_stylus_options(config) do + if StylusVerifierInterface.enabled?() do + config + |> Map.put(:stylus_compiler_versions, CompilerVersion.fetch_version_list(:stylus)) + else + config + end + end + def verification_via_flattened_code( conn, %{"address_hash" => address_hash_string, "compiler_version" => compiler_version, "source_code" => source_code} = @@ -291,6 +307,59 @@ defmodule BlockScoutWeb.API.V2.VerificationController do end end + @doc """ + Initiates verification of a Stylus smart contract using its GitHub repository source code. + + Validates the request parameters and queues the verification job to be processed + asynchronously by the Stylus publisher worker. + + ## Parameters + - `conn`: The connection struct + - `params`: A map containing: + - `address_hash`: Contract address to verify + - `cargo_stylus_version`: Version of cargo-stylus used for deployment + - `repository_url`: GitHub repository URL containing contract code + - `commit`: Git commit hash used for deployment + - `path_prefix`: Optional path prefix if contract is not in repository root + + ## Returns + - JSON response with: + - Success message if verification request is queued successfully + - Error message if: + - Stylus verification is not enabled + - Address format is invalid + - Contract is already verified + - Access is restricted + """ + @spec verification_via_stylus_github_repository(Plug.Conn.t(), %{String.t() => any()}) :: + {:already_verified, true} + | {:format, :error} + | {:not_found, false | nil} + | {:restricted_access, true} + | Plug.Conn.t() + def verification_via_stylus_github_repository( + conn, + %{ + "address_hash" => address_hash_string, + "cargo_stylus_version" => _, + "repository_url" => _, + "commit" => _, + "path_prefix" => _ + } = params + ) do + Logger.info("API v2 stylus smart-contract #{address_hash_string} verification via github repository") + + with {:not_found, true} <- {:not_found, StylusVerifierInterface.enabled?()}, + :validated <- validate_address(params) do + log_sc_verification_started(address_hash_string) + Que.add(StylusPublisherWorker, {"github_repository", params}) + + conn + |> put_view(ApiView) + |> render(:message, %{message: @sc_verification_started}) + end + end + defp parse_interfaces(interfaces) do cond do is_binary(interfaces) -> diff --git a/apps/block_scout_web/lib/block_scout_web/routers/smart_contracts_api_v2_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/smart_contracts_api_v2_router.ex index 479c83d24785..aee2f7363b69 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/smart_contracts_api_v2_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/smart_contracts_api_v2_router.ex @@ -79,5 +79,9 @@ defmodule BlockScoutWeb.Routers.SmartContractsApiV2Router do post("/vyper-multi-part", V2.VerificationController, :verification_via_vyper_multipart) post("/vyper-standard-input", V2.VerificationController, :verification_via_vyper_standard_input) end + + if Application.compile_env(:explorer, :chain_type) === :arbitrum do + post("/stylus-github-repository", V2.VerificationController, :verification_via_stylus_github_repository) + end end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex index 398bc56f67e9..82a2270b65c4 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex @@ -26,8 +26,9 @@ if Application.compile_env(:explorer, :chain_type) == :filecoin do end @spec preload_and_put_filecoin_robust_address(map(), %{ - address_hash: String.t() | nil, - field_prefix: String.t() | nil + optional(:address_hash) => String.t() | nil, + optional(:field_prefix) => String.t() | nil, + optional(any) => any }) :: map() def preload_and_put_filecoin_robust_address(result, %{address_hash: address_hash} = params) do @@ -36,6 +37,10 @@ if Application.compile_env(:explorer, :chain_type) == :filecoin do put_filecoin_robust_address(result, Map.put(params, :address, address)) end + def preload_and_put_filecoin_robust_address(result, _params) do + result + end + @doc """ Adds a Filecoin robust address to the given result. diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex index d895db108ff4..70da14e7fb09 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex @@ -230,8 +230,11 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do "is_blueprint" => if(smart_contract.is_blueprint, do: smart_contract.is_blueprint, else: false) } |> Map.merge(bytecode_info(address)) - |> add_zksync_info(target_contract) - |> chain_type_fields(%{address_hash: verified_twin_address_hash, field_prefix: "verified_twin"}) + |> chain_type_fields(%{ + address_hash: verified_twin_address_hash, + field_prefix: "verified_twin", + target_contract: target_contract + }) end def prepare_smart_contract(%Address{proxy_implementations: implementations} = address, conn) do @@ -284,16 +287,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do end end - defp add_zksync_info(smart_contract_info, target_contract) do - if Application.get_env(:explorer, :chain_type) == :zksync do - Map.merge(smart_contract_info, %{ - "zk_compiler_version" => target_contract.zk_compiler_version - }) - else - smart_contract_info - end - end - defp prepare_external_libraries(libraries) when is_list(libraries) do Enum.map(libraries, fn %Explorer.Chain.SmartContract.ExternalLibrary{name: name, address_hash: address_hash} -> {:ok, hash} = Chain.string_to_address_hash(address_hash) @@ -366,7 +359,9 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do } smart_contract_info - |> add_zksync_info(smart_contract) + |> chain_type_fields(%{ + target_contract: smart_contract + }) end defp smart_contract_language(smart_contract) do @@ -374,6 +369,9 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do smart_contract.is_vyper_contract -> "vyper" + not is_nil(smart_contract.language) -> + smart_contract.language + is_nil(smart_contract.abi) -> "yul" @@ -452,6 +450,19 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do BlockScoutWeb.API.V2.FilecoinView.preload_and_put_filecoin_robust_address(result, params) end + :arbitrum -> + defp chain_type_fields(result, %{target_contract: target_contract}) do + result + |> Map.put("package_name", target_contract.package_name) + |> Map.put("github_repository_metadata", target_contract.github_repository_metadata) + end + + :zksync -> + defp chain_type_fields(result, %{target_contract: target_contract}) do + result + |> Map.put("zk_compiler_version", target_contract.zk_compiler_version) + end + _ -> defp chain_type_fields(result, _address) do result diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index 09f8d297bb6b..1fc439ee344b 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -20,6 +20,15 @@ defmodule Explorer.Chain.SmartContract.Schema do ] ) + :arbitrum -> + @chain_type_fields quote( + do: [ + field(:package_name, :string), + field(:github_repository_metadata, :map), + field(:optimization_runs, :integer) + ] + ) + _ -> @chain_type_fields quote(do: [field(:optimization_runs, :integer)]) end @@ -51,6 +60,7 @@ defmodule Explorer.Chain.SmartContract.Schema do field(:license_type, Ecto.Enum, values: @license_enum, default: :none) field(:certified, :boolean) field(:is_blueprint, :boolean) + field(:language, Ecto.Enum, values: [solidity: 1, vyper: 2, yul: 3, stylus_rust: 4], default: :solidity) has_many( :decompiled_smart_contracts, @@ -123,7 +133,7 @@ defmodule Explorer.Chain.SmartContract do @burn_address_hash_string "0x0000000000000000000000000000000000000000" @dead_address_hash_string "0x000000000000000000000000000000000000dEaD" - @required_attrs ~w(compiler_version optimization address_hash contract_code_md5)a + @required_attrs ~w(compiler_version optimization address_hash contract_code_md5 language)a @optional_common_attrs ~w(name contract_source_code evm_version optimization_runs constructor_arguments verified_via_sourcify verified_via_eth_bytecode_db verified_via_verifier_alliance partially_verified file_path is_vyper_contract is_changed_bytecode bytecode_checked_at autodetect_constructor_args license_type certified is_blueprint)a @@ -134,6 +144,9 @@ defmodule Explorer.Chain.SmartContract do :zksync -> ~w(zk_compiler_version)a + :arbitrum -> + ~w(package_name github_repository_metadata)a + _ -> ~w()a end) @@ -388,6 +401,10 @@ defmodule Explorer.Chain.SmartContract do :zksync -> """ * `zk_compiler_version` - the version of ZkSolc or ZkVyper compilers. """ + :arbitrum -> """ + * `package_name` - package name of stylus contract. + * `github_repository_metadata` - map with repository details. + """ _ -> "" end} * `optimization` - whether optimizations were turned on when compiling `contract_source_code` into `address` @@ -410,6 +427,7 @@ defmodule Explorer.Chain.SmartContract do * `is_yul` - field was added for storing user's choice * `certified` - boolean flag, which can be set for set of smart-contracts via runtime env variable to prioritize those smart-contracts in the search. * `is_blueprint` - boolean flag, determines if contract is ERC-5202 compatible blueprint contract or not. + * `language` - enum for smart contract language tracking, stands for getting rid of is_vyper_contract/is_yul bool flags. """ Explorer.Chain.SmartContract.Schema.generate() diff --git a/apps/explorer/lib/explorer/smart_contract/compiler_version.ex b/apps/explorer/lib/explorer/smart_contract/compiler_version.ex index 0151ee24bd4f..99f164c43e40 100644 --- a/apps/explorer/lib/explorer/smart_contract/compiler_version.ex +++ b/apps/explorer/lib/explorer/smart_contract/compiler_version.ex @@ -4,7 +4,7 @@ defmodule Explorer.SmartContract.CompilerVersion do """ alias Explorer.Helper - alias Explorer.SmartContract.RustVerifierInterface + alias Explorer.SmartContract.{RustVerifierInterface, StylusVerifierInterface} @unsupported_solc_versions ~w(0.1.1 0.1.2) @unsupported_vyper_versions ~w(v0.2.9 v0.2.10) @@ -12,15 +12,38 @@ defmodule Explorer.SmartContract.CompilerVersion do @doc """ Fetches a list of compilers from the Ethereum Solidity API. """ - @spec fetch_versions(:solc | :vyper | :zk) :: {atom, [map]} - def fetch_versions(compiler) do - case compiler do - :solc -> fetch_solc_versions() - :vyper -> fetch_vyper_versions() - :zk -> fetch_zk_versions() - end + @spec fetch_versions(:solc | :vyper | :zk | :stylus) :: {atom, [binary()]} + def fetch_versions(compiler) + + def fetch_versions(:solc) do + fetch_compiler_versions(&RustVerifierInterface.get_versions_list/0, :solc) + end + + def fetch_versions(:vyper) do + fetch_compiler_versions(&RustVerifierInterface.vyper_get_versions_list/0, :vyper) + end + + def fetch_versions(:zk) do + fetch_compiler_versions(&RustVerifierInterface.get_versions_list/0, :zk) + end + + def fetch_versions(:stylus) do + fetch_compiler_versions(&StylusVerifierInterface.get_versions_list/0, :stylus) end + @doc """ + Fetches the list of compiler versions for the given compiler. + + ## Parameters + + - compiler: The name of the compiler for which to fetch the version list. + + ## Returns + + - A list of available compiler versions. + + """ + @spec fetch_version_list(:solc | :vyper | :zk | :stylus) :: [binary()] def fetch_version_list(compiler) do case fetch_versions(compiler) do {:ok, compiler_versions} -> @@ -31,38 +54,38 @@ defmodule Explorer.SmartContract.CompilerVersion do end end - defp fetch_solc_versions do - fetch_compiler_versions(&RustVerifierInterface.get_versions_list/0, :solc) - end - - defp fetch_zk_versions do - fetch_compiler_versions(&RustVerifierInterface.get_versions_list/0, :zk) + defp fetch_compiler_versions(compiler_list_fn, :stylus = compiler_type) do + if StylusVerifierInterface.enabled?() do + fetch_compiler_versions_sc_verified_enabled(compiler_list_fn, compiler_type) + else + {:ok, []} + end end - defp fetch_vyper_versions do - fetch_compiler_versions(&RustVerifierInterface.vyper_get_versions_list/0, :vyper) + defp fetch_compiler_versions(compiler_list_fn, :zk = compiler_type) do + if RustVerifierInterface.enabled?() do + fetch_compiler_versions_sc_verified_enabled(compiler_list_fn, compiler_type) + else + {:ok, []} + end end defp fetch_compiler_versions(compiler_list_fn, compiler_type) do if RustVerifierInterface.enabled?() do fetch_compiler_versions_sc_verified_enabled(compiler_list_fn, compiler_type) else - if compiler_type == :zk do - {:ok, []} - else - headers = [{"Content-Type", "application/json"}] + headers = [{"Content-Type", "application/json"}] - # credo:disable-for-next-line - case HTTPoison.get(source_url(compiler_type), headers) do - {:ok, %{status_code: 200, body: body}} -> - {:ok, format_data(body, compiler_type)} + # credo:disable-for-next-line + case HTTPoison.get(source_url(compiler_type), headers) do + {:ok, %{status_code: 200, body: body}} -> + {:ok, format_data(body, compiler_type)} - {:ok, %{status_code: _status_code, body: body}} -> - {:error, Helper.decode_json(body)["error"]} + {:ok, %{status_code: _status_code, body: body}} -> + {:error, Helper.decode_json(body)["error"]} - {:error, %{reason: reason}} -> - {:error, reason} - end + {:error, %{reason: reason}} -> + {:error, reason} end end end diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex b/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex index cf24dcae9af3..908f90241a09 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex @@ -405,7 +405,8 @@ defmodule Explorer.SmartContract.Solidity.Publisher do is_yul: params["is_yul"] || false, compiler_settings: clean_compiler_settings, license_type: prepare_license_type(params["license_type"]) || :none, - is_blueprint: params["is_blueprint"] || false + is_blueprint: params["is_blueprint"] || false, + language: (is_nil(abi) && :yul) || :solidity } base_attributes diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex b/apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex index ae2caacc5e5a..f1d4dff9de43 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex @@ -74,9 +74,12 @@ defmodule Explorer.SmartContract.Solidity.PublisherWorker do Logger.info("Smart-contract #{address_hash} verification: broadcast verification results") if conn do - EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result, conn}}], :on_demand) + EventsPublisher.broadcast( + [{:contract_verification_result, {String.downcase(address_hash), result, conn}}], + :on_demand + ) else - EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result}}], :on_demand) + EventsPublisher.broadcast([{:contract_verification_result, {String.downcase(address_hash), result}}], :on_demand) end end diff --git a/apps/explorer/lib/explorer/smart_contract/stylus/publisher.ex b/apps/explorer/lib/explorer/smart_contract/stylus/publisher.ex new file mode 100644 index 000000000000..06dc5bfc6cd4 --- /dev/null +++ b/apps/explorer/lib/explorer/smart_contract/stylus/publisher.ex @@ -0,0 +1,235 @@ +defmodule Explorer.SmartContract.Stylus.Publisher do + @moduledoc """ + Module responsible for verifying and publishing Stylus smart contracts. + + The verification process includes: + 1. Initiating verification through a microservice that compares GitHub repository + source code against deployed bytecode + 2. Processing the verification response, including ABI and source files + 3. Creating or updating the smart contract record in the database + 4. Handling verification failures by creating invalid changesets with error messages + """ + + require Logger + + alias Explorer.Chain.SmartContract + alias Explorer.SmartContract.Helper + alias Explorer.SmartContract.Stylus.Verifier + + @default_file_name "src/lib.rs" + + @sc_verification_via_github_repository_started "Smart-contract verification via Github repository started" + + @doc """ + Verifies and publishes a Stylus smart contract using GitHub repository source code. + + Initiates verification of a contract through the verification microservice. On + successful verification, processes and stores the contract details in the + database. On failure, creates an invalid changeset with appropriate error + messages. + + ## Parameters + - `address_hash`: The contract's address hash as binary or `t:Explorer.Chain.Hash.t/0` + - `params`: Map containing verification parameters: + - `"cargo_stylus_version"`: Version of cargo-stylus used for deployment + - `"repository_url"`: GitHub repository URL containing contract code + - `"commit"`: Git commit hash used for deployment + - `"path_prefix"`: Optional path prefix if contract is not in repository root + + ## Returns + - `{:ok, smart_contract}` if verification and database storage succeed + - `{:error, changeset}` if verification fails or there are validation errors + """ + @spec publish(binary() | Explorer.Chain.Hash.t(), %{String.t() => any()}) :: + {:error, Ecto.Changeset.t()} | {:ok, Explorer.Chain.SmartContract.t()} + def publish(address_hash, params) do + Logger.info(@sc_verification_via_github_repository_started) + + case Verifier.evaluate_authenticity(address_hash, params) do + { + :ok, + %{ + "abi" => _, + "cargo_stylus_version" => _, + "contract_name" => _, + "files" => _, + "package_name" => _, + "github_repository_metadata" => _ + } = result_params + } -> + process_verifier_response(result_params, address_hash) + + {:error, error} -> + {:error, unverified_smart_contract(address_hash, params, error, nil)} + + _ -> + {:error, unverified_smart_contract(address_hash, params, "Unexpected error", nil)} + end + end + + # Processes successful Stylus contract verification response and stores contract data. + # + # Takes the verification response from `evaluate_authenticity/2` containing verified contract + # details and prepares them for storage in the database. The main source file is extracted + # from `files` map using the default filename, while other files are stored as secondary + # sources. + # + # ## Parameters + # - `response`: Verification response map containing: + # - `abi`: Contract ABI as JSON string + # - `cargo_stylus_version`: Version of cargo-stylus used + # - `contract_name`: Name of the contract + # - `files`: Map of file paths to source code contents + # - `package_name`: Package name of the contract + # - `github_repository_metadata`: Repository metadata + # - `address_hash`: The contract's address hash as binary or `t:Explorer.Chain.Hash.t/0` + # + # ## Returns + # - `{:ok, smart_contract}` if database storage succeeds + # - `{:error, changeset}` if there are validation errors + # - `{:error, message}` if the database operation fails + @spec process_verifier_response(%{String.t() => any()}, binary() | Explorer.Chain.Hash.t()) :: + {:ok, Explorer.Chain.SmartContract.t()} | {:error, Ecto.Changeset.t() | String.t()} + defp process_verifier_response( + %{ + "abi" => abi_string, + "cargo_stylus_version" => cargo_stylus_version, + "contract_name" => contract_name, + "files" => files, + "package_name" => package_name, + "github_repository_metadata" => github_repository_metadata + }, + address_hash + ) do + secondary_sources = + for {file, code} <- files, + file != @default_file_name, + do: %{"file_name" => file, "contract_source_code" => code, "address_hash" => address_hash} + + contract_source_code = files[@default_file_name] + + prepared_params = + %{} + |> Map.put("compiler_version", cargo_stylus_version) + |> Map.put("contract_source_code", contract_source_code) + |> Map.put("name", contract_name) + |> Map.put("file_path", contract_source_code && @default_file_name) + |> Map.put("secondary_sources", secondary_sources) + |> Map.put("package_name", package_name) + |> Map.put("github_repository_metadata", github_repository_metadata) + + publish_smart_contract(address_hash, prepared_params, Jason.decode!(abi_string || "null")) + end + + # Stores information about a verified Stylus smart contract in the database. + # + # ## Parameters + # - `address_hash`: The contract's address hash as binary or `t:Explorer.Chain.Hash.t/0` + # - `params`: Map containing contract details: + # - `name`: Contract name + # - `file_path`: Path to the contract source file + # - `compiler_version`: Version of the Stylus compiler + # - `contract_source_code`: Source code of the contract + # - `secondary_sources`: Additional source files + # - `package_name`: Package name for Stylus contract + # - `github_repository_metadata`: Repository metadata + # - `abi`: Contract's ABI (Application Binary Interface) + # + # ## Returns + # - `{:ok, smart_contract}` if publishing succeeds + # - `{:error, changeset}` if there are validation errors + # - `{:error, message}` if the database operation fails + @spec publish_smart_contract(binary() | Explorer.Chain.Hash.t(), %{String.t() => any()}, map()) :: + {:error, Ecto.Changeset.t() | String.t()} | {:ok, Explorer.Chain.SmartContract.t()} + defp publish_smart_contract(address_hash, params, abi) do + attrs = address_hash |> attributes(params, abi) + + create_or_update_smart_contract(address_hash, attrs) + end + + # This function first checks if a smart contract already exists in the database + # at the given address. If it exists, updates the contract with new attributes. + # Otherwise, creates a new smart contract record. + @spec create_or_update_smart_contract(binary() | Explorer.Chain.Hash.t(), map()) :: + {:error, Ecto.Changeset.t() | String.t()} | {:ok, Explorer.Chain.SmartContract.t()} + defp create_or_update_smart_contract(address_hash, attrs) do + Logger.info("Publish successfully verified Stylus smart-contract #{address_hash} into the DB") + + if SmartContract.verified?(address_hash) do + SmartContract.update_smart_contract(attrs, attrs.external_libraries, attrs.secondary_sources) + else + SmartContract.create_smart_contract(attrs, attrs.external_libraries, attrs.secondary_sources) + end + end + + # Creates an invalid changeset for a Stylus smart contract that failed verification. + # + # Prepares contract attributes with MD5 hash of bytecode and creates an invalid changeset + # with appropriate error messages. The changeset is marked with `:insert` action to + # indicate a failed verification attempt. + # + # ## Parameters + # - `address_hash`: The contract's address hash + # - `params`: Map containing contract details from verification attempt + # - `error`: The verification error that occurred + # - `error_message`: Optional custom error message + # - `verification_with_files?`: Boolean indicating if verification used source files. + # Defaults to `false` + # + # ## Returns + # An invalid `t:Ecto.Changeset.t/0` with: + # - Contract attributes including MD5 hash of bytecode + # - Error message attached to appropriate field + # - Action set to `:insert` + @spec unverified_smart_contract(binary() | Explorer.Chain.Hash.t(), %{String.t() => any()}, any(), any(), boolean()) :: + Ecto.Changeset.t() + defp unverified_smart_contract(address_hash, params, error, error_message, verification_with_files? \\ false) do + attrs = + address_hash + |> attributes(params |> Map.put("compiler_version", params["cargo_stylus_version"])) + |> Helper.add_contract_code_md5() + + changeset = + SmartContract.invalid_contract_changeset( + %SmartContract{address_hash: address_hash}, + attrs, + error, + error_message, + verification_with_files? + ) + + Logger.error("Stylus smart-contract verification #{address_hash} failed because of the error #{inspect(error)}") + + %{changeset | action: :insert} + end + + defp attributes(address_hash, params, abi \\ %{}) do + %{ + address_hash: address_hash, + name: params["name"], + file_path: params["file_path"], + compiler_version: params["compiler_version"], + evm_version: nil, + optimization_runs: nil, + optimization: false, + contract_source_code: params["contract_source_code"], + constructor_arguments: nil, + external_libraries: [], + secondary_sources: params["secondary_sources"], + abi: abi, + verified_via_sourcify: false, + verified_via_eth_bytecode_db: false, + verified_via_verifier_alliance: false, + partially_verified: false, + is_vyper_contract: false, + autodetect_constructor_args: false, + is_yul: false, + compiler_settings: nil, + license_type: :none, + is_blueprint: false, + language: :stylus_rust, + package_name: params["package_name"], + github_repository_metadata: params["github_repository_metadata"] + } + end +end diff --git a/apps/explorer/lib/explorer/smart_contract/stylus/publisher_worker.ex b/apps/explorer/lib/explorer/smart_contract/stylus/publisher_worker.ex new file mode 100644 index 000000000000..ad5357978cc7 --- /dev/null +++ b/apps/explorer/lib/explorer/smart_contract/stylus/publisher_worker.ex @@ -0,0 +1,72 @@ +defmodule Explorer.SmartContract.Stylus.PublisherWorker do + @moduledoc """ + Processes Stylus smart contract verification requests asynchronously in the background. + + This module implements a worker that handles verification of Stylus smart contracts + through their GitHub repository source code. It uses a job queue system to: + - Receive verification requests containing contract address and GitHub details + - Delegate verification to the Publisher module + - Broadcast verification results through the events system + """ + + require Logger + + use Que.Worker, concurrency: 5 + + alias Explorer.Chain.Events.Publisher, as: EventsPublisher + alias Explorer.SmartContract.Stylus.Publisher + + @doc """ + Processes a Stylus smart contract verification request. + + Initiates the verification process by broadcasting the verification request to + the module responsible for the actual verification and consequent update of + the database. This function is called automatically by the job queue system. + + ## Parameters + - `{"github_repository", params}`: Tuple containing: + - First element: `"github_repository"` indicating the verification source + - Second element: Map containing: + - `"address_hash"`: The contract's address hash to verify + + ## Returns + - Result of the broadcast operation + """ + @spec perform({binary(), %{String.t() => any()}}) :: any() + def perform({"github_repository", %{"address_hash" => address_hash} = params}) do + broadcast(:publish, address_hash, [address_hash, params]) + end + + # Broadcasts the result of a Stylus smart contract verification attempt. + # + # Executes the specified verification method in the `Publisher` module and + # broadcasts the result through the events publisher. + # + # ## Parameters + # - `method`: The verification method to execute + # - `address_hash`: Contract address + # - `args`: Arguments to pass to the verification method + # + # ## Returns + # - `{:ok, contract}` if verification succeeds + # - `{:error, changeset}` if verification fails + @spec broadcast(atom(), binary() | Explorer.Chain.Hash.t(), any()) :: any() + defp broadcast(method, address_hash, args) do + result = + case apply(Publisher, method, args) do + {:ok, _contract} = result -> + result + + {:error, changeset} -> + Logger.error( + "Stylus smart-contract verification #{address_hash} failed because of the error: #{inspect(changeset)}" + ) + + {:error, changeset} + end + + Logger.info("Smart-contract #{address_hash} verification: broadcast verification results") + + EventsPublisher.broadcast([{:contract_verification_result, {String.downcase(address_hash), result}}], :on_demand) + end +end diff --git a/apps/explorer/lib/explorer/smart_contract/stylus/verifier.ex b/apps/explorer/lib/explorer/smart_contract/stylus/verifier.ex new file mode 100644 index 000000000000..4d90ee37d22b --- /dev/null +++ b/apps/explorer/lib/explorer/smart_contract/stylus/verifier.ex @@ -0,0 +1,108 @@ +defmodule Explorer.SmartContract.Stylus.Verifier do + @moduledoc """ + Verifies Stylus smart contracts by comparing their source code against deployed bytecode. + + This module handles verification of Stylus smart contracts through their GitHub repository + source code. It interfaces with a verification microservice that: + - Fetches source code from the specified GitHub repository and commit + - Compiles the code using the specified cargo-stylus version + - Compares the resulting bytecode against the deployed contract bytecode + - Returns verification details including ABI and contract metadata + """ + alias Explorer.Chain.{Hash, SmartContract} + alias Explorer.SmartContract.StylusVerifierInterface + + require Logger + + @doc """ + Verifies a Stylus smart contract by comparing source code from a GitHub repository against the deployed bytecode using a verification microservice. + + ## Parameters + - `address_hash`: Contract address + - `params`: Map containing verification parameters: + - `cargo_stylus_version`: Version of cargo-stylus used for deployment + - `repository_url`: GitHub repository URL containing contract code + - `commit`: Git commit hash used for deployment + - `path_prefix`: Optional path prefix if contract is not in repository root + + ## Returns + - `{:ok, map}` with verification details: + - `abi`: Contract ABI (optional) + - `contract_name`: Contract name (optional) + - `package_name`: Package name + - `files`: Map of file paths to contents used in verification + - `cargo_stylus_version`: Version of cargo-stylus used + - `github_repository_metadata`: Repository metadata (optional) + - `{:error, any}` if verification fails or is disabled + """ + @spec evaluate_authenticity(EthereumJSONRPC.address() | Hash.Address.t(), map()) :: + {:ok, map()} | {:error, any()} + def evaluate_authenticity(address_hash, params) do + evaluate_authenticity_inner(StylusVerifierInterface.enabled?(), address_hash, params) + rescue + exception -> + Logger.error(fn -> + [ + "Error while verifying smart-contract address: #{address_hash}, params: #{inspect(params, limit: :infinity, printable_limit: :infinity)}: ", + Exception.format(:error, exception, __STACKTRACE__) + ] + end) + end + + # Verifies the authenticity of a Stylus smart contract using GitHub repository source code. + # + # This function retrieves the contract creation transaction and blockchain RPC endpoint, + # which together with passed parameters are required by the verification microservice to + # validate the contract deployment and verify the source code against the deployed + # bytecode. + # + # ## Parameters + # - `true`: Required boolean flag to proceed with verification + # - `address_hash`: Contract address + # - `params`: Map containing verification parameters + # + # ## Returns + # - `{:ok, map}` with verification details including ABI, contract name, and source files + # - `{:error, any}` if verification fails + @spec evaluate_authenticity_inner(boolean(), EthereumJSONRPC.address() | Hash.Address.t(), map()) :: + {:ok, map()} | {:error, any()} + defp evaluate_authenticity_inner(true, address_hash, params) do + transaction_hash = fetch_data_for_stylus_verification(address_hash) + rpc_endpoint = Application.get_env(:explorer, :json_rpc_named_arguments)[:transport_options][:url] + + params + |> Map.take(["cargo_stylus_version", "repository_url", "commit", "path_prefix"]) + |> Map.put("rpc_endpoint", rpc_endpoint) + |> Map.put("deployment_transaction", transaction_hash) + |> StylusVerifierInterface.verify_github_repository() + end + + defp evaluate_authenticity_inner(false, _address_hash, _params) do + {:error, "Stylus verification is disabled"} + end + + # Retrieves the transaction hash that created a Stylus smart contract. + + # Looks up the creation transaction for the given contract address and returns its hash. + # Checks both regular transactions and internal transactions. + + # ## Parameters + # - `address_hash`: The address hash of the smart contract as a binary or `t:Hash.Address.t/0` + + # ## Returns + # - `t:Hash.t/0` - The transaction hash if found + # - `nil` - If no creation transaction exists + @spec fetch_data_for_stylus_verification(binary() | Hash.Address.t()) :: Hash.t() | nil + defp fetch_data_for_stylus_verification(address_hash) do + case SmartContract.creation_transaction_with_bytecode(address_hash) do + %{transaction: transaction} -> + transaction.hash + + %{internal_transaction: internal_transaction} -> + internal_transaction.transaction_hash + + _ -> + nil + end + end +end diff --git a/apps/explorer/lib/explorer/smart_contract/stylus_verifier_interface.ex b/apps/explorer/lib/explorer/smart_contract/stylus_verifier_interface.ex new file mode 100644 index 000000000000..596ebc6b2ce8 --- /dev/null +++ b/apps/explorer/lib/explorer/smart_contract/stylus_verifier_interface.ex @@ -0,0 +1,165 @@ +defmodule Explorer.SmartContract.StylusVerifierInterface do + @moduledoc """ + Provides an interface for verifying Stylus smart contracts by interacting with a verification + microservice. + + Handles verification requests for Stylus contracts deployed from GitHub repositories by + communicating with an external verification service. + """ + alias HTTPoison.Response + require Logger + + @post_timeout :timer.minutes(5) + @request_error_msg "Error while sending request to stylus verification microservice" + + @doc """ + Verifies a Stylus smart contract using source code from a GitHub repository. + + Sends verification request to the verification microservice with repository details + and deployment information. + + ## Parameters + - `body`: A map containing: + - `deployment_transaction`: Transaction hash where contract was deployed + - `rpc_endpoint`: RPC endpoint URL for the chain + - `cargo_stylus_version`: Version of cargo-stylus used for deployment + - `repository_url`: GitHub repository URL containing contract code + - `commit`: Git commit hash used for deployment + - `path_prefix`: Optional path prefix if contract is not in repository root + + ## Returns + - `{:ok, map}` with verification details: + - `abi`: Contract ABI (optional) + - `contract_name`: Contract name (optional) + - `package_name`: Package name + - `files`: Map of file paths to contents used in verification + - `cargo_stylus_version`: Version of cargo-stylus used + - `github_repository_metadata`: Repository metadata (optional) + - `{:error, any}` if verification fails + """ + @spec verify_github_repository(map()) :: {:ok, map()} | {:error, any()} + def verify_github_repository( + %{ + "deployment_transaction" => _, + "rpc_endpoint" => _, + "cargo_stylus_version" => _, + "repository_url" => _, + "commit" => _, + "path_prefix" => _ + } = body + ) do + http_post_request(github_repository_verification_url(), body) + end + + @spec http_post_request(String.t(), map()) :: {:ok, map()} | {:error, any()} + defp http_post_request(url, body) do + headers = [{"Content-Type", "application/json"}] + + case HTTPoison.post(url, Jason.encode!(body), headers, recv_timeout: @post_timeout) do + {:ok, %Response{body: body, status_code: _}} -> + process_verifier_response(body) + + {:error, error} -> + Logger.error(fn -> + [ + "Error while sending request to verification microservice url: #{url}, body: #{inspect(body, limit: :infinity, printable_limit: :infinity)}: ", + inspect(error, limit: :infinity, printable_limit: :infinity) + ] + end) + + {:error, @request_error_msg} + end + end + + @spec http_get_request(String.t()) :: {:ok, [String.t()]} | {:error, any()} + defp http_get_request(url) do + case HTTPoison.get(url) do + {:ok, %Response{body: body, status_code: 200}} -> + process_verifier_response(body) + + {:ok, %Response{body: body, status_code: _}} -> + {:error, body} + + {:error, error} -> + Logger.error(fn -> + [ + "Error while sending request to verification microservice url: #{url}: ", + inspect(error, limit: :infinity, printable_limit: :infinity) + ] + end) + + {:error, @request_error_msg} + end + end + + @doc """ + Retrieves a list of supported versions of Cargo Stylus package from the verification microservice. + + ## Returns + - `{:ok, [String.t()]}` - List of versions on success + - `{:error, any()}` - Error message if the request fails + """ + @spec get_versions_list() :: {:ok, [String.t()]} | {:error, any()} + def get_versions_list do + http_get_request(versions_list_url()) + end + + @spec process_verifier_response(binary()) :: {:ok, map() | [String.t()]} | {:error, any()} + defp process_verifier_response(body) when is_binary(body) do + case Jason.decode(body) do + {:ok, decoded} -> + process_verifier_response(decoded) + + _ -> + {:error, body} + end + end + + # Handles response from `stylus-sdk-rs/verify-github-repository` of stylus verifier microservice + @spec process_verifier_response(map()) :: {:ok, map()} + defp process_verifier_response(%{"verification_success" => source}) do + {:ok, source} + end + + # Handles response from `stylus-sdk-rs/verify-github-repository` of stylus verifier microservice + @spec process_verifier_response(map()) :: {:ok, map()} + defp process_verifier_response(%{"verificationSuccess" => source}) do + {:ok, source} + end + + # Handles response from `stylus-sdk-rs/verify-github-repository` of stylus verifier microservice + @spec process_verifier_response(map()) :: {:error, String.t()} + defp process_verifier_response(%{"verification_failure" => %{"message" => error_message}}) do + {:error, error_message} + end + + # Handles response from `stylus-sdk-rs/verify-github-repository` of stylus verifier microservice + @spec process_verifier_response(map()) :: {:error, String.t()} + defp process_verifier_response(%{"verificationFailure" => %{"message" => error_message}}) do + {:error, error_message} + end + + # Handles response from `stylus-sdk-rs/cargo-stylus-versions` of stylus verifier microservice + @spec process_verifier_response(map()) :: {:ok, [String.t()]} + defp process_verifier_response(%{"versions" => versions}), do: {:ok, Enum.map(versions, &Map.fetch!(&1, "version"))} + + @spec process_verifier_response(any()) :: {:error, any()} + defp process_verifier_response(other) do + {:error, other} + end + + # Uses url encoded ("%3A") version of ':', as ':' symbol breaks `Bypass` library during tests. + # https://github.com/PSPDFKit-labs/bypass/issues/122 + + defp github_repository_verification_url, + do: base_api_url() <> "%3Averify-github-repository" + + defp versions_list_url, do: base_api_url() <> "/cargo-stylus-versions" + + defp base_api_url, do: "#{base_url()}" <> "/api/v1/stylus-sdk-rs" + + defp base_url, do: Application.get_env(:explorer, __MODULE__)[:service_url] + + def enabled?, + do: !is_nil(base_url()) && Application.get_env(:explorer, :chain_type) == :arbitrum +end diff --git a/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex b/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex index da7c92854acd..c2fcfa976888 100644 --- a/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex +++ b/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex @@ -220,7 +220,8 @@ defmodule Explorer.SmartContract.Vyper.Publisher do file_path: params["file_path"], compiler_settings: clean_compiler_settings, license_type: prepare_license_type(params["license_type"]) || :none, - is_blueprint: params["is_blueprint"] || false + is_blueprint: params["is_blueprint"] || false, + language: :vyper } end diff --git a/apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex b/apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex index 690efc346635..a060197d2ffd 100644 --- a/apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex +++ b/apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex @@ -39,9 +39,12 @@ defmodule Explorer.SmartContract.Vyper.PublisherWorker do Logger.info("Smart-contract #{address_hash} verification: broadcast verification results") if conn do - EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result, conn}}], :on_demand) + EventsPublisher.broadcast( + [{:contract_verification_result, {String.downcase(address_hash), result, conn}}], + :on_demand + ) else - EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result}}], :on_demand) + EventsPublisher.broadcast([{:contract_verification_result, {String.downcase(address_hash), result}}], :on_demand) end end end diff --git a/apps/explorer/priv/arbitrum/migrations/20241111195112_add_stylus_fields.exs b/apps/explorer/priv/arbitrum/migrations/20241111195112_add_stylus_fields.exs new file mode 100644 index 000000000000..fb9ae49174e1 --- /dev/null +++ b/apps/explorer/priv/arbitrum/migrations/20241111195112_add_stylus_fields.exs @@ -0,0 +1,10 @@ +defmodule Explorer.Repo.Arbitrum.Migrations.AddStylusFields do + use Ecto.Migration + + def change do + alter table(:smart_contracts) do + add(:package_name, :string, null: true) + add(:github_repository_metadata, :jsonb, null: true) + end + end +end diff --git a/apps/explorer/priv/repo/migrations/20241111200520_add_language_field.exs b/apps/explorer/priv/repo/migrations/20241111200520_add_language_field.exs new file mode 100644 index 000000000000..28a811359dc8 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20241111200520_add_language_field.exs @@ -0,0 +1,9 @@ +defmodule Explorer.Repo.Migrations.AddLanguageField do + use Ecto.Migration + + def change do + alter table(:smart_contracts) do + add(:language, :int2, null: true) + end + end +end diff --git a/config/config_helper.exs b/config/config_helper.exs index 512291e8b04f..22bf43f26480 100644 --- a/config/config_helper.exs +++ b/config/config_helper.exs @@ -346,4 +346,42 @@ defmodule ConfigHelper do urls -> urls end end + + @doc """ + Parses and validates a microservice URL from an environment variable, removing any trailing slash. + + ## Parameters + - `env_name`: The name of the environment variable containing the URL + + ## Returns + - The validated URL string with any trailing slash removed + - `nil` if the URL is invalid or missing required components + """ + @spec parse_microservice_url(String.t()) :: String.t() | nil + def parse_microservice_url(env_name) do + url = System.get_env(env_name) + + cond do + not valid_url?(url) -> + nil + + String.ends_with?(url, "/") -> + url + |> String.slice(0..(String.length(url) - 2)) + + true -> + url + end + end + + # Validates if the given string is a valid URL by checking if it has both scheme (like http, + # https, ftp) and host components. + @spec valid_url?(String.t()) :: boolean() + defp valid_url?(string) when is_binary(string) do + uri = URI.parse(string) + + !is_nil(uri.scheme) && !is_nil(uri.host) + end + + defp valid_url?(_), do: false end diff --git a/config/runtime.exs b/config/runtime.exs index 1c6944773238..263bf4b34a33 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -538,6 +538,9 @@ config :explorer, Explorer.MicroserviceInterfaces.Metadata, service_url: System.get_env("MICROSERVICE_METADATA_URL"), enabled: ConfigHelper.parse_bool_env_var("MICROSERVICE_METADATA_ENABLED") +config :explorer, Explorer.SmartContract.StylusVerifierInterface, + service_url: ConfigHelper.parse_microservice_url("MICROSERVICE_STYLUS_VERIFIER_URL") + config :explorer, :air_table_public_tags, table_url: System.get_env("ACCOUNT_PUBLIC_TAGS_AIRTABLE_URL"), api_key: System.get_env("ACCOUNT_PUBLIC_TAGS_AIRTABLE_API_KEY") diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index c3608980e277..1782375eb092 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -398,6 +398,7 @@ MICROSERVICE_ACCOUNT_ABSTRACTION_ENABLED=false MICROSERVICE_ACCOUNT_ABSTRACTION_URL=http://user-ops-indexer:8050/ # MICROSERVICE_METADATA_URL= # MICROSERVICE_METADATA_ENABLED= +# MICROSERVICE_STYLUS_VERIFIER_URL= DECODE_NOT_A_CONTRACT_CALLS=true # DATABASE_READ_ONLY_API_URL= # ACCOUNT_DATABASE_URL= From 64935748648765e477885019fb296cbfec7e80de Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Fri, 22 Nov 2024 14:32:43 +0300 Subject: [PATCH 323/363] fix: handle simultaneous api key creation (#11233) * fix: handle simultaneous api key creation * Fix migration Co-Authored-By: Victor Baranov --------- Co-authored-by: Victor Baranov --- apps/explorer/lib/explorer/account/api/key.ex | 59 +++++++++++++++++-- .../20241121140138_remove_abused_api_keys.exs | 20 +++++++ 2 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 apps/explorer/priv/account/migrations/20241121140138_remove_abused_api_keys.exs diff --git a/apps/explorer/lib/explorer/account/api/key.ex b/apps/explorer/lib/explorer/account/api/key.ex index 7f286ee823a8..8b20283a16d7 100644 --- a/apps/explorer/lib/explorer/account/api/key.ex +++ b/apps/explorer/lib/explorer/account/api/key.ex @@ -24,6 +24,8 @@ defmodule Explorer.Account.Api.Key do @attrs ~w(value name identity_id)a + @user_not_found "User not found" + def changeset do %__MODULE__{} |> cast(%{}, @attrs) @@ -35,14 +37,63 @@ defmodule Explorer.Account.Api.Key do |> validate_required(@attrs, message: "Required") |> validate_length(:name, min: 1, max: 255) |> unique_constraint(:value, message: "API key already exists") - |> foreign_key_constraint(:identity_id, message: "User not found") + |> foreign_key_constraint(:identity_id, message: @user_not_found) |> api_key_count_constraint() end + @doc """ + Creates a new API key associated with an identity or returns an error when no identity is specified. + + When `identity_id` is provided in the attributes, the function acquires a lock on the + identity record and creates a new API key within a transaction. If the identity is not + found or if the changeset validation fails, returns an error. + + When `identity_id` is not provided, immediately returns an error with an invalid + changeset. + + ## Parameters + - `attrs`: A map of attributes that may contain: + - `identity_id`: The ID of the identity to associate the API key with + - `name`: The name for the API key (required, 1 to 255 characters) + - `value`: Optional. If not provided, will be auto-generated using UUID v4 + + ## Returns + - `{:ok, api_key}` if the API key was created successfully + - `{:error, changeset}` if validation fails or when no identity is provided + """ + @spec create(map()) :: {:ok, t()} | {:error, Changeset.t()} + def create(%{identity_id: identity_id} = attrs) do + Multi.new() + |> Multi.run(:acquire_identity, fn repo, _changes -> + identity_query = from(identity in Identity, where: identity.id == ^identity_id, lock: "FOR UPDATE") + + case repo.one(identity_query) do + nil -> + {:error, + %__MODULE__{} + |> changeset(Map.put(attrs, :value, generate_api_key())) + |> add_error(:identity_id, @user_not_found, + constraint: :foreign, + constraint_name: "account_api_keys_identity_id_fkey" + )} + + identity -> + {:ok, identity} + end + end) + |> Multi.insert(:api_key, fn _ -> + %__MODULE__{} + |> changeset(Map.put(attrs, :value, generate_api_key())) + end) + |> Repo.account_repo().transaction() + |> case do + {:ok, %{api_key: api_key}} -> {:ok, api_key} + {:error, _failed_operation, error, _changes} -> {:error, error} + end + end + def create(attrs) do - %__MODULE__{} - |> changeset(Map.put(attrs, :value, generate_api_key())) - |> Repo.account_repo().insert() + {:error, %__MODULE__{} |> changeset(Map.put(attrs, :value, generate_api_key()))} end def api_key_count_constraint(%Changeset{changes: %{identity_id: identity_id}} = api_key) do diff --git a/apps/explorer/priv/account/migrations/20241121140138_remove_abused_api_keys.exs b/apps/explorer/priv/account/migrations/20241121140138_remove_abused_api_keys.exs new file mode 100644 index 000000000000..c4fe2b74a579 --- /dev/null +++ b/apps/explorer/priv/account/migrations/20241121140138_remove_abused_api_keys.exs @@ -0,0 +1,20 @@ +defmodule Explorer.Repo.Account.Migrations.RemoveAbusedApiKeys do + use Ecto.Migration + + def up do + execute(""" + WITH ranked_keys AS (SELECT value, + identity_id, + inserted_at, + ROW_NUMBER() OVER ( + PARTITION BY identity_id + ) as row_number + FROM account_api_keys) + DELETE + FROM account_api_keys + WHERE value IN (SELECT value + FROM ranked_keys + WHERE row_number > 3) + """) + end +end From 296057037f7c1da6947fdb20ecc2f5718dc4051e Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Fri, 22 Nov 2024 15:36:35 +0400 Subject: [PATCH 324/363] fix: Fix fake json_rpc_named_arguments for multiple urls usage (#11243) --- apps/indexer/lib/indexer/fetcher/optimism.ex | 2 +- apps/indexer/lib/indexer/fetcher/polygon_edge.ex | 2 +- apps/indexer/lib/indexer/fetcher/shibarium/l1.ex | 2 +- .../lib/indexer/fetcher/zksync/batches_status_tracker.ex | 2 +- apps/indexer/lib/indexer/helper.ex | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/indexer/lib/indexer/fetcher/optimism.ex b/apps/indexer/lib/indexer/fetcher/optimism.ex index 11ba4aa5f1d6..68834687cdf3 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism.ex @@ -192,7 +192,7 @@ defmodule Indexer.Fetcher.Optimism do transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: optimism_l1_rpc, + urls: [optimism_l1_rpc], http_options: [ recv_timeout: :timer.minutes(10), timeout: :timer.minutes(10), diff --git a/apps/indexer/lib/indexer/fetcher/polygon_edge.ex b/apps/indexer/lib/indexer/fetcher/polygon_edge.ex index 649255ba22be..835cb56ced18 100644 --- a/apps/indexer/lib/indexer/fetcher/polygon_edge.ex +++ b/apps/indexer/lib/indexer/fetcher/polygon_edge.ex @@ -573,7 +573,7 @@ defmodule Indexer.Fetcher.PolygonEdge do transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: polygon_edge_l1_rpc, + urls: [polygon_edge_l1_rpc], http_options: [ recv_timeout: :timer.minutes(10), timeout: :timer.minutes(10), diff --git a/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex b/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex index aa3f7a65d845..95151b9e2d03 100644 --- a/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex +++ b/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex @@ -567,7 +567,7 @@ defmodule Indexer.Fetcher.Shibarium.L1 do transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: rpc_url, + urls: [rpc_url], http_options: [ recv_timeout: :timer.minutes(10), timeout: :timer.minutes(10), diff --git a/apps/indexer/lib/indexer/fetcher/zksync/batches_status_tracker.ex b/apps/indexer/lib/indexer/fetcher/zksync/batches_status_tracker.ex index 74d7ec5b3bd8..7c733bba89fc 100644 --- a/apps/indexer/lib/indexer/fetcher/zksync/batches_status_tracker.ex +++ b/apps/indexer/lib/indexer/fetcher/zksync/batches_status_tracker.ex @@ -88,7 +88,7 @@ defmodule Indexer.Fetcher.ZkSync.BatchesStatusTracker do transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: l1_rpc, + urls: [l1_rpc], http_options: [ recv_timeout: :timer.minutes(10), timeout: :timer.minutes(10), diff --git a/apps/indexer/lib/indexer/helper.ex b/apps/indexer/lib/indexer/helper.ex index 60c634ad7d3e..9d709360e986 100644 --- a/apps/indexer/lib/indexer/helper.ex +++ b/apps/indexer/lib/indexer/helper.ex @@ -206,7 +206,7 @@ defmodule Indexer.Helper do transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, - url: rpc_url, + urls: [rpc_url], http_options: [ recv_timeout: :timer.minutes(10), timeout: :timer.minutes(10), From 6b5f4e62b7390209e95e19e643b9b4559a7fac45 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:27:52 +0300 Subject: [PATCH 325/363] fix: Fix ETH JSON RPC deriving for Stylus verification (#11247) --- apps/explorer/lib/explorer/smart_contract/stylus/verifier.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/smart_contract/stylus/verifier.ex b/apps/explorer/lib/explorer/smart_contract/stylus/verifier.ex index 4d90ee37d22b..62496b90ffcf 100644 --- a/apps/explorer/lib/explorer/smart_contract/stylus/verifier.ex +++ b/apps/explorer/lib/explorer/smart_contract/stylus/verifier.ex @@ -68,7 +68,7 @@ defmodule Explorer.SmartContract.Stylus.Verifier do {:ok, map()} | {:error, any()} defp evaluate_authenticity_inner(true, address_hash, params) do transaction_hash = fetch_data_for_stylus_verification(address_hash) - rpc_endpoint = Application.get_env(:explorer, :json_rpc_named_arguments)[:transport_options][:url] + rpc_endpoint = Application.get_env(:explorer, :json_rpc_named_arguments)[:transport_options][:urls] |> List.first() params |> Map.take(["cargo_stylus_version", "repository_url", "commit", "path_prefix"]) From d512914493b721fec88346784fe08a89f799872a Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:38:24 +0300 Subject: [PATCH 326/363] Fixate 6.9.2 as the latest release (#11265) [no ci] --- .github/workflows/pre-release-arbitrum.yml | 2 +- .github/workflows/pre-release-blackfort.yml | 2 +- .github/workflows/pre-release-celo.yml | 2 +- .github/workflows/pre-release-eth.yml | 2 +- .github/workflows/pre-release-filecoin.yml | 2 +- .github/workflows/pre-release-optimism.yml | 2 +- .github/workflows/pre-release-polygon-zkevm.yml | 2 +- .github/workflows/pre-release-redstone.yml | 2 +- .github/workflows/pre-release-scroll.yml | 2 +- .github/workflows/pre-release-shibarium.yml | 2 +- .github/workflows/pre-release-zilliqa.yml | 2 +- .github/workflows/pre-release-zksync.yml | 2 +- .github/workflows/pre-release.yml | 2 +- .github/workflows/publish-docker-image-every-push.yml | 2 +- .github/workflows/publish-docker-image-for-arbitrum.yml | 2 +- .github/workflows/publish-docker-image-for-blackfort.yml | 2 +- .github/workflows/publish-docker-image-for-celo.yml | 2 +- .github/workflows/publish-docker-image-for-core.yml | 2 +- .github/workflows/publish-docker-image-for-eth-sepolia.yml | 2 +- .github/workflows/publish-docker-image-for-eth.yml | 2 +- .github/workflows/publish-docker-image-for-filecoin.yml | 2 +- .github/workflows/publish-docker-image-for-fuse.yml | 2 +- .github/workflows/publish-docker-image-for-gnosis-chain.yml | 2 +- .github/workflows/publish-docker-image-for-l2-staging.yml | 2 +- .github/workflows/publish-docker-image-for-lukso.yml | 2 +- .github/workflows/publish-docker-image-for-optimism.yml | 2 +- .github/workflows/publish-docker-image-for-polygon-edge.yml | 2 +- .github/workflows/publish-docker-image-for-redstone.yml | 2 +- .github/workflows/publish-docker-image-for-rootstock.yml | 2 +- .github/workflows/publish-docker-image-for-scroll.yml | 2 +- .github/workflows/publish-docker-image-for-shibarium.yml | 2 +- .github/workflows/publish-docker-image-for-stability.yml | 2 +- .github/workflows/publish-docker-image-for-suave.yml | 2 +- .github/workflows/publish-docker-image-for-zetachain.yml | 2 +- .github/workflows/publish-docker-image-for-zilliqa.yml | 2 +- .github/workflows/publish-docker-image-for-zkevm.yml | 2 +- .github/workflows/publish-docker-image-for-zksync.yml | 2 +- .github/workflows/publish-docker-image-staging-on-demand.yml | 2 +- .github/workflows/publish-regular-docker-image-on-demand.yml | 2 +- .github/workflows/release-arbitrum.yml | 2 +- .github/workflows/release-blackfort.yml | 2 +- .github/workflows/release-celo.yml | 2 +- .github/workflows/release-eth.yml | 2 +- .github/workflows/release-filecoin.yml | 2 +- .github/workflows/release-fuse.yml | 2 +- .github/workflows/release-gnosis.yml | 2 +- .github/workflows/release-optimism.yml | 2 +- .github/workflows/release-polygon-edge.yml | 2 +- .github/workflows/release-polygon-zkevm.yml | 2 +- .github/workflows/release-redstone.yml | 2 +- .github/workflows/release-rootstock.yml | 2 +- .github/workflows/release-scroll.yml | 2 +- .github/workflows/release-shibarium.yml | 2 +- .github/workflows/release-stability.yml | 2 +- .github/workflows/release-suave.yml | 2 +- .github/workflows/release-zetachain.yml | 2 +- .github/workflows/release-zilliqa.yml | 2 +- .github/workflows/release-zksync.yml | 2 +- .github/workflows/release.yml | 2 +- apps/block_scout_web/mix.exs | 2 +- apps/ethereum_jsonrpc/mix.exs | 2 +- apps/explorer/lib/explorer/token/metadata_retriever.ex | 2 +- apps/explorer/mix.exs | 2 +- apps/indexer/mix.exs | 2 +- docker-compose/docker-compose.yml | 2 +- docker/Makefile | 2 +- mix.exs | 2 +- rel/config.exs | 2 +- 68 files changed, 68 insertions(+), 68 deletions(-) diff --git a/.github/workflows/pre-release-arbitrum.yml b/.github/workflows/pre-release-arbitrum.yml index 6a5de3655a59..a58456762081 100644 --- a/.github/workflows/pre-release-arbitrum.yml +++ b/.github/workflows/pre-release-arbitrum.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-blackfort.yml b/.github/workflows/pre-release-blackfort.yml index 2ffe9133bce9..1ec25b0f9745 100644 --- a/.github/workflows/pre-release-blackfort.yml +++ b/.github/workflows/pre-release-blackfort.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-celo.yml b/.github/workflows/pre-release-celo.yml index b441005d4041..6c358f235bcc 100644 --- a/.github/workflows/pre-release-celo.yml +++ b/.github/workflows/pre-release-celo.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 API_GRAPHQL_MAX_COMPLEXITY: 10400 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/pre-release-eth.yml b/.github/workflows/pre-release-eth.yml index 593f3c4f2109..91ffd072bf0a 100644 --- a/.github/workflows/pre-release-eth.yml +++ b/.github/workflows/pre-release-eth.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-filecoin.yml b/.github/workflows/pre-release-filecoin.yml index d7c9a5a6acf5..bf4e309bc30e 100644 --- a/.github/workflows/pre-release-filecoin.yml +++ b/.github/workflows/pre-release-filecoin.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-optimism.yml b/.github/workflows/pre-release-optimism.yml index 673fa9304cf4..c51a5d23c702 100644 --- a/.github/workflows/pre-release-optimism.yml +++ b/.github/workflows/pre-release-optimism.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-polygon-zkevm.yml b/.github/workflows/pre-release-polygon-zkevm.yml index 59a8dd38e95d..90d5e2558166 100644 --- a/.github/workflows/pre-release-polygon-zkevm.yml +++ b/.github/workflows/pre-release-polygon-zkevm.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-redstone.yml b/.github/workflows/pre-release-redstone.yml index 09358ed202b1..8611fa5c0c5e 100644 --- a/.github/workflows/pre-release-redstone.yml +++ b/.github/workflows/pre-release-redstone.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-scroll.yml b/.github/workflows/pre-release-scroll.yml index 84d479593dfb..879e7ec0740d 100644 --- a/.github/workflows/pre-release-scroll.yml +++ b/.github/workflows/pre-release-scroll.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-shibarium.yml b/.github/workflows/pre-release-shibarium.yml index e34c7f0f3fe2..bf75929d8ed3 100644 --- a/.github/workflows/pre-release-shibarium.yml +++ b/.github/workflows/pre-release-shibarium.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-zilliqa.yml b/.github/workflows/pre-release-zilliqa.yml index 4a40958eed00..e01858682424 100644 --- a/.github/workflows/pre-release-zilliqa.yml +++ b/.github/workflows/pre-release-zilliqa.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release-zksync.yml b/.github/workflows/pre-release-zksync.yml index f8b504a1f731..0f08894580a3 100644 --- a/.github/workflows/pre-release-zksync.yml +++ b/.github/workflows/pre-release-zksync.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index 027f7a12562a..c0096e7fd050 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -16,7 +16,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/publish-docker-image-every-push.yml b/.github/workflows/publish-docker-image-every-push.yml index 6ae1b0e4ce6d..4c141897ca7c 100644 --- a/.github/workflows/publish-docker-image-every-push.yml +++ b/.github/workflows/publish-docker-image-every-push.yml @@ -11,7 +11,7 @@ on: env: OTP_VERSION: ${{ vars.OTP_VERSION }} ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 jobs: push_to_registry: diff --git a/.github/workflows/publish-docker-image-for-arbitrum.yml b/.github/workflows/publish-docker-image-for-arbitrum.yml index b8be835c7dfe..3266fbee762e 100644 --- a/.github/workflows/publish-docker-image-for-arbitrum.yml +++ b/.github/workflows/publish-docker-image-for-arbitrum.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: arbitrum steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-blackfort.yml b/.github/workflows/publish-docker-image-for-blackfort.yml index 41ded9361071..83d5a1fee86b 100644 --- a/.github/workflows/publish-docker-image-for-blackfort.yml +++ b/.github/workflows/publish-docker-image-for-blackfort.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: blackfort steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-celo.yml b/.github/workflows/publish-docker-image-for-celo.yml index 61b324cb414b..e73638df14c1 100644 --- a/.github/workflows/publish-docker-image-for-celo.yml +++ b/.github/workflows/publish-docker-image-for-celo.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: celo API_GRAPHQL_MAX_COMPLEXITY: 10400 steps: diff --git a/.github/workflows/publish-docker-image-for-core.yml b/.github/workflows/publish-docker-image-for-core.yml index 1e6f4fb38a04..d0957f0ab45a 100644 --- a/.github/workflows/publish-docker-image-for-core.yml +++ b/.github/workflows/publish-docker-image-for-core.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: poa steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-eth-sepolia.yml b/.github/workflows/publish-docker-image-for-eth-sepolia.yml index a94e8bd3936b..bd320c9ef175 100644 --- a/.github/workflows/publish-docker-image-for-eth-sepolia.yml +++ b/.github/workflows/publish-docker-image-for-eth-sepolia.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: eth-sepolia steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-eth.yml b/.github/workflows/publish-docker-image-for-eth.yml index 6d7248b707d1..210f06150a2f 100644 --- a/.github/workflows/publish-docker-image-for-eth.yml +++ b/.github/workflows/publish-docker-image-for-eth.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: mainnet steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-filecoin.yml b/.github/workflows/publish-docker-image-for-filecoin.yml index 9dce15aeca19..797ebe61a0bf 100644 --- a/.github/workflows/publish-docker-image-for-filecoin.yml +++ b/.github/workflows/publish-docker-image-for-filecoin.yml @@ -9,7 +9,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: filecoin steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-fuse.yml b/.github/workflows/publish-docker-image-for-fuse.yml index 7b14ecda818e..4931f89775fa 100644 --- a/.github/workflows/publish-docker-image-for-fuse.yml +++ b/.github/workflows/publish-docker-image-for-fuse.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: fuse steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-gnosis-chain.yml b/.github/workflows/publish-docker-image-for-gnosis-chain.yml index 9d73227b52ea..1b5f1db55d0b 100644 --- a/.github/workflows/publish-docker-image-for-gnosis-chain.yml +++ b/.github/workflows/publish-docker-image-for-gnosis-chain.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: xdai steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-l2-staging.yml b/.github/workflows/publish-docker-image-for-l2-staging.yml index 7c03c5599f42..abccf4862355 100644 --- a/.github/workflows/publish-docker-image-for-l2-staging.yml +++ b/.github/workflows/publish-docker-image-for-l2-staging.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: optimism-l2-advanced steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-lukso.yml b/.github/workflows/publish-docker-image-for-lukso.yml index 8b910865b2fd..3af3ac67f165 100644 --- a/.github/workflows/publish-docker-image-for-lukso.yml +++ b/.github/workflows/publish-docker-image-for-lukso.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: lukso steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-optimism.yml b/.github/workflows/publish-docker-image-for-optimism.yml index b31b4422f2c6..2ca241b2b1d8 100644 --- a/.github/workflows/publish-docker-image-for-optimism.yml +++ b/.github/workflows/publish-docker-image-for-optimism.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: optimism steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-polygon-edge.yml b/.github/workflows/publish-docker-image-for-polygon-edge.yml index ada5c6f34600..fd336962c29d 100644 --- a/.github/workflows/publish-docker-image-for-polygon-edge.yml +++ b/.github/workflows/publish-docker-image-for-polygon-edge.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: polygon-edge steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-redstone.yml b/.github/workflows/publish-docker-image-for-redstone.yml index 0b80420c4e4a..8e33916160dd 100644 --- a/.github/workflows/publish-docker-image-for-redstone.yml +++ b/.github/workflows/publish-docker-image-for-redstone.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: redstone steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-rootstock.yml b/.github/workflows/publish-docker-image-for-rootstock.yml index 117d4e7423ca..a50526ea0664 100644 --- a/.github/workflows/publish-docker-image-for-rootstock.yml +++ b/.github/workflows/publish-docker-image-for-rootstock.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: rsk steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-scroll.yml b/.github/workflows/publish-docker-image-for-scroll.yml index 3a6d2eea110d..a24e39923d22 100644 --- a/.github/workflows/publish-docker-image-for-scroll.yml +++ b/.github/workflows/publish-docker-image-for-scroll.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: scroll steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-shibarium.yml b/.github/workflows/publish-docker-image-for-shibarium.yml index db54268d4b71..25882949dd7f 100644 --- a/.github/workflows/publish-docker-image-for-shibarium.yml +++ b/.github/workflows/publish-docker-image-for-shibarium.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: shibarium steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-stability.yml b/.github/workflows/publish-docker-image-for-stability.yml index 578fb0a0e924..34c5bf65b8cb 100644 --- a/.github/workflows/publish-docker-image-for-stability.yml +++ b/.github/workflows/publish-docker-image-for-stability.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: stability steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-suave.yml b/.github/workflows/publish-docker-image-for-suave.yml index b5b496061ebf..0da3dbdcd576 100644 --- a/.github/workflows/publish-docker-image-for-suave.yml +++ b/.github/workflows/publish-docker-image-for-suave.yml @@ -13,7 +13,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: suave steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-zetachain.yml b/.github/workflows/publish-docker-image-for-zetachain.yml index adb84c5ddb34..f1794658b6cc 100644 --- a/.github/workflows/publish-docker-image-for-zetachain.yml +++ b/.github/workflows/publish-docker-image-for-zetachain.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: zetachain steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-zilliqa.yml b/.github/workflows/publish-docker-image-for-zilliqa.yml index 5051b844ad2b..4893d6db99a2 100644 --- a/.github/workflows/publish-docker-image-for-zilliqa.yml +++ b/.github/workflows/publish-docker-image-for-zilliqa.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: zilliqa steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-zkevm.yml b/.github/workflows/publish-docker-image-for-zkevm.yml index 463515911056..a68643cdbdad 100644 --- a/.github/workflows/publish-docker-image-for-zkevm.yml +++ b/.github/workflows/publish-docker-image-for-zkevm.yml @@ -10,7 +10,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: zkevm steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-for-zksync.yml b/.github/workflows/publish-docker-image-for-zksync.yml index 56dbd127097e..0187041b0fd4 100644 --- a/.github/workflows/publish-docker-image-for-zksync.yml +++ b/.github/workflows/publish-docker-image-for-zksync.yml @@ -9,7 +9,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 DOCKER_CHAIN_NAME: zksync steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/publish-docker-image-staging-on-demand.yml b/.github/workflows/publish-docker-image-staging-on-demand.yml index 7cd48280eb5f..5983a75ea663 100644 --- a/.github/workflows/publish-docker-image-staging-on-demand.yml +++ b/.github/workflows/publish-docker-image-staging-on-demand.yml @@ -12,7 +12,7 @@ on: env: OTP_VERSION: ${{ vars.OTP_VERSION }} ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 jobs: push_to_registry: diff --git a/.github/workflows/publish-regular-docker-image-on-demand.yml b/.github/workflows/publish-regular-docker-image-on-demand.yml index 100cb8472d46..fe0cb2d354f9 100644 --- a/.github/workflows/publish-regular-docker-image-on-demand.yml +++ b/.github/workflows/publish-regular-docker-image-on-demand.yml @@ -5,7 +5,7 @@ on: env: OTP_VERSION: ${{ vars.OTP_VERSION }} ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 jobs: push_to_registry: diff --git a/.github/workflows/release-arbitrum.yml b/.github/workflows/release-arbitrum.yml index d0a7eadf5aca..8f437f92da0a 100644 --- a/.github/workflows/release-arbitrum.yml +++ b/.github/workflows/release-arbitrum.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-blackfort.yml b/.github/workflows/release-blackfort.yml index f0b67e28b8c6..01d7ffaa78df 100644 --- a/.github/workflows/release-blackfort.yml +++ b/.github/workflows/release-blackfort.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-celo.yml b/.github/workflows/release-celo.yml index 1d3f419dffd3..ef7315ab0561 100644 --- a/.github/workflows/release-celo.yml +++ b/.github/workflows/release-celo.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 API_GRAPHQL_MAX_COMPLEXITY: 10400 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release-eth.yml b/.github/workflows/release-eth.yml index eb5c1fb089e9..26b6e17c9902 100644 --- a/.github/workflows/release-eth.yml +++ b/.github/workflows/release-eth.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-filecoin.yml b/.github/workflows/release-filecoin.yml index 9a7508059077..a92c1dac7216 100644 --- a/.github/workflows/release-filecoin.yml +++ b/.github/workflows/release-filecoin.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-fuse.yml b/.github/workflows/release-fuse.yml index 14fc591eba69..e01aff007637 100644 --- a/.github/workflows/release-fuse.yml +++ b/.github/workflows/release-fuse.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-gnosis.yml b/.github/workflows/release-gnosis.yml index e42d92f30c4a..652e98dcdbe4 100644 --- a/.github/workflows/release-gnosis.yml +++ b/.github/workflows/release-gnosis.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-optimism.yml b/.github/workflows/release-optimism.yml index 2fccba0ccecf..2ee5108d00be 100644 --- a/.github/workflows/release-optimism.yml +++ b/.github/workflows/release-optimism.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-polygon-edge.yml b/.github/workflows/release-polygon-edge.yml index 17142d6fa71a..472acc50d914 100644 --- a/.github/workflows/release-polygon-edge.yml +++ b/.github/workflows/release-polygon-edge.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-polygon-zkevm.yml b/.github/workflows/release-polygon-zkevm.yml index be273e8330be..35711ba553ba 100644 --- a/.github/workflows/release-polygon-zkevm.yml +++ b/.github/workflows/release-polygon-zkevm.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-redstone.yml b/.github/workflows/release-redstone.yml index bfa2fc510245..cb979069d02b 100644 --- a/.github/workflows/release-redstone.yml +++ b/.github/workflows/release-redstone.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-rootstock.yml b/.github/workflows/release-rootstock.yml index dbf2ac5d79b4..98563c6c8a82 100644 --- a/.github/workflows/release-rootstock.yml +++ b/.github/workflows/release-rootstock.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-scroll.yml b/.github/workflows/release-scroll.yml index a7c9c2e9cc6f..ce1083825d38 100644 --- a/.github/workflows/release-scroll.yml +++ b/.github/workflows/release-scroll.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-shibarium.yml b/.github/workflows/release-shibarium.yml index d3ca78c811b1..61b12acfa68c 100644 --- a/.github/workflows/release-shibarium.yml +++ b/.github/workflows/release-shibarium.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-stability.yml b/.github/workflows/release-stability.yml index 6cda8ed056a7..b2230d43a12c 100644 --- a/.github/workflows/release-stability.yml +++ b/.github/workflows/release-stability.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-suave.yml b/.github/workflows/release-suave.yml index fdb07ec963a8..64c1fad129fc 100644 --- a/.github/workflows/release-suave.yml +++ b/.github/workflows/release-suave.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-zetachain.yml b/.github/workflows/release-zetachain.yml index 85b77055ac9b..807cff7e3226 100644 --- a/.github/workflows/release-zetachain.yml +++ b/.github/workflows/release-zetachain.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-zilliqa.yml b/.github/workflows/release-zilliqa.yml index e855f5971447..1bbd0b2a09ad 100644 --- a/.github/workflows/release-zilliqa.yml +++ b/.github/workflows/release-zilliqa.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release-zksync.yml b/.github/workflows/release-zksync.yml index 48160b844b72..11c6d2791e7c 100644 --- a/.github/workflows/release-zksync.yml +++ b/.github/workflows/release-zksync.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 66f70c1e22eb..27b0abc5643e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: name: Push Docker image to Docker Hub runs-on: ubuntu-latest env: - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 steps: - uses: actions/checkout@v4 - name: Setup repo diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs index f50c75e3e23f..7ffaf8b09e0c 100644 --- a/apps/block_scout_web/mix.exs +++ b/apps/block_scout_web/mix.exs @@ -23,7 +23,7 @@ defmodule BlockScoutWeb.Mixfile do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "6.9.1", + version: "6.9.2", xref: [ exclude: [ Explorer.Chain.PolygonZkevm.Reader, diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs index 153b2c9d3ec4..b950b31b8097 100644 --- a/apps/ethereum_jsonrpc/mix.exs +++ b/apps/ethereum_jsonrpc/mix.exs @@ -23,7 +23,7 @@ defmodule EthereumJSONRPC.MixProject do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "6.9.1" + version: "6.9.2" ] end diff --git a/apps/explorer/lib/explorer/token/metadata_retriever.ex b/apps/explorer/lib/explorer/token/metadata_retriever.ex index f1b6b3489ce7..9381fe9ff0ee 100644 --- a/apps/explorer/lib/explorer/token/metadata_retriever.ex +++ b/apps/explorer/lib/explorer/token/metadata_retriever.ex @@ -14,7 +14,7 @@ defmodule Explorer.Token.MetadataRetriever do @no_uri_error "no uri" @vm_execution_error "VM execution error" @invalid_base64_data "invalid data:application/json;base64" - @default_headers [{"User-Agent", "blockscout-6.9.1"}] + @default_headers [{"User-Agent", "blockscout-6.9.2"}] # https://eips.ethereum.org/EIPS/eip-1155#metadata @erc1155_token_id_placeholder "{id}" diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index f9bec33effef..f69c2a17bc7f 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -24,7 +24,7 @@ defmodule Explorer.Mixfile do dialyzer: :test ], start_permanent: Mix.env() == :prod, - version: "6.9.1", + version: "6.9.2", xref: [exclude: [BlockScoutWeb.Routers.WebRouter.Helpers, Indexer.Helper]] ] end diff --git a/apps/indexer/mix.exs b/apps/indexer/mix.exs index eca20bcbc880..c84d313fc024 100644 --- a/apps/indexer/mix.exs +++ b/apps/indexer/mix.exs @@ -14,7 +14,7 @@ defmodule Indexer.MixProject do elixirc_paths: elixirc_paths(Mix.env()), lockfile: "../../mix.lock", start_permanent: Mix.env() == :prod, - version: "6.9.1", + version: "6.9.2", xref: [ exclude: [ Explorer.Chain.Optimism.Deposit, diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index d91d67adf356..a20322750b4a 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -37,7 +37,7 @@ services: CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED: "" CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL: "" ADMIN_PANEL_ENABLED: "" - RELEASE_VERSION: 6.9.1 + RELEASE_VERSION: 6.9.2 links: - db:database environment: diff --git a/docker/Makefile b/docker/Makefile index 4735936b4117..9b79726b5872 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -10,7 +10,7 @@ STATS_CONTAINER_NAME := stats STATS_DB_CONTAINER_NAME := stats-db PROXY_CONTAINER_NAME := proxy PG_CONTAINER_NAME := postgres -RELEASE_VERSION ?= '6.9.1' +RELEASE_VERSION ?= '6.9.2' TAG := $(RELEASE_VERSION)-commit-$(shell git log -1 --pretty=format:"%h") STABLE_TAG := $(RELEASE_VERSION) diff --git a/mix.exs b/mix.exs index 40eb4eaba8ab..9bce59f941d8 100644 --- a/mix.exs +++ b/mix.exs @@ -7,7 +7,7 @@ defmodule BlockScout.Mixfile do [ # app: :block_scout, # aliases: aliases(config_env()), - version: "6.9.1", + version: "6.9.2", apps_path: "apps", deps: deps(), dialyzer: dialyzer(), diff --git a/rel/config.exs b/rel/config.exs index ec27a9a3e3f0..5f7d8e9afaaa 100644 --- a/rel/config.exs +++ b/rel/config.exs @@ -71,7 +71,7 @@ end # will be used by default release :blockscout do - set version: "6.9.1-beta" + set version: "6.9.2-beta" set applications: [ :runtime_tools, block_scout_web: :permanent, From 8f0013b5c2ed002285a0c65ff8630dc64203c9b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Nov 2024 10:33:40 +0300 Subject: [PATCH 327/363] chore(deps-dev): bump ex_doc from 0.34.2 to 0.35.1 (#11263) Bumps [ex_doc](https://github.com/elixir-lang/ex_doc) from 0.34.2 to 0.35.1. - [Release notes](https://github.com/elixir-lang/ex_doc/releases) - [Changelog](https://github.com/elixir-lang/ex_doc/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-lang/ex_doc/compare/v0.34.2...v0.35.1) --- updated-dependencies: - dependency-name: ex_doc dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.exs | 2 +- mix.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index 9bce59f941d8..8cb5633b0ca7 100644 --- a/mix.exs +++ b/mix.exs @@ -96,7 +96,7 @@ defmodule BlockScout.Mixfile do {:absinthe_plug, git: "https://github.com/blockscout/absinthe_plug.git", tag: "1.5.8", override: true}, {:tesla, "~> 1.13.0"}, # Documentation - {:ex_doc, "~> 0.34.1", only: :dev, runtime: false}, + {:ex_doc, "~> 0.35.1", only: :dev, runtime: false}, {:number, "~> 1.0.3"} ] end diff --git a/mix.lock b/mix.lock index 28e5ddb353e7..c3f365591fa9 100644 --- a/mix.lock +++ b/mix.lock @@ -49,7 +49,7 @@ "ex_cldr_lists": {:hex, :ex_cldr_lists, "2.11.1", "ad18f861d7c5ca82aac6d173469c6a2339645c96790172ab0aa255b64fb7303b", [: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", "00161c04510ccb3f18b19a6b8562e50c21f1e9c15b8ff4c934bea5aad0b4ade2"}, "ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.33.4", "ecb06f40fc63f484a53d4ea80e1bdd6860ec44d3032f2b10b17340d34c0a13d5", [: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.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, "~> 2.16", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "d15b7e217e9e60c328e73045e51dc67d7ac5d2997247b833efab2c69b2ed06f5"}, "ex_cldr_units": {:hex, :ex_cldr_units, "3.17.2", "b0483d5c61c6c8649aafdcafc7372dd71a7a30f52dd4c9b072576467bf721454", [:mix], [{:cldr_utils, "~> 2.25", [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.33.0", [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", "457d76c6e3b548bd7aba3c7b5d157213be2842d1162c2283abf81d9e2f1e1fc7"}, - "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, + "ex_doc": {:hex, :ex_doc, "0.35.1", "de804c590d3df2d9d5b8aec77d758b00c814b356119b3d4455e4b8a8687aecaf", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2121c6402c8d44b05622677b761371a759143b958c6c19f6558ff64d0aed40df"}, "ex_json_schema": {:hex, :ex_json_schema, "0.10.2", "7c4b8c1481fdeb1741e2ce66223976edfb9bccebc8014f6aec35d4efe964fb71", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "37f43be60f8407659d4d0155a7e45e7f406dab1f827051d3d35858a709baf6a6"}, "ex_keccak": {:hex, :ex_keccak, "0.7.5", "f3b733173510d48ae9a1ea1de415e694b2651f35c787e63f33b5ed0013fbfd35", [:mix], [{:rustler, ">= 0.0.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.7", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "8a5e1cb7f96fff5e480ff6a121477b90c4fd8c150984086dffd98819f5d83763"}, "ex_machina": {:hex, :ex_machina, "2.8.0", "a0e847b5712065055ec3255840e2c78ef9366634d62390839d4880483be38abe", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "79fe1a9c64c0c1c1fab6c4fa5d871682cb90de5885320c187d117004627a7729"}, @@ -82,8 +82,8 @@ "junit_formatter": {:hex, :junit_formatter, "3.4.0", "d0e8db6c34dab6d3c4154c3b46b21540db1109ae709d6cf99ba7e7a2ce4b1ac2", [:mix], [], "hexpm", "bb36e2ae83f1ced6ab931c4ce51dd3dbef1ef61bb4932412e173b0cfa259dacd"}, "logger_file_backend": {:hex, :logger_file_backend, "0.0.14", "774bb661f1c3fed51b624d2859180c01e386eb1273dc22de4f4a155ef749a602", [:mix], [], "hexpm", "071354a18196468f3904ef09413af20971d55164267427f6257b52cfba03f9e6"}, "logger_json": {:hex, :logger_json, "5.1.4", "9e30a4f2e31a8b9e402bdc20bd37cf9b67d3a31f19d0b33082a19a06b4c50f6d", [:mix], [{:ecto, "~> 2.1 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.5.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "3f20eea58e406a33d3eb7814c7dff5accb503bab2ee8601e84da02976fa3934c"}, - "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, + "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, + "makeup_elixir": {:hex, :makeup_elixir, "1.0.0", "74bb8348c9b3a51d5c589bf5aebb0466a84b33274150e3b6ece1da45584afc82", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "49159b7d7d999e836bedaf09dcf35ca18b312230cf901b725a64f3f42e407983"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, "math": {:hex, :math, "0.7.0", "12af548c3892abf939a2e242216c3e7cbfb65b9b2fe0d872d05c6fb609f8127b", [:mix], [], "hexpm", "7987af97a0c6b58ad9db43eb5252a49fc1dfe1f6d98f17da9282e297f594ebc2"}, "meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"}, From 4826f47425461bf9a97f5dbd0e6de91b95c0c640 Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Wed, 27 Nov 2024 10:58:34 +0300 Subject: [PATCH 328/363] chore: fix watchlist address flaking test (#11242) --- .../account/api/v2/user_controller_test.exs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs index 68f7047ddfa9..339be0b0a2ae 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/account/api/v2/user_controller_test.exs @@ -768,10 +768,10 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do ) |> Repo.preload([:token]) - Decimal.div( - Decimal.mult(ctb.value, ctb.token.fiat_value), - Decimal.new(10 ** Decimal.to_integer(ctb.token.decimals)) - ) + ctb.value + |> Decimal.mult(ctb.token.fiat_value) + |> Decimal.div(Decimal.new(10 ** Decimal.to_integer(ctb.token.decimals))) + |> Decimal.round(16) end values_1 = @@ -782,24 +782,24 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do ) |> Repo.preload([:token]) - Decimal.div( - Decimal.mult(ctb.value, ctb.token.fiat_value), - Decimal.new(10 ** Decimal.to_integer(ctb.token.decimals)) - ) + ctb.value + |> Decimal.mult(ctb.token.fiat_value) + |> Decimal.div(Decimal.new(10 ** Decimal.to_integer(ctb.token.decimals))) + |> Decimal.round(16) end |> Enum.sort(fn x1, x2 -> Decimal.compare(x1, x2) in [:gt, :eq] end) |> Enum.take(150) [wa2, wa1] = conn |> get("/api/account/v2/user/watchlist") |> json_response(200) |> Map.get("items") - assert wa1["tokens_fiat_value"] |> Decimal.new() |> Decimal.round(13) == - values |> Enum.reduce(Decimal.new(0), fn x, acc -> Decimal.add(x, acc) end) |> Decimal.round(13) + assert wa1["tokens_fiat_value"] |> Decimal.new() == + values |> Enum.reduce(Decimal.new(0), fn x, acc -> Decimal.add(x, acc) end) assert wa1["tokens_count"] == 150 assert wa1["tokens_overflow"] == false - assert wa2["tokens_fiat_value"] |> Decimal.new() |> Decimal.round(13) == - values_1 |> Enum.reduce(Decimal.new(0), fn x, acc -> Decimal.add(x, acc) end) |> Decimal.round(13) + assert wa2["tokens_fiat_value"] |> Decimal.new() == + values_1 |> Enum.reduce(Decimal.new(0), fn x, acc -> Decimal.add(x, acc) end) assert wa2["tokens_count"] == 150 assert wa2["tokens_overflow"] == true @@ -824,10 +824,10 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do ) |> Repo.preload([:token]) - Decimal.div( - Decimal.mult(ctb.value, ctb.token.fiat_value), - Decimal.new(10 ** Decimal.to_integer(ctb.token.decimals)) - ) + ctb.value + |> Decimal.mult(ctb.token.fiat_value) + |> Decimal.div(Decimal.new(10 ** Decimal.to_integer(ctb.token.decimals))) + |> Decimal.round(16) end token = insert(:token, fiat_value: nil) @@ -840,8 +840,8 @@ defmodule BlockScoutWeb.Account.Api.V2.UserControllerTest do [wa1] = conn |> get("/api/account/v2/user/watchlist") |> json_response(200) |> Map.get("items") - assert wa1["tokens_fiat_value"] |> Decimal.new() |> Decimal.round(13) == - values |> Enum.reduce(Decimal.new(0), fn x, acc -> Decimal.add(x, acc) end) |> Decimal.round(13) + assert wa1["tokens_fiat_value"] |> Decimal.new() == + values |> Enum.reduce(Decimal.new(0), fn x, acc -> Decimal.add(x, acc) end) assert wa1["tokens_count"] == 150 assert wa1["tokens_overflow"] == false From a6e2ac88a633d4865fde3cd9f2a8bcc328f060d4 Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:09:40 +0300 Subject: [PATCH 329/363] fix: Handle eth rpc request without params (#11269) --- .../controllers/api/rpc/eth_controller_test.exs | 13 +++++++++++++ apps/explorer/lib/explorer/eth_rpc.ex | 6 +++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs index 1e25c003ffa7..56d45d8842eb 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs @@ -38,6 +38,19 @@ defmodule BlockScoutWeb.API.RPC.EthControllerTest do topic end + test "handles request without params if possible", %{conn: conn} do + assert response = + conn + |> post("/api/eth-rpc", %{ + "method" => "eth_blockNumber", + "jsonrpc" => "2.0", + "id" => 0 + }) + |> json_response(200) + + assert %{"id" => 0, "jsonrpc" => "2.0", "result" => "0x0"} == response + end + describe "eth_get_logs" do setup do %{ diff --git a/apps/explorer/lib/explorer/eth_rpc.ex b/apps/explorer/lib/explorer/eth_rpc.ex index 0bd3381d6d6b..dc138cbfa971 100644 --- a/apps/explorer/lib/explorer/eth_rpc.ex +++ b/apps/explorer/lib/explorer/eth_rpc.ex @@ -1204,8 +1204,12 @@ defmodule Explorer.EthRPC do {:error, "Invalid params. Params must be a list."} end + defp do_eth_request(%{"jsonrpc" => jsonrpc, "method" => method}) do + do_eth_request(%{"jsonrpc" => jsonrpc, "method" => method, "params" => []}) + end + defp do_eth_request(_) do - {:error, "Method, params, and jsonrpc, are all required parameters."} + {:error, "Method, and jsonrpc are required parameters."} end defp get_action(action) do From 4124d621f51ef149030f582cf5b1e2a3c3fd2990 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:29:56 +0400 Subject: [PATCH 330/363] fix: Multiple json rpc urls fixes (#11264) --- .../lib/ethereum_jsonrpc/http.ex | 26 +--------- .../ethereum_jsonrpc/utility/common_helper.ex | 25 +++++++++ .../utility/endpoint_availability_checker.ex | 2 +- .../test/ethereum_jsonrpc/http/mox_test.exs | 4 +- .../utility/common_helper_test.exs | 52 +++++++++++++++++++ .../case/geth/http_websocket.ex | 2 +- .../case/nethermind/http_websocket.ex | 2 +- .../support/ethereum_jsonrpc/http/case.ex | 2 +- .../lib/explorer/chain/bridged_token.ex | 17 +++--- .../smart_contract/stylus/verifier.ex | 3 +- .../catchup/bound_interval_supervisor.ex | 2 +- 11 files changed, 99 insertions(+), 38 deletions(-) create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/utility/common_helper_test.exs diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex index 7d3543f75e09..0335c5e9af12 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex @@ -240,35 +240,13 @@ defmodule EthereumJSONRPC.HTTP do with {:ok, method_to_url} <- Keyword.fetch(options, :method_to_url), {:ok, method_atom} <- to_existing_atom(method), {:ok, url_type} <- Keyword.fetch(method_to_url, method_atom) do - fallback_urls = CommonHelper.url_type_to_urls(url_type, options, :fallback) - - url = - url_type - |> CommonHelper.url_type_to_urls(options) - |> EndpointAvailabilityObserver.maybe_replace_urls(fallback_urls, url_type) - |> select_single_url() - - {url_type, url} + {url_type, CommonHelper.get_available_url(options, url_type)} else _ -> - url_type = :http - - url = - url_type - |> CommonHelper.url_type_to_urls(options) - |> EndpointAvailabilityObserver.maybe_replace_urls(options[:fallback_urls], url_type) - |> select_single_url() - - {url_type, url} + {:http, CommonHelper.get_available_url(options, :http)} end end - defp select_single_url([]), do: nil - - defp select_single_url(urls) do - Enum.random(urls) - end - defp to_existing_atom(string) do {:ok, String.to_existing_atom(string)} rescue diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/common_helper.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/common_helper.ex index 615d9f1ddaad..d6bca798daad 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/common_helper.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/common_helper.ex @@ -3,6 +3,8 @@ defmodule EthereumJSONRPC.Utility.CommonHelper do Common helper functions """ + alias EthereumJSONRPC.Utility.EndpointAvailabilityObserver + # converts duration like "5s", "2m", "1h5m" to milliseconds @duration_regex ~r/(\d+)([smhSMH]?)/ def parse_duration(duration) do @@ -30,6 +32,23 @@ defmodule EthereumJSONRPC.Utility.CommonHelper do Keyword.put(keyword || [], nearest_path, put_in_keyword_nested(keyword[nearest_path], rest_path, value)) end + @doc """ + Get available json rpc url from `json_rpc_transport_options` (or global `json_rpc_named_arguments`) of `url_type` type + based on `EthereumJSONRPC.Utility.EndpointAvailabilityObserver`. + """ + @spec get_available_url(Keyword.t() | nil, atom()) :: String.t() | nil + def get_available_url(json_rpc_transport_options \\ nil, url_type \\ :http) do + transport_options = + json_rpc_transport_options || Application.get_env(:explorer, :json_rpc_named_arguments)[:transport_options] + + fallback_urls = url_type_to_urls(url_type, transport_options, :fallback) + + url_type + |> url_type_to_urls(transport_options) + |> EndpointAvailabilityObserver.maybe_replace_urls(fallback_urls, url_type) + |> select_single_url() + end + @doc """ Extracts urls corresponding to `url_type` from json rpc transport options """ @@ -41,6 +60,12 @@ defmodule EthereumJSONRPC.Utility.CommonHelper do json_rpc_transport_options[urls_key] end + defp select_single_url([]), do: nil + + defp select_single_url(urls) do + Enum.random(urls) + end + defp convert_to_ms(number, "s"), do: :timer.seconds(number) defp convert_to_ms(number, "m"), do: :timer.minutes(number) defp convert_to_ms(number, "h"), do: :timer.hours(number) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/endpoint_availability_checker.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/endpoint_availability_checker.ex index 78242fe679bf..7beae80476be 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/endpoint_availability_checker.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/utility/endpoint_availability_checker.ex @@ -38,7 +38,7 @@ defmodule EthereumJSONRPC.Utility.EndpointAvailabilityChecker do Enum.reduce(unavailable_endpoints_arguments, [], fn {json_rpc_named_arguments, url_type}, acc -> case fetch_latest_block_number(json_rpc_named_arguments) do {:ok, _number} -> - url = json_rpc_named_arguments[:transport_options][:url] + [url] = json_rpc_named_arguments[:transport_options][:urls] EndpointAvailabilityObserver.enable_endpoint(url, url_type, json_rpc_named_arguments) acc diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/http/mox_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/http/mox_test.exs index 09eee029ba42..d72721d62ab8 100644 --- a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/http/mox_test.exs +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/http/mox_test.exs @@ -16,7 +16,7 @@ defmodule EthereumJSONRPC.HTTP.MoxTest do transport: EthereumJSONRPC.HTTP, transport_options: [ http: EthereumJSONRPC.HTTP.Mox, - url: url(), + urls: [url()], http_options: http_options() ], # Which one does not matter, so pick one @@ -289,7 +289,7 @@ defmodule EthereumJSONRPC.HTTP.MoxTest do transport_options = Keyword.fetch!(json_rpc_named_arguments, :transport_options) http = Keyword.fetch!(transport_options, :http) - url = Keyword.fetch!(transport_options, :url) + url = transport_options |> Keyword.fetch!(:urls) |> List.first() json = Jason.encode_to_iodata!(payload) http_options = Keyword.fetch!(transport_options, :http_options) diff --git a/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/utility/common_helper_test.exs b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/utility/common_helper_test.exs new file mode 100644 index 000000000000..12dd93c30923 --- /dev/null +++ b/apps/ethereum_jsonrpc/test/ethereum_jsonrpc/utility/common_helper_test.exs @@ -0,0 +1,52 @@ +defmodule EthereumJSONRPC.Utility.CommonHelperTest do + use ExUnit.Case, async: true + + alias EthereumJSONRPC.Utility.{EndpointAvailabilityObserver, CommonHelper} + + @options [ + urls: ["url_1", "url_2"], + trace_urls: ["trace_url_1", "trace_url_2"], + eth_call_urls: ["eth_call_url_1", "eth_call_url_2"], + fallback_urls: ["fallback_url_1", "fallback_url_2"], + fallback_trace_urls: ["fallback_trace_url_1", "fallback_trace_url_2"], + fallback_eth_call_urls: ["fallback_ec_url_1", "fallback_ec_url_2"] + ] + + test "url_type_to_urls/3" do + assert ["url_1", "url_2"] = CommonHelper.url_type_to_urls(:http, @options) + assert ["trace_url_1", "trace_url_2"] = CommonHelper.url_type_to_urls(:trace, @options) + assert ["eth_call_url_1", "eth_call_url_2"] = CommonHelper.url_type_to_urls(:eth_call, @options) + assert ["fallback_url_1", "fallback_url_2"] = CommonHelper.url_type_to_urls(:http, @options, :fallback) + assert ["fallback_trace_url_1", "fallback_trace_url_2"] = CommonHelper.url_type_to_urls(:trace, @options, :fallback) + assert ["fallback_ec_url_1", "fallback_ec_url_2"] = CommonHelper.url_type_to_urls(:eth_call, @options, :fallback) + end + + test "get_available_url/2" do + EndpointAvailabilityObserver.start_link([]) + + assert CommonHelper.get_available_url(@options, :http) in @options[:urls] + assert CommonHelper.get_available_url(@options, :trace) in @options[:trace_urls] + assert CommonHelper.get_available_url(@options, :eth_call) in @options[:eth_call_urls] + + set_url_unavailable("url_1", :http) + set_url_unavailable("url_2", :http) + + assert CommonHelper.get_available_url(@options, :http) in @options[:fallback_urls] + + set_url_unavailable("trace_url_1", :trace) + set_url_unavailable("trace_url_2", :trace) + + assert CommonHelper.get_available_url(@options, :trace) in @options[:fallback_trace_urls] + + set_url_unavailable("eth_call_url_1", :eth_call) + set_url_unavailable("eth_call_url_2", :eth_call) + + assert CommonHelper.get_available_url(@options, :eth_call) in @options[:fallback_eth_call_urls] + end + + defp set_url_unavailable(url, url_type) do + Enum.each(1..3, fn _ -> + EndpointAvailabilityObserver.inc_error_count(url, [transport_options: @options], url_type) + end) + end +end diff --git a/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/case/geth/http_websocket.ex b/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/case/geth/http_websocket.ex index 05fe92d0ae6c..403daf407b1f 100644 --- a/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/case/geth/http_websocket.ex +++ b/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/case/geth/http_websocket.ex @@ -12,7 +12,7 @@ defmodule EthereumJSONRPC.Case.Geth.HTTPWebSocket do transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, http_options: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]], - url: "https://mainnet.infura.io/8lTvJTKmHPCHazkneJsY" + urls: ["https://mainnet.infura.io/8lTvJTKmHPCHazkneJsY"] ], variant: EthereumJSONRPC.Geth ) diff --git a/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/case/nethermind/http_websocket.ex b/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/case/nethermind/http_websocket.ex index 0c704d3bf5d3..4f516d59613f 100644 --- a/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/case/nethermind/http_websocket.ex +++ b/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/case/nethermind/http_websocket.ex @@ -12,7 +12,7 @@ defmodule EthereumJSONRPC.Case.Nethermind.HTTPWebSocket do transport_options: [ http: EthereumJSONRPC.HTTP.HTTPoison, http_options: [recv_timeout: 60_000, timeout: 60_000, hackney: [pool: :ethereum_jsonrpc]], - url: "http://3.85.253.242:8545" + urls: ["http://3.85.253.242:8545"] ], variant: EthereumJSONRPC.Nethermind ) diff --git a/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/http/case.ex b/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/http/case.ex index f71bd9d64770..767f41e4d58f 100644 --- a/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/http/case.ex +++ b/apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/http/case.ex @@ -9,7 +9,7 @@ defmodule EthereumJSONRPC.HTTP.Case do transport: EthereumJSONRPC.HTTP, transport_options: [ http: http(), - url: url(), + urls: [url()], http_options: http_options() ] ] diff --git a/apps/explorer/lib/explorer/chain/bridged_token.ex b/apps/explorer/lib/explorer/chain/bridged_token.ex index 8e5c62e81940..074601a302ef 100644 --- a/apps/explorer/lib/explorer/chain/bridged_token.ex +++ b/apps/explorer/lib/explorer/chain/bridged_token.ex @@ -828,13 +828,18 @@ defmodule Explorer.Chain.BridgedToken do end defp update_transport_options_set_foreign_json_rpc(transport_options, foreign_json_rpc) do - Keyword.get_and_update(transport_options, :method_to_url, fn method_to_url -> - {_, updated_method_to_url} = - Keyword.get_and_update(method_to_url, :eth_call, fn eth_call -> - {eth_call, foreign_json_rpc} - end) + {_, updated_transport_options} = + Keyword.get_and_update(transport_options, :method_to_url, fn method_to_url -> + {_, updated_method_to_url} = + Keyword.get_and_update(method_to_url, :eth_call, fn eth_call -> + {eth_call, :eth_call} + end) + + {method_to_url, updated_method_to_url} + end) - {method_to_url, updated_method_to_url} + Keyword.get_and_update(updated_transport_options, :eth_call_urls, fn eth_call_urls -> + {eth_call_urls, [foreign_json_rpc]} end) end diff --git a/apps/explorer/lib/explorer/smart_contract/stylus/verifier.ex b/apps/explorer/lib/explorer/smart_contract/stylus/verifier.ex index 62496b90ffcf..e1f6baebe816 100644 --- a/apps/explorer/lib/explorer/smart_contract/stylus/verifier.ex +++ b/apps/explorer/lib/explorer/smart_contract/stylus/verifier.ex @@ -9,6 +9,7 @@ defmodule Explorer.SmartContract.Stylus.Verifier do - Compares the resulting bytecode against the deployed contract bytecode - Returns verification details including ABI and contract metadata """ + alias EthereumJSONRPC.Utility.CommonHelper alias Explorer.Chain.{Hash, SmartContract} alias Explorer.SmartContract.StylusVerifierInterface @@ -68,7 +69,7 @@ defmodule Explorer.SmartContract.Stylus.Verifier do {:ok, map()} | {:error, any()} defp evaluate_authenticity_inner(true, address_hash, params) do transaction_hash = fetch_data_for_stylus_verification(address_hash) - rpc_endpoint = Application.get_env(:explorer, :json_rpc_named_arguments)[:transport_options][:urls] |> List.first() + rpc_endpoint = CommonHelper.get_available_url() params |> Map.take(["cargo_stylus_version", "repository_url", "commit", "path_prefix"]) diff --git a/apps/indexer/lib/indexer/block/catchup/bound_interval_supervisor.ex b/apps/indexer/lib/indexer/block/catchup/bound_interval_supervisor.ex index 24bc22412f6f..493d774baeae 100644 --- a/apps/indexer/lib/indexer/block/catchup/bound_interval_supervisor.ex +++ b/apps/indexer/lib/indexer/block/catchup/bound_interval_supervisor.ex @@ -311,7 +311,7 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisor do } = state ) do Logger.error(fn -> - "Catchup index stream exited because the archive node endpoint at #{Keyword.get(options, :url)} is unavailable. Restarting" + "Catchup index stream exited because the archive node endpoint at #{Keyword.get(options, :urls)} is unavailable. Restarting" end) send(self(), :catchup_index) From 9b1e8028299d855dd565e1b13e6c4760914bd557 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:41:06 +0400 Subject: [PATCH 331/363] fix: Check if flash is fetched before getting it in app.html (#11270) --- .../lib/block_scout_web/templates/layout/app.html.eex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex index e3ea973fa791..996abc8c2bc8 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex @@ -99,9 +99,9 @@ <%= if session && !session[:email_verified] do %> <% else %> - + <% end %> - +
<%= @inner_content %>
From f005976152fe7441cfebd16ac30bb32374530978 Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:48:48 +0300 Subject: [PATCH 332/363] fix: handle excessive otp confirmations (#11244) --- .../lib/explorer/third_party_integrations/auth0.ex | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/explorer/lib/explorer/third_party_integrations/auth0.ex b/apps/explorer/lib/explorer/third_party_integrations/auth0.ex index 51c59806d75d..80042c99c1dc 100644 --- a/apps/explorer/lib/explorer/third_party_integrations/auth0.ex +++ b/apps/explorer/lib/explorer/third_party_integrations/auth0.ex @@ -481,6 +481,16 @@ defmodule Explorer.ThirdPartyIntegrations.Auth0 do }} -> {:error, "Wrong verification code."} + {:error, + %OAuth2.Response{ + status_code: 403, + body: %{ + "error" => "invalid_grant", + "error_description" => "You've reached the maximum number of attempts. Please try to login again." + } + }} -> + {:error, "Max attempts reached. Please resend code."} + other -> Logger.error("Error while confirming otp: #{inspect(other)}") From cacbf6060401db498752164b03f3ae914d159764 Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:49:09 +0300 Subject: [PATCH 333/363] fix: eth_getLogs paging (#11248) --- apps/explorer/lib/explorer/etherscan/logs.ex | 2 +- .../test/explorer/etherscan/logs_test.exs | 52 +++++++++++++++---- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/apps/explorer/lib/explorer/etherscan/logs.ex b/apps/explorer/lib/explorer/etherscan/logs.ex index 3cce390ea530..14afe2c16709 100644 --- a/apps/explorer/lib/explorer/etherscan/logs.ex +++ b/apps/explorer/lib/explorer/etherscan/logs.ex @@ -355,7 +355,7 @@ defmodule Explorer.Etherscan.Logs do defp page_logs(query, %{block_number: block_number, log_index: log_index}) do from( data in query, - where: data.index > ^log_index and data.block_number >= ^block_number + where: {data.block_number, data.index} > {^block_number, ^log_index} ) end diff --git a/apps/explorer/test/explorer/etherscan/logs_test.exs b/apps/explorer/test/explorer/etherscan/logs_test.exs index 02cc8f3e0262..2df2a32b15c1 100644 --- a/apps/explorer/test/explorer/etherscan/logs_test.exs +++ b/apps/explorer/test/explorer/etherscan/logs_test.exs @@ -208,23 +208,53 @@ defmodule Explorer.Etherscan.LogsTest do test "paginates logs" do contract_address = insert(:contract_address) - transaction = - %Transaction{block: block} = + transaction_a = + :transaction + |> insert(to_address: contract_address) + |> with_block() + + transaction_b = + :transaction + |> insert(to_address: contract_address) + |> with_block() + + transaction_c = :transaction |> insert(to_address: contract_address) |> with_block() inserted_records = - insert_list(2000, :log, - address: contract_address, - transaction: transaction, - block_number: block.number, - block: block - ) + for i <- 1..700 do + insert(:log, + address: contract_address, + transaction: transaction_a, + block_number: transaction_a.block.number, + block: transaction_a.block, + index: i + ) + end ++ + for i <- 1..700 do + insert(:log, + address: contract_address, + transaction: transaction_b, + block_number: transaction_b.block.number, + block: transaction_b.block, + index: i + ) + end ++ + for i <- 1..600 do + insert(:log, + address: contract_address, + transaction: transaction_c, + block_number: transaction_c.block.number, + block: transaction_c.block, + index: i + ) + end filter = %{ - from_block: block.number, - to_block: block.number, + from_block: transaction_a.block.number, + to_block: transaction_c.block.number, address_hash: contract_address.hash } @@ -236,7 +266,7 @@ defmodule Explorer.Etherscan.LogsTest do next_page_params = %{ log_index: last_record.index, - block_number: transaction.block_number + block_number: last_record.block_number } second_found_logs = Logs.list_logs(filter, next_page_params) From 0134bd078a8965d36fe703ebdc07f0820c31014a Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Thu, 28 Nov 2024 12:22:47 +0400 Subject: [PATCH 334/363] chore: Optimize CurrentTokenBalances import runner (#11191) * chore: Optimize CurrentTokenBalances import runner * Update apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex Co-authored-by: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> * CurrentTokenBalance runner optimization refactor --------- Co-authored-by: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> --- .../chain/address/current_token_balance.ex | 23 ++++- .../runner/address/current_token_balances.ex | 85 ++++++++++++++++++- .../lib/explorer/chain/token_transfer.ex | 13 +-- apps/explorer/lib/explorer/helper.ex | 16 +++- 4 files changed, 123 insertions(+), 14 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex index a40fc51b11a4..7d2efd461704 100644 --- a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex @@ -13,7 +13,7 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] - alias Explorer.{Chain, PagingOptions, Repo} + alias Explorer.{Chain, Helper, PagingOptions, Repo} alias Explorer.Chain.{Address, Block, CurrencyHelper, Hash, Token} alias Explorer.Chain.Address.TokenBalance @@ -354,4 +354,25 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do Stream.concat([row_names], holders_list) end + + @doc """ + Encode `address_hash`, `token_contract_address_hash` and `token_id` into a string that can be used in + `(address_hash, token_contract_address_hash, token_id) IN (...)` WHERE clause + """ + @spec encode_ids([{Hash.t(), Hash.t(), non_neg_integer()}] | [{Hash.t(), Hash.t()}]) :: binary() + def encode_ids(ids) do + encoded_values = + ids + |> Enum.reduce("", fn + {address_hash, token_hash, token_id}, acc -> + acc <> + "('#{Helper.hash_to_query_string(address_hash)}', '#{Helper.hash_to_query_string(token_hash)}', #{token_id})," + + {address_hash, token_hash}, acc -> + acc <> "('#{Helper.hash_to_query_string(address_hash)}', '#{Helper.hash_to_query_string(token_hash)}')," + end) + |> String.trim_trailing(",") + + "(#{encoded_values})" + end end diff --git a/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex b/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex index cc0dd4dfef26..b3ceb1935110 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex @@ -116,7 +116,15 @@ defmodule Explorer.Chain.Import.Runner.Address.CurrentTokenBalances do :filter_ctb_placeholders ) end) - |> Multi.run(:address_current_token_balances, fn repo, %{filter_ctb_placeholders: filtered_changes_list} -> + |> Multi.run(:filter_params, fn repo, %{filter_ctb_placeholders: filtered_changes_list} -> + Instrumenter.block_import_stage_runner( + fn -> filter_params(repo, filtered_changes_list) end, + :block_following, + :current_token_balances, + :filter_params + ) + end) + |> Multi.run(:address_current_token_balances, fn repo, %{filter_params: filtered_changes_list} -> Instrumenter.block_import_stage_runner( fn -> insert(repo, filtered_changes_list, insert_options) end, :block_following, @@ -205,6 +213,81 @@ defmodule Explorer.Chain.Import.Runner.Address.CurrentTokenBalances do end end + defp filter_params(repo, changes_list) do + {params_without_token_id, params_with_token_id} = Enum.split_with(changes_list, &is_nil(&1[:token_id])) + + existing_ctb_without_token_id = select_existing_current_token_balances(repo, params_without_token_id, false) + existing_ctb_with_token_id = select_existing_current_token_balances(repo, params_with_token_id, true) + + existing_ctb_map = + existing_ctb_without_token_id + |> Enum.concat(existing_ctb_with_token_id) + |> Map.new(fn [address_hash, token_contract_address_hash, token_id, block_number, value, value_fetched_at] -> + {{address_hash, token_contract_address_hash, token_id}, + %{block_number: block_number, value: value, value_fetched_at: value_fetched_at}} + end) + + filtered_ctbs = + Enum.filter(changes_list, fn ctb -> + existing_ctb = existing_ctb_map[{ctb[:address_hash], ctb[:token_contract_address_hash], ctb[:token_id]}] + should_update?(ctb, existing_ctb) + end) + + {:ok, filtered_ctbs} + end + + defp select_existing_current_token_balances(_repo, [], _with_token_id?), do: [] + + defp select_existing_current_token_balances(repo, params, with_token_id?) do + params + |> existing_ctb_query(with_token_id?) + |> repo.query!() + |> Map.get(:rows) + end + + defp existing_ctb_query(params, false) do + encoded_ids = + params + |> Enum.map(&{&1.address_hash, &1.token_contract_address_hash}) + |> CurrentTokenBalance.encode_ids() + + """ + SELECT ctb.address_hash, ctb.token_contract_address_hash, ctb.token_id, ctb.block_number, ctb.value, ctb.value_fetched_at + FROM address_current_token_balances ctb + WHERE (ctb.address_hash, ctb.token_contract_address_hash) IN #{encoded_ids} AND ctb.token_id IS NULL + """ + end + + defp existing_ctb_query(params, true) do + encoded_ids = + params + |> Enum.map(&{&1.address_hash, &1.token_contract_address_hash, &1.token_id}) + |> CurrentTokenBalance.encode_ids() + + """ + SELECT ctb.address_hash, ctb.token_contract_address_hash, ctb.token_id, ctb.block_number, ctb.value, ctb.value_fetched_at + FROM address_current_token_balances ctb + WHERE (ctb.address_hash, ctb.token_contract_address_hash, ctb.token_id) IN #{encoded_ids} + """ + end + + # ctb does not exist + defp should_update?(_new_ctb, nil), do: true + + # new ctb has no value + defp should_update?(%{value_fetched_at: nil}, _existing_ctb), do: false + + # new ctb is newer + defp should_update?(%{block_number: new_ctb_block_number}, %{block_number: existing_ctb_block_number}) + when new_ctb_block_number > existing_ctb_block_number, + do: true + + # new ctb is the same height or older + defp should_update?(new_ctb, existing_ctb) do + existing_ctb.block_number == new_ctb.block_number and not is_nil(new_ctb.value) and + (is_nil(existing_ctb.value_fetched_at) or existing_ctb.value_fetched_at < new_ctb.value_fetched_at) + end + @spec insert(Repo.t(), [map()], %{ optional(:on_conflict) => Import.Runner.on_conflict(), required(:timeout) => timeout(), diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index 74e0287c5c1c..4d4aae5b98ac 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -137,7 +137,7 @@ defmodule Explorer.Chain.TokenTransfer do import Ecto.Changeset - alias Explorer.Chain + alias Explorer.{Chain, Helper} alias Explorer.Chain.{DenormalizationHelper, Hash, Log, TokenTransfer} alias Explorer.Chain.SmartContract.Proxy.Models.Implementation alias Explorer.{PagingOptions, Repo} @@ -648,22 +648,13 @@ defmodule Explorer.Chain.TokenTransfer do encoded_values = ids |> Enum.reduce("", fn {t_hash, b_hash, log_index}, acc -> - acc <> "('#{hash_to_query_string(t_hash)}', '#{hash_to_query_string(b_hash)}', #{log_index})," + acc <> "('#{Helper.hash_to_query_string(t_hash)}', '#{Helper.hash_to_query_string(b_hash)}', #{log_index})," end) |> String.trim_trailing(",") "(#{encoded_values})" end - defp hash_to_query_string(hash) do - s_hash = - hash - |> to_string() - |> String.trim_leading("0") - - "\\#{s_hash}" - end - @doc """ Fetches token transfers from logs. """ diff --git a/apps/explorer/lib/explorer/helper.ex b/apps/explorer/lib/explorer/helper.ex index 226379d89cfb..62b1adba791f 100644 --- a/apps/explorer/lib/explorer/helper.ex +++ b/apps/explorer/lib/explorer/helper.ex @@ -5,7 +5,7 @@ defmodule Explorer.Helper do alias ABI.TypeDecoder alias Explorer.Chain - alias Explorer.Chain.Data + alias Explorer.Chain.{Data, Hash} import Ecto.Query, only: [join: 5, where: 3] import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] @@ -296,4 +296,18 @@ defmodule Explorer.Helper do def get_app_host do Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:host] end + + @doc """ + Converts `Explorer.Chain.Hash.t()` or string hash to DB-acceptable format. + For example "0xabcdef1234567890abcdef1234567890abcdef" -> "\\xabcdef1234567890abcdef1234567890abcdef" + """ + @spec hash_to_query_string(Hash.t() | String.t()) :: String.t() + def hash_to_query_string(hash) do + s_hash = + hash + |> to_string() + |> String.trim_leading("0") + + "\\#{s_hash}" + end end From 481547f35639587fa2f18dfb19f371abc2610efd Mon Sep 17 00:00:00 2001 From: varasev <33550681+varasev@users.noreply.github.com> Date: Fri, 29 Nov 2024 17:02:21 +0400 Subject: [PATCH 335/363] fix: Rework initialization of the `RollupL1ReorgMonitor` and fix `read_system_config` for fallback cases (#11275) * Fix init in RollupL1ReorgMonitor * Update PULL_REQUEST_TEMPLATE * Fix init in RollupL1ReorgMonitor * Update PULL_REQUEST_TEMPLATE * Fix read_system_config function for fallback cases * Update rollup_l1_reorg_monitor.ex Co-authored-by: Victor Baranov --------- Co-authored-by: POA <33550681+poa@users.noreply.github.com> Co-authored-by: Victor Baranov --- PULL_REQUEST_TEMPLATE.md | 2 +- apps/indexer/lib/indexer/fetcher/optimism.ex | 64 ++++++++++------- .../fetcher/optimism/transaction_batch.ex | 70 ++++++++++++------- .../fetcher/polygon_zkevm/bridge_l1.ex | 6 +- .../fetcher/rollup_l1_reorg_monitor.ex | 44 ++++++++++-- .../lib/indexer/fetcher/scroll/batch.ex | 6 +- .../lib/indexer/fetcher/scroll/bridge_l1.ex | 6 +- .../lib/indexer/fetcher/shibarium/l1.ex | 6 +- 8 files changed, 129 insertions(+), 75 deletions(-) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index ef743c30f6d6..1d8394249928 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -27,6 +27,6 @@ - [ ] If I added new functionality, I added tests covering it. - [ ] If I fixed a bug, I added a regression test to prevent the bug from silently reappearing again. - [ ] I checked whether I should update the docs and did so by submitting a PR to [docs repository](https://github.com/blockscout/docs). -- [ ] If I added/changed/removed ENV var, I submitted a PR to [docs repository](https://github.com/blockscout/docs) to update the list of [env vars](https://github.com/blockscout/docs/blob/master/for-developers/information-and-settings/env-variables.md) and I updated the version to `master` in the Version column. If I removed variable, I added it to [Deprecated ENV Variables](https://github.com/blockscout/docs/blob/master/for-developers/information-and-settings/env-variables/deprecated-env-variables/README.md) page. After merging docs PR, changes will be reflected in these [pages](https://docs.blockscout.com/for-developers/information-and-settings/env-variables). +- [ ] If I added/changed/removed ENV var, I submitted a PR to [docs repository](https://github.com/blockscout/docs) to update the list of [env vars](https://github.com/blockscout/docs/blob/master/setup/env-variables/README.md) and I updated the version to `master` in the Version column. If I removed variable, I added it to [Deprecated ENV Variables](https://github.com/blockscout/docs/blob/master/setup/env-variables/deprecated-env-variables/README.md) page. After merging docs PR, changes will be reflected in these [pages](https://docs.blockscout.com/setup/env-variables). - [ ] If I added new DB indices, I checked, that they are not redundant, with PGHero or other tools. - [ ] If I added/removed chain type, I modified the Github CI matrix and PR labels accordingly. diff --git a/apps/indexer/lib/indexer/fetcher/optimism.ex b/apps/indexer/lib/indexer/fetcher/optimism.ex index 68834687cdf3..9deaa81fd95e 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism.ex @@ -20,6 +20,7 @@ defmodule Indexer.Fetcher.Optimism do alias EthereumJSONRPC.Block.ByNumber alias EthereumJSONRPC.Contract alias Explorer.Repo + alias Indexer.Fetcher.RollupL1ReorgMonitor alias Indexer.Helper @fetcher_name :optimism @@ -238,8 +239,7 @@ defmodule Indexer.Fetcher.Optimism do optimism_l1_rpc = l1_rpc_url() with {:system_config_valid, true} <- {:system_config_valid, Helper.address_correct?(system_config)}, - {:reorg_monitor_started, true} <- - {:reorg_monitor_started, !is_nil(Process.whereis(Indexer.Fetcher.RollupL1ReorgMonitor))}, + _ <- RollupL1ReorgMonitor.wait_for_start(caller), {:rpc_l1_undefined, false} <- {:rpc_l1_undefined, is_nil(optimism_l1_rpc)}, json_rpc_named_arguments = json_rpc_named_arguments(optimism_l1_rpc), {optimism_portal, start_block_l1} <- read_system_config(system_config, json_rpc_named_arguments), @@ -275,13 +275,6 @@ defmodule Indexer.Fetcher.Optimism do stop: false }} else - {:reorg_monitor_started, false} -> - Logger.error( - "Cannot start this process as reorg monitor in Indexer.Fetcher.RollupL1ReorgMonitor is not started." - ) - - {:stop, :normal, %{}} - {:rpc_l1_undefined, true} -> Logger.error("L1 RPC URL is not defined.") {:stop, :normal, %{}} @@ -353,23 +346,46 @@ defmodule Indexer.Fetcher.Optimism do error_message = &"Cannot call public getters of SystemConfig. Error: #{inspect(&1)}" - case Helper.repeated_call( - &json_rpc/2, - [requests, json_rpc_named_arguments], - error_message, - Helper.finite_retries_number() - ) do - {:ok, responses} -> - "0x000000000000000000000000" <> optimism_portal = Enum.at(responses, 0).result - start_block = quantity_to_integer(Enum.at(responses, 1).result) - {"0x" <> optimism_portal, start_block} + env = Application.get_all_env(:indexer)[__MODULE__] + fallback_start_block = env[:start_block_l1] + + {optimism_portal, start_block} = + case Helper.repeated_call( + &json_rpc/2, + [requests, json_rpc_named_arguments], + error_message, + Helper.finite_retries_number() + ) do + {:ok, responses} -> + optimism_portal_result = Map.get(Enum.at(responses, 0), :result) + + optimism_portal = + with {:nil_result, true, _} <- {:nil_result, is_nil(optimism_portal_result), optimism_portal_result}, + {:fallback_defined, true} <- {:fallback_defined, Helper.address_correct?(env[:portal])} do + env[:portal] + else + {:nil_result, false, portal} -> + "0x000000000000000000000000" <> optimism_portal = portal + "0x" <> optimism_portal + + {:fallback_defined, false} -> + nil + end + + start_block = + responses + |> Enum.at(1) + |> Map.get(:result, fallback_start_block) + |> quantity_to_integer() + + {optimism_portal, start_block} - _ -> - env = Application.get_all_env(:indexer)[__MODULE__] + _ -> + {env[:portal], fallback_start_block} + end - if Helper.address_correct?(env[:portal]) and not is_nil(env[:start_block_l1]) do - {env[:portal], env[:start_block_l1]} - end + if Helper.address_correct?(optimism_portal) and !is_nil(start_block) do + {String.downcase(optimism_portal), start_block} end end diff --git a/apps/indexer/lib/indexer/fetcher/optimism/transaction_batch.ex b/apps/indexer/lib/indexer/fetcher/optimism/transaction_batch.ex index aa0623e05997..a8daf641a109 100644 --- a/apps/indexer/lib/indexer/fetcher/optimism/transaction_batch.ex +++ b/apps/indexer/lib/indexer/fetcher/optimism/transaction_batch.ex @@ -102,8 +102,7 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do {:system_config_valid, Helper.address_correct?(system_config)}, {:genesis_block_l2_invalid, false} <- {:genesis_block_l2_invalid, is_nil(env[:genesis_block_l2]) or env[:genesis_block_l2] < 0}, - {:reorg_monitor_started, true} <- - {:reorg_monitor_started, !is_nil(Process.whereis(RollupL1ReorgMonitor))}, + _ <- RollupL1ReorgMonitor.wait_for_start(__MODULE__), {:rpc_l1_undefined, false} <- {:rpc_l1_undefined, is_nil(optimism_l1_rpc)}, json_rpc_named_arguments = Optimism.json_rpc_named_arguments(optimism_l1_rpc), {:system_config_read, {start_block_l1, batch_inbox, batch_submitter}} <- @@ -160,13 +159,6 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do Logger.error("L2 genesis block number is undefined or invalid.") {:stop, :normal, state} - {:reorg_monitor_started, false} -> - Logger.error( - "Cannot start this process as reorg monitor in Indexer.Fetcher.RollupL1ReorgMonitor is not started." - ) - - {:stop, :normal, state} - {:rpc_l1_undefined, true} -> Logger.error("L1 RPC URL is not defined.") {:stop, :normal, state} @@ -1447,25 +1439,51 @@ defmodule Indexer.Fetcher.Optimism.TransactionBatch do error_message = &"Cannot call public getters of SystemConfig. Error: #{inspect(&1)}" - case Helper.repeated_call( - &json_rpc/2, - [requests, json_rpc_named_arguments], - error_message, - Helper.finite_retries_number() - ) do - {:ok, responses} -> - start_block = quantity_to_integer(Enum.at(responses, 0).result) - "0x000000000000000000000000" <> batch_inbox = Enum.at(responses, 1).result - "0x000000000000000000000000" <> batch_submitter = Enum.at(responses, 2).result - {start_block, String.downcase("0x" <> batch_inbox), String.downcase("0x" <> batch_submitter)} + env = Application.get_all_env(:indexer)[__MODULE__] + fallback_start_block = Application.get_all_env(:indexer)[Indexer.Fetcher.Optimism][:start_block_l1] + + {start_block, batch_inbox, batch_submitter} = + case Helper.repeated_call( + &json_rpc/2, + [requests, json_rpc_named_arguments], + error_message, + Helper.finite_retries_number() + ) do + {:ok, responses} -> + start_block = + responses + |> Enum.at(0) + |> Map.get(:result, fallback_start_block) + |> quantity_to_integer() + + inbox_result = Map.get(Enum.at(responses, 1), :result) + submitter_result = Map.get(Enum.at(responses, 2), :result) + + {batch_inbox, batch_submitter} = + with {:nil_result, true, _, _} <- + {:nil_result, is_nil(inbox_result) or is_nil(submitter_result), inbox_result, submitter_result}, + {:fallback_defined, true} <- + {:fallback_defined, + Helper.address_correct?(env[:inbox]) and Helper.address_correct?(env[:submitter])} do + {env[:inbox], env[:submitter]} + else + {:nil_result, false, inbox, submitter} -> + "0x000000000000000000000000" <> batch_inbox = inbox + "0x000000000000000000000000" <> batch_submitter = submitter + {"0x" <> batch_inbox, "0x" <> batch_submitter} + + {:fallback_defined, false} -> + {nil, nil} + end - _ -> - start_block = Application.get_all_env(:indexer)[Indexer.Fetcher.Optimism][:start_block_l1] - env = Application.get_all_env(:indexer)[__MODULE__] + {start_block, batch_inbox, batch_submitter} - if not is_nil(start_block) and Helper.address_correct?(env[:inbox]) and Helper.address_correct?(env[:submitter]) do - {start_block, String.downcase(env[:inbox]), String.downcase(env[:submitter])} - end + _ -> + {fallback_start_block, env[:inbox], env[:submitter]} + end + + if !is_nil(start_block) and Helper.address_correct?(batch_inbox) and Helper.address_correct?(batch_submitter) do + {start_block, String.downcase(batch_inbox), String.downcase(batch_submitter)} end end diff --git a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex index 6d086d5d4446..1e45795d35ff 100644 --- a/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex +++ b/apps/indexer/lib/indexer/fetcher/polygon_zkevm/bridge_l1.ex @@ -57,7 +57,7 @@ defmodule Indexer.Fetcher.PolygonZkevm.BridgeL1 do env_l2 = Application.get_all_env(:indexer)[Indexer.Fetcher.PolygonZkevm.BridgeL2] with {:start_block_undefined, false} <- {:start_block_undefined, is_nil(env[:start_block])}, - {:reorg_monitor_started, true} <- {:reorg_monitor_started, !is_nil(Process.whereis(RollupL1ReorgMonitor))}, + _ <- RollupL1ReorgMonitor.wait_for_start(__MODULE__), rpc = env[:rpc], {:rpc_undefined, false} <- {:rpc_undefined, is_nil(rpc)}, {:rollup_network_id_l1_is_valid, true} <- @@ -101,10 +101,6 @@ defmodule Indexer.Fetcher.PolygonZkevm.BridgeL1 do # the process shouldn't start if the start block is not defined {:stop, :normal, %{}} - {:reorg_monitor_started, false} -> - Logger.error("Cannot start this process as Indexer.Fetcher.RollupL1ReorgMonitor is not started.") - {:stop, :normal, %{}} - {:rpc_undefined, true} -> Logger.error("L1 RPC URL is not defined.") {:stop, :normal, %{}} diff --git a/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex b/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex index b2f9541b2b5d..0860c65c93db 100644 --- a/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex +++ b/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex @@ -16,6 +16,7 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do alias Indexer.Helper @fetcher_name :rollup_l1_reorg_monitor + @start_recheck_period_seconds 3 @modules_can_use_reorg_monitor (case Application.compile_env(:explorer, :chain_type) do :optimism -> @@ -67,6 +68,11 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do GenServer.start_link(__MODULE__, args, Keyword.put_new(gen_server_options, :name, __MODULE__)) end + @impl GenServer + def init(_args) do + {:ok, %{}, {:continue, :ok}} + end + @doc """ This function initializes L1 blocks reorg monitor for the current rollup defined by CHAIN_TYPE. If the current chain is not a rollup, the module just doesn't start. @@ -84,10 +90,10 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do ## Returns - `{:ok, state}` with the determined parameters for the monitor loop if at least one rollup module is launched. - - `:ignore` if the monitor is not needed. + - `{:stop, :normal, %{}}` if the monitor is not needed. """ @impl GenServer - def init(_args) do + def handle_continue(:ok, _state) do Logger.metadata(fetcher: @fetcher_name) modules_using_reorg_monitor = @@ -96,7 +102,7 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do if Enum.empty?(modules_using_reorg_monitor) do # don't start reorg monitor as there is no module which would use it - :ignore + {:stop, :normal, %{}} else l1_rpc = Enum.at(modules_using_reorg_monitor, 0).l1_rpc_url() @@ -106,7 +112,7 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do Process.send(self(), :reorg_monitor, []) - {:ok, + {:noreply, %{ block_check_interval: block_check_interval, json_rpc_named_arguments: json_rpc_named_arguments, @@ -155,4 +161,34 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do {:noreply, %{state | prev_latest: latest}} end + + @doc """ + Infinitely waits for the module to be initialized and started. + + ## Parameters + - `waiting_module`: The module which called this function. + + ## Returns + - nothing + """ + @spec wait_for_start(module()) :: any() + def wait_for_start(waiting_module) do + state = + try do + __MODULE__ + |> Process.whereis() + |> :sys.get_state() + catch + :exit, _ -> %{} + end + + if map_size(state) == 0 do + Logger.warning( + "#{waiting_module} waits for #{__MODULE__} to start. Rechecking in #{@start_recheck_period_seconds} second(s)..." + ) + + :timer.sleep(@start_recheck_period_seconds * 1_000) + wait_for_start(waiting_module) + end + end end diff --git a/apps/indexer/lib/indexer/fetcher/scroll/batch.ex b/apps/indexer/lib/indexer/fetcher/scroll/batch.ex index 10e0c3a32d97..cf356b1c2309 100644 --- a/apps/indexer/lib/indexer/fetcher/scroll/batch.ex +++ b/apps/indexer/lib/indexer/fetcher/scroll/batch.ex @@ -78,7 +78,7 @@ defmodule Indexer.Fetcher.Scroll.Batch do env = Application.get_all_env(:indexer)[__MODULE__] with {:start_block_undefined, false} <- {:start_block_undefined, is_nil(env[:start_block])}, - {:reorg_monitor_started, true} <- {:reorg_monitor_started, !is_nil(Process.whereis(RollupL1ReorgMonitor))}, + _ <- RollupL1ReorgMonitor.wait_for_start(__MODULE__), rpc = l1_rpc_url(), {:rpc_undefined, false} <- {:rpc_undefined, is_nil(rpc)}, {:scroll_chain_contract_address_is_valid, true} <- @@ -114,10 +114,6 @@ defmodule Indexer.Fetcher.Scroll.Batch do # the process shouldn't start if the start block is not defined {:stop, :normal, %{}} - {:reorg_monitor_started, false} -> - Logger.error("Cannot start this process as Indexer.Fetcher.RollupL1ReorgMonitor is not started.") - {:stop, :normal, %{}} - {:rpc_undefined, true} -> Logger.error("L1 RPC URL is not defined.") {:stop, :normal, %{}} diff --git a/apps/indexer/lib/indexer/fetcher/scroll/bridge_l1.ex b/apps/indexer/lib/indexer/fetcher/scroll/bridge_l1.ex index be75a223b973..408cc72b8464 100644 --- a/apps/indexer/lib/indexer/fetcher/scroll/bridge_l1.ex +++ b/apps/indexer/lib/indexer/fetcher/scroll/bridge_l1.ex @@ -69,7 +69,7 @@ defmodule Indexer.Fetcher.Scroll.BridgeL1 do env = Application.get_all_env(:indexer)[__MODULE__] with {:start_block_undefined, false} <- {:start_block_undefined, is_nil(env[:start_block])}, - {:reorg_monitor_started, true} <- {:reorg_monitor_started, !is_nil(Process.whereis(RollupL1ReorgMonitor))}, + _ <- RollupL1ReorgMonitor.wait_for_start(__MODULE__), rpc = l1_rpc_url(), {:rpc_undefined, false} <- {:rpc_undefined, is_nil(rpc)}, {:messenger_contract_address_is_valid, true} <- @@ -104,10 +104,6 @@ defmodule Indexer.Fetcher.Scroll.BridgeL1 do # the process shouldn't start if the start block is not defined {:stop, :normal, %{}} - {:reorg_monitor_started, false} -> - Logger.error("Cannot start this process as Indexer.Fetcher.RollupL1ReorgMonitor is not started.") - {:stop, :normal, %{}} - {:rpc_undefined, true} -> Logger.error("L1 RPC URL is not defined.") {:stop, :normal, %{}} diff --git a/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex b/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex index 95151b9e2d03..99b7d7343d0e 100644 --- a/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex +++ b/apps/indexer/lib/indexer/fetcher/shibarium/l1.ex @@ -112,7 +112,7 @@ defmodule Indexer.Fetcher.Shibarium.L1 do env = Application.get_all_env(:indexer)[__MODULE__] with {:start_block_undefined, false} <- {:start_block_undefined, is_nil(env[:start_block])}, - {:reorg_monitor_started, true} <- {:reorg_monitor_started, !is_nil(Process.whereis(RollupL1ReorgMonitor))}, + _ <- RollupL1ReorgMonitor.wait_for_start(__MODULE__), rpc = env[:rpc], {:rpc_undefined, false} <- {:rpc_undefined, is_nil(rpc)}, {:deposit_manager_address_is_valid, true} <- @@ -164,10 +164,6 @@ defmodule Indexer.Fetcher.Shibarium.L1 do # the process shouldn't start if the start block is not defined {:stop, :normal, %{}} - {:reorg_monitor_started, false} -> - Logger.error("Cannot start this process as Indexer.Fetcher.RollupL1ReorgMonitor is not started.") - {:stop, :normal, %{}} - {:rpc_undefined, true} -> Logger.error("L1 RPC URL is not defined.") {:stop, :normal, %{}} From 718e63d5f7e85c44fcfbf9d28ec2ad1cbaeee9ed Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Fri, 29 Nov 2024 17:06:53 +0300 Subject: [PATCH 336/363] feat: Add gzip encoding option (#11292) * feat: Add gzip encoding option * add ETHEREUM_JSONRPC_HTTP_GZIP_ENABLED to common-blockscout.env * Update config/runtime.exs Co-authored-by: Victor Baranov --------- Co-authored-by: Viktor Baranov --- .../lib/ethereum_jsonrpc/http/httpoison.ex | 40 ++++++++++++++++++- config/runtime.exs | 3 +- cspell.json | 7 ++-- docker-compose/envs/common-blockscout.env | 30 +++++++++----- 4 files changed, 64 insertions(+), 16 deletions(-) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http/httpoison.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http/httpoison.ex index d7f561075192..3cc475d3ded4 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http/httpoison.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http/httpoison.ex @@ -9,9 +9,18 @@ defmodule EthereumJSONRPC.HTTP.HTTPoison do @impl HTTP def json_rpc(url, json, headers, options) when is_binary(url) and is_list(options) do + gzip_enabled? = Application.get_env(:ethereum_jsonrpc, EthereumJSONRPC.HTTP)[:gzip_enabled?] + + headers = + if gzip_enabled? do + [{"Accept-Encoding", "gzip"} | headers] + else + headers + end + case HTTPoison.post(url, json, headers, options) do - {:ok, %HTTPoison.Response{body: body, status_code: status_code}} -> - {:ok, %{body: body, status_code: status_code}} + {:ok, %HTTPoison.Response{body: body, status_code: status_code, headers: headers}} -> + {:ok, %{body: try_unzip(gzip_enabled?, body, headers), status_code: status_code}} {:error, %HTTPoison.Error{reason: reason}} -> {:error, reason} @@ -19,4 +28,31 @@ defmodule EthereumJSONRPC.HTTP.HTTPoison do end def json_rpc(url, _json, _headers, _options) when is_nil(url), do: {:error, "URL is nil"} + + defp try_unzip(true, body, headers) do + gzipped = + Enum.any?( + headers + |> Enum.map(fn {k, v} -> + {String.downcase(k), String.downcase(v)} + end), + fn kv -> + case kv do + {"content-encoding", "gzip"} -> true + {"content-encoding", "x-gzip"} -> true + _ -> false + end + end + ) + + if gzipped do + :zlib.gunzip(body) + else + body + end + end + + defp try_unzip(_gzip_enabled?, body, _headers) do + body + end end diff --git a/config/runtime.exs b/config/runtime.exs index 263bf4b34a33..d15ac01c21f5 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -189,7 +189,8 @@ config :ethereum_jsonrpc, EthereumJSONRPC.HTTP, headers: %{"Content-Type" => "application/json"} |> Map.merge(ConfigHelper.parse_json_env_var("ETHEREUM_JSONRPC_HTTP_HEADERS", "{}")) - |> Map.to_list() + |> Map.to_list(), + gzip_enabled?: ConfigHelper.parse_bool_env_var("ETHEREUM_JSONRPC_HTTP_GZIP_ENABLED", "false") config :ethereum_jsonrpc, EthereumJSONRPC.Geth, block_traceable?: ConfigHelper.parse_bool_env_var("ETHEREUM_JSONRPC_GETH_TRACE_BY_BLOCK"), diff --git a/cspell.json b/cspell.json index 0d89d8063545..13e79262603d 100644 --- a/cspell.json +++ b/cspell.json @@ -78,8 +78,8 @@ "blockreward", "blockscout", "blockscoutuser", - "bools", "Boneh", + "bools", "bridgedtokenlist", "brotli", "browserconfig", @@ -255,6 +255,7 @@ "grecaptcha", "greymatter", "gtag", + "gzipped", "happygokitty", "haspopup", "Hazkne", @@ -643,12 +644,12 @@ "xakgj", "xbaddress", "xdai", - "Xname", "xffff", "xlevel", "xlink", "xmark", "xmlhttprequest", + "Xname", "xnonsense", "xzzz", "yellowgreen", @@ -671,5 +672,5 @@ "dotenv", "html-eex", "makefile" - ], + ] } diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 1782375eb092..594c97a5c4f7 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -1,30 +1,41 @@ ETHEREUM_JSONRPC_VARIANT=geth ETHEREUM_JSONRPC_HTTP_URL=http://host.docker.internal:8545/ -# ETHEREUM_JSONRPC_FALLBACK_HTTP_URL= DATABASE_URL=postgresql://blockscout:ceWb1MeLBEeOIfk65gU8EjF8@db:5432/blockscout + # DATABASE_EVENT_URL= # DATABASE_QUEUE_TARGET # TEST_DATABASE_URL= # TEST_DATABASE_READ_ONLY_API_URL= + +ETHEREUM_JSONRPC_TRANSPORT=http +ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES=false +# ETHEREUM_JSONRPC_FALLBACK_HTTP_URL= ETHEREUM_JSONRPC_TRACE_URL=http://host.docker.internal:8545/ # ETHEREUM_JSONRPC_FALLBACK_TRACE_URL= -# ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL= # ETHEREUM_JSONRPC_ETH_CALL_URL= -# ETHEREUM_JSONRPC_HTTP_TIMEOUT= -# CHAIN_TYPE= -NETWORK= -SUBNETWORK=Awesome chain -LOGO=/images/blockscout_logo.svg +# ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL= # ETHEREUM_JSONRPC_WS_URL= # ETHEREUM_JSONRPC_FALLBACK_WS_URL= # ETHEREUM_JSONRPC_WS_RETRY_INTERVAL= -ETHEREUM_JSONRPC_TRANSPORT=http -ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES=false # ETHEREUM_JSONRPC_ARCHIVE_BALANCES_WINDOW=200 +# ETHEREUM_JSONRPC_HTTP_TIMEOUT= # ETHEREUM_JSONRPC_HTTP_HEADERS= +# ETHEREUM_JSONRPC_HTTP_GZIP_ENABLED= # ETHEREUM_JSONRPC_WAIT_PER_TIMEOUT= # ETHEREUM_JSONRPC_GETH_TRACE_BY_BLOCK= # ETHEREUM_JSONRPC_GETH_ALLOW_EMPTY_TRACES= +# ETHEREUM_JSONRPC_DEBUG_TRACE_TRANSACTION_TIMEOUT= +# ETHEREUM_JSONRPC_HTTP_URLS= +# ETHEREUM_JSONRPC_FALLBACK_HTTP_URLS= +# ETHEREUM_JSONRPC_TRACE_URLS= +# ETHEREUM_JSONRPC_FALLBACK_TRACE_URLS= +# ETHEREUM_JSONRPC_ETH_CALL_URLS= +# ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URLS= + +# CHAIN_TYPE= +NETWORK= +SUBNETWORK=Awesome chain +LOGO=/images/blockscout_logo.svg IPC_PATH= NETWORK_PATH=/ BLOCKSCOUT_HOST= @@ -175,7 +186,6 @@ INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false # INDEXER_EMPTY_BLOCKS_SANITIZER_INTERVAL= # INDEXER_INTERNAL_TRANSACTIONS_BATCH_SIZE= # INDEXER_INTERNAL_TRANSACTIONS_CONCURRENCY= -# ETHEREUM_JSONRPC_DEBUG_TRACE_TRANSACTION_TIMEOUT= # INDEXER_BLOCK_REWARD_BATCH_SIZE= # INDEXER_BLOCK_REWARD_CONCURRENCY= # INDEXER_TOKEN_INSTANCE_USE_BASE_URI_RETRY= From b2c2722a97a8a893c9d9901854fb8e1b15437992 Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Sat, 30 Nov 2024 10:39:15 +0400 Subject: [PATCH 337/363] feat: Switch DB requests from replica to master in case of replica inaccessibility (#11020) --- apps/explorer/lib/explorer/application.ex | 3 +- apps/explorer/lib/explorer/repo.ex | 4 +- .../utility/replica_accessibility_manager.ex | 65 +++++++++++++++++++ config/runtime.exs | 3 +- cspell.json | 1 + 5 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 apps/explorer/lib/explorer/utility/replica_accessibility_manager.ex diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index cfa4f10dfc5b..99350f00877a 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -82,7 +82,8 @@ defmodule Explorer.Application do con_cache_child_spec(MarketHistoryCache.cache_name()), con_cache_child_spec(RSK.cache_name(), ttl_check_interval: :timer.minutes(1), global_ttl: :timer.minutes(30)), {Redix, redix_opts()}, - {Explorer.Utility.MissingRangesManipulator, []} + {Explorer.Utility.MissingRangesManipulator, []}, + {Explorer.Utility.ReplicaAccessibilityManager, []} ] children = base_children ++ configurable_children() diff --git a/apps/explorer/lib/explorer/repo.ex b/apps/explorer/lib/explorer/repo.ex index 7b159e263556..436008ed8ed6 100644 --- a/apps/explorer/lib/explorer/repo.ex +++ b/apps/explorer/lib/explorer/repo.ex @@ -111,9 +111,11 @@ defmodule Explorer.Repo do if Mix.env() == :test do def replica, do: __MODULE__ else - def replica, do: Explorer.Repo.Replica1 + def replica, do: (Application.get_env(:explorer, :replica_inaccessible?) && Explorer.Repo) || replica_repo() end + def replica_repo, do: Explorer.Repo.Replica1 + def account_repo, do: Explorer.Repo.Account defmodule Replica1 do diff --git a/apps/explorer/lib/explorer/utility/replica_accessibility_manager.ex b/apps/explorer/lib/explorer/utility/replica_accessibility_manager.ex new file mode 100644 index 000000000000..a3388db20c04 --- /dev/null +++ b/apps/explorer/lib/explorer/utility/replica_accessibility_manager.ex @@ -0,0 +1,65 @@ +defmodule Explorer.Utility.ReplicaAccessibilityManager do + @moduledoc """ + Module responsible for periodically checking replica accessibility. + """ + + use GenServer + + alias Explorer.Repo + + @interval :timer.seconds(10) + + @spec start_link(term()) :: GenServer.on_start() + def start_link(_) do + GenServer.start_link(__MODULE__, :ok, name: __MODULE__) + end + + def init(_) do + if System.get_env("DATABASE_READ_ONLY_API_URL") do + schedule_next_check(0) + + {:ok, %{}} + else + :ignore + end + end + + def handle_info(:check, state) do + check() + schedule_next_check(@interval) + + {:noreply, state} + end + + defp check do + case Repo.replica_repo().query(query()) do + {:ok, %{rows: [[is_slave, lag]]}} -> + replica_inaccessible? = is_slave and :timer.seconds(lag || 0) > max_lag() + set_replica_inaccessibility(replica_inaccessible?) + + _ -> + set_replica_inaccessibility(true) + end + end + + defp query do + """ + SELECT pg_is_in_recovery(), ( + EXTRACT(EPOCH FROM now()) - + EXTRACT(EPOCH FROM pg_last_xact_replay_timestamp()) + )::int; + """ + end + + defp max_lag do + Application.get_env(:explorer, :replica_max_lag) + end + + defp set_replica_inaccessibility(inaccessible?) do + Application.put_env(:explorer, :replica_inaccessible?, inaccessible?) + end + + defp schedule_next_check(interval) do + Process.send_after(self(), :check, interval) + end +end diff --git a/config/runtime.exs b/config/runtime.exs index d15ac01c21f5..635a5ccb8060 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -247,7 +247,8 @@ config :explorer, elasticity_multiplier: ConfigHelper.parse_integer_env_var("EIP_1559_ELASTICITY_MULTIPLIER", 2), base_fee_max_change_denominator: ConfigHelper.parse_integer_env_var("EIP_1559_BASE_FEE_MAX_CHANGE_DENOMINATOR", 8), csv_export_limit: ConfigHelper.parse_integer_env_var("CSV_EXPORT_LIMIT", 10_000), - shrink_internal_transactions_enabled: ConfigHelper.parse_bool_env_var("SHRINK_INTERNAL_TRANSACTIONS_ENABLED") + shrink_internal_transactions_enabled: ConfigHelper.parse_bool_env_var("SHRINK_INTERNAL_TRANSACTIONS_ENABLED"), + replica_max_lag: ConfigHelper.parse_time_env_var("REPLICA_MAX_LAG", "5m") config :explorer, :proxy, caching_implementation_data_enabled: true, diff --git a/cspell.json b/cspell.json index 13e79262603d..cbe17cb92673 100644 --- a/cspell.json +++ b/cspell.json @@ -641,6 +641,7 @@ "whereis", "whiler", "wysdvjkizxonu", + "xact", "xakgj", "xbaddress", "xdai", From 077440d91d7c12fcd04e2837f4838d139f2bfa59 Mon Sep 17 00:00:00 2001 From: Donny <130464015+defitricks@users.noreply.github.com> Date: Sat, 30 Nov 2024 12:56:18 +0200 Subject: [PATCH 338/363] grammatical error Update PULL_REQUEST_TEMPLATE.md (#11298) --- PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 1d8394249928..442777d478c7 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -12,7 +12,7 @@ ### Bug Fixes -*Things you changed that fix bugs. If a fixes a bug, but in so doing adds a new requirement, removes code, or requires a database reset and reindex, the breaking part of the change should be added to Incompatible Changes below also.* +*Things you changed that fix bugs. If it fixes a bug, but in so doing adds a new requirement, removes code, or requires a database reset and reindex, the breaking part of the change should be added to Incompatible Changes below also.* ### Incompatible Changes From 9ff1575d3cdfe88382d1af16592d40b545e11656 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:00:07 +0400 Subject: [PATCH 339/363] chore(deps-dev): bump postcss in /apps/block_scout_web/assets (#11311) Bumps [postcss](https://github.com/postcss/postcss) from 8.4.47 to 8.4.49. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.4.47...8.4.49) --- updated-dependencies: - dependency-name: postcss dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 35 +++++++++---------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index f32b03fb552c..be757c324759 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -87,7 +87,7 @@ "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "mini-css-extract-plugin": "^2.9.2", - "postcss": "^8.4.47", + "postcss": "^8.4.49", "postcss-loader": "^8.1.1", "sass": "^1.79.4", "sass-loader": "^14.2.1", @@ -101,11 +101,10 @@ } }, "../../../deps/phoenix": { - "version": "1.5.14", - "license": "MIT" + "version": "0.0.1" }, "../../../deps/phoenix_html": { - "version": "3.0.4" + "version": "0.0.1" }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", @@ -12319,9 +12318,9 @@ } }, "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.0", @@ -12481,9 +12480,9 @@ } }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "dev": true, "funding": [ { @@ -12501,7 +12500,7 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -25481,9 +25480,9 @@ "integrity": "sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==" }, "picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "picomatch": { "version": "2.3.0", @@ -25583,13 +25582,13 @@ "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==" }, "postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "dev": true, "requires": { "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index b30e2dc2b7ac..7559566e1745 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -99,7 +99,7 @@ "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "mini-css-extract-plugin": "^2.9.2", - "postcss": "^8.4.47", + "postcss": "^8.4.49", "postcss-loader": "^8.1.1", "sass": "^1.79.4", "sass-loader": "^14.2.1", From 228eca384744b4c1887dd6cb2fb6249522a51766 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:02:13 +0400 Subject: [PATCH 340/363] chore(deps-dev): bump @babel/preset-env in /apps/block_scout_web/assets (#11313) Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.25.4 to 7.26.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.26.0/packages/babel-preset-env) --- updated-dependencies: - dependency-name: "@babel/preset-env" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 2209 ++++++++--------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 1003 insertions(+), 1208 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index be757c324759..2fac36c0b864 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -72,7 +72,7 @@ }, "devDependencies": { "@babel/core": "^7.25.2", - "@babel/preset-env": "^7.25.4", + "@babel/preset-env": "^7.26.0", "autoprefixer": "^10.4.20", "babel-loader": "^9.2.1", "copy-webpack-plugin": "^12.0.2", @@ -262,11 +262,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dependencies": { - "@babel/highlight": "^7.24.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -274,9 +275,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", - "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", "engines": { "node": ">=6.9.0" } @@ -316,51 +317,52 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/@babel/generator": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", - "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "dependencies": { - "@babel/types": "^7.25.6", + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz", - "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", + "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", "dev": true, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", - "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dependencies": { - "@babel/compat-data": "^7.25.2", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -382,17 +384,17 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz", - "integrity": "sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.8", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.25.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/traverse": "^7.25.4", + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", "semver": "^6.3.1" }, "engines": { @@ -403,13 +405,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", - "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", + "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "regexpu-core": "^5.3.1", + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.1.1", "semver": "^6.3.1" }, "engines": { @@ -436,39 +438,38 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", - "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", "dev": true, "dependencies": { - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.8" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", - "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.2" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -478,34 +479,34 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", - "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", "dev": true, "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", - "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", - "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-wrap-function": "^7.25.0", - "@babel/traverse": "^7.25.0" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -515,14 +516,14 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", - "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", "dev": true, "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.24.8", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/traverse": "^7.25.0" + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -532,63 +533,64 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", + "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", + "dev": true, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", - "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", "dev": true, "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", - "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", "dev": true, "dependencies": { - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -606,26 +608,12 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/parser": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", - "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dependencies": { - "@babel/types": "^7.25.6" + "@babel/types": "^7.26.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -635,13 +623,13 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", - "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.3" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -651,12 +639,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", - "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -666,12 +654,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz", - "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -681,14 +669,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz", - "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -698,13 +686,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", - "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.0" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -761,52 +749,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", - "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -816,12 +765,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", - "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -941,21 +890,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", @@ -1003,12 +937,12 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz", - "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1018,15 +952,14 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz", - "integrity": "sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", + "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-remap-async-to-generator": "^7.25.0", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/traverse": "^7.25.4" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1036,14 +969,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", - "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1053,12 +986,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz", - "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", + "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1068,12 +1001,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", - "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1083,13 +1016,13 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz", - "integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.4", - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1099,14 +1032,13 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz", - "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1116,16 +1048,16 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz", - "integrity": "sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-replace-supers": "^7.25.0", - "@babel/traverse": "^7.25.4", + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", "globals": "^11.1.0" }, "engines": { @@ -1136,13 +1068,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", - "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/template": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1152,12 +1084,12 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz", - "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1167,13 +1099,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz", - "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1183,12 +1115,12 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz", - "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1198,13 +1130,13 @@ } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", - "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.0", - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1214,13 +1146,12 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", - "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1230,13 +1161,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz", - "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", + "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", "dev": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1246,13 +1177,12 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz", - "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1262,13 +1192,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", - "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", + "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1278,14 +1208,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.25.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", - "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.1" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1295,13 +1225,12 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz", - "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1311,12 +1240,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", - "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1326,13 +1255,12 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz", - "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1342,12 +1270,12 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz", - "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1357,13 +1285,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz", - "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1373,14 +1301,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz", - "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", + "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.24.8", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-simple-access": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-simple-access": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1390,15 +1318,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", - "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.25.0", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.0" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1408,13 +1336,13 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz", - "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1424,13 +1352,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz", - "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1440,12 +1368,12 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz", - "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1455,13 +1383,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz", - "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", + "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1471,13 +1398,12 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz", - "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1487,15 +1413,14 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz", - "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.7" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1505,13 +1430,13 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz", - "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1521,13 +1446,12 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz", - "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1537,14 +1461,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz", - "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1554,12 +1477,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz", - "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1569,13 +1492,13 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz", - "integrity": "sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.4", - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1585,15 +1508,14 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz", - "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1603,12 +1525,12 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", - "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1618,12 +1540,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", - "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.25.9", "regenerator-transform": "^0.15.2" }, "engines": { @@ -1633,13 +1555,29 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", + "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz", - "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1680,12 +1618,12 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", - "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1695,13 +1633,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz", - "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1711,12 +1649,12 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz", - "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1726,12 +1664,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz", - "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", + "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1741,12 +1679,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz", - "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", + "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1756,12 +1694,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", - "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1771,13 +1709,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz", - "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1787,13 +1725,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz", - "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1803,13 +1741,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz", - "integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.2", - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1819,93 +1757,79 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.4.tgz", - "integrity": "sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.25.4", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-validator-option": "^7.24.8", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", + "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.7", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.4", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.25.0", - "@babel/plugin-transform-class-properties": "^7.25.4", - "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.25.4", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.8", - "@babel/plugin-transform-dotall-regex": "^7.24.7", - "@babel/plugin-transform-duplicate-keys": "^7.24.7", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", - "@babel/plugin-transform-dynamic-import": "^7.24.7", - "@babel/plugin-transform-exponentiation-operator": "^7.24.7", - "@babel/plugin-transform-export-namespace-from": "^7.24.7", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.25.1", - "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.25.2", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-member-expression-literals": "^7.24.7", - "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-modules-systemjs": "^7.25.0", - "@babel/plugin-transform-modules-umd": "^7.24.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-new-target": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-object-super": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.8", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.25.4", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-property-literals": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-reserved-words": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.8", - "@babel/plugin-transform-unicode-escapes": "^7.24.7", - "@babel/plugin-transform-unicode-property-regex": "^7.24.7", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.4", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.37.1", + "core-js-compat": "^3.38.1", "semver": "^6.3.1" }, "engines": { @@ -1971,12 +1895,6 @@ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "dev": true - }, "node_modules/@babel/runtime": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.0.tgz", @@ -1989,28 +1907,28 @@ } }, "node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", - "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.6", - "@babel/parser": "^7.25.6", - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.6", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2019,13 +1937,12 @@ } }, "node_modules/@babel/types": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", - "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -5033,19 +4950,6 @@ "node": ">=4" } }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", @@ -6553,14 +6457,6 @@ "node": ">=6" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/escodegen": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", @@ -11031,14 +10927,14 @@ } }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-parse-even-better-errors": { @@ -13500,9 +13396,9 @@ "dev": true }, "node_modules/regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "dev": true, "dependencies": { "regenerate": "^1.4.2" @@ -13556,15 +13452,15 @@ } }, "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", "dev": true, "dependencies": { - "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" }, @@ -13572,27 +13468,24 @@ "node": ">=4" } }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true + }, "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", "dev": true, "dependencies": { - "jsesc": "~0.5.0" + "jsesc": "~3.0.2" }, "bin": { "regjsparser": "bin/parser" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, "node_modules/request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -14723,14 +14616,6 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -14980,9 +14865,9 @@ } }, "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "dev": true, "engines": { "node": ">=4" @@ -15002,9 +14887,9 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", "dev": true, "engines": { "node": ">=4" @@ -16345,18 +16230,19 @@ } }, "@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "requires": { - "@babel/highlight": "^7.24.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "@babel/compat-data": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", - "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==" + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==" }, "@babel/core": { "version": "7.25.2", @@ -16388,42 +16274,43 @@ } }, "@babel/generator": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", - "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "requires": { - "@babel/types": "^7.25.6", + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" } }, "@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "requires": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.9" } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz", - "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", + "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", "dev": true, "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" } }, "@babel/helper-compilation-targets": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", - "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "requires": { - "@babel/compat-data": "^7.25.2", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -16444,28 +16331,28 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz", - "integrity": "sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.8", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.25.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/traverse": "^7.25.4", + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", "semver": "^6.3.1" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", - "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", + "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "regexpu-core": "^5.3.1", + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.1.1", "semver": "^6.3.1" } }, @@ -16483,114 +16370,114 @@ } }, "@babel/helper-member-expression-to-functions": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", - "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", "dev": true, "requires": { - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.8" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" } }, "@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" } }, "@babel/helper-module-transforms": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", - "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "requires": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.2" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" } }, "@babel/helper-optimise-call-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", - "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", "dev": true, "requires": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.9" } }, "@babel/helper-plugin-utils": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", - "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==" + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==" }, "@babel/helper-remap-async-to-generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", - "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-wrap-function": "^7.25.0", - "@babel/traverse": "^7.25.0" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" } }, "@babel/helper-replace-supers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", - "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.24.8", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/traverse": "^7.25.0" + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" } }, "@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", + "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", + "dev": true, "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", - "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", "dev": true, "requires": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" } }, "@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==" + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==" }, "@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==" + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==" }, "@babel/helper-validator-option": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==" + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==" }, "@babel/helper-wrap-function": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", - "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", "dev": true, "requires": { - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" } }, "@babel/helpers": { @@ -16602,72 +16489,61 @@ "@babel/types": "^7.25.6" } }, - "@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "requires": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - } - }, "@babel/parser": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", - "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "requires": { - "@babel/types": "^7.25.6" + "@babel/types": "^7.26.0" } }, "@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", - "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.3" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" } }, "@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", - "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz", - "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz", - "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" } }, "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", - "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.0" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" } }, "@babel/plugin-proposal-private-property-in-object": { @@ -16704,49 +16580,22 @@ "@babel/helper-plugin-utils": "^7.12.13" } }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, "@babel/plugin-syntax-import-assertions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", - "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-syntax-import-attributes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", - "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-syntax-import-meta": { @@ -16830,15 +16679,6 @@ "@babel/helper-plugin-utils": "^7.8.0" } }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, "@babel/plugin-syntax-top-level-await": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", @@ -16868,409 +16708,407 @@ } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz", - "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-async-generator-functions": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz", - "integrity": "sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", + "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-remap-async-to-generator": "^7.25.0", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/traverse": "^7.25.4" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.25.9" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", - "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz", - "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", + "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", - "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-class-properties": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz", - "integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.25.4", - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-class-static-block": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz", - "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-classes": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz", - "integrity": "sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-replace-supers": "^7.25.0", - "@babel/traverse": "^7.25.4", + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", "globals": "^11.1.0" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", - "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/template": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" } }, "@babel/plugin-transform-destructuring": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz", - "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz", - "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz", - "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", - "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.25.0", - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-dynamic-import": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", - "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz", - "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", + "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", "dev": true, "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-export-namespace-from": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz", - "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-for-of": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", - "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", + "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" } }, "@babel/plugin-transform-function-name": { - "version": "7.25.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", - "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", "dev": true, "requires": { - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.1" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" } }, "@babel/plugin-transform-json-strings": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz", - "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-literals": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", - "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz", - "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz", - "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz", - "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz", - "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", + "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.24.8", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-simple-access": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-simple-access": "^7.25.9" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", - "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.25.0", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.0" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" } }, "@babel/plugin-transform-modules-umd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz", - "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz", - "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-new-target": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz", - "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz", - "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", + "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-numeric-separator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz", - "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-object-rest-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz", - "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", "dev": true, "requires": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.7" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" } }, "@babel/plugin-transform-object-super": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz", - "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" } }, "@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz", - "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-optional-chaining": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz", - "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" } }, "@babel/plugin-transform-parameters": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz", - "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-private-methods": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz", - "integrity": "sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.25.4", - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-private-property-in-object": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz", - "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-property-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", - "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-regenerator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", - "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.25.9", "regenerator-transform": "^0.15.2" } }, + "@babel/plugin-transform-regexp-modifiers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", + "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + } + }, "@babel/plugin-transform-reserved-words": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz", - "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-runtime": { @@ -17298,178 +17136,164 @@ } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", - "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz", - "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz", - "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-template-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz", - "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", + "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz", - "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", + "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", - "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz", - "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz", - "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/plugin-transform-unicode-sets-regex": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz", - "integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.25.2", - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" } }, "@babel/preset-env": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.4.tgz", - "integrity": "sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.25.4", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-validator-option": "^7.24.8", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", + "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.7", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.4", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.25.0", - "@babel/plugin-transform-class-properties": "^7.25.4", - "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.25.4", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.8", - "@babel/plugin-transform-dotall-regex": "^7.24.7", - "@babel/plugin-transform-duplicate-keys": "^7.24.7", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", - "@babel/plugin-transform-dynamic-import": "^7.24.7", - "@babel/plugin-transform-exponentiation-operator": "^7.24.7", - "@babel/plugin-transform-export-namespace-from": "^7.24.7", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.25.1", - "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.25.2", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-member-expression-literals": "^7.24.7", - "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-modules-systemjs": "^7.25.0", - "@babel/plugin-transform-modules-umd": "^7.24.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-new-target": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-object-super": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.8", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.25.4", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-property-literals": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-reserved-words": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.8", - "@babel/plugin-transform-unicode-escapes": "^7.24.7", - "@babel/plugin-transform-unicode-property-regex": "^7.24.7", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.4", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.37.1", + "core-js-compat": "^3.38.1", "semver": "^6.3.1" }, "dependencies": { @@ -17519,12 +17343,6 @@ "esutils": "^2.0.2" } }, - "@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "dev": true - }, "@babel/runtime": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.0.tgz", @@ -17534,37 +17352,36 @@ } }, "@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "requires": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" } }, "@babel/traverse": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", - "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", - "requires": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.6", - "@babel/parser": "^7.25.6", - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.6", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "requires": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", "debug": "^4.3.1", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", - "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "requires": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" } }, "@bcoe/v8-coverage": { @@ -19933,16 +19750,6 @@ "type-detect": "^4.0.5" } }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, "char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", @@ -21074,11 +20881,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" - }, "escodegen": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", @@ -24427,9 +24229,9 @@ } }, "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==" }, "json-parse-even-better-errors": { "version": "2.3.1", @@ -26271,9 +26073,9 @@ "dev": true }, "regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "dev": true, "requires": { "regenerate": "^1.4.2" @@ -26312,34 +26114,32 @@ "dev": true }, "regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", "dev": true, "requires": { - "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" } }, + "regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true + }, "regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", "dev": true, "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "dev": true - } + "jsesc": "~3.0.2" } }, "request": { @@ -27160,11 +26960,6 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -27354,9 +27149,9 @@ } }, "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "dev": true }, "unicode-match-property-ecmascript": { @@ -27370,9 +27165,9 @@ } }, "unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", "dev": true }, "unicode-property-aliases-ecmascript": { diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 7559566e1745..f8cd5fb1e30d 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -84,7 +84,7 @@ }, "devDependencies": { "@babel/core": "^7.25.2", - "@babel/preset-env": "^7.25.4", + "@babel/preset-env": "^7.26.0", "autoprefixer": "^10.4.20", "babel-loader": "^9.2.1", "copy-webpack-plugin": "^12.0.2", From 69e74c58ac56ee6de534fba7b8b6f1f10351b75c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:18:09 +0400 Subject: [PATCH 341/363] chore(deps): bump crypto-browserify in /apps/block_scout_web/assets (#11312) Bumps [crypto-browserify](https://github.com/browserify/crypto-browserify) from 3.12.0 to 3.12.1. - [Commits](https://github.com/browserify/crypto-browserify/compare/v3.12.0...v3.12.1) --- updated-dependencies: - dependency-name: crypto-browserify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 347 ++++++++++++++---- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 271 insertions(+), 78 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 2fac36c0b864..ce796e0d3bc0 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -18,7 +18,7 @@ "chartjs-adapter-luxon": "^1.3.1", "clipboard": "^2.0.11", "core-js": "^3.39.0", - "crypto-browserify": "^3.12.0", + "crypto-browserify": "^3.12.1", "dropzone": "^5.9.3", "eth-net-props": "^1.0.41", "highlight.js": "^11.10.0", @@ -4060,20 +4060,19 @@ } }, "node_modules/asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" + "minimalistic-assert": "^1.0.0" } }, "node_modules/asn1.js/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==" }, "node_modules/assert": { "version": "2.1.0", @@ -4655,24 +4654,56 @@ } }, "node_modules/browserify-sign": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", - "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", "dependencies": { "bn.js": "^5.2.1", "browserify-rsa": "^4.1.0", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.4", + "elliptic": "^6.5.5", + "hash-base": "~3.0", "inherits": "^2.0.4", - "parse-asn1": "^5.1.6", - "readable-stream": "^3.6.2", + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", "safe-buffer": "^5.2.1" }, "engines": { - "node": ">= 4" + "node": ">= 0.12" + } + }, + "node_modules/browserify-sign/node_modules/hash-base": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz", + "integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, + "node_modules/browserify-sign/node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/browserify-sign/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -4692,6 +4723,19 @@ } ] }, + "node_modules/browserify-sign/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/browserify-sign/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/browserslist": { "version": "4.24.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", @@ -5441,26 +5485,61 @@ } }, "node_modules/crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz", + "integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==", "dependencies": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" + "browserify-cipher": "^1.0.1", + "browserify-sign": "^4.2.3", + "create-ecdh": "^4.0.4", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "diffie-hellman": "^5.0.3", + "hash-base": "~3.0.4", + "inherits": "^2.0.4", + "pbkdf2": "^3.1.2", + "public-encrypt": "^4.0.3", + "randombytes": "^2.1.0", + "randomfill": "^1.0.4" }, "engines": { - "node": "*" + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/crypto-browserify/node_modules/hash-base": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz", + "integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/crypto-browserify/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", @@ -12083,17 +12162,52 @@ } }, "node_modules/parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", - "dependencies": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "dependencies": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-asn1/node_modules/hash-base": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz", + "integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" } }, + "node_modules/parse-asn1/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/parse-headers": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.4.tgz", @@ -19083,20 +19197,19 @@ } }, "asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "requires": { "bn.js": "^4.0.0", "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" + "minimalistic-assert": "^1.0.0" }, "dependencies": { "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==" } } }, @@ -19547,25 +19660,71 @@ } }, "browserify-sign": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", - "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", "requires": { "bn.js": "^5.2.1", "browserify-rsa": "^4.1.0", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.4", + "elliptic": "^6.5.5", + "hash-base": "~3.0", "inherits": "^2.0.4", - "parse-asn1": "^5.1.6", - "readable-stream": "^3.6.2", + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", "safe-buffer": "^5.2.1" }, "dependencies": { + "hash-base": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz", + "integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==", + "requires": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + } + }, + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } } } }, @@ -20135,21 +20294,38 @@ } }, "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz", + "integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==", "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" + "browserify-cipher": "^1.0.1", + "browserify-sign": "^4.2.3", + "create-ecdh": "^4.0.4", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "diffie-hellman": "^5.0.3", + "hash-base": "~3.0.4", + "inherits": "^2.0.4", + "pbkdf2": "^3.1.2", + "public-encrypt": "^4.0.3", + "randombytes": "^2.1.0", + "randomfill": "^1.0.4" + }, + "dependencies": { + "hash-base": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz", + "integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==", + "requires": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "css-color-keywords": { @@ -25180,15 +25356,32 @@ } }, "parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", - "requires": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "requires": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, + "dependencies": { + "hash-base": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz", + "integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==", + "requires": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "parse-headers": { diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index f8cd5fb1e30d..eb00c3ca0a13 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -30,7 +30,7 @@ "chartjs-adapter-luxon": "^1.3.1", "clipboard": "^2.0.11", "core-js": "^3.39.0", - "crypto-browserify": "^3.12.0", + "crypto-browserify": "^3.12.1", "dropzone": "^5.9.3", "eth-net-props": "^1.0.41", "highlight.js": "^11.10.0", From 071af703f5debcfc5348036b49d1420e459935ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:19:36 +0400 Subject: [PATCH 342/363] chore(deps): bump chart.js in /apps/block_scout_web/assets (#11309) Bumps [chart.js](https://github.com/chartjs/Chart.js) from 4.4.6 to 4.4.7. - [Release notes](https://github.com/chartjs/Chart.js/releases) - [Commits](https://github.com/chartjs/Chart.js/compare/v4.4.6...v4.4.7) --- updated-dependencies: - dependency-name: chart.js dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 ce796e0d3bc0..53eab0eeea38 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -14,7 +14,7 @@ "assert": "^2.1.0", "bignumber.js": "^9.1.2", "bootstrap": "^4.6.0", - "chart.js": "^4.4.6", + "chart.js": "^4.4.7", "chartjs-adapter-luxon": "^1.3.1", "clipboard": "^2.0.11", "core-js": "^3.39.0", @@ -5004,9 +5004,9 @@ } }, "node_modules/chart.js": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.6.tgz", - "integrity": "sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==", + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.7.tgz", + "integrity": "sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==", "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -19916,9 +19916,9 @@ "dev": true }, "chart.js": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.6.tgz", - "integrity": "sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==", + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.7.tgz", + "integrity": "sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==", "requires": { "@kurkle/color": "^0.3.0" } diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index eb00c3ca0a13..b2709c5d34ac 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -26,7 +26,7 @@ "assert": "^2.1.0", "bignumber.js": "^9.1.2", "bootstrap": "^4.6.0", - "chart.js": "^4.4.6", + "chart.js": "^4.4.7", "chartjs-adapter-luxon": "^1.3.1", "clipboard": "^2.0.11", "core-js": "^3.39.0", From a81008821908ae7dbe14a7bdc0ce922844c38dd7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:21:13 +0400 Subject: [PATCH 343/363] chore(deps): bump mixpanel-browser in /apps/block_scout_web/assets (#11306) Bumps [mixpanel-browser](https://github.com/mixpanel/mixpanel-js) from 2.55.1 to 2.56.0. - [Release notes](https://github.com/mixpanel/mixpanel-js/releases) - [Changelog](https://github.com/mixpanel/mixpanel-js/blob/master/CHANGELOG.md) - [Commits](https://github.com/mixpanel/mixpanel-js/compare/v2.55.1...v2.56.0) --- updated-dependencies: - dependency-name: mixpanel-browser dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 53eab0eeea38..51bf4d834d46 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -46,7 +46,7 @@ "lodash.reduce": "^4.6.0", "luxon": "^3.5.0", "malihu-custom-scrollbar-plugin": "3.1.5", - "mixpanel-browser": "^2.55.1", + "mixpanel-browser": "^2.56.0", "moment": "^2.30.1", "nanomorph": "^5.4.0", "numeral": "^2.0.6", @@ -11757,9 +11757,9 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" }, "node_modules/mixpanel-browser": { - "version": "2.55.1", - "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.55.1.tgz", - "integrity": "sha512-NSEPdFSJxoR1OCKWKHbtqd3BeH1c9NjXbEt0tN5TgBEO1nSDji6niU9n4MopAXOP0POET9spjpQKxZtLZKTJwA==", + "version": "2.56.0", + "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.56.0.tgz", + "integrity": "sha512-GYeEz58pV2M9MZtK8vSPL4oJmCwGS08FDDRZvZwr5VJpWdT4Lgyg6zXhmNfCmSTEIw2coaarm7HZ4FL9dAVvnA==", "dependencies": { "rrweb": "2.0.0-alpha.13" } @@ -25062,9 +25062,9 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" }, "mixpanel-browser": { - "version": "2.55.1", - "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.55.1.tgz", - "integrity": "sha512-NSEPdFSJxoR1OCKWKHbtqd3BeH1c9NjXbEt0tN5TgBEO1nSDji6niU9n4MopAXOP0POET9spjpQKxZtLZKTJwA==", + "version": "2.56.0", + "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.56.0.tgz", + "integrity": "sha512-GYeEz58pV2M9MZtK8vSPL4oJmCwGS08FDDRZvZwr5VJpWdT4Lgyg6zXhmNfCmSTEIw2coaarm7HZ4FL9dAVvnA==", "requires": { "rrweb": "2.0.0-alpha.13" } diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index b2709c5d34ac..63e7c4745fea 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -58,7 +58,7 @@ "lodash.reduce": "^4.6.0", "luxon": "^3.5.0", "malihu-custom-scrollbar-plugin": "3.1.5", - "mixpanel-browser": "^2.55.1", + "mixpanel-browser": "^2.56.0", "moment": "^2.30.1", "nanomorph": "^5.4.0", "numeral": "^2.0.6", From 617506a1e854caa539b0870b6b67a666c08958ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:35:46 +0400 Subject: [PATCH 344/363] chore(deps-dev): bump @babel/core in /apps/block_scout_web/assets (#11310) Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.25.2 to 7.26.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.26.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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 70 +++++++++---------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 51bf4d834d46..9bd96d3d26b2 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.15" }, "devDependencies": { - "@babel/core": "^7.25.2", + "@babel/core": "^7.26.0", "@babel/preset-env": "^7.26.0", "autoprefixer": "^10.4.20", "babel-loader": "^9.2.1", @@ -283,20 +283,20 @@ } }, "node_modules/@babel/core": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", - "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-module-transforms": "^7.25.2", - "@babel/helpers": "^7.25.0", - "@babel/parser": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.2", - "@babel/types": "^7.25.2", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -597,12 +597,12 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", - "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dependencies": { - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.6" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" }, "engines": { "node": ">=6.9.0" @@ -16359,20 +16359,20 @@ "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==" }, "@babel/core": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", - "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "requires": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-module-transforms": "^7.25.2", - "@babel/helpers": "^7.25.0", - "@babel/parser": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.2", - "@babel/types": "^7.25.2", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -16595,12 +16595,12 @@ } }, "@babel/helpers": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", - "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "requires": { - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.6" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" } }, "@babel/parser": { diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 63e7c4745fea..6fb82dda27cb 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -83,7 +83,7 @@ "xss": "^1.0.15" }, "devDependencies": { - "@babel/core": "^7.25.2", + "@babel/core": "^7.26.0", "@babel/preset-env": "^7.26.0", "autoprefixer": "^10.4.20", "babel-loader": "^9.2.1", From 3e353571c637b1f4903058475b2b27b40b39a91c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:37:31 +0400 Subject: [PATCH 345/363] chore(deps): bump @amplitude/analytics-browser (#11305) Bumps [@amplitude/analytics-browser](https://github.com/amplitude/Amplitude-TypeScript) from 2.11.8 to 2.11.9. - [Release notes](https://github.com/amplitude/Amplitude-TypeScript/releases) - [Commits](https://github.com/amplitude/Amplitude-TypeScript/compare/@amplitude/analytics-browser@2.11.8...@amplitude/analytics-browser@2.11.9) --- updated-dependencies: - dependency-name: "@amplitude/analytics-browser" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 98 +++++++++---------- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 9bd96d3d26b2..564825bac30e 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.11.8", + "@amplitude/analytics-browser": "^2.11.9", "@fortawesome/fontawesome-free": "^6.6.0", "@tarekraafat/autocomplete.js": "^10.2.9", "@walletconnect/web3-provider": "^1.8.0", @@ -121,16 +121,16 @@ "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==" }, "node_modules/@amplitude/analytics-browser": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.11.8.tgz", - "integrity": "sha512-lFv8deROLwBfSlg92+r1NitWJ6BN45IKwpPLoixA0fZytScXEJqc0Gl5O+BY4qScbFECYt9PFKblhB+jC+IvPg==", + "version": "2.11.9", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.11.9.tgz", + "integrity": "sha512-FHejpsW3OypNKaIBvMwLm74UUSBcR+VwrBsj7V2VlPDNRdeaFi21kJgVYUW5AcjxTsadMzBQGBb4BarZ4k2+9Q==", "dependencies": { - "@amplitude/analytics-client-common": "^2.3.4", - "@amplitude/analytics-core": "^2.5.3", + "@amplitude/analytics-client-common": "^2.3.5", + "@amplitude/analytics-core": "^2.5.4", "@amplitude/analytics-remote-config": "^0.4.0", - "@amplitude/analytics-types": "^2.8.3", + "@amplitude/analytics-types": "^2.8.4", "@amplitude/plugin-autocapture-browser": "^1.0.2", - "@amplitude/plugin-page-view-tracking-browser": "^2.3.4", + "@amplitude/plugin-page-view-tracking-browser": "^2.3.5", "tslib": "^2.4.1" } }, @@ -140,13 +140,13 @@ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" }, "node_modules/@amplitude/analytics-client-common": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.3.4.tgz", - "integrity": "sha512-3oqdvca5W4BPblTaxf60YRtlh2uC+N3rA99wowDAhTBJoMJJaauOBoXu5BbiQO1u8Zw/c8ymyr8E20+glyptUg==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.3.5.tgz", + "integrity": "sha512-BCP+jorfLMAKK/g87fAk4IPP/NzQLMCep+Qe23tqOCWguwTEINYnyzD/GmhaIKXSM2o9pmMLlHbhkA1vXUtF8g==", "dependencies": { "@amplitude/analytics-connector": "^1.4.8", - "@amplitude/analytics-core": "^2.5.3", - "@amplitude/analytics-types": "^2.8.3", + "@amplitude/analytics-core": "^2.5.4", + "@amplitude/analytics-types": "^2.8.4", "tslib": "^2.4.1" } }, @@ -164,11 +164,11 @@ } }, "node_modules/@amplitude/analytics-core": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.5.3.tgz", - "integrity": "sha512-dvx3PS0adnHRS22VbuP9YtWg//bQGF2c61Pj5IYXVsemtRRHqiS7XJ860brk3WeQgOkqf3Gyc023DoYcsWGoNQ==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.5.4.tgz", + "integrity": "sha512-J5ZF8hQmxmxM+7bu25a2TfTnk/LQ/oH5FYdg79f1lJ85Aa6oUlCDxgvXwy1RVpwaFjWlZQgV4XVaAUrxtSPRFw==", "dependencies": { - "@amplitude/analytics-types": "^2.8.3", + "@amplitude/analytics-types": "^2.8.4", "tslib": "^2.4.1" } }, @@ -194,9 +194,9 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@amplitude/analytics-types": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.8.3.tgz", - "integrity": "sha512-HNmKVd0ACoi3xTi86xi+is7WgqKT78JA4fYLcM25/ckFkZ1zVCqD1AubaADEh26m34nJ3qDLK5Pob4QptQNPAg==" + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.8.4.tgz", + "integrity": "sha512-jQ8WY1aPbpBshl0L/0YEeQn/wZlBr8Jlqc20qf8nbuDuimFy8RqAkE+BVaMI86FCkr3AJ7PjMXkGwCSbUx88CA==" }, "node_modules/@amplitude/experiment-core": { "version": "0.10.0", @@ -223,12 +223,12 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/@amplitude/plugin-page-view-tracking-browser": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.3.4.tgz", - "integrity": "sha512-l7RS5gssG0BPYlgirV0NQ94EPzTOdDkp0z2jqU45D3DQAJXkoloUyw5lw/cbUXYwNulHZTG/BExcERfdvVWkLA==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.3.5.tgz", + "integrity": "sha512-qcV4DLxRAZRriYBNvjc2PGW1EDad6PSsIXmxVs6j8i9fxY2SfdvsFd/Qd23CHj1e6Dt5QpAVJZpUMCEdqqDZbA==", "dependencies": { - "@amplitude/analytics-client-common": "^2.3.4", - "@amplitude/analytics-types": "^2.8.3", + "@amplitude/analytics-client-common": "^2.3.5", + "@amplitude/analytics-types": "^2.8.4", "tslib": "^2.4.1" } }, @@ -16195,16 +16195,16 @@ "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==" }, "@amplitude/analytics-browser": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.11.8.tgz", - "integrity": "sha512-lFv8deROLwBfSlg92+r1NitWJ6BN45IKwpPLoixA0fZytScXEJqc0Gl5O+BY4qScbFECYt9PFKblhB+jC+IvPg==", + "version": "2.11.9", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.11.9.tgz", + "integrity": "sha512-FHejpsW3OypNKaIBvMwLm74UUSBcR+VwrBsj7V2VlPDNRdeaFi21kJgVYUW5AcjxTsadMzBQGBb4BarZ4k2+9Q==", "requires": { - "@amplitude/analytics-client-common": "^2.3.4", - "@amplitude/analytics-core": "^2.5.3", + "@amplitude/analytics-client-common": "^2.3.5", + "@amplitude/analytics-core": "^2.5.4", "@amplitude/analytics-remote-config": "^0.4.0", - "@amplitude/analytics-types": "^2.8.3", + "@amplitude/analytics-types": "^2.8.4", "@amplitude/plugin-autocapture-browser": "^1.0.2", - "@amplitude/plugin-page-view-tracking-browser": "^2.3.4", + "@amplitude/plugin-page-view-tracking-browser": "^2.3.5", "tslib": "^2.4.1" }, "dependencies": { @@ -16216,13 +16216,13 @@ } }, "@amplitude/analytics-client-common": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.3.4.tgz", - "integrity": "sha512-3oqdvca5W4BPblTaxf60YRtlh2uC+N3rA99wowDAhTBJoMJJaauOBoXu5BbiQO1u8Zw/c8ymyr8E20+glyptUg==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-2.3.5.tgz", + "integrity": "sha512-BCP+jorfLMAKK/g87fAk4IPP/NzQLMCep+Qe23tqOCWguwTEINYnyzD/GmhaIKXSM2o9pmMLlHbhkA1vXUtF8g==", "requires": { "@amplitude/analytics-connector": "^1.4.8", - "@amplitude/analytics-core": "^2.5.3", - "@amplitude/analytics-types": "^2.8.3", + "@amplitude/analytics-core": "^2.5.4", + "@amplitude/analytics-types": "^2.8.4", "tslib": "^2.4.1" }, "dependencies": { @@ -16242,11 +16242,11 @@ } }, "@amplitude/analytics-core": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.5.3.tgz", - "integrity": "sha512-dvx3PS0adnHRS22VbuP9YtWg//bQGF2c61Pj5IYXVsemtRRHqiS7XJ860brk3WeQgOkqf3Gyc023DoYcsWGoNQ==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-2.5.4.tgz", + "integrity": "sha512-J5ZF8hQmxmxM+7bu25a2TfTnk/LQ/oH5FYdg79f1lJ85Aa6oUlCDxgvXwy1RVpwaFjWlZQgV4XVaAUrxtSPRFw==", "requires": { - "@amplitude/analytics-types": "^2.8.3", + "@amplitude/analytics-types": "^2.8.4", "tslib": "^2.4.1" }, "dependencies": { @@ -16276,9 +16276,9 @@ } }, "@amplitude/analytics-types": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.8.3.tgz", - "integrity": "sha512-HNmKVd0ACoi3xTi86xi+is7WgqKT78JA4fYLcM25/ckFkZ1zVCqD1AubaADEh26m34nJ3qDLK5Pob4QptQNPAg==" + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-2.8.4.tgz", + "integrity": "sha512-jQ8WY1aPbpBshl0L/0YEeQn/wZlBr8Jlqc20qf8nbuDuimFy8RqAkE+BVaMI86FCkr3AJ7PjMXkGwCSbUx88CA==" }, "@amplitude/experiment-core": { "version": "0.10.0", @@ -16307,12 +16307,12 @@ } }, "@amplitude/plugin-page-view-tracking-browser": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.3.4.tgz", - "integrity": "sha512-l7RS5gssG0BPYlgirV0NQ94EPzTOdDkp0z2jqU45D3DQAJXkoloUyw5lw/cbUXYwNulHZTG/BExcERfdvVWkLA==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-2.3.5.tgz", + "integrity": "sha512-qcV4DLxRAZRriYBNvjc2PGW1EDad6PSsIXmxVs6j8i9fxY2SfdvsFd/Qd23CHj1e6Dt5QpAVJZpUMCEdqqDZbA==", "requires": { - "@amplitude/analytics-client-common": "^2.3.4", - "@amplitude/analytics-types": "^2.8.3", + "@amplitude/analytics-client-common": "^2.3.5", + "@amplitude/analytics-types": "^2.8.4", "tslib": "^2.4.1" }, "dependencies": { diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 6fb82dda27cb..0a102e58b0d0 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.6.0", - "@amplitude/analytics-browser": "^2.11.8", + "@amplitude/analytics-browser": "^2.11.9", "@tarekraafat/autocomplete.js": "^10.2.9", "@walletconnect/web3-provider": "^1.8.0", "assert": "^2.1.0", From b2c75917079d33232f3c0531af951f2317380dd1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:59:06 +0400 Subject: [PATCH 346/363] chore(deps-dev): bump sass in /apps/block_scout_web/assets (#11308) Bumps [sass](https://github.com/sass/dart-sass) from 1.79.4 to 1.81.0. - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.79.4...1.81.0) --- updated-dependencies: - dependency-name: sass dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apps/block_scout_web/assets/package-lock.json | 515 ++++++++++++++++-- apps/block_scout_web/assets/package.json | 2 +- 2 files changed, 485 insertions(+), 32 deletions(-) diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index 564825bac30e..c83ba164f7fe 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -89,7 +89,7 @@ "mini-css-extract-plugin": "^2.9.2", "postcss": "^8.4.49", "postcss-loader": "^8.1.1", - "sass": "^1.79.4", + "sass": "^1.81.0", "sass-loader": "^14.2.1", "style-loader": "^4.0.0", "webpack": "^5.96.1", @@ -2959,6 +2959,309 @@ "node": ">= 8" } }, + "node_modules/@parcel/watcher": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", + "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.0", + "@parcel/watcher-darwin-arm64": "2.5.0", + "@parcel/watcher-darwin-x64": "2.5.0", + "@parcel/watcher-freebsd-x64": "2.5.0", + "@parcel/watcher-linux-arm-glibc": "2.5.0", + "@parcel/watcher-linux-arm-musl": "2.5.0", + "@parcel/watcher-linux-arm64-glibc": "2.5.0", + "@parcel/watcher-linux-arm64-musl": "2.5.0", + "@parcel/watcher-linux-x64-glibc": "2.5.0", + "@parcel/watcher-linux-x64-musl": "2.5.0", + "@parcel/watcher-win32-arm64": "2.5.0", + "@parcel/watcher-win32-ia32": "2.5.0", + "@parcel/watcher-win32-x64": "2.5.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", + "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", + "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", + "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", + "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", + "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", + "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", + "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", + "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", + "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", + "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "optional": true + }, "node_modules/@rrweb/types": { "version": "2.0.0-alpha.14", "resolved": "https://registry.npmjs.org/@rrweb/types/-/types-2.0.0-alpha.14.tgz", @@ -6115,6 +6418,19 @@ "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.2.1.tgz", "integrity": "sha512-eAcRiEPTs7utXWPaAgu/OX1HRJpxW7xSHpw4LTDrGFaeWnJ37HRlqpUkKsDm0AoTbtrvHQhH+5U2Cd87EGhJTg==" }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -8590,9 +8906,9 @@ "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" }, "node_modules/immutable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", - "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz", + "integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==", "dev": true }, "node_modules/import-fresh": { @@ -11638,13 +11954,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" @@ -12333,9 +12649,9 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -13901,13 +14217,13 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sass": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.4.tgz", - "integrity": "sha512-K0QDSNPXgyqO4GZq2HO5Q70TLxTH6cIT59RdoCHMivrC8rqzaTw5ab9prjz9KUN1El4FLXrBXJhik61JR4HcGg==", + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.81.0.tgz", + "integrity": "sha512-Q4fOxRfhmv3sqCLoGfvrC9pRV8btc0UtqL9mN6Yrv6Qi9ScL55CVH1vlPP863ISLEEMNLLuu9P+enCeGHlnzhA==", "dev": true, "dependencies": { "chokidar": "^4.0.0", - "immutable": "^4.0.0", + "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -13915,6 +14231,9 @@ }, "engines": { "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" } }, "node_modules/sass-loader": { @@ -18260,6 +18579,132 @@ "fastq": "^1.6.0" } }, + "@parcel/watcher": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", + "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", + "dev": true, + "optional": true, + "requires": { + "@parcel/watcher-android-arm64": "2.5.0", + "@parcel/watcher-darwin-arm64": "2.5.0", + "@parcel/watcher-darwin-x64": "2.5.0", + "@parcel/watcher-freebsd-x64": "2.5.0", + "@parcel/watcher-linux-arm-glibc": "2.5.0", + "@parcel/watcher-linux-arm-musl": "2.5.0", + "@parcel/watcher-linux-arm64-glibc": "2.5.0", + "@parcel/watcher-linux-arm64-musl": "2.5.0", + "@parcel/watcher-linux-x64-glibc": "2.5.0", + "@parcel/watcher-linux-x64-musl": "2.5.0", + "@parcel/watcher-win32-arm64": "2.5.0", + "@parcel/watcher-win32-ia32": "2.5.0", + "@parcel/watcher-win32-x64": "2.5.0", + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "dependencies": { + "node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "optional": true + } + } + }, + "@parcel/watcher-android-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", + "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", + "dev": true, + "optional": true + }, + "@parcel/watcher-darwin-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", + "dev": true, + "optional": true + }, + "@parcel/watcher-darwin-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", + "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", + "dev": true, + "optional": true + }, + "@parcel/watcher-freebsd-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", + "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", + "dev": true, + "optional": true + }, + "@parcel/watcher-linux-arm-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", + "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", + "dev": true, + "optional": true + }, + "@parcel/watcher-linux-arm-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", + "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", + "dev": true, + "optional": true + }, + "@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", + "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", + "dev": true, + "optional": true + }, + "@parcel/watcher-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", + "dev": true, + "optional": true + }, + "@parcel/watcher-linux-x64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", + "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", + "dev": true, + "optional": true + }, + "@parcel/watcher-linux-x64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", + "dev": true, + "optional": true + }, + "@parcel/watcher-win32-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", + "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", + "dev": true, + "optional": true + }, + "@parcel/watcher-win32-ia32": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", + "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", + "dev": true, + "optional": true + }, + "@parcel/watcher-win32-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", + "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", + "dev": true, + "optional": true + }, "@rrweb/types": { "version": "2.0.0-alpha.14", "resolved": "https://registry.npmjs.org/@rrweb/types/-/types-2.0.0-alpha.14.tgz", @@ -20720,6 +21165,13 @@ "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.2.1.tgz", "integrity": "sha512-eAcRiEPTs7utXWPaAgu/OX1HRJpxW7xSHpw4LTDrGFaeWnJ37HRlqpUkKsDm0AoTbtrvHQhH+5U2Cd87EGhJTg==" }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "optional": true + }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -22636,9 +23088,9 @@ "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" }, "immutable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", - "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz", + "integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==", "dev": true }, "import-fresh": { @@ -24969,13 +25421,13 @@ } }, "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.3", + "picomatch": "^2.3.1" } }, "miller-rabin": { @@ -25480,9 +25932,9 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pify": { @@ -26573,13 +27025,14 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.4.tgz", - "integrity": "sha512-K0QDSNPXgyqO4GZq2HO5Q70TLxTH6cIT59RdoCHMivrC8rqzaTw5ab9prjz9KUN1El4FLXrBXJhik61JR4HcGg==", + "version": "1.81.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.81.0.tgz", + "integrity": "sha512-Q4fOxRfhmv3sqCLoGfvrC9pRV8btc0UtqL9mN6Yrv6Qi9ScL55CVH1vlPP863ISLEEMNLLuu9P+enCeGHlnzhA==", "dev": true, "requires": { + "@parcel/watcher": "^2.4.1", "chokidar": "^4.0.0", - "immutable": "^4.0.0", + "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" } }, diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 0a102e58b0d0..38615bb0b3f9 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -101,7 +101,7 @@ "mini-css-extract-plugin": "^2.9.2", "postcss": "^8.4.49", "postcss-loader": "^8.1.1", - "sass": "^1.79.4", + "sass": "^1.81.0", "sass-loader": "^14.2.1", "style-loader": "^4.0.0", "webpack": "^5.96.1", From 050e13f4288a7be566e5507a244adbd3cad04ad2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:59:21 +0400 Subject: [PATCH 347/363] chore(deps): bump @fortawesome/fontawesome-free (#11307) Bumps [@fortawesome/fontawesome-free](https://github.com/FortAwesome/Font-Awesome) from 6.6.0 to 6.7.1. - [Release notes](https://github.com/FortAwesome/Font-Awesome/releases) - [Changelog](https://github.com/FortAwesome/Font-Awesome/blob/6.x/CHANGELOG.md) - [Commits](https://github.com/FortAwesome/Font-Awesome/compare/6.6.0...6.7.1) --- updated-dependencies: - dependency-name: "@fortawesome/fontawesome-free" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 c83ba164f7fe..8ebf3c7dd3ed 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -8,7 +8,7 @@ "license": "GPL-3.0", "dependencies": { "@amplitude/analytics-browser": "^2.11.9", - "@fortawesome/fontawesome-free": "^6.6.0", + "@fortawesome/fontawesome-free": "^6.7.1", "@tarekraafat/autocomplete.js": "^10.2.9", "@walletconnect/web3-provider": "^1.8.0", "assert": "^2.1.0", @@ -2099,9 +2099,9 @@ } }, "node_modules/@fortawesome/fontawesome-free": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.6.0.tgz", - "integrity": "sha512-60G28ke/sXdtS9KZCpZSHHkCbdsOGEhIUGlwq6yhY74UpTiToIh8np7A8yphhM4BWsvNFtIvLpi4co+h9Mr9Ow==", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.7.1.tgz", + "integrity": "sha512-ALIk/MOh5gYe1TG/ieS5mVUsk7VUIJTJKPMK9rFFqOgfp0Q3d5QiBXbcOMwUvs37fyZVCz46YjOE6IFeOAXCHA==", "engines": { "node": ">=6" } @@ -17928,9 +17928,9 @@ "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==" }, "@fortawesome/fontawesome-free": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.6.0.tgz", - "integrity": "sha512-60G28ke/sXdtS9KZCpZSHHkCbdsOGEhIUGlwq6yhY74UpTiToIh8np7A8yphhM4BWsvNFtIvLpi4co+h9Mr9Ow==" + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.7.1.tgz", + "integrity": "sha512-ALIk/MOh5gYe1TG/ieS5mVUsk7VUIJTJKPMK9rFFqOgfp0Q3d5QiBXbcOMwUvs37fyZVCz46YjOE6IFeOAXCHA==" }, "@humanwhocodes/config-array": { "version": "0.13.0", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 38615bb0b3f9..1302f2873e31 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -19,7 +19,7 @@ "eslint": "eslint js/**" }, "dependencies": { - "@fortawesome/fontawesome-free": "^6.6.0", + "@fortawesome/fontawesome-free": "^6.7.1", "@amplitude/analytics-browser": "^2.11.9", "@tarekraafat/autocomplete.js": "^10.2.9", "@walletconnect/web3-provider": "^1.8.0", From cc4acdda0a1888a11905968de585120f86cf55f2 Mon Sep 17 00:00:00 2001 From: Donny <130464015+defitricks@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:02:33 +0200 Subject: [PATCH 348/363] Corrected missing .yml extension in Nethermind example (#11316) --- docker-compose/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose/README.md b/docker-compose/README.md index fcd8f8b588b8..1a0926a0123c 100644 --- a/docker-compose/README.md +++ b/docker-compose/README.md @@ -45,7 +45,7 @@ The repo contains built-in configs for different JSON RPC clients without need t | Erigon | `docker-compose -f erigon.yml up -d` | | Geth (suitable for Reth as well) | `docker-compose -f geth.yml up -d` | | Geth Clique | `docker-compose -f geth-clique-consensus.yml up -d` | -| Nethermind, OpenEthereum | `docker-compose -f nethermind up -d` | +| Nethermind, OpenEthereum | `docker-compose -f nethermind.yml up -d` | | Ganache | `docker-compose -f ganache.yml up -d` | | HardHat network | `docker-compose -f hardhat-network.yml up -d` | From 91d642b45799dc6efc99467075b758817362900a Mon Sep 17 00:00:00 2001 From: Qwerty5Uiop <105209995+Qwerty5Uiop@users.noreply.github.com> Date: Mon, 2 Dec 2024 13:30:25 +0400 Subject: [PATCH 349/363] chore: Refactor import stages (#11013) --- apps/explorer/lib/explorer/chain/import.ex | 68 ++++++++++++++----- .../chain/import/stage/block_following.ex | 31 --------- .../chain/import/stage/block_pending.ex | 29 -------- .../chain/import/stage/block_referencing.ex | 35 ---------- .../stage/block_transaction_referencing.ex | 34 ++++++++++ .../import/stage/internal_transactions.ex | 27 ++++++++ .../stage/{block_related.ex => main.ex} | 8 +-- .../chain/import/stage/token_referencing.ex | 29 ++++++++ 8 files changed, 144 insertions(+), 117 deletions(-) delete mode 100644 apps/explorer/lib/explorer/chain/import/stage/block_following.ex delete mode 100644 apps/explorer/lib/explorer/chain/import/stage/block_pending.ex delete mode 100644 apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex create mode 100644 apps/explorer/lib/explorer/chain/import/stage/block_transaction_referencing.ex create mode 100644 apps/explorer/lib/explorer/chain/import/stage/internal_transactions.ex rename apps/explorer/lib/explorer/chain/import/stage/{block_related.ex => main.ex} (80%) create mode 100644 apps/explorer/lib/explorer/chain/import/stage/token_referencing.ex diff --git a/apps/explorer/lib/explorer/chain/import.ex b/apps/explorer/lib/explorer/chain/import.ex index 4886ab9dd3fa..2a2ad02d26df 100644 --- a/apps/explorer/lib/explorer/chain/import.ex +++ b/apps/explorer/lib/explorer/chain/import.ex @@ -12,16 +12,24 @@ defmodule Explorer.Chain.Import do require Logger @stages [ - Import.Stage.BlockRelated, - Import.Stage.BlockReferencing, - Import.Stage.BlockFollowing, - Import.Stage.BlockPending, - Import.Stage.ChainTypeSpecific + [ + Import.Stage.Main + ], + [ + Import.Stage.BlockTransactionReferencing, + Import.Stage.TokenReferencing, + Import.Stage.InternalTransactions, + Import.Stage.ChainTypeSpecific + ] ] # in order so that foreign keys are inserted before being referenced - @configured_runners Enum.flat_map(@stages, fn stage -> stage.runners() end) - @all_runners Enum.flat_map(@stages, fn stage -> stage.all_runners() end) + @configured_runners Enum.flat_map(@stages, fn stage_batch -> + Enum.flat_map(stage_batch, fn stage -> stage.runners() end) + end) + @all_runners Enum.flat_map(@stages, fn stage_batch -> + Enum.flat_map(stage_batch, fn stage -> stage.all_runners() end) + end) quoted_runner_option_value = quote do @@ -283,9 +291,11 @@ defmodule Explorer.Chain.Import do timestamps = timestamps() full_options = Map.put(options, :timestamps, timestamps) - {multis, final_runner_to_changes_list} = - Enum.flat_map_reduce(@stages, runner_to_changes_list, fn stage, remaining_runner_to_changes_list -> - stage.multis(remaining_runner_to_changes_list, full_options) + {multis_batches, final_runner_to_changes_list} = + Enum.map_reduce(@stages, runner_to_changes_list, fn stage_batch, remaining_runner_to_changes_list -> + Enum.flat_map_reduce(stage_batch, remaining_runner_to_changes_list, fn stage, inner_remaining_list -> + stage.multis(inner_remaining_list, full_options) + end) end) unless Enum.empty?(final_runner_to_changes_list) do @@ -293,7 +303,7 @@ defmodule Explorer.Chain.Import do "No stages consumed the following runners: #{final_runner_to_changes_list |> Map.keys() |> inspect()}" end - multis + multis_batches end def insert_changes_list(repo, changes_list, options) when is_atom(repo) and is_list(changes_list) do @@ -337,17 +347,21 @@ defmodule Explorer.Chain.Import do reraise exception, __STACKTRACE__ end - defp logged_import(multis, options) when is_list(multis) and is_map(options) do + defp logged_import(multis_batches, options) when is_list(multis_batches) and is_map(options) do import_id = :erlang.unique_integer([:positive]) - Explorer.Logger.metadata(fn -> import_transactions(multis, options) end, import_id: import_id) + Explorer.Logger.metadata(fn -> import_batch_transactions(multis_batches, options) end, import_id: import_id) end - defp import_transactions(multis, options) when is_list(multis) and is_map(options) do - Enum.reduce_while(multis, {:ok, %{}}, fn multi, {:ok, acc_changes} -> - case import_transaction(multi, options) do - {:ok, changes} -> {:cont, {:ok, Map.merge(acc_changes, changes)}} - {:error, _, _, _} = error -> {:halt, error} + defp import_batch_transactions(multis_batches, options) when is_list(multis_batches) and is_map(options) do + Enum.reduce_while(multis_batches, {:ok, %{}}, fn multis, {:ok, acc_changes} -> + multis + |> run_parallel_multis(options) + |> Task.yield_many(:infinity) + |> handle_task_results(acc_changes) + |> case do + {:ok, changes} -> {:cont, {:ok, changes}} + error -> {:halt, error} end end) rescue @@ -358,8 +372,26 @@ defmodule Explorer.Chain.Import do end end + defp run_parallel_multis(multis, options) do + Enum.map(multis, fn multi -> Task.async(fn -> import_transaction(multi, options) end) end) + end + defp import_transaction(multi, options) when is_map(options) do Repo.logged_transaction(multi, timeout: Map.get(options, :timeout, @transaction_timeout)) + rescue + exception -> {:exception, exception, __STACKTRACE__} + end + + defp handle_task_results(task_results, acc_changes) do + Enum.reduce_while(task_results, {:ok, acc_changes}, fn {_task, task_result}, {:ok, acc_changes_inner} -> + case task_result do + {:ok, {:ok, changes}} -> {:cont, {:ok, Map.merge(acc_changes_inner, changes)}} + {:ok, {:exception, exception, stacktrace}} -> reraise exception, stacktrace + {:ok, error} -> {:halt, error} + {:exit, reason} -> {:halt, reason} + nil -> {:halt, :timeout} + end + end) end defp set_refetch_needed_for_partially_imported_blocks(%{blocks: %{params: blocks_params}}) do diff --git a/apps/explorer/lib/explorer/chain/import/stage/block_following.ex b/apps/explorer/lib/explorer/chain/import/stage/block_following.ex deleted file mode 100644 index 984892673ed9..000000000000 --- a/apps/explorer/lib/explorer/chain/import/stage/block_following.ex +++ /dev/null @@ -1,31 +0,0 @@ -defmodule Explorer.Chain.Import.Stage.BlockFollowing do - @moduledoc """ - Imports any tables that follows and cannot be imported at the same time as - those imported by `Explorer.Chain.Import.Stage.BlockRelated` and `Explorer.Chain.Import.Stage.BlockReferencing` - """ - - alias Explorer.Chain.Import.{Runner, Stage} - - @behaviour Stage - - @impl Stage - def runners, - do: [ - Runner.Block.SecondDegreeRelations, - Runner.Block.Rewards, - Runner.Address.TokenBalances, - Runner.Address.CurrentTokenBalances - ] - - @impl Stage - def all_runners, - do: runners() - - @impl Stage - def multis(runner_to_changes_list, options) do - {final_multi, final_remaining_runner_to_changes_list} = - Stage.single_multi(runners(), runner_to_changes_list, options) - - {[final_multi], final_remaining_runner_to_changes_list} - end -end diff --git a/apps/explorer/lib/explorer/chain/import/stage/block_pending.ex b/apps/explorer/lib/explorer/chain/import/stage/block_pending.ex deleted file mode 100644 index abcd95e141cd..000000000000 --- a/apps/explorer/lib/explorer/chain/import/stage/block_pending.ex +++ /dev/null @@ -1,29 +0,0 @@ -defmodule Explorer.Chain.Import.Stage.BlockPending do - @moduledoc """ - Imports any tables that uses `Explorer.Chain.PendingBlockOperation` to track - progress and cannot be imported at the same time as those imported by - `Explorer.Chain.Import.Stage.BlockRelated` and `Explorer.Chain.Import.Stage.BlockReferencing` - """ - - alias Explorer.Chain.Import.{Runner, Stage} - - @behaviour Stage - - @impl Stage - def runners, - do: [ - Runner.InternalTransactions - ] - - @impl Stage - def all_runners, - do: runners() - - @impl Stage - def multis(runner_to_changes_list, options) do - {final_multi, final_remaining_runner_to_changes_list} = - Stage.single_multi(runners(), runner_to_changes_list, options) - - {[final_multi], final_remaining_runner_to_changes_list} - end -end diff --git a/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex b/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex deleted file mode 100644 index af647c274c6c..000000000000 --- a/apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex +++ /dev/null @@ -1,35 +0,0 @@ -defmodule Explorer.Chain.Import.Stage.BlockReferencing do - @moduledoc """ - Imports any tables that reference `t:Explorer.Chain.Block.t/0` and that were - imported by `Explorer.Chain.Import.Stage.BlockRelated`. - """ - - alias Explorer.Chain.Import.{Runner, Stage} - - @behaviour Stage - - @impl Stage - def runners do - [ - Runner.Transaction.Forks, - Runner.Logs, - Runner.Tokens, - Runner.TokenInstances, - Runner.TransactionActions, - Runner.Withdrawals, - Runner.SignedAuthorizations - ] - end - - @impl Stage - def all_runners, - do: runners() - - @impl Stage - def multis(runner_to_changes_list, options) do - {final_multi, final_remaining_runner_to_changes_list} = - Stage.single_multi(runners(), runner_to_changes_list, options) - - {[final_multi], final_remaining_runner_to_changes_list} - end -end diff --git a/apps/explorer/lib/explorer/chain/import/stage/block_transaction_referencing.ex b/apps/explorer/lib/explorer/chain/import/stage/block_transaction_referencing.ex new file mode 100644 index 000000000000..91c5a201206e --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/stage/block_transaction_referencing.ex @@ -0,0 +1,34 @@ +defmodule Explorer.Chain.Import.Stage.BlockTransactionReferencing do + @moduledoc """ + Imports any data that is related to blocks and transactions. + """ + + alias Explorer.Chain.Import.{Runner, Stage} + + @behaviour Stage + + @runners [ + Runner.TokenTransfers, + Runner.Transaction.Forks, + Runner.Logs, + Runner.Block.Rewards, + Runner.Block.SecondDegreeRelations, + Runner.TransactionActions, + Runner.Withdrawals, + Runner.SignedAuthorizations + ] + + @impl Stage + def runners, do: @runners + + @impl Stage + def all_runners, do: runners() + + @impl Stage + def multis(runner_to_changes_list, options) do + {final_multi, final_remaining_runner_to_changes_list} = + Stage.single_multi(runners(), runner_to_changes_list, options) + + {[final_multi], final_remaining_runner_to_changes_list} + end +end diff --git a/apps/explorer/lib/explorer/chain/import/stage/internal_transactions.ex b/apps/explorer/lib/explorer/chain/import/stage/internal_transactions.ex new file mode 100644 index 000000000000..178c6a1b95a3 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/stage/internal_transactions.ex @@ -0,0 +1,27 @@ +defmodule Explorer.Chain.Import.Stage.InternalTransactions do + @moduledoc """ + Imports the rest of the data. + """ + + alias Explorer.Chain.Import.{Runner, Stage} + + @behaviour Stage + + @runners [ + Runner.InternalTransactions + ] + + @impl Stage + def runners, do: @runners + + @impl Stage + def all_runners, do: runners() + + @impl Stage + def multis(runner_to_changes_list, options) do + {final_multi, final_remaining_runner_to_changes_list} = + Stage.single_multi(runners(), runner_to_changes_list, options) + + {[final_multi], final_remaining_runner_to_changes_list} + end +end diff --git a/apps/explorer/lib/explorer/chain/import/stage/block_related.ex b/apps/explorer/lib/explorer/chain/import/stage/main.ex similarity index 80% rename from apps/explorer/lib/explorer/chain/import/stage/block_related.ex rename to apps/explorer/lib/explorer/chain/import/stage/main.ex index b18808fb7367..3f3a581691d2 100644 --- a/apps/explorer/lib/explorer/chain/import/stage/block_related.ex +++ b/apps/explorer/lib/explorer/chain/import/stage/main.ex @@ -1,6 +1,6 @@ -defmodule Explorer.Chain.Import.Stage.BlockRelated do +defmodule Explorer.Chain.Import.Stage.Main do @moduledoc """ - Import blocks along with block related entities. + Imports main data (addresses, address_coin_balances, address_coin_balances_daily, tokens, blocks, transactions). """ alias Explorer.Chain.Import.{Runner, Stage} @@ -10,11 +10,11 @@ defmodule Explorer.Chain.Import.Stage.BlockRelated do @addresses_runner Runner.Addresses @rest_runners [ + Runner.Tokens, Runner.Blocks, Runner.Address.CoinBalances, Runner.Address.CoinBalancesDaily, - Runner.Transactions, - Runner.TokenTransfers + Runner.Transactions ] @impl Stage diff --git a/apps/explorer/lib/explorer/chain/import/stage/token_referencing.ex b/apps/explorer/lib/explorer/chain/import/stage/token_referencing.ex new file mode 100644 index 000000000000..22cb8466e827 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/stage/token_referencing.ex @@ -0,0 +1,29 @@ +defmodule Explorer.Chain.Import.Stage.TokenReferencing do + @moduledoc """ + Imports any data that is related to tokens. + """ + + alias Explorer.Chain.Import.{Runner, Stage} + + @behaviour Stage + + @runners [ + Runner.TokenInstances, + Runner.Address.TokenBalances, + Runner.Address.CurrentTokenBalances + ] + + @impl Stage + def runners, do: @runners + + @impl Stage + def all_runners, do: runners() + + @impl Stage + def multis(runner_to_changes_list, options) do + {final_multi, final_remaining_runner_to_changes_list} = + Stage.single_multi(runners(), runner_to_changes_list, options) + + {[final_multi], final_remaining_runner_to_changes_list} + end +end From 69915c919a11b11592031234ced8ecf9a6bd3783 Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:37:52 +0300 Subject: [PATCH 350/363] perf: advanced filters optimization (#11186) * perf: optimize internal transactions query * perf: optimize transactions query --- .../lib/explorer/chain/advanced_filter.ex | 237 +++++++----------- 1 file changed, 88 insertions(+), 149 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/advanced_filter.ex b/apps/explorer/lib/explorer/chain/advanced_filter.ex index 5924084c64e0..2f38bc27ab71 100644 --- a/apps/explorer/lib/explorer/chain/advanced_filter.ex +++ b/apps/explorer/lib/explorer/chain/advanced_filter.ex @@ -283,7 +283,11 @@ defmodule Explorer.Chain.AdvancedFilter do query |> page_transactions(paging_options) |> limit_query(paging_options) - |> apply_transactions_filters(options) + |> apply_transactions_filters( + options, + fn query -> query |> order_by([transaction], desc: transaction.block_number, desc: transaction.index) end + ) + |> limit_query(paging_options) end defp page_transactions(query, %PagingOptions{ @@ -315,8 +319,8 @@ defmodule Explorer.Chain.AdvancedFilter do (internal_transaction.type == :call and internal_transaction.index > 0) or internal_transaction.type != :call, order_by: [ - desc: internal_transaction.block_number, - desc: internal_transaction.transaction_index, + desc: transaction.block_number, + desc: transaction.index, desc: internal_transaction.index ] ) @@ -332,8 +336,8 @@ defmodule Explorer.Chain.AdvancedFilter do (internal_transaction.type == :call and internal_transaction.index > 0) or internal_transaction.type != :call, order_by: [ - desc: internal_transaction.block_number, - desc: internal_transaction.transaction_index, + desc: transaction.block_number, + desc: transaction.index, desc: internal_transaction.index ] ) @@ -342,7 +346,14 @@ defmodule Explorer.Chain.AdvancedFilter do query |> page_internal_transactions(paging_options) |> limit_query(paging_options) - |> apply_internal_transactions_filters(options) + |> apply_transactions_filters(options, fn query -> + query + |> order_by([internal_transaction], + desc: internal_transaction.block_number, + desc: internal_transaction.transaction_index, + desc: internal_transaction.index + ) + end) |> limit_query(paging_options) |> preload([:transaction]) end @@ -698,25 +709,17 @@ defmodule Explorer.Chain.AdvancedFilter do |> filter_token_transfers_by_amount(options[:amount][:from], options[:amount][:to]) end - defp apply_transactions_filters(query, options) do - query - |> filter_transactions_by_amount(options[:amount][:from], options[:amount][:to]) - |> filter_transactions_by_methods(options[:methods]) - |> only_collated_transactions() - |> filter_by_addresses(options[:from_address_hashes], options[:to_address_hashes], options[:address_relation]) - |> filter_by_age(:transaction, options) - end - - defp apply_internal_transactions_filters(query, options) do + defp apply_transactions_filters(query, options, order_by) do query |> filter_transactions_by_amount(options[:amount][:from], options[:amount][:to]) |> filter_transactions_by_methods(options[:methods]) |> only_collated_transactions() |> filter_by_age(:transaction, options) - |> filter_internal_transactions_by_addresses( + |> filter_transactions_by_addresses( options[:from_address_hashes], options[:to_address_hashes], - options[:address_relation] + options[:address_relation], + order_by ) end @@ -815,59 +818,6 @@ defmodule Explorer.Chain.AdvancedFilter do defp filter_by_timestamp(query, _, _), do: query - defp filter_by_addresses(query, from_addresses, to_addresses, relation) do - to_address_dynamic = do_filter_by_addresses(:to_address_hash, to_addresses) - - from_address_dynamic = do_filter_by_addresses(:from_address_hash, from_addresses) - - final_condition = - case {to_address_dynamic, from_address_dynamic} do - {not_nil_to_address, not_nil_from_address} when nil not in [not_nil_to_address, not_nil_from_address] -> - combine_filter_by_addresses(not_nil_to_address, not_nil_from_address, relation) - - _ -> - to_address_dynamic || from_address_dynamic - end - - case final_condition do - not_nil when not is_nil(not_nil) -> query |> where(^not_nil) - _ -> query - end - end - - defp do_filter_by_addresses(field, addresses) do - to_include_dynamic = do_filter_by_addresses_inclusion(field, addresses && Keyword.get(addresses, :include)) - to_exclude_dynamic = do_filter_by_addresses_exclusion(field, addresses && Keyword.get(addresses, :exclude)) - - case {to_include_dynamic, to_exclude_dynamic} do - {not_nil_include, not_nil_exclude} when nil not in [not_nil_include, not_nil_exclude] -> - dynamic([t], ^not_nil_include and ^not_nil_exclude) - - _ -> - to_include_dynamic || to_exclude_dynamic - end - end - - defp do_filter_by_addresses_inclusion(field, [_ | _] = addresses) do - dynamic([t], field(t, ^field) in ^addresses) - end - - defp do_filter_by_addresses_inclusion(_, _), do: nil - - defp do_filter_by_addresses_exclusion(field, [_ | _] = addresses) do - dynamic([t], field(t, ^field) not in ^addresses) - end - - defp do_filter_by_addresses_exclusion(_, _), do: nil - - defp combine_filter_by_addresses(from_addresses_dynamic, to_addresses_dynamic, :or) do - dynamic([t], ^from_addresses_dynamic or ^to_addresses_dynamic) - end - - defp combine_filter_by_addresses(from_addresses_dynamic, to_addresses_dynamic, _) do - dynamic([t], ^from_addresses_dynamic and ^to_addresses_dynamic) - end - defp filter_token_transfers_by_addresses(query_function, from_addresses_params, to_addresses_params, relation) do case {process_address_inclusion(from_addresses_params), process_address_inclusion(to_addresses_params)} do {nil, nil} -> query_function @@ -1050,152 +1000,149 @@ defmodule Explorer.Chain.AdvancedFilter do end end - defp filter_internal_transactions_by_addresses(query, from_addresses, to_addresses, relation) do + defp filter_transactions_by_addresses(query, from_addresses, to_addresses, relation, order_by) do + order_by = fn query -> query |> exclude(:order_by) |> order_by.() end + case {process_address_inclusion(from_addresses), process_address_inclusion(to_addresses)} do {nil, nil} -> query - {from, nil} -> do_filter_internal_transactions_by_address(query, from, :from_address_hash) - {nil, to} -> do_filter_internal_transactions_by_address(query, to, :to_address_hash) - {from, to} -> do_filter_internal_transactions_by_both_addresses(query, from, to, relation) + {from, nil} -> do_filter_transactions_by_address(query, from, :from_address_hash, order_by) + {nil, to} -> do_filter_transactions_by_address(query, to, :to_address_hash, order_by) + {from, to} -> do_filter_transactions_by_both_addresses(query, from, to, relation, order_by) end end - defp do_filter_internal_transactions_by_address(query, {:include, addresses}, field) do + defp do_filter_transactions_by_address(query, {:include, addresses}, field, order_by) do queries = addresses |> Enum.map(fn address -> - query |> where([token_transfer], field(token_transfer, ^field) == ^address) + query + |> where([transaction], field(transaction, ^field) == ^address) + |> order_by.() end) |> map_first(&subquery/1) |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) - from(internal_transaction in subquery(queries), - order_by: [ - desc: internal_transaction.block_number, - desc: internal_transaction.transaction_index, - desc: internal_transaction.index - ] - ) + filtered_query = from(transaction in subquery(queries)) + filtered_query |> order_by.() end - defp do_filter_internal_transactions_by_address(query, {:exclude, addresses}, field) do - query |> where([t], field(t, ^field) not in ^addresses) + defp do_filter_transactions_by_address(query, {:exclude, addresses}, field, order_by) do + query + |> where([transaction], field(transaction, ^field) not in ^addresses) + |> order_by.() end - defp do_filter_internal_transactions_by_both_addresses(query, {:include, from}, {:include, to}, relation) do + defp do_filter_transactions_by_both_addresses(query, {:include, from}, {:include, to}, relation, order_by) do from_queries = from |> Enum.map(fn from_address -> - query |> where([internal_transaction], internal_transaction.from_address_hash == ^from_address) + query + |> where([transaction], transaction.from_address_hash == ^from_address) + |> order_by.() end) to_queries = to |> Enum.map(fn to_address -> - query |> where([internal_transaction], internal_transaction.to_address_hash == ^to_address) + query + |> where([transaction], transaction.to_address_hash == ^to_address) + |> order_by.() end) - do_filter_internal_transactions_by_both_addresses_to_include(from_queries, to_queries, relation) + do_filter_transactions_by_both_addresses_to_include(from_queries, to_queries, relation, order_by) end - defp do_filter_internal_transactions_by_both_addresses(query, {:include, from}, {:exclude, to}, :and) do + defp do_filter_transactions_by_both_addresses(query, {:include, from}, {:exclude, to}, :and, order_by) do from_queries = from |> Enum.map(fn from_address -> query |> where( - [internal_transaction], - internal_transaction.from_address_hash == ^from_address and internal_transaction.to_address_hash not in ^to + [transaction], + transaction.from_address_hash == ^from_address and transaction.to_address_hash not in ^to ) + |> order_by.() end) |> map_first(&subquery/1) |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) - from(internal_transaction in subquery(from_queries), - order_by: [ - desc: internal_transaction.block_number, - desc: internal_transaction.transaction_index, - desc: internal_transaction.index - ] - ) + filtered_query = from(transaction in subquery(from_queries)) + filtered_query |> order_by.() end - defp do_filter_internal_transactions_by_both_addresses(query, {:include, from}, {:exclude, to}, _relation) do + defp do_filter_transactions_by_both_addresses(query, {:include, from}, {:exclude, to}, _relation, order_by) do from_queries = from |> Enum.map(fn from_address -> query |> where( - [internal_transaction], - internal_transaction.from_address_hash == ^from_address or internal_transaction.to_address_hash not in ^to + [transaction], + transaction.from_address_hash == ^from_address or transaction.to_address_hash not in ^to ) + |> order_by.() end) |> map_first(&subquery/1) |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) - from(internal_transaction in subquery(from_queries), - order_by: [ - desc: internal_transaction.block_number, - desc: internal_transaction.transaction_index, - desc: internal_transaction.index - ] - ) + filtered_query = from(transaction in subquery(from_queries)) + filtered_query |> order_by.() end - defp do_filter_internal_transactions_by_both_addresses(query, {:exclude, from}, {:include, to}, :and) do + defp do_filter_transactions_by_both_addresses(query, {:exclude, from}, {:include, to}, :and, order_by) do to_queries = to |> Enum.map(fn to_address -> query |> where( - [internal_transaction], - internal_transaction.to_address_hash == ^to_address and internal_transaction.from_address_hash not in ^from + [transaction], + transaction.to_address_hash == ^to_address and transaction.from_address_hash not in ^from ) + |> order_by.() end) |> map_first(&subquery/1) |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) - from(internal_transaction in subquery(to_queries), - order_by: [ - desc: internal_transaction.block_number, - desc: internal_transaction.transaction_index, - desc: internal_transaction.index - ] - ) + filtered_query = from(transaction in subquery(to_queries)) + filtered_query |> order_by.() end - defp do_filter_internal_transactions_by_both_addresses(query, {:exclude, from}, {:include, to}, _relation) do + defp do_filter_transactions_by_both_addresses(query, {:exclude, from}, {:include, to}, _relation, order_by) do to_queries = to |> Enum.map(fn to_address -> query |> where( - [internal_transaction], - internal_transaction.to_address_hash == ^to_address or internal_transaction.from_address_hash not in ^from + [transaction], + transaction.to_address_hash == ^to_address or transaction.from_address_hash not in ^from ) + |> order_by.() end) |> map_first(&subquery/1) |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) - from(internal_transaction in subquery(to_queries), - order_by: [ - desc: internal_transaction.block_number, - desc: internal_transaction.transaction_index, - desc: internal_transaction.index - ] - ) + filtered_query = from(transaction in subquery(to_queries)) + filtered_query |> order_by.() end - defp do_filter_internal_transactions_by_both_addresses(query, {:exclude, from}, {:exclude, to}, :and) do + defp do_filter_transactions_by_both_addresses(query, {:exclude, from}, {:exclude, to}, :and, order_by) do query - |> where([t], t.from_address_hash not in ^from and t.to_address_hash not in ^to) + |> where( + [transaction], + transaction.from_address_hash not in ^from and transaction.to_address_hash not in ^to + ) + |> order_by.() end - defp do_filter_internal_transactions_by_both_addresses(query, {:exclude, from}, {:exclude, to}, _relation) do + defp do_filter_transactions_by_both_addresses(query, {:exclude, from}, {:exclude, to}, _relation, order_by) do query - |> where([t], t.from_address_hash not in ^from or t.to_address_hash not in ^to) + |> where( + [transaction], + transaction.from_address_hash not in ^from or transaction.to_address_hash not in ^to + ) + |> order_by.() end - defp do_filter_internal_transactions_by_both_addresses_to_include(from_queries, to_queries, relation) do + defp do_filter_transactions_by_both_addresses_to_include(from_queries, to_queries, relation, order_by) do case relation do :and -> united_from_queries = @@ -1204,13 +1151,10 @@ defmodule Explorer.Chain.AdvancedFilter do united_to_queries = to_queries |> map_first(&subquery/1) |> Enum.reduce(fn query, acc -> union_all(acc, ^query) end) - from(internal_transaction in subquery(intersect_all(united_from_queries, ^united_to_queries)), - order_by: [ - desc: internal_transaction.block_number, - desc: internal_transaction.transaction_index, - desc: internal_transaction.index - ] - ) + filtered_query = from(transaction in subquery(intersect_all(united_from_queries, ^united_to_queries))) + + filtered_query + |> order_by.() _ -> union_query = @@ -1219,13 +1163,8 @@ defmodule Explorer.Chain.AdvancedFilter do |> map_first(&subquery/1) |> Enum.reduce(fn query, acc -> union(acc, ^query) end) - from(internal_transaction in subquery(union_query), - order_by: [ - desc: internal_transaction.block_number, - desc: internal_transaction.transaction_index, - desc: internal_transaction.index - ] - ) + filtered_query = from(transaction in subquery(union_query)) + filtered_query |> order_by.() end end From a2ddd4c1a6efabda683622b0062266af36864443 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Mon, 2 Dec 2024 13:38:31 +0300 Subject: [PATCH 351/363] fix: Wrong usage of env in TokenInstanceMetadataRefetch (#11317) --- .../fetcher/on_demand/token_instance_metadata_refetch.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/indexer/lib/indexer/fetcher/on_demand/token_instance_metadata_refetch.ex b/apps/indexer/lib/indexer/fetcher/on_demand/token_instance_metadata_refetch.ex index 02792dd625c6..6d1dd42bc8d7 100644 --- a/apps/indexer/lib/indexer/fetcher/on_demand/token_instance_metadata_refetch.ex +++ b/apps/indexer/lib/indexer/fetcher/on_demand/token_instance_metadata_refetch.ex @@ -46,7 +46,7 @@ defmodule Indexer.Fetcher.OnDemand.TokenInstanceMetadataRefetch do end defp fetch_and_broadcast_metadata(token_instance, _state) do - from_base_uri? = Application.get_env(:indexer, __MODULE__)[:base_uri_retry?] + from_base_uri? = Application.get_env(:indexer, TokenInstanceHelper)[:base_uri_retry?] token_id = TokenInstanceHelper.prepare_token_id(token_instance.token_id) contract_address_hash_string = to_string(token_instance.token_contract_address_hash) From 1c0db9bd8bf932422d2b49981fcbdec2b51e35f5 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Mon, 2 Dec 2024 13:45:43 +0300 Subject: [PATCH 352/363] fix: Get rid of scientific notation in CSV token holders export (#11281) * fix: Get rid of scientific notation in CSV token holders export * Fix credo --- .../lib/explorer/chain/address/current_token_balance.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex index 7d2efd461704..65d356d5441b 100644 --- a/apps/explorer/lib/explorer/chain/address/current_token_balance.ex +++ b/apps/explorer/lib/explorer/chain/address/current_token_balance.ex @@ -348,7 +348,7 @@ defmodule Explorer.Chain.Address.CurrentTokenBalance do |> Stream.map(fn ctb -> [ Address.checksum(ctb.address_hash), - CurrencyHelper.divide_decimals(ctb.value, token.decimals) + ctb.value |> CurrencyHelper.divide_decimals(token.decimals) |> Decimal.to_string(:xsd) ] end) From bb5f6acd7ba357a0c4e1c7b57febe4c882c78398 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:16:19 +0300 Subject: [PATCH 353/363] feat: Add request to /cache/{tx_hash} of transaction interpreter (#11279) --- .../transaction_interpretation.ex | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex index d2af052ea967..a2cc867cfa5e 100644 --- a/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex +++ b/apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex @@ -35,14 +35,17 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do | {:error, Jason.DecodeError.t()} | {:ok, any()} def interpret(transaction_or_map, request_builder \\ &prepare_request_body/1) do - if enabled?() do + with {:enabled, true} <- {:enabled, enabled?()}, + {:cache, :no_cached_data} <- + {:cache, try_get_cached_value(get_hash(transaction_or_map))} do url = interpret_url() body = request_builder.(transaction_or_map) http_post_request(url, body) else - {{:error, :disabled}, 403} + {:cache, {:ok, _response} = result} -> result + {:enabled, false} -> {{:error, :disabled}, 403} end end @@ -96,6 +99,16 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do end end + defp try_get_cached_value(hash) do + with {:ok, %Response{body: body, status_code: 200}} <- HTTPoison.get(cache_url(hash)), + {:ok, json} <- body |> Jason.decode() do + {:ok, json |> Map.get("response") |> Map.put("success", true)} |> preload_template_variables() + else + _ -> + :no_cached_data + end + end + defp http_response_code({:ok, %Response{status_code: status_code}}), do: status_code defp http_response_code(_), do: 500 @@ -105,6 +118,10 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do base_url(:block_scout_web, __MODULE__) <> "/transactions/summary" end + defp cache_url(hash) do + base_url(:block_scout_web, __MODULE__) <> "/cache/#{hash}" + end + defp prepare_request_body(transaction) do transaction = Chain.select_repo(@api_true).preload(transaction, [ @@ -391,4 +408,7 @@ defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do {mock_transaction, decoded_input, decoded_input |> Transaction.format_decoded_input() |> TransactionView.decoded_input()} end + + defp get_hash(%{hash: hash}), do: hash + defp get_hash(%{"hash" => hash}), do: hash end From 673f1718f2b8e5da74c65ec924f937bea58bd1f9 Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Tue, 3 Dec 2024 17:16:20 +0900 Subject: [PATCH 354/363] chore: refactor Dockerfile (#11130) * chore: refactor dockerfile * chore: update eth-sepolia ci --- .github/workflows/pre-release-arbitrum.yml | 34 ----- .../publish-docker-image-for-arbitrum.yml | 34 ----- .../publish-docker-image-for-eth-sepolia.yml | 34 ----- docker-compose/docker-compose.yml | 7 - docker-compose/envs/common-blockscout.env | 3 +- docker/Dockerfile | 105 +++++++------- docker/oldUI.Dockerfile | 130 ++++++++++-------- 7 files changed, 133 insertions(+), 214 deletions(-) diff --git a/.github/workflows/pre-release-arbitrum.yml b/.github/workflows/pre-release-arbitrum.yml index a58456762081..23d40950aeb7 100644 --- a/.github/workflows/pre-release-arbitrum.yml +++ b/.github/workflows/pre-release-arbitrum.yml @@ -41,13 +41,6 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - DISABLE_WEBAPP=false - API_V1_READ_METHODS_DISABLED=false - API_V1_WRITE_METHODS_DISABLED=false - CACHE_EXCHANGE_RATES_PERIOD= - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= - ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=arbitrum @@ -65,11 +58,6 @@ jobs: linux/arm64/v8 build-args: | DISABLE_API=true - DISABLE_WEBAPP=true - CACHE_EXCHANGE_RATES_PERIOD= - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= - ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=arbitrum @@ -87,11 +75,6 @@ jobs: linux/arm64/v8 build-args: | DISABLE_INDEXER=true - DISABLE_WEBAPP=true - CACHE_EXCHANGE_RATES_PERIOD= - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= - ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=arbitrum @@ -108,13 +91,6 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - DISABLE_WEBAPP=false - API_V1_READ_METHODS_DISABLED=false - API_V1_WRITE_METHODS_DISABLED=false - CACHE_EXCHANGE_RATES_PERIOD= - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=arbitrum @@ -133,11 +109,6 @@ jobs: linux/arm64/v8 build-args: | DISABLE_API=true - DISABLE_WEBAPP=true - CACHE_EXCHANGE_RATES_PERIOD= - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= - ADMIN_PANEL_ENABLED=false BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=arbitrum @@ -156,11 +127,6 @@ jobs: linux/arm64/v8 build-args: | DISABLE_INDEXER=true - DISABLE_WEBAPP=true - CACHE_EXCHANGE_RATES_PERIOD= - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - ADMIN_PANEL_ENABLED=false - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=arbitrum diff --git a/.github/workflows/publish-docker-image-for-arbitrum.yml b/.github/workflows/publish-docker-image-for-arbitrum.yml index 3266fbee762e..06dd671ad3a1 100644 --- a/.github/workflows/publish-docker-image-for-arbitrum.yml +++ b/.github/workflows/publish-docker-image-for-arbitrum.yml @@ -36,13 +36,6 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - CACHE_EXCHANGE_RATES_PERIOD= - API_V1_READ_METHODS_DISABLED=false - DISABLE_WEBAPP=false - API_V1_WRITE_METHODS_DISABLED=false - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - ADMIN_PANEL_ENABLED=false - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=arbitrum @@ -59,12 +52,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_WEBAPP=true DISABLE_API=true - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - ADMIN_PANEL_ENABLED=false - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=arbitrum @@ -81,12 +69,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_WEBAPP=true DISABLE_INDEXER=true - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - ADMIN_PANEL_ENABLED=false - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=arbitrum @@ -103,13 +86,6 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - CACHE_EXCHANGE_RATES_PERIOD= - API_V1_READ_METHODS_DISABLED=false - DISABLE_WEBAPP=false - API_V1_WRITE_METHODS_DISABLED=false - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - ADMIN_PANEL_ENABLED=false - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=arbitrum @@ -127,12 +103,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_WEBAPP=true DISABLE_API=true - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - ADMIN_PANEL_ENABLED=false - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=arbitrum @@ -150,12 +121,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_WEBAPP=true DISABLE_INDEXER=true - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - ADMIN_PANEL_ENABLED=false - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=arbitrum diff --git a/.github/workflows/publish-docker-image-for-eth-sepolia.yml b/.github/workflows/publish-docker-image-for-eth-sepolia.yml index bd320c9ef175..eba3c88a9b3b 100644 --- a/.github/workflows/publish-docker-image-for-eth-sepolia.yml +++ b/.github/workflows/publish-docker-image-for-eth-sepolia.yml @@ -36,13 +36,6 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - CACHE_EXCHANGE_RATES_PERIOD= - API_V1_READ_METHODS_DISABLED=false - DISABLE_WEBAPP=false - API_V1_WRITE_METHODS_DISABLED=false - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - ADMIN_PANEL_ENABLED=false - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=ethereum @@ -59,12 +52,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_WEBAPP=true DISABLE_API=true - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - ADMIN_PANEL_ENABLED=false - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=ethereum @@ -81,12 +69,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_WEBAPP=true DISABLE_INDEXER=true - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - ADMIN_PANEL_ENABLED=false - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }} RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=ethereum @@ -103,13 +86,6 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - CACHE_EXCHANGE_RATES_PERIOD= - API_V1_READ_METHODS_DISABLED=false - DISABLE_WEBAPP=false - API_V1_WRITE_METHODS_DISABLED=false - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - ADMIN_PANEL_ENABLED=false - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }}-shrink-internal-txs RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=ethereum @@ -127,12 +103,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_WEBAPP=true DISABLE_API=true - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - ADMIN_PANEL_ENABLED=false - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }}-shrink-internal-txs RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=ethereum @@ -150,12 +121,7 @@ jobs: linux/amd64 linux/arm64/v8 build-args: | - CACHE_EXCHANGE_RATES_PERIOD= - DISABLE_WEBAPP=true DISABLE_INDEXER=true - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED= - ADMIN_PANEL_ENABLED=false - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }}-shrink-internal-txs RELEASE_VERSION=${{ env.RELEASE_VERSION }} CHAIN_TYPE=ethereum diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index a20322750b4a..26b21da2da7e 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -30,13 +30,6 @@ services: context: .. dockerfile: ./docker/Dockerfile args: - CACHE_EXCHANGE_RATES_PERIOD: "" - API_V1_READ_METHODS_DISABLED: "false" - DISABLE_WEBAPP: "false" - API_V1_WRITE_METHODS_DISABLED: "false" - CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED: "" - CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL: "" - ADMIN_PANEL_ENABLED: "" RELEASE_VERSION: 6.9.2 links: - db:database diff --git a/docker-compose/envs/common-blockscout.env b/docker-compose/envs/common-blockscout.env index 594c97a5c4f7..df4b2670e2c7 100644 --- a/docker-compose/envs/common-blockscout.env +++ b/docker-compose/envs/common-blockscout.env @@ -138,7 +138,8 @@ CONTRACT_MAX_STRING_LENGTH_WITHOUT_TRIMMING=2040 # CONTRACT_AUDIT_REPORTS_AIRTABLE_API_KEY= # CONTRACT_CERTIFIED_LIST= UNCLES_IN_AVERAGE_BLOCK_TIME=false -DISABLE_WEBAPP=false +DISABLE_WEBAPP=true +ADMIN_PANEL_ENABLED=false API_V2_ENABLED=true API_V1_READ_METHODS_DISABLED=false API_V1_WRITE_METHODS_DISABLED=false diff --git a/docker/Dockerfile b/docker/Dockerfile index 8d1f4cd8b813..3a1e0858f10a 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,26 +1,39 @@ -FROM hexpm/elixir:1.17.3-erlang-27.1-alpine-3.20.3 AS builder +FROM hexpm/elixir:1.17.3-erlang-27.1-alpine-3.20.3 AS builder-deps WORKDIR /app -ENV MIX_ENV="prod" +RUN apk --no-cache --update add \ + alpine-sdk gmp-dev automake libtool inotify-tools autoconf python3 file gcompat libstdc++ curl ca-certificates git make + +# Cache elixir deps +COPY mix.exs mix.lock ./ +COPY apps/block_scout_web/mix.exs ./apps/block_scout_web/ +COPY apps/explorer/mix.exs ./apps/explorer/ +COPY apps/ethereum_jsonrpc/mix.exs ./apps/ethereum_jsonrpc/ +COPY apps/indexer/mix.exs ./apps/indexer/ -RUN apk --no-cache --update add alpine-sdk gmp-dev automake libtool inotify-tools autoconf python3 file gcompat +ENV MIX_ENV="prod" +ENV MIX_HOME=/opt/mix +RUN mix local.hex --force +RUN mix do deps.get, local.rebar --force, deps.compile --skip-umbrella-children -RUN set -ex && \ - apk --update add libstdc++ curl ca-certificates gcompat +COPY config ./config +COPY rel ./rel +COPY apps ./apps -ARG CACHE_EXCHANGE_RATES_PERIOD +############################################################## +FROM builder-deps AS builder + +ENV DISABLE_WEBAPP=true +ENV ADMIN_PANEL_ENABLED=false +ARG DISABLE_INDEXER +ENV DISABLE_INDEXER=${DISABLE_INDEXER} +ARG DISABLE_API +ENV DISABLE_API=${DISABLE_API} ARG API_V1_READ_METHODS_DISABLED -ARG DISABLE_WEBAPP +ENV API_V1_READ_METHODS_DISABLED=${API_V1_READ_METHODS_DISABLED} ARG API_V1_WRITE_METHODS_DISABLED -ARG CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED -ARG ADMIN_PANEL_ENABLED -ARG CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL -ARG SESSION_COOKIE_DOMAIN -ARG MIXPANEL_TOKEN -ARG MIXPANEL_URL -ARG AMPLITUDE_API_KEY -ARG AMPLITUDE_URL +ENV API_V1_WRITE_METHODS_DISABLED=${API_V1_WRITE_METHODS_DISABLED} ARG CHAIN_TYPE ENV CHAIN_TYPE=${CHAIN_TYPE} ARG BRIDGED_TOKENS_ENABLED @@ -32,44 +45,18 @@ ENV SHRINK_INTERNAL_TRANSACTIONS_ENABLED=${SHRINK_INTERNAL_TRANSACTIONS_ENABLED} ARG API_GRAPHQL_MAX_COMPLEXITY ENV API_GRAPHQL_MAX_COMPLEXITY=${API_GRAPHQL_MAX_COMPLEXITY} -# Cache elixir deps -ADD mix.exs mix.lock ./ -ADD apps/block_scout_web/mix.exs ./apps/block_scout_web/ -ADD apps/explorer/mix.exs ./apps/explorer/ -ADD apps/ethereum_jsonrpc/mix.exs ./apps/ethereum_jsonrpc/ -ADD apps/indexer/mix.exs ./apps/indexer/ - -ENV MIX_HOME=/opt/mix -RUN mix local.hex --force -RUN mix do deps.get, local.rebar --force, deps.compile - -ADD apps ./apps -ADD config ./config -ADD rel ./rel -ADD *.exs ./ - # Run backend compilation RUN mix compile -RUN apk add --update git make - -RUN mkdir -p /opt/release \ - && mix release blockscout \ - && mv _build/${MIX_ENV}/rel/blockscout /opt/release +RUN mkdir -p /opt/release && \ + mix release blockscout && \ + mv _build/${MIX_ENV}/rel/blockscout /opt/release ############################################################## FROM hexpm/elixir:1.17.3-erlang-27.1-alpine-3.20.3 -ARG RELEASE_VERSION -ENV RELEASE_VERSION=${RELEASE_VERSION} -ARG CHAIN_TYPE -ENV CHAIN_TYPE=${CHAIN_TYPE} -ARG BRIDGED_TOKENS_ENABLED -ENV BRIDGED_TOKENS_ENABLED=${BRIDGED_TOKENS_ENABLED} -ARG SHRINK_INTERNAL_TRANSACTIONS_ENABLED -ENV SHRINK_INTERNAL_TRANSACTIONS_ENABLED=${SHRINK_INTERNAL_TRANSACTIONS_ENABLED} -ARG BLOCKSCOUT_VERSION -ENV BLOCKSCOUT_VERSION=${BLOCKSCOUT_VERSION} +WORKDIR /app + ARG BLOCKSCOUT_USER=blockscout ARG BLOCKSCOUT_GROUP=blockscout ARG BLOCKSCOUT_UID=10001 @@ -79,7 +66,31 @@ RUN apk --no-cache --update add jq curl && \ addgroup --system --gid ${BLOCKSCOUT_GID} ${BLOCKSCOUT_GROUP} && \ adduser --system --uid ${BLOCKSCOUT_UID} --ingroup ${BLOCKSCOUT_GROUP} --disabled-password ${BLOCKSCOUT_USER} -WORKDIR /app +ENV DISABLE_WEBAPP=true +ENV ADMIN_PANEL_ENABLED=false +ARG DISABLE_INDEXER +ENV DISABLE_INDEXER=${DISABLE_INDEXER} +ARG DISABLE_API +ENV DISABLE_API=${DISABLE_API} +ARG API_V1_READ_METHODS_DISABLED +ENV API_V1_READ_METHODS_DISABLED=${API_V1_READ_METHODS_DISABLED} +ARG API_V1_WRITE_METHODS_DISABLED +ENV API_V1_WRITE_METHODS_DISABLED=${API_V1_WRITE_METHODS_DISABLED} +ARG CHAIN_TYPE +ENV CHAIN_TYPE=${CHAIN_TYPE} +ARG BRIDGED_TOKENS_ENABLED +ENV BRIDGED_TOKENS_ENABLED=${BRIDGED_TOKENS_ENABLED} +ARG MUD_INDEXER_ENABLED +ENV MUD_INDEXER_ENABLED=${MUD_INDEXER_ENABLED} +ARG SHRINK_INTERNAL_TRANSACTIONS_ENABLED +ENV SHRINK_INTERNAL_TRANSACTIONS_ENABLED=${SHRINK_INTERNAL_TRANSACTIONS_ENABLED} +ARG API_GRAPHQL_MAX_COMPLEXITY +ENV API_GRAPHQL_MAX_COMPLEXITY=${API_GRAPHQL_MAX_COMPLEXITY} + +ARG RELEASE_VERSION +ENV RELEASE_VERSION=${RELEASE_VERSION} +ARG BLOCKSCOUT_VERSION +ENV BLOCKSCOUT_VERSION=${BLOCKSCOUT_VERSION} COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /opt/release/blockscout . COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/config/config_helper.exs ./config/config_helper.exs diff --git a/docker/oldUI.Dockerfile b/docker/oldUI.Dockerfile index 1a2b1da1755a..a98878763bfd 100644 --- a/docker/oldUI.Dockerfile +++ b/docker/oldUI.Dockerfile @@ -1,87 +1,78 @@ -FROM hexpm/elixir:1.17.3-erlang-27.1-alpine-3.20.3 AS builder +FROM hexpm/elixir:1.17.3-erlang-27.1-alpine-3.20.3 AS builder-deps WORKDIR /app -ENV MIX_ENV="prod" - -RUN apk --no-cache --update add alpine-sdk gmp-dev automake libtool inotify-tools autoconf python3 file gcompat - -RUN set -ex && \ - apk --update add libstdc++ curl ca-certificates gcompat - -ARG CACHE_EXCHANGE_RATES_PERIOD -ARG API_V1_READ_METHODS_DISABLED -ARG DISABLE_WEBAPP -ARG API_V1_WRITE_METHODS_DISABLED -ARG CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED -ARG ADMIN_PANEL_ENABLED -ARG CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL -ARG SESSION_COOKIE_DOMAIN -ARG MIXPANEL_TOKEN -ARG MIXPANEL_URL -ARG AMPLITUDE_API_KEY -ARG AMPLITUDE_URL -ARG CHAIN_TYPE -ENV CHAIN_TYPE=${CHAIN_TYPE} -ARG BRIDGED_TOKENS_ENABLED -ENV BRIDGED_TOKENS_ENABLED=${BRIDGED_TOKENS_ENABLED} -ARG MUD_INDEXER_ENABLED -ENV MUD_INDEXER_ENABLED=${MUD_INDEXER_ENABLED} -ARG SHRINK_INTERNAL_TRANSACTIONS_ENABLED -ENV SHRINK_INTERNAL_TRANSACTIONS_ENABLED=${SHRINK_INTERNAL_TRANSACTIONS_ENABLED} +RUN apk --no-cache --update add \ + alpine-sdk gmp-dev automake libtool inotify-tools autoconf python3 file gcompat libstdc++ curl ca-certificates git make # Cache elixir deps -ADD mix.exs mix.lock ./ -ADD apps/block_scout_web/mix.exs ./apps/block_scout_web/ -ADD apps/explorer/mix.exs ./apps/explorer/ -ADD apps/ethereum_jsonrpc/mix.exs ./apps/ethereum_jsonrpc/ -ADD apps/indexer/mix.exs ./apps/indexer/ +COPY mix.exs mix.lock ./ +COPY apps/block_scout_web/mix.exs ./apps/block_scout_web/ +COPY apps/explorer/mix.exs ./apps/explorer/ +COPY apps/ethereum_jsonrpc/mix.exs ./apps/ethereum_jsonrpc/ +COPY apps/indexer/mix.exs ./apps/indexer/ +ENV MIX_ENV="prod" ENV MIX_HOME=/opt/mix RUN mix local.hex --force -RUN mix do deps.get, local.rebar --force, deps.compile +RUN mix do deps.get, local.rebar --force, deps.compile --skip-umbrella-children -ADD apps ./apps -ADD config ./config -ADD rel ./rel -ADD *.exs ./ +COPY config ./config +COPY rel ./rel +COPY apps ./apps -RUN apk add --update nodejs npm +############################################################## +FROM builder-deps AS builder-ui -# Run backend compilation and install latest npm -RUN mix compile && npm install npm@latest +RUN apk --no-cache --update add nodejs npm && \ + npm install npm@latest # Add blockscout npm deps RUN cd apps/block_scout_web/assets/ && \ npm install && \ npm run deploy && \ cd /app/apps/explorer/ && \ - npm install && \ - apk update && \ - apk del --force-broken-world alpine-sdk gmp-dev automake libtool inotify-tools autoconf python3 - - -RUN apk add --update git make + npm install RUN mix phx.digest -RUN mkdir -p /opt/release \ - && mix release blockscout \ - && mv _build/${MIX_ENV}/rel/blockscout /opt/release - ############################################################## -FROM hexpm/elixir:1.17.3-erlang-27.1-alpine-3.20.3 +FROM builder-ui AS builder -ARG RELEASE_VERSION -ENV RELEASE_VERSION=${RELEASE_VERSION} +ENV DISABLE_WEBAPP=false +ARG ADMIN_PANEL_ENABLED +ENV ADMIN_PANEL_ENABLED=${ADMIN_PANEL_ENABLED} +ARG DISABLE_INDEXER +ENV DISABLE_INDEXER=${DISABLE_INDEXER} +ARG DISABLE_API +ENV DISABLE_API=${DISABLE_API} +ARG API_V1_READ_METHODS_DISABLED +ENV API_V1_READ_METHODS_DISABLED=${API_V1_READ_METHODS_DISABLED} +ARG API_V1_WRITE_METHODS_DISABLED +ENV API_V1_WRITE_METHODS_DISABLED=${API_V1_WRITE_METHODS_DISABLED} ARG CHAIN_TYPE ENV CHAIN_TYPE=${CHAIN_TYPE} ARG BRIDGED_TOKENS_ENABLED ENV BRIDGED_TOKENS_ENABLED=${BRIDGED_TOKENS_ENABLED} +ARG MUD_INDEXER_ENABLED +ENV MUD_INDEXER_ENABLED=${MUD_INDEXER_ENABLED} ARG SHRINK_INTERNAL_TRANSACTIONS_ENABLED ENV SHRINK_INTERNAL_TRANSACTIONS_ENABLED=${SHRINK_INTERNAL_TRANSACTIONS_ENABLED} -ARG BLOCKSCOUT_VERSION -ENV BLOCKSCOUT_VERSION=${BLOCKSCOUT_VERSION} +ARG API_GRAPHQL_MAX_COMPLEXITY +ENV API_GRAPHQL_MAX_COMPLEXITY=${API_GRAPHQL_MAX_COMPLEXITY} + +# Run backend compilation +RUN mix compile + +RUN mkdir -p /opt/release && \ + mix release blockscout && \ + mv _build/${MIX_ENV}/rel/blockscout /opt/release + +############################################################## +FROM hexpm/elixir:1.17.3-erlang-27.1-alpine-3.20.3 + +WORKDIR /app + ARG BLOCKSCOUT_USER=blockscout ARG BLOCKSCOUT_GROUP=blockscout ARG BLOCKSCOUT_UID=10001 @@ -91,7 +82,32 @@ RUN apk --no-cache --update add jq curl && \ addgroup --system --gid ${BLOCKSCOUT_GID} ${BLOCKSCOUT_GROUP} && \ adduser --system --uid ${BLOCKSCOUT_UID} --ingroup ${BLOCKSCOUT_GROUP} --disabled-password ${BLOCKSCOUT_USER} -WORKDIR /app +ENV DISABLE_WEBAPP=false +ARG ADMIN_PANEL_ENABLED +ENV ADMIN_PANEL_ENABLED=${ADMIN_PANEL_ENABLED} +ARG DISABLE_INDEXER +ENV DISABLE_INDEXER=${DISABLE_INDEXER} +ARG DISABLE_API +ENV DISABLE_API=${DISABLE_API} +ARG API_V1_READ_METHODS_DISABLED +ENV API_V1_READ_METHODS_DISABLED=${API_V1_READ_METHODS_DISABLED} +ARG API_V1_WRITE_METHODS_DISABLED +ENV API_V1_WRITE_METHODS_DISABLED=${API_V1_WRITE_METHODS_DISABLED} +ARG CHAIN_TYPE +ENV CHAIN_TYPE=${CHAIN_TYPE} +ARG BRIDGED_TOKENS_ENABLED +ENV BRIDGED_TOKENS_ENABLED=${BRIDGED_TOKENS_ENABLED} +ARG MUD_INDEXER_ENABLED +ENV MUD_INDEXER_ENABLED=${MUD_INDEXER_ENABLED} +ARG SHRINK_INTERNAL_TRANSACTIONS_ENABLED +ENV SHRINK_INTERNAL_TRANSACTIONS_ENABLED=${SHRINK_INTERNAL_TRANSACTIONS_ENABLED} +ARG API_GRAPHQL_MAX_COMPLEXITY +ENV API_GRAPHQL_MAX_COMPLEXITY=${API_GRAPHQL_MAX_COMPLEXITY} + +ARG RELEASE_VERSION +ENV RELEASE_VERSION=${RELEASE_VERSION} +ARG BLOCKSCOUT_VERSION +ENV BLOCKSCOUT_VERSION=${BLOCKSCOUT_VERSION} COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /opt/release/blockscout . COPY --from=builder --chown=${BLOCKSCOUT_USER}:${BLOCKSCOUT_GROUP} /app/apps/explorer/node_modules ./node_modules From 3fe9d9f1582b939add4474cec20d7f361df9eaa3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:17:00 +0400 Subject: [PATCH 355/363] chore(deps): bump ecto from 3.12.4 to 3.12.5 (#11323) Bumps [ecto](https://github.com/elixir-ecto/ecto) from 3.12.4 to 3.12.5. - [Release notes](https://github.com/elixir-ecto/ecto/releases) - [Changelog](https://github.com/elixir-ecto/ecto/blob/master/CHANGELOG.md) - [Commits](https://github.com/elixir-ecto/ecto/commits) --- updated-dependencies: - dependency-name: ecto dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index c3f365591fa9..cd8003e73624 100644 --- a/mix.lock +++ b/mix.lock @@ -39,7 +39,7 @@ "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, "digital_token": {:hex, :digital_token, "1.0.0", "454a4444061943f7349a51ef74b7fb1ebd19e6a94f43ef711f7dae88c09347df", [:mix], [{:cldr_utils, "~> 2.17", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "8ed6f5a8c2fa7b07147b9963db506a1b4c7475d9afca6492136535b064c9e9e6"}, "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, - "ecto": {:hex, :ecto, "3.12.4", "267c94d9f2969e6acc4dd5e3e3af5b05cdae89a4d549925f3008b2b7eb0b93c3", [:mix], [{:decimal, "~> 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", "ef04e4101688a67d061e1b10d7bc1fbf00d1d13c17eef08b71d070ff9188f747"}, + "ecto": {:hex, :ecto, "3.12.5", "4a312960ce612e17337e7cefcf9be45b95a3be6b36b6f94dfb3d8c361d631866", [:mix], [{:decimal, "~> 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", "6eb18e80bef8bb57e17f5a7f068a1719fbda384d40fc37acb8eb8aeca493b6ea"}, "ecto_sql": {:hex, :ecto_sql, "3.12.1", "c0d0d60e85d9ff4631f12bafa454bc392ce8b9ec83531a412c12a0d415a3a4d0", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 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", "aff5b958a899762c5f09028c847569f7dfb9cc9d63bdb8133bff8a5546de6bf5"}, "elixir_make": {:hex, :elixir_make, "0.8.4", "4960a03ce79081dee8fe119d80ad372c4e7badb84c493cc75983f9d3bc8bde0f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "6e7f1d619b5f61dfabd0a20aa268e575572b542ac31723293a4c1a567d5ef040"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, From 50edb2eb9f19b7ca28ff963ee815435d7da0b6fa Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Tue, 3 Dec 2024 11:21:08 +0300 Subject: [PATCH 356/363] feat: add CSV export of epoch transactions for address (#11195) * feat: add CSV export of epoch transactions for address * fix: pagination in election rewards for delegated payments * fix: block fetcher test * chore: remove N/A columns from CSV export table * fix: ecto errors bacause of `select_merge: block_number` * fix: avoid scientific notation * fix: correct usage of sigils * chore: process review comments * refactor: remove unnecessary import --- .../address_transaction_controller.ex | 8 ++ .../controllers/api/v2/block_controller.ex | 2 +- .../lib/block_scout_web/routers/api_router.ex | 4 + .../explorer/chain/celo/election_reward.ex | 62 ++++++++++--- .../lib/explorer/chain/celo/reader.ex | 27 +++--- .../address_election_rewards_csv_exporter.ex | 87 +++++++++++++++++++ .../test/indexer/block/fetcher_test.exs | 10 ++- 7 files changed, 168 insertions(+), 32 deletions(-) create mode 100644 apps/explorer/lib/explorer/chain/csv_export/celo/address_election_rewards_csv_exporter.ex diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex index 7317039553e7..9f112f1ec32b 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex @@ -22,6 +22,9 @@ defmodule BlockScoutWeb.AddressTransactionController do AddressTransactionCsvExporter } + alias Explorer.Chain.CSVExport.Celo.AddressElectionRewardsCsvExporter, + as: CeloAddressElectionRewardsCsvExporter + alias Explorer.Chain.{DenormalizationHelper, Transaction, Wei} alias Indexer.Fetcher.OnDemand.CoinBalance, as: CoinBalanceOnDemand @@ -223,4 +226,9 @@ defmodule BlockScoutWeb.AddressTransactionController do def logs_csv(conn, params) do items_csv(conn, params, AddressLogCsvExporter) end + + @spec celo_election_rewards_csv(Conn.t(), map()) :: Conn.t() + def celo_election_rewards_csv(conn, params) do + items_csv(conn, params, CeloAddressElectionRewardsCsvExporter) + end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex index d7a3164b476e..f0cb82f8d542 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex @@ -479,7 +479,7 @@ defmodule BlockScoutWeb.API.V2.BlockController do defp celo_reward_type_to_atom(reward_type_string) do reward_type_string - |> CeloElectionReward.type_from_string() + |> CeloElectionReward.type_from_url_string() |> case do {:ok, type} -> {:ok, type} :error -> {:error, {:invalid, :celo_election_reward_type}} diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index 80038e0b103a..5f0c3e1c9eb0 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -463,6 +463,10 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/logs-csv", AddressTransactionController, :logs_csv) + if Application.compile_env(:explorer, :chain_type) == :celo do + get("/celo-election-rewards-csv", AddressTransactionController, :celo_election_rewards_csv) + end + scope "/health" do get("/", HealthController, :health) get("/liveness", HealthController, :liveness) diff --git a/apps/explorer/lib/explorer/chain/celo/election_reward.ex b/apps/explorer/lib/explorer/chain/celo/election_reward.ex index e45d563f2b35..6b69d02e2b37 100644 --- a/apps/explorer/lib/explorer/chain/celo/election_reward.ex +++ b/apps/explorer/lib/explorer/chain/celo/election_reward.ex @@ -39,13 +39,20 @@ defmodule Explorer.Chain.Celo.ElectionReward do @type type :: :voter | :validator | :group | :delegated_payment @types_enum ~w(voter validator group delegated_payment)a - @reward_type_string_to_atom %{ + @reward_type_url_string_to_atom %{ "voter" => :voter, "validator" => :validator, "group" => :group, "delegated-payment" => :delegated_payment } + @reward_type_string_to_atom %{ + "voter" => :voter, + "validator" => :validator, + "group" => :group, + "delegated_payment" => :delegated_payment + } + @reward_type_atom_to_token_atom %{ :voter => :celo_token, :validator => :usd_token, @@ -113,12 +120,6 @@ defmodule Explorer.Chain.Celo.ElectionReward do |> foreign_key_constraint(:block_hash) |> foreign_key_constraint(:account_address_hash) |> foreign_key_constraint(:associated_account_address_hash) - - # todo: do I need to set this unique constraint here? or it is redundant? - # |> unique_constraint( - # [:block_hash, :type, :account_address_hash, :associated_account_address_hash], - # name: :celo_election_rewards_pkey - # ) end @doc """ @@ -128,7 +129,7 @@ defmodule Explorer.Chain.Celo.ElectionReward do def types, do: @types_enum @doc """ - Converts a reward type string to its corresponding atom. + Converts a reward type url string to its corresponding atom. ## Parameters - `type_string` (`String.t()`): The string representation of the reward type. @@ -138,15 +139,15 @@ defmodule Explorer.Chain.Celo.ElectionReward do ## Examples - iex> ElectionReward.type_from_string("voter") + iex> ElectionReward.type_from_url_string("voter") {:ok, :voter} - iex> ElectionReward.type_from_string("invalid") + iex> ElectionReward.type_from_url_string("invalid") :error """ - @spec type_from_string(String.t()) :: {:ok, type} | :error - def type_from_string(type_string) do - Map.fetch(@reward_type_string_to_atom, type_string) + @spec type_from_url_string(String.t()) :: {:ok, type} | :error + def type_from_url_string(type_string) do + Map.fetch(@reward_type_url_string_to_atom, type_string) end @doc """ @@ -360,7 +361,7 @@ defmodule Explorer.Chain.Celo.ElectionReward do {amount, ""} <- Decimal.parse(amount_string), {:ok, associated_account_address_hash} <- Hash.Address.cast(associated_account_address_hash_string), - {:ok, type} <- type_from_string(type_string) do + {:ok, type} <- Map.fetch(@reward_type_string_to_atom, type_string) do [ paging_options: %{ default_paging_options() @@ -521,4 +522,37 @@ defmodule Explorer.Chain.Celo.ElectionReward do "type" => type } end + + @doc """ + Custom filter for `ElectionReward`, inspired by + `Chain.where_block_number_in_period/3`. + + TODO: Consider reusing `Chain.where_block_number_in_period/3`. This would + require storing or making `merge_select` of `block_number`. + """ + @spec where_block_number_in_period( + Ecto.Query.t(), + String.t() | integer() | nil, + String.t() | integer() | nil + ) :: Ecto.Query.t() + def where_block_number_in_period(base_query, from_block, to_block) + when is_nil(from_block) and not is_nil(to_block), + do: where(base_query, [_, block], block.number <= ^to_block) + + def where_block_number_in_period(base_query, from_block, to_block) + when not is_nil(from_block) and is_nil(to_block), + do: where(base_query, [_, block], block.number > ^from_block) + + def where_block_number_in_period(base_query, from_block, to_block) + when is_nil(from_block) and is_nil(to_block), + do: base_query + + def where_block_number_in_period(base_query, from_block, to_block), + do: + where( + base_query, + [_, block], + block.number > ^from_block and + block.number <= ^to_block + ) end diff --git a/apps/explorer/lib/explorer/chain/celo/reader.ex b/apps/explorer/lib/explorer/chain/celo/reader.ex index ac49b8787b4a..8a76aed80e23 100644 --- a/apps/explorer/lib/explorer/chain/celo/reader.ex +++ b/apps/explorer/lib/explorer/chain/celo/reader.ex @@ -5,21 +5,14 @@ defmodule Explorer.Chain.Celo.Reader do import Ecto.Query, only: [limit: 2] - import Explorer.Chain, - only: [ - select_repo: 1, - join_associations: 2, - default_paging_options: 0, - max_consensus_block_number: 1 - ] - + alias Explorer.Chain alias Explorer.Chain.Block alias Explorer.Chain.Cache.{Blocks, CeloCoreContracts} alias Explorer.Chain.Celo.{ElectionReward, Helper} alias Explorer.Chain.{Hash, Token, Wei} @election_reward_types ElectionReward.types() - @default_paging_options default_paging_options() + @default_paging_options Chain.default_paging_options() @doc """ Retrieves election rewards associated with a given address hash. @@ -50,13 +43,17 @@ defmodule Explorer.Chain.Celo.Reader do necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) paging_options = Keyword.get(options, :paging_options, @default_paging_options) + from_block = Chain.from_block(options) + to_block = Chain.to_block(options) + address_hash |> ElectionReward.address_hash_to_ordered_rewards_query() + |> ElectionReward.where_block_number_in_period(from_block, to_block) |> ElectionReward.join_token() |> ElectionReward.paginate(paging_options) |> limit(^paging_options.page_size) - |> join_associations(necessity_by_association) - |> select_repo(options).all() + |> Chain.join_associations(necessity_by_association) + |> Chain.select_repo(options).all() end @doc """ @@ -94,8 +91,8 @@ defmodule Explorer.Chain.Celo.Reader do |> ElectionReward.block_hash_to_rewards_by_type_query(reward_type) |> ElectionReward.paginate(paging_options) |> limit(^paging_options.page_size) - |> join_associations(necessity_by_association) - |> select_repo(options).all() + |> Chain.join_associations(necessity_by_association) + |> Chain.select_repo(options).all() end @doc """ @@ -132,7 +129,7 @@ defmodule Explorer.Chain.Celo.Reader do reward_type_to_aggregated_rewards = block_hash |> ElectionReward.block_hash_to_aggregated_rewards_by_type_query() - |> select_repo(options).all() + |> Chain.select_repo(options).all() |> Map.new(fn {type, total, count} -> {type, %{total: total, count: count}} end) @@ -204,7 +201,7 @@ defmodule Explorer.Chain.Celo.Reader do |> Blocks.atomic_take_enough() |> case do [%Block{number: number}] -> {:ok, number} - nil -> max_consensus_block_number(options) + nil -> Chain.max_consensus_block_number(options) end |> case do {:ok, number} -> number diff --git a/apps/explorer/lib/explorer/chain/csv_export/celo/address_election_rewards_csv_exporter.ex b/apps/explorer/lib/explorer/chain/csv_export/celo/address_election_rewards_csv_exporter.ex new file mode 100644 index 000000000000..cd596132ae89 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/csv_export/celo/address_election_rewards_csv_exporter.ex @@ -0,0 +1,87 @@ +defmodule Explorer.Chain.CSVExport.Celo.AddressElectionRewardsCsvExporter do + @moduledoc """ + Exports Celo election rewards to a csv file. + """ + import Explorer.Chain.Celo.Helper, + only: [ + block_number_to_epoch_number: 1 + ] + + alias Explorer.Chain.Celo.Reader + alias Explorer.Chain.CSVExport.Helper + alias Explorer.Chain.{Hash, Wei} + + @spec export(Hash.Address.t(), String.t() | nil, String.t() | nil, any(), any()) :: Enumerable.t() + def export(address_hash, from_period, to_period, _filter_type, _filter_value) do + {from_block, to_block} = Helper.block_from_period(from_period, to_period) + + options = [ + paging_options: Helper.paging_options(), + from_block: from_block, + to_block: to_block + ] + + address_hash + |> Reader.address_hash_to_election_rewards(options) + |> to_csv_format() + |> Helper.dump_to_stream() + end + + @spec to_csv_format(Enumerable.t()) :: Enumerable.t() + defp to_csv_format(election_rewards) do + column_names = [ + "EpochNumber", + "BlockNumber", + "TimestampUTC", + "EpochTxType", + "ValidatorAddress", + "ValidatorGroupAddress", + "ToAddress", + "Type", + "Value", + "ValueInWei", + "TokenSymbol", + "TokenContractAddress" + ] + + reward_type_to_human_readable = %{ + voter: "Voter Rewards", + validator: "Validator Rewards", + group: "Validator Group Rewards", + delegated_payment: "Delegated Validator Rewards" + } + + rows = + election_rewards + |> Stream.map(fn reward -> + [ + # EpochNumber + reward.block.number |> block_number_to_epoch_number(), + # BlockNumber + reward.block.number, + # TimestampUTC + reward.block.timestamp, + # EpochTxType + Map.get(reward_type_to_human_readable, reward.type, "N/A"), + # ValidatorAddress + (reward.type in ~w(group delegated_payment)a && reward.associated_account_address_hash) || "N/A", + # ValidatorGroupAddress + (reward.type in ~w(validator voter)a && reward.associated_account_address_hash) || "N/A", + # ToAddress + reward.account_address_hash, + # Type + "IN", + # Value + reward.amount |> Wei.to(:ether) |> Decimal.to_string(:normal), + # ValueInWei + reward.amount |> Wei.to(:wei) |> Decimal.to_string(:normal), + # TokenSymbol + reward.token.symbol, + # TokenContractAddress + reward.token.contract_address_hash + ] + end) + + Stream.concat([column_names], rows) + end +end diff --git a/apps/indexer/test/indexer/block/fetcher_test.exs b/apps/indexer/test/indexer/block/fetcher_test.exs index 023affce464a..6963eff33064 100644 --- a/apps/indexer/test/indexer/block/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/fetcher_test.exs @@ -952,7 +952,7 @@ defmodule Indexer.Block.FetcherTest do end) # async requests need to be grouped in one expect because the order is non-deterministic while multiple expect # calls on the same name/arity are used in order - |> expect(:json_rpc, 10, fn json, _options -> + |> expect(:json_rpc, 5, fn json, _options -> case json do [ %{ @@ -1148,7 +1148,13 @@ defmodule Indexer.Block.FetcherTest do errors: [] }} = Fetcher.fetch_and_import_range(block_fetcher, block_number..block_number) - wait_for_tasks(InternalTransaction) + configuration = Application.get_env(:indexer, Indexer.Fetcher.InternalTransaction.Supervisor) + Application.put_env(:indexer, Indexer.Fetcher.InternalTransaction.Supervisor, disabled?: false) + + on_exit(fn -> + Application.put_env(:indexer, Indexer.Fetcher.InternalTransaction.Supervisor, configuration) + end) + wait_for_tasks(CoinBalanceCatchup) assert Repo.aggregate(Chain.Block, :count, :hash) == 1 From 8ddb85beb5ee8ef6c5344ee0da662e55330298f9 Mon Sep 17 00:00:00 2001 From: Fedor Ivanov Date: Tue, 3 Dec 2024 13:32:43 +0300 Subject: [PATCH 357/363] fix: handle entries with not specified `retries_count` (#11206) --- .../lib/indexer/fetcher/token_balance.ex | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/apps/indexer/lib/indexer/fetcher/token_balance.ex b/apps/indexer/lib/indexer/fetcher/token_balance.ex index 5bfd3deaffaa..8b2692707892 100644 --- a/apps/indexer/lib/indexer/fetcher/token_balance.ex +++ b/apps/indexer/lib/indexer/fetcher/token_balance.ex @@ -259,14 +259,15 @@ defmodule Indexer.Fetcher.TokenBalance do end end - defp entry(%{ - token_contract_address_hash: token_contract_address_hash, - address_hash: address_hash, - block_number: block_number, - token_type: token_type, - token_id: token_id, - retries_count: retries_count - }) do + defp entry( + %{ + token_contract_address_hash: token_contract_address_hash, + address_hash: address_hash, + block_number: block_number, + token_type: token_type, + token_id: token_id + } = params + ) do token_id_int = case token_id do %Decimal{} -> Decimal.to_integer(token_id) @@ -274,7 +275,14 @@ defmodule Indexer.Fetcher.TokenBalance do _ -> token_id end - {address_hash.bytes, token_contract_address_hash.bytes, block_number, token_type, token_id_int, retries_count || 0} + { + address_hash.bytes, + token_contract_address_hash.bytes, + block_number, + token_type, + token_id_int, + Map.get(params, :retries_count) || 0 + } end defp format_params( From 01792faf7b2db55f3f2ab7c6698956eddfb36501 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Wed, 4 Dec 2024 13:41:09 +0400 Subject: [PATCH 358/363] fix: Fix metrics modules warnings (#11340) --- apps/block_scout_web/lib/block_scout_web/application.ex | 7 ++----- apps/explorer/lib/explorer/chain/metrics/queries.ex | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/application.ex b/apps/block_scout_web/lib/block_scout_web/application.ex index b96ea10c6fc3..dc9f6dad7b96 100644 --- a/apps/block_scout_web/lib/block_scout_web/application.ex +++ b/apps/block_scout_web/lib/block_scout_web/application.ex @@ -6,16 +6,12 @@ defmodule BlockScoutWeb.Application do use Application alias BlockScoutWeb.Endpoint - alias BlockScoutWeb.Prometheus.Exporter, as: PrometheusExporter - alias BlockScoutWeb.Prometheus.PublicExporter, as: PrometheusPublicExporter def start(_type, _args) do base_children = [Supervisor.child_spec(Endpoint, [])] api_children = setup_and_define_children() all_children = base_children ++ api_children opts = [strategy: :one_for_one, name: BlockScoutWeb.Supervisor, max_restarts: 1_000] - PrometheusExporter.setup() - PrometheusPublicExporter.setup() Supervisor.start_link(all_children, opts) end @@ -32,13 +28,14 @@ defmodule BlockScoutWeb.Application do defp setup_and_define_children do alias BlockScoutWeb.API.APILogger alias BlockScoutWeb.Counters.{BlocksIndexedCounter, InternalTransactionsIndexedCounter} - alias BlockScoutWeb.Prometheus.{Exporter, PhoenixInstrumenter} + alias BlockScoutWeb.Prometheus.{Exporter, PhoenixInstrumenter, PublicExporter} alias BlockScoutWeb.{MainPageRealtimeEventHandler, RealtimeEventHandler, SmartContractRealtimeEventHandler} alias BlockScoutWeb.Utility.EventHandlersMetrics alias Explorer.Chain.Metrics, as: ChainMetrics PhoenixInstrumenter.setup() Exporter.setup() + PublicExporter.setup() APILogger.message( "Current global API rate limit #{inspect(Application.get_env(:block_scout_web, :api_rate_limit)[:global_limit])} reqs/sec" diff --git a/apps/explorer/lib/explorer/chain/metrics/queries.ex b/apps/explorer/lib/explorer/chain/metrics/queries.ex index 496b6ea4d4a0..927f762ce428 100644 --- a/apps/explorer/lib/explorer/chain/metrics/queries.ex +++ b/apps/explorer/lib/explorer/chain/metrics/queries.ex @@ -1,6 +1,6 @@ defmodule Explorer.Chain.Metrics.Queries do @moduledoc """ - Module for DB queries to get chain metrics exposed at /metrics endpoint + Module for DB queries to get chain metrics exposed at /public-metrics endpoint """ import Ecto.Query, From 2f1c2e26008ae5a44865055a08aaaf9af6977033 Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:30:28 +0300 Subject: [PATCH 359/363] fix: Return 404 instead of 200 for nonexistent NFT (#11280) * fix: Return 404 instead of 200 for nonexistent NFT * Fix test * Rename test --- .../controllers/api/v2/token_controller.ex | 28 ++++--------------- .../api/v2/token_controller_test.exs | 16 ++--------- 2 files changed, 8 insertions(+), 36 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex index 2e72170b1454..6aef700255b7 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex @@ -195,8 +195,12 @@ defmodule BlockScoutWeb.API.V2.TokenController do {:ok, false} <- AccessHelper.restricted_access?(address_hash_string, params), {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash, @api_true)}, {:not_found, false} <- {:not_found, Chain.erc_20_token?(token)}, - {:format, {token_id, ""}} <- {:format, Integer.parse(token_id_string)} do - token_instance = token_instance_from_token_id_and_token_address(token_id, address_hash, token) + {:format, {token_id, ""}} <- {:format, Integer.parse(token_id_string)}, + {:ok, token_instance} <- Chain.nft_instance_from_token_id_and_token_address(token_id, address_hash, @api_true) do + token_instance = + token_instance + |> Chain.select_repo(@api_true).preload(owner: [:names, :smart_contract, proxy_implementations_association()]) + |> Chain.put_owner_to_token_instance(token, @api_true) conn |> put_status(200) @@ -361,26 +365,6 @@ defmodule BlockScoutWeb.API.V2.TokenController do defp put_owner(token_instances, holder_address), do: Enum.map(token_instances, fn token_instance -> %Instance{token_instance | owner: holder_address} end) - defp token_instance_from_token_id_and_token_address(token_id, address_hash, token) do - case Chain.nft_instance_from_token_id_and_token_address(token_id, address_hash, @api_true) do - {:ok, token_instance} -> - token_instance - |> Chain.select_repo(@api_true).preload(owner: [:names, :smart_contract, proxy_implementations_association()]) - |> Chain.put_owner_to_token_instance(token, @api_true) - - {:error, :not_found} -> - %Instance{ - token_id: Decimal.new(token_id), - metadata: nil, - owner: nil, - token: nil, - token_contract_address_hash: address_hash - } - |> Instance.put_is_unique(token, @api_true) - |> Chain.put_owner_to_token_instance(token, @api_true) - end - end - @spec put_token_to_instance(Instance.t(), Token.t()) :: Instance.t() defp put_token_to_instance( token_instance, diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs index 1e391497e9cb..b66848970826 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/token_controller_test.exs @@ -1050,24 +1050,12 @@ defmodule BlockScoutWeb.API.V2.TokenControllerTest do assert Address.checksum(instance.owner_address_hash) == data["owner"]["hash"] end - test "get token instance by token id which is not presented in DB", %{conn: conn} do + test "get 404 on token instance which is not presented in DB", %{conn: conn} do token = insert(:token, type: "ERC-721") request = get(conn, "/api/v2/tokens/#{token.contract_address.hash}/instances/0") - token_address = Address.checksum(token.contract_address.hash) - token_name = token.name - token_type = token.type - assert %{ - "animation_url" => nil, - "external_app_url" => nil, - "id" => "0", - "image_url" => nil, - "is_unique" => true, - "metadata" => nil, - "owner" => nil, - "token" => %{"address" => ^token_address, "name" => ^token_name, "type" => ^token_type} - } = json_response(request, 200) + assert %{"message" => "Not found"} = json_response(request, 404) end # https://github.com/blockscout/blockscout/issues/9906 From 0f461d82a40d407b4e5e81883f6551b5ef34c1aa Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:30:50 +0300 Subject: [PATCH 360/363] fix: Fix log decoding bug (#11266) * fix: Fix log decoding bug * Small optimization * Remove var --- .../views/api/v2/transaction_view_test.exs | 75 +++++++++++++++++++ apps/explorer/lib/explorer/chain/log.ex | 41 +++++----- 2 files changed, 94 insertions(+), 22 deletions(-) diff --git a/apps/block_scout_web/test/block_scout_web/views/api/v2/transaction_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/api/v2/transaction_view_test.exs index 6b42d89a5f83..a5ff0b18f6b9 100644 --- a/apps/block_scout_web/test/block_scout_web/views/api/v2/transaction_view_test.exs +++ b/apps/block_scout_web/test/block_scout_web/views/api/v2/transaction_view_test.exs @@ -79,6 +79,81 @@ defmodule BlockScoutWeb.API.V2.TransactionViewTest do ]} ] = TransactionView.decode_logs(logs, false) end + + test "properly decode logs if they have same topics" do + insert(:contract_method, + identifier: Base.decode16!("d20a68b2", case: :lower), + abi: %{ + "name" => "OptionSettled", + "type" => "event", + "inputs" => [ + %{"name" => "accountId", "type" => "uint256", "indexed" => true, "internalType" => "uint256"}, + %{"name" => "option", "type" => "address", "indexed" => false, "internalType" => "address"}, + %{"name" => "subId", "type" => "uint256", "indexed" => false, "internalType" => "uint256"}, + %{"name" => "amount", "type" => "int256", "indexed" => false, "internalType" => "int256"}, + %{"name" => "value", "type" => "int256", "indexed" => false, "internalType" => "int256"} + ], + "anonymous" => false + } + ) + + topic1_bytes = ExKeccak.hash_256("OptionSettled(uint256,address,uint256,int256,int256)") + topic1 = "0x" <> Base.encode16(topic1_bytes, case: :lower) + topic2 = "0x0000000000000000000000000000000000000000000000000000000000005d19" + + log1_data = + "0x000000000000000000000000aeb81cbe6b19ceeb0dbe0d230cffe35bb40a13a700000000000000000000000000000000000000000000045d964b80006597b700fffffffffffffffffffffffffffffffffffffffffffffffffe55aca2c2f40000ffffffffffffffffffffffffffffffffffffffffffffffe3a8289da3d7a13ef2" + + log2_data = + "0x000000000000000000000000aeb81cbe6b19ceeb0dbe0d230cffe35bb40a13a700000000000000000000000000000000000000000000045d964b80006597b700000000000000000000000000000000000000000000000000011227ebced227ae00000000000000000000000000000000000000000000001239fdf180a3d6bd85" + + transaction = insert(:transaction) + + log1 = + insert(:log, + transaction: transaction, + first_topic: topic(topic1), + second_topic: topic(topic2), + third_topic: nil, + fourth_topic: nil, + data: log1_data + ) + + log2 = + insert(:log, + transaction: transaction, + first_topic: topic(topic1), + second_topic: topic(topic2), + third_topic: nil, + fourth_topic: nil, + data: log2_data + ) + + logs = [log1, log2] + + assert [ + {:ok, "d20a68b2", + "OptionSettled(uint256 indexed accountId, address option, uint256 subId, int256 amount, int256 value)", + [ + {"accountId", "uint256", true, 23833}, + {"option", "address", false, + <<174, 184, 28, 190, 107, 25, 206, 235, 13, 190, 13, 35, 12, 255, 227, 91, 180, 10, 19, 167>>}, + {"subId", "uint256", false, 20_615_843_020_801_704_441_600}, + {"amount", "int256", false, -120_000_000_000_000_000}, + {"value", "int256", false, -522_838_470_013_113_778_446} + ]}, + {:ok, "d20a68b2", + "OptionSettled(uint256 indexed accountId, address option, uint256 subId, int256 amount, int256 value)", + [ + {"accountId", "uint256", true, 23833}, + {"option", "address", false, + <<174, 184, 28, 190, 107, 25, 206, 235, 13, 190, 13, 35, 12, 255, 227, 91, 180, 10, 19, 167>>}, + {"subId", "uint256", false, 20_615_843_020_801_704_441_600}, + {"amount", "int256", false, 77_168_037_359_396_782}, + {"value", "int256", false, 336_220_154_890_848_484_741} + ]} + ] = TransactionView.decode_logs(logs, false) + end end defp topic(topic_hex_string) do diff --git a/apps/explorer/lib/explorer/chain/log.ex b/apps/explorer/lib/explorer/chain/log.ex index 92018eb1bf74..8f344a7e1e3a 100644 --- a/apps/explorer/lib/explorer/chain/log.ex +++ b/apps/explorer/lib/explorer/chain/log.ex @@ -197,7 +197,7 @@ defmodule Explorer.Chain.Log do defp handle_method_decode_error(error, log, transaction, options, skip_sig_provider?, contracts_acc, events_acc) do case error do {:error, _reason} -> - case find_method_candidates(log, transaction, options, events_acc, skip_sig_provider?) do + case find_method_candidates(log, transaction, options, events_acc) do {{:error, :contract_not_verified, []}, events_acc} -> {decode_event_via_sig_provider(log, transaction, false, skip_sig_provider?), contracts_acc, events_acc} @@ -233,28 +233,33 @@ defmodule Explorer.Chain.Log do end end - defp find_method_candidates(log, transaction, options, events_acc, skip_sig_provider?) do + defp find_method_candidates(log, transaction, options, events_acc) do if is_nil(log.first_topic) do {{:error, :could_not_decode}, events_acc} else <> = log.first_topic.bytes - key = {method_id, log.second_topic, log.third_topic, log.fourth_topic} - if Map.has_key?(events_acc, key) do - {events_acc[key], events_acc} + if Map.has_key?(events_acc, method_id) do + {find_and_decode_in_candidates(events_acc[method_id], log, transaction), events_acc} else - result = find_method_candidates_from_db(method_id, log, transaction, options, skip_sig_provider?) - {result, Map.put(events_acc, key, result)} + {result, event_candidates} = find_method_candidates_from_db(method_id, log, transaction, options) + {result, Map.put(events_acc, method_id, event_candidates)} end end end - defp find_method_candidates_from_db(method_id, log, transaction, options, skip_sig_provider?) do - candidates_query = ContractMethod.find_contract_method_query(method_id, 3) - - candidates = - candidates_query + defp find_method_candidates_from_db(method_id, log, transaction, options) do + event_candidates = + method_id + |> ContractMethod.find_contract_method_query(3) |> Chain.select_repo(options).all() + + {find_and_decode_in_candidates(event_candidates, log, transaction), event_candidates} + end + + defp find_and_decode_in_candidates(event_candidates, log, transaction) do + result = + event_candidates |> Enum.flat_map(fn contract_method -> case find_and_decode([contract_method.abi], log, transaction.hash) do {:ok, selector, mapping} -> @@ -269,15 +274,7 @@ defmodule Explorer.Chain.Log do end) |> Enum.take(1) - {:error, :contract_not_verified, - if(candidates == [], - do: - if(skip_sig_provider?, - do: [], - else: decode_event_via_sig_provider(log, transaction, true) - ), - else: candidates - )} + {:error, :contract_not_verified, result} end @spec find_and_decode([map()], __MODULE__.t(), Hash.t()) :: @@ -351,7 +348,7 @@ defmodule Explorer.Chain.Log do log, transaction, only_candidates?, - skip_sig_provider? \\ false + skip_sig_provider? ) do with true <- SigProviderInterface.enabled?(), false <- skip_sig_provider?, From ee8f527f1e2e72947adfca22ab6738cd095f7e2c Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:34:37 +0300 Subject: [PATCH 361/363] chore: refactor compile time envs usage (#11148) * chore: refactor compile time envs usage * Add doc Co-authored-by: Alexander Kolotov * Add doc Co-Authored-By: Alexander Kolotov * Add custom credo check --------- Co-authored-by: Alexander Kolotov --- .credo.exs | 5 +- .github/workflows/config.yml | 36 +++- CONTRIBUTING.md | 21 ++- .../lib/block_scout_web/application.ex | 3 +- .../channels/address_channel.ex | 3 +- .../channels/user_socket_v2.ex | 3 +- .../account/api/v2/email_controller.ex | 3 +- .../api/rpc/contract_controller.ex | 3 +- .../controllers/api/rpc/token_controller.ex | 4 +- .../controllers/api/v2/address_controller.ex | 5 +- .../controllers/api/v2/api_key_controller.ex | 3 +- .../controllers/api/v2/block_controller.ex | 3 +- .../api/v2/main_page_controller.ex | 3 +- .../controllers/api/v2/stats_controller.ex | 3 +- .../controllers/api/v2/token_controller.ex | 6 +- .../api/v2/transaction_controller.ex | 3 +- .../api/v2/verification_controller.ex | 3 +- .../counters/blocks_indexed_counter.ex | 10 +- .../internal_transactions_indexed_counter.ex | 10 +- .../lib/block_scout_web/endpoint.ex | 14 +- .../lib/block_scout_web/etherscan.ex | 6 +- .../lib/block_scout_web/graphql/schema.ex | 5 +- .../block_scout_web/graphql/schema/types.ex | 7 +- .../lib/block_scout_web/notifier.ex | 5 +- .../lib/block_scout_web/paging_helper.ex | 4 +- .../block_scout_web/realtime_event_handler.ex | 4 +- .../lib/block_scout_web/router.ex | 17 +- .../lib/block_scout_web/routers/api_router.ex | 78 ++++---- .../routers/smart_contracts_api_v2_router.ex | 6 +- .../routers/tokens_api_v2_router.ex | 4 +- .../views/api/v2/address_view.ex | 3 +- .../views/api/v2/block_view.ex | 3 +- .../views/api/v2/filecoin_view.ex | 18 +- .../block_scout_web/views/api/v2/helper.ex | 3 +- .../views/api/v2/search_view.ex | 3 +- .../views/api/v2/smart_contract_view.ex | 3 +- .../views/api/v2/token_view.ex | 3 +- .../views/api/v2/transaction_view.ex | 3 +- .../views/api/v2/zilliqa_view.ex | 16 +- apps/block_scout_web/mix.exs | 2 +- .../lib/ethereum_jsonrpc/block.ex | 15 +- .../lib/ethereum_jsonrpc/blocks.ex | 7 +- .../lib/ethereum_jsonrpc/receipt.ex | 9 +- .../lib/ethereum_jsonrpc/receipts.ex | 3 +- .../lib/ethereum_jsonrpc/transaction.ex | 8 +- apps/ethereum_jsonrpc/mix.exs | 3 +- apps/explorer/lib/explorer/chain/address.ex | 8 +- apps/explorer/lib/explorer/chain/block.ex | 9 +- .../lib/explorer/chain/block_number_helper.ex | 3 +- .../cache/blackfort_validators_counters.ex | 18 +- .../explorer/chain/cache/contracts_counter.ex | 14 +- .../lib/explorer/chain/cache/gas_usage.ex | 4 +- .../chain/cache/new_contracts_counter.ex | 14 +- .../cache/new_verified_contracts_counter.ex | 14 +- .../cache/stability_validators_counters.ex | 18 +- .../chain/cache/verified_contracts_counter.ex | 14 +- .../explorer/chain/cache/withdrawals_sum.ex | 10 +- .../lib/explorer/chain/events/publisher.ex | 3 +- .../lib/explorer/chain/events/subscriber.ex | 3 +- .../explorer/chain/import/runner/tokens.ex | 3 +- .../chain/import/runner/transactions.ex | 3 +- .../chain/internal_transaction/call_type.ex | 11 +- .../chain/internal_transaction/type.ex | 11 +- apps/explorer/lib/explorer/chain/log.ex | 8 +- .../lib/explorer/chain/smart_contract.ex | 9 +- .../explorer/chain/smart_contract/proxy.ex | 3 +- .../proxy/models/implementation.ex | 3 +- apps/explorer/lib/explorer/chain/token.ex | 3 +- .../lib/explorer/chain/token_transfer.ex | 8 +- .../lib/explorer/chain/transaction.ex | 13 +- .../counters/address_gas_usage_counter.ex | 4 +- .../address_token_transfers_counter.ex | 4 +- .../counters/address_tokens_usd_sum.ex | 4 +- .../counters/address_transactions_counter.ex | 4 +- .../explorer/counters/addresses_counter.ex | 18 +- .../addresses_with_balance_counter.ex | 18 +- .../counters/block_burnt_fee_counter.ex | 4 +- .../counters/block_priority_fee_counter.ex | 4 +- .../counters/token_holders_counter.ex | 4 +- .../counters/token_transfers_counter.ex | 4 +- apps/explorer/mix.exs | 4 +- apps/explorer/test/support/factory.ex | 9 +- .../block/catchup/missing_ranges_collector.ex | 2 +- apps/indexer/lib/indexer/block/fetcher.ex | 3 +- .../lib/indexer/block/realtime/fetcher.ex | 5 +- .../fetcher/rollup_l1_reorg_monitor.ex | 3 +- ...in_pending_address_operations_collector.ex | 18 +- .../transform/address_coin_balances.ex | 3 +- .../lib/indexer/transform/addresses.ex | 3 +- .../indexer/transform/scroll/l1_fee_params.ex | 3 +- apps/indexer/mix.exs | 3 +- apps/utils/.gitignore | 26 +++ .../lib/credo/checks/compile_env_usage.ex | 54 ++++++ .../lib/utils/compile_time_env_helper.ex | 170 ++++++++++++++++++ apps/utils/mix.exs | 39 ++++ .../test/checks/compile_env_usage_test.exs | 38 ++++ apps/utils/test/test_helper.exs | 1 + bin/version_bump.sh | 1 + cspell.json | 2 + mix.exs | 5 +- 100 files changed, 743 insertions(+), 306 deletions(-) create mode 100644 apps/utils/.gitignore create mode 100644 apps/utils/lib/credo/checks/compile_env_usage.ex create mode 100644 apps/utils/lib/utils/compile_time_env_helper.ex create mode 100644 apps/utils/mix.exs create mode 100644 apps/utils/test/checks/compile_env_usage_test.exs create mode 100644 apps/utils/test/test_helper.exs diff --git a/.credo.exs b/.credo.exs index 09db2f0079d9..444b59323dc9 100644 --- a/.credo.exs +++ b/.credo.exs @@ -33,7 +33,7 @@ # If you create your own checks, you must specify the source files for # them here, so they can be loaded by Credo before running the analysis. # - requires: [], + requires: ["apps/utils/lib/credo/**/*.ex"], # # If you want to enforce a style guide and need a more traditional linting # experience, you can change `strict` to `true` below: @@ -140,9 +140,10 @@ {Credo.Check.Refactor.AppendSingleItem}, {Credo.Check.Refactor.VariableRebinding}, {Credo.Check.Warning.MapGetUnsafePass}, - {Credo.Check.Consistency.MultiAliasImportRequireUse} + {Credo.Check.Consistency.MultiAliasImportRequireUse}, # Custom checks can be created using `mix credo.gen.check`. + {Utils.Credo.Checks.CompileEnvUsage} # ] } diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml index 85c1efdcb249..af751b9c0976 100644 --- a/.github/workflows/config.yml +++ b/.github/workflows/config.yml @@ -21,10 +21,10 @@ on: - production-zksync - staging-l2 paths-ignore: - - 'CHANGELOG.md' - - '**/README.md' - - 'docker/*' - - 'docker-compose/*' + - "CHANGELOG.md" + - "**/README.md" + - "docker/*" + - "docker-compose/*" pull_request: types: [opened, synchronize, reopened, labeled] branches: @@ -459,6 +459,34 @@ jobs: - run: ./node_modules/.bin/jest working-directory: apps/block_scout_web/assets + test_utils: + name: Utils Tests + runs-on: ubuntu-latest + needs: build-and-cache + steps: + - uses: actions/checkout@v4 + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VERSION }} + elixir-version: ${{ env.ELIXIR_VERSION }} + hexpm-mirrors: | + https://builds.hex.pm + https://cdn.jsdelivr.net/hex + + - name: Restore Mix Deps Cache + uses: actions/cache/restore@v4 + id: deps-cache + with: + path: | + deps + _build + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash- + + - working-directory: apps/utils + run: mix test + test_nethermind_mox_ethereum_jsonrpc: strategy: fail-fast: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 321123881c5d..c5db206a67e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -74,7 +74,6 @@ When contributing to the codebase, please adhere to the following naming convent By following these conventions, we can maintain a clean and understandable codebase. - ### API V2 Naming Convention When contributing to the API v2, please adhere to the following naming conventions for response fields to ensure clarity and consistency: @@ -83,3 +82,23 @@ When contributing to the API v2, please adhere to the following naming conventio - The transaction hash should be returned as a hex string in the `transaction_hash` property. - All fields that contain the "index" suffix should be returned as numbers. +## Compile time Environment Variables + +When working with compile time environment variables in the codebase, follow these guidelines: + +- Always use the `Utils.CompileTimeEnvHelper` module instead of direct `Application.compile_env/2` calls: + +```elixir +# DO use this approach +use Utils.CompileTimeEnvHelper, + attribute_name: [:app, :test] + +# Access the value using the module attribute +@attribute_name + +# DON'T use this approach +Application.compile_env(:app, :test) # avoid direct compile_env calls +``` + +This approach provides faster compilation time and simplifies development and maintenance. + diff --git a/apps/block_scout_web/lib/block_scout_web/application.ex b/apps/block_scout_web/lib/block_scout_web/application.ex index dc9f6dad7b96..e50cc806c161 100644 --- a/apps/block_scout_web/lib/block_scout_web/application.ex +++ b/apps/block_scout_web/lib/block_scout_web/application.ex @@ -4,6 +4,7 @@ defmodule BlockScoutWeb.Application do """ use Application + use Utils.CompileTimeEnvHelper, disable_api?: [:block_scout_web, :disable_api?] alias BlockScoutWeb.Endpoint @@ -22,7 +23,7 @@ defmodule BlockScoutWeb.Application do :ok end - if Application.compile_env(:block_scout_web, :disable_api?) do + if @disable_api? do defp setup_and_define_children, do: [] else defp setup_and_define_children do diff --git a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex index 86dec8a3f9fb..424a3f068f27 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.AddressChannel do Establishes pub/sub channel for address page live updates. """ use BlockScoutWeb, :channel + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] @@ -38,7 +39,7 @@ defmodule BlockScoutWeb.AddressChannel do @burn_address_hash burn_address_hash @current_token_balances_limit 50 - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :celo -> @chain_type_transaction_associations [ :gas_token diff --git a/apps/block_scout_web/lib/block_scout_web/channels/user_socket_v2.ex b/apps/block_scout_web/lib/block_scout_web/channels/user_socket_v2.ex index 696b3b0de4a2..7047543bba42 100644 --- a/apps/block_scout_web/lib/block_scout_web/channels/user_socket_v2.ex +++ b/apps/block_scout_web/lib/block_scout_web/channels/user_socket_v2.ex @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.UserSocketV2 do Module to distinct new and old UI websocket connections """ use Phoenix.Socket + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] channel("addresses:*", BlockScoutWeb.AddressChannel) channel("blocks:*", BlockScoutWeb.BlockChannel) @@ -13,7 +14,7 @@ defmodule BlockScoutWeb.UserSocketV2 do channel("token_instances:*", BlockScoutWeb.TokenInstanceChannel) channel("zkevm_batches:*", BlockScoutWeb.PolygonZkevmConfirmedBatchChannel) - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :arbitrum -> channel("arbitrum:*", BlockScoutWeb.ArbitrumChannel) # todo: change `optimism*"` to `optimism:*` after the deprecated `optimism_deposits:new_deposits` topic is removed :optimism -> channel("optimism*", BlockScoutWeb.OptimismChannel) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/email_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/email_controller.ex index 829b914220f3..4bec5561cece 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/email_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v2/email_controller.ex @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.Account.API.V2.EmailController do use BlockScoutWeb, :controller + use Utils.CompileTimeEnvHelper, invalid_session_key: [:block_scout_web, :invalid_session_key] import BlockScoutWeb.Account.AuthController, only: [current_user: 1] @@ -11,8 +12,6 @@ defmodule BlockScoutWeb.Account.API.V2.EmailController do require Logger - @invalid_session_key Application.compile_env(:block_scout_web, :invalid_session_key) - action_fallback(BlockScoutWeb.Account.API.V2.FallbackController) plug(:fetch_cookies, signed: [@invalid_session_key]) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex index a2629b1344d6..ae1aa0a65165 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.API.RPC.ContractController do use BlockScoutWeb, :controller + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] require Logger @@ -17,7 +18,7 @@ defmodule BlockScoutWeb.API.RPC.ContractController do alias Explorer.ThirdPartyIntegrations.Sourcify import BlockScoutWeb.API.V2.AddressController, only: [validate_address: 2, validate_address: 3] - if Application.compile_env(:explorer, :chain_type) == :zksync do + if @chain_type == :zksync do @optimization_runs "0" else @optimization_runs 200 diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/token_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/token_controller.ex index 51e1aff18221..e6423b577b9c 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/token_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/token_controller.ex @@ -1,9 +1,9 @@ defmodule BlockScoutWeb.API.RPC.TokenController do use BlockScoutWeb, :controller + use Utils.CompileTimeEnvHelper, bridged_token_enabled: [:explorer, [Explorer.Chain.BridgedToken, :enabled]] alias BlockScoutWeb.API.RPC.Helper alias Explorer.{Chain, PagingOptions} - alias Explorer.Chain.BridgedToken def gettoken(conn, params) do with {:contractaddress_param, {:ok, contractaddress_param}} <- fetch_contractaddress(params), @@ -51,7 +51,7 @@ defmodule BlockScoutWeb.API.RPC.TokenController do end end - if Application.compile_env(:explorer, BridgedToken)[:enabled] do + if @bridged_token_enabled do @api_true [api?: true] def bridgedtokenlist(conn, params) do import BlockScoutWeb.PagingHelper, diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index bc09024c9810..db7353cf8b02 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.API.V2.AddressController do use BlockScoutWeb, :controller + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] import BlockScoutWeb.Chain, only: [ @@ -39,7 +40,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do alias Indexer.Fetcher.OnDemand.ContractCode, as: ContractCodeOnDemand alias Indexer.Fetcher.OnDemand.TokenBalance, as: TokenBalanceOnDemand - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :celo -> @chain_type_transaction_necessity_by_association %{ :gas_token => :optional @@ -83,7 +84,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do api?: true ] - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :filecoin -> @contract_address_preloads [ :smart_contract, diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/api_key_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/api_key_controller.ex index 2ca19379af2d..995d063863a8 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/api_key_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/api_key_controller.ex @@ -1,10 +1,9 @@ defmodule BlockScoutWeb.API.V2.APIKeyController do use BlockScoutWeb, :controller + use Utils.CompileTimeEnvHelper, api_v2_temp_token_key: [:block_scout_web, :api_v2_temp_token_key] alias BlockScoutWeb.{AccessHelper, CaptchaHelper} - @api_v2_temp_token_key Application.compile_env(:block_scout_web, :api_v2_temp_token_key) - action_fallback(BlockScoutWeb.API.V2.FallbackController) plug(:fetch_cookies, signed: [@api_v2_temp_token_key]) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex index f0cb82f8d542..2fb34766ab51 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.API.V2.BlockController do use BlockScoutWeb, :controller + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] import BlockScoutWeb.Chain, only: [ @@ -38,7 +39,7 @@ defmodule BlockScoutWeb.API.V2.BlockController do alias Explorer.Chain.Optimism.TransactionBatch, as: OptimismTransactionBatch alias Explorer.Chain.Scroll.Reader, as: ScrollReader - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :ethereum -> @chain_type_transaction_necessity_by_association %{ :beacon_blob_transaction => :optional diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex index f82b4f476e64..74cf80675e82 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.API.V2.MainPageController do use BlockScoutWeb, :controller + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias Explorer.{Chain, PagingOptions} alias BlockScoutWeb.API.V2.{BlockView, OptimismView, TransactionView} @@ -10,7 +11,7 @@ defmodule BlockScoutWeb.API.V2.MainPageController do import Explorer.MicroserviceInterfaces.BENS, only: [maybe_preload_ens: 1] import Explorer.MicroserviceInterfaces.Metadata, only: [maybe_preload_metadata: 1] - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :celo -> @chain_type_transaction_necessity_by_association %{ :gas_token => :optional diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex index 84d42690f934..9a00ea3b7065 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.API.V2.StatsController do use Phoenix.Controller + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias BlockScoutWeb.API.V2.Helper alias BlockScoutWeb.Chain.MarketHistoryChartController @@ -177,7 +178,7 @@ defmodule BlockScoutWeb.API.V2.StatsController do end end - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :rsk -> defp add_chain_type_fields(response) do alias Explorer.Chain.Cache.RootstockLockedBTC diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex index 6aef700255b7..42b17d3e76a0 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex @@ -1,10 +1,10 @@ defmodule BlockScoutWeb.API.V2.TokenController do - alias Explorer.PagingOptions use BlockScoutWeb, :controller + use Utils.CompileTimeEnvHelper, bridged_token_enabled: [:explorer, [Explorer.Chain.BridgedToken, :enabled]] alias BlockScoutWeb.{AccessHelper, CaptchaHelper} alias BlockScoutWeb.API.V2.{AddressView, TransactionView} - alias Explorer.{Chain, Helper} + alias Explorer.{Chain, Helper, PagingOptions} alias Explorer.Chain.{Address, BridgedToken, Token, Token.Instance} alias Indexer.Fetcher.OnDemand.TokenInstanceMetadataRefetch, as: TokenInstanceMetadataRefetchOnDemand alias Indexer.Fetcher.OnDemand.TokenTotalSupply, as: TokenTotalSupplyOnDemand @@ -46,7 +46,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do end end - if Application.compile_env(:explorer, Explorer.Chain.BridgedToken)[:enabled] do + if @bridged_token_enabled do defp token_response(conn, token, address_hash) do if token.bridged do bridged_token = diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex index 4c648221fffd..668cf6fa1945 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.API.V2.TransactionController do use BlockScoutWeb, :controller + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] import BlockScoutWeb.Account.AuthController, only: [current_user: 1] alias BlockScoutWeb.API.V2.BlobView @@ -51,7 +52,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do action_fallback(BlockScoutWeb.API.V2.FallbackController) - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :ethereum -> @chain_type_transaction_necessity_by_association %{ :beacon_blob_transaction => :optional diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex index 5450b97173e4..89e0c722a5c1 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/verification_controller.ex @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.API.V2.VerificationController do use BlockScoutWeb, :controller + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] import Explorer.SmartContract.Solidity.Verifier, only: [parse_boolean: 1] @@ -21,7 +22,7 @@ defmodule BlockScoutWeb.API.V2.VerificationController do @sc_verification_started "Smart-contract verification started" @zk_optimization_modes ["0", "1", "2", "3", "s", "z"] - if Application.compile_env(:explorer, :chain_type) == :zksync do + if @chain_type == :zksync do @optimization_runs "0" else @optimization_runs 200 diff --git a/apps/block_scout_web/lib/block_scout_web/counters/blocks_indexed_counter.ex b/apps/block_scout_web/lib/block_scout_web/counters/blocks_indexed_counter.ex index d5e7b64ab487..3ca57e6b87aa 100644 --- a/apps/block_scout_web/lib/block_scout_web/counters/blocks_indexed_counter.ex +++ b/apps/block_scout_web/lib/block_scout_web/counters/blocks_indexed_counter.ex @@ -6,15 +6,13 @@ defmodule BlockScoutWeb.Counters.BlocksIndexedCounter do """ use GenServer - - alias BlockScoutWeb.Notifier - alias Explorer.Chain - # It is undesirable to automatically start the counter in all environments. # Consider the test environment: if it initiates but does not finish before a # test ends, that test will fail. - config = Application.compile_env(:block_scout_web, __MODULE__) - @enabled Keyword.get(config, :enabled) + use Utils.CompileTimeEnvHelper, enabled: [:block_scout_web, [__MODULE__, :enabled]] + + alias BlockScoutWeb.Notifier + alias Explorer.Chain @doc """ Starts a process to periodically update the % of blocks indexed. diff --git a/apps/block_scout_web/lib/block_scout_web/counters/internal_transactions_indexed_counter.ex b/apps/block_scout_web/lib/block_scout_web/counters/internal_transactions_indexed_counter.ex index cb9d5937875f..f0c7df3ebd25 100644 --- a/apps/block_scout_web/lib/block_scout_web/counters/internal_transactions_indexed_counter.ex +++ b/apps/block_scout_web/lib/block_scout_web/counters/internal_transactions_indexed_counter.ex @@ -6,15 +6,13 @@ defmodule BlockScoutWeb.Counters.InternalTransactionsIndexedCounter do """ use GenServer - - alias BlockScoutWeb.Notifier - alias Explorer.Chain - # It is undesirable to automatically start the counter in all environments. # Consider the test environment: if it initiates but does not finish before a # test ends, that test will fail. - config = Application.compile_env(:block_scout_web, __MODULE__) - @enabled Keyword.get(config, :enabled) + use Utils.CompileTimeEnvHelper, enabled: [:block_scout_web, [__MODULE__, :enabled]] + + alias BlockScoutWeb.Notifier + alias Explorer.Chain @doc """ Starts a process to periodically update the % of internal transactions indexed. diff --git a/apps/block_scout_web/lib/block_scout_web/endpoint.ex b/apps/block_scout_web/lib/block_scout_web/endpoint.ex index dfb2f2b84434..7d642b6f6a20 100644 --- a/apps/block_scout_web/lib/block_scout_web/endpoint.ex +++ b/apps/block_scout_web/lib/block_scout_web/endpoint.ex @@ -2,11 +2,17 @@ defmodule BlockScoutWeb.Endpoint do use Phoenix.Endpoint, otp_app: :block_scout_web use Absinthe.Phoenix.Endpoint - if Application.compile_env(:block_scout_web, :sql_sandbox) do + use Utils.CompileTimeEnvHelper, + disable_api?: [:block_scout_web, :disable_api?], + sql_sandbox: [:block_scout_web, :sql_sandbox], + cookie_domain: [:block_scout_web, :cookie_domain], + session_cookie_ttl: [:block_scout_web, :session_cookie_ttl] + + if @sql_sandbox do plug(Phoenix.Ecto.SQL.Sandbox, repo: Explorer.Repo) end - if Application.compile_env(:block_scout_web, :disable_api?) do + if @disable_api? do plug(BlockScoutWeb.HealthRouter) else socket("/socket", BlockScoutWeb.UserSocket, websocket: [timeout: 45_000]) @@ -60,8 +66,8 @@ defmodule BlockScoutWeb.Endpoint do signing_salt: "iC2ksJHS", same_site: "Lax", http_only: false, - domain: Application.compile_env(:block_scout_web, :cookie_domain), - max_age: Application.compile_env(:block_scout_web, :session_cookie_ttl) + domain: @cookie_domain, + max_age: @session_cookie_ttl ) use SpandexPhoenix diff --git a/apps/block_scout_web/lib/block_scout_web/etherscan.ex b/apps/block_scout_web/lib/block_scout_web/etherscan.ex index 472bf82780d7..06367f6e8666 100644 --- a/apps/block_scout_web/lib/block_scout_web/etherscan.ex +++ b/apps/block_scout_web/lib/block_scout_web/etherscan.ex @@ -2,7 +2,7 @@ defmodule BlockScoutWeb.Etherscan do @moduledoc """ Documentation data for Etherscan-compatible API. """ - alias Explorer.Chain.BridgedToken + use Utils.CompileTimeEnvHelper, bridged_token_enabled: [:block_scout_web, [Explorer.Chain.BridgedToken, :enabled]] @account_balance_example_value %{ "status" => "1", @@ -2010,7 +2010,7 @@ defmodule BlockScoutWeb.Etherscan do ] } - if Application.compile_env(:explorer, BridgedToken)[:enabled] do + if @bridged_token_enabled do @success_status_type %{ type: "status", enum: ~s(["1"]), @@ -3064,7 +3064,7 @@ defmodule BlockScoutWeb.Etherscan do @token_gettokenholders_action ] - @token_actions if Application.compile_env(:explorer, BridgedToken)[:enabled], + @token_actions if @bridged_token_enabled, do: [@token_bridgedtokenlist_action, @base_token_actions], else: @base_token_actions diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/schema.ex b/apps/block_scout_web/lib/block_scout_web/graphql/schema.ex index 2a01ac5b8f29..b579a9dbc58f 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/schema.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/schema.ex @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.GraphQL.Schema do use Absinthe.Schema use Absinthe.Relay.Schema, :modern + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias Absinthe.Middleware.Dataloader, as: AbsintheDataloaderMiddleware alias Absinthe.Plugin, as: AbsinthePlugin @@ -24,7 +25,7 @@ defmodule BlockScoutWeb.GraphQL.Schema do import_types(BlockScoutWeb.GraphQL.Schema.Types) - if Application.compile_env(:explorer, :chain_type) == :celo do + if @chain_type == :celo do import_types(BlockScoutWeb.GraphQL.Celo.Schema.Types) end @@ -107,7 +108,7 @@ defmodule BlockScoutWeb.GraphQL.Schema do resolve(&Transaction.get_by/3) end - if Application.compile_env(:explorer, :chain_type) == :celo do + if @chain_type == :celo do require BlockScoutWeb.GraphQL.Celo.QueryFields alias BlockScoutWeb.GraphQL.Celo.QueryFields diff --git a/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex b/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex index f9fdee7ba7e1..4dd86565e538 100644 --- a/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex +++ b/apps/block_scout_web/lib/block_scout_web/graphql/schema/types.ex @@ -1,9 +1,10 @@ defmodule BlockScoutWeb.GraphQL.Schema.Transaction do @moduledoc false + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias BlockScoutWeb.GraphQL.Resolvers.{Block, InternalTransaction} - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :celo -> @chain_type_fields quote( do: [ @@ -62,8 +63,10 @@ defmodule BlockScoutWeb.GraphQL.Schema.Transaction do end defmodule BlockScoutWeb.GraphQL.Schema.SmartContracts do + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] + @moduledoc false - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :zksync -> @chain_type_fields quote( do: [ diff --git a/apps/block_scout_web/lib/block_scout_web/notifier.ex b/apps/block_scout_web/lib/block_scout_web/notifier.ex index 9ed9e000273d..4cb73aa5f015 100644 --- a/apps/block_scout_web/lib/block_scout_web/notifier.ex +++ b/apps/block_scout_web/lib/block_scout_web/notifier.ex @@ -2,6 +2,7 @@ defmodule BlockScoutWeb.Notifier do @moduledoc """ Responds to events by sending appropriate channel updates to front-end. """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] require Logger @@ -31,7 +32,7 @@ defmodule BlockScoutWeb.Notifier do @check_broadcast_sequence_period 500 - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :arbitrum -> @chain_type_specific_events ~w(new_arbitrum_batches new_messages_to_arbitrum_amount)a @@ -296,7 +297,7 @@ defmodule BlockScoutWeb.Notifier do }) end - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :arbitrum -> def handle_event({:chain_event, topic, _, _} = event) when topic in @chain_type_specific_events, # credo:disable-for-next-line Credo.Check.Design.AliasUsage diff --git a/apps/block_scout_web/lib/block_scout_web/paging_helper.ex b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex index 8360aadc5ed3..77c7b5409d62 100644 --- a/apps/block_scout_web/lib/block_scout_web/paging_helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex @@ -2,6 +2,8 @@ defmodule BlockScoutWeb.PagingHelper do @moduledoc """ Helper for fetching filters and other url query parameters """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] + import Explorer.Chain, only: [string_to_transaction_hash: 1] import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] @@ -13,7 +15,7 @@ defmodule BlockScoutWeb.PagingHelper do @default_paging_options %PagingOptions{page_size: @page_size + 1} @allowed_filter_labels ["validated", "pending"] - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :ethereum -> @allowed_type_labels [ "coin_transfer", diff --git a/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex b/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex index c0fb18915f34..f075509c6867 100644 --- a/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex +++ b/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex @@ -2,8 +2,8 @@ defmodule BlockScoutWeb.RealtimeEventHandler do @moduledoc """ Subscribing process for broadcast events from realtime. """ - use GenServer + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias BlockScoutWeb.Notifier alias Explorer.Chain.Events.Subscriber @@ -12,7 +12,7 @@ defmodule BlockScoutWeb.RealtimeEventHandler do GenServer.start_link(__MODULE__, [], name: __MODULE__) end - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :arbitrum -> def chain_type_specific_subscriptions do Subscriber.to(:new_arbitrum_batches, :realtime) diff --git a/apps/block_scout_web/lib/block_scout_web/router.ex b/apps/block_scout_web/lib/block_scout_web/router.ex index 07300fd79bbd..43426fbe25df 100644 --- a/apps/block_scout_web/lib/block_scout_web/router.ex +++ b/apps/block_scout_web/lib/block_scout_web/router.ex @@ -1,12 +1,18 @@ defmodule BlockScoutWeb.Router do use BlockScoutWeb, :router + use Utils.CompileTimeEnvHelper, + admin_panel_enabled: [:block_scout_web, :admin_panel_enabled], + graphql_enabled: [:block_scout_web, [Api.GraphQL, :enabled]], + api_router_reading_enabled: [:block_scout_web, [BlockScoutWeb.Routers.ApiRouter, :reading_enabled]], + web_router_enabled: [:block_scout_web, [BlockScoutWeb.Routers.WebRouter, :enabled]] + alias BlockScoutWeb.Plug.{GraphQL, RateLimit} - alias BlockScoutWeb.Routers.{AccountRouter, ApiRouter, WebRouter} + alias BlockScoutWeb.Routers.{AccountRouter, ApiRouter} @max_query_string_length 5_000 - if Application.compile_env(:block_scout_web, :admin_panel_enabled) do + if @admin_panel_enabled do forward("/admin", BlockScoutWeb.Routers.AdminRouter) end @@ -62,8 +68,7 @@ defmodule BlockScoutWeb.Router do scope "/graphiql" do pipe_through(:api_v1_graphql) - if Application.compile_env(:block_scout_web, Api.GraphQL)[:enabled] && - Application.compile_env(:block_scout_web, ApiRouter)[:reading_enabled] do + if @graphql_enabled && @api_router_reading_enabled do forward("/", Absinthe.Plug.GraphiQL, schema: BlockScoutWeb.GraphQL.Schema, interface: :advanced, @@ -79,7 +84,7 @@ defmodule BlockScoutWeb.Router do get("/robots.txt", RobotsController, :robots) get("/sitemap.xml", RobotsController, :sitemap) - if Application.compile_env(:block_scout_web, ApiRouter)[:reading_enabled] do + if @api_router_reading_enabled do get("/api-docs", APIDocsController, :index) get("/eth-rpc-api-docs", APIDocsController, :eth_rpc) else @@ -94,7 +99,7 @@ defmodule BlockScoutWeb.Router do post("/contract_verifications", BlockScoutWeb.AddressContractVerificationController, :create) end - if Application.compile_env(:block_scout_web, WebRouter)[:enabled] do + if @web_router_enabled do forward("/", BlockScoutWeb.Routers.WebRouter) end end diff --git a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex index 5f0c3e1c9eb0..ece2cf66fb39 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/api_router.ex @@ -13,6 +13,16 @@ defmodule BlockScoutWeb.Routers.ApiRouter do Router for API """ use BlockScoutWeb, :router + + use Utils.CompileTimeEnvHelper, + chain_type: [:explorer, :chain_type], + mud_enabled: [:explorer, [Explorer.Chain.Mud, :enabled]], + graphql_enabled: [:block_scout_web, [Api.GraphQL, :enabled]], + graphql_max_complexity: [:block_scout_web, [Api.GraphQL, :max_complexity]], + graphql_token_limit: [:block_scout_web, [Api.GraphQL, :token_limit]], + reading_enabled: [:block_scout_web, [__MODULE__, :reading_enabled]], + writing_enabled: [:block_scout_web, [__MODULE__, :writing_enabled]] + alias BlockScoutWeb.AddressTransactionController alias BlockScoutWeb.Routers.{ @@ -127,27 +137,27 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/watchlist", V2.TransactionController, :watchlist_transactions) get("/stats", V2.TransactionController, :stats) - if Application.compile_env(:explorer, :chain_type) == :polygon_zkevm do + if @chain_type == :polygon_zkevm do get("/zkevm-batch/:batch_number", V2.TransactionController, :polygon_zkevm_batch) end - if Application.compile_env(:explorer, :chain_type) == :zksync do + if @chain_type == :zksync do get("/zksync-batch/:batch_number", V2.TransactionController, :zksync_batch) end - if Application.compile_env(:explorer, :chain_type) == :arbitrum do + if @chain_type == :arbitrum do get("/arbitrum-batch/:batch_number", V2.TransactionController, :arbitrum_batch) end - if Application.compile_env(:explorer, :chain_type) == :optimism do + if @chain_type == :optimism do get("/optimism-batch/:batch_number", V2.TransactionController, :optimism_batch) end - if Application.compile_env(:explorer, :chain_type) == :scroll do + if @chain_type == :scroll do get("/scroll-batch/:batch_number", V2.TransactionController, :scroll_batch) end - if Application.compile_env(:explorer, :chain_type) == :suave do + if @chain_type == :suave do get("/execution-node/:execution_node_hash_param", V2.TransactionController, :execution_node) end @@ -159,7 +169,7 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/:transaction_hash_param/state-changes", V2.TransactionController, :state_changes) get("/:transaction_hash_param/summary", V2.TransactionController, :summary) - if Application.compile_env(:explorer, :chain_type) == :ethereum do + if @chain_type == :ethereum do get("/:transaction_hash_param/blobs", V2.TransactionController, :blobs) end end @@ -180,20 +190,20 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/:block_hash_or_number/internal-transactions", V2.BlockController, :internal_transactions) get("/:block_hash_or_number/withdrawals", V2.BlockController, :withdrawals) - if Application.compile_env(:explorer, :chain_type) == :arbitrum do + if @chain_type == :arbitrum do get("/arbitrum-batch/:batch_number", V2.BlockController, :arbitrum_batch) end - if Application.compile_env(:explorer, :chain_type) == :celo do + if @chain_type == :celo do get("/:block_hash_or_number/epoch", V2.BlockController, :celo_epoch) get("/:block_hash_or_number/election-rewards/:reward_type", V2.BlockController, :celo_election_rewards) end - if Application.compile_env(:explorer, :chain_type) == :optimism do + if @chain_type == :optimism do get("/optimism-batch/:batch_number", V2.BlockController, :optimism_batch) end - if Application.compile_env(:explorer, :chain_type) == :scroll do + if @chain_type == :scroll do get("/scroll-batch/:batch_number", V2.BlockController, :scroll_batch) end end @@ -216,7 +226,7 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/:address_hash_param/nft", V2.AddressController, :nft_list) get("/:address_hash_param/nft/collections", V2.AddressController, :nft_collections) - if Application.compile_env(:explorer, :chain_type) == :celo do + if @chain_type == :celo do get("/:address_hash_param/election-rewards", V2.AddressController, :celo_election_rewards) end end @@ -227,21 +237,21 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/transactions/watchlist", V2.MainPageController, :watchlist_transactions) get("/indexing-status", V2.MainPageController, :indexing_status) - if Application.compile_env(:explorer, :chain_type) == :optimism do + if @chain_type == :optimism do get("/optimism-deposits", V2.MainPageController, :optimism_deposits) end - if Application.compile_env(:explorer, :chain_type) == :polygon_zkevm do + if @chain_type == :polygon_zkevm do get("/zkevm/batches/confirmed", V2.PolygonZkevmController, :batches_confirmed) get("/zkevm/batches/latest-number", V2.PolygonZkevmController, :batch_latest_number) end - if Application.compile_env(:explorer, :chain_type) == :zksync do + if @chain_type == :zksync do get("/zksync/batches/confirmed", V2.ZkSyncController, :batches_confirmed) get("/zksync/batches/latest-number", V2.ZkSyncController, :batch_latest_number) end - if Application.compile_env(:explorer, :chain_type) == :arbitrum do + if @chain_type == :arbitrum do get("/arbitrum/messages/to-rollup", V2.ArbitrumController, :recent_messages_to_l2) get("/arbitrum/batches/committed", V2.ArbitrumController, :batches_committed) get("/arbitrum/batches/latest-number", V2.ArbitrumController, :batch_latest_number) @@ -259,7 +269,7 @@ defmodule BlockScoutWeb.Routers.ApiRouter do end scope "/optimism" do - if Application.compile_env(:explorer, :chain_type) == :optimism do + if @chain_type == :optimism do get("/txn-batches", V2.OptimismController, :transaction_batches) get("/txn-batches/count", V2.OptimismController, :transaction_batches_count) get("/txn-batches/:l2_block_range_start/:l2_block_range_end", V2.OptimismController, :transaction_batches) @@ -279,7 +289,7 @@ defmodule BlockScoutWeb.Routers.ApiRouter do end scope "/polygon-edge" do - if Application.compile_env(:explorer, :chain_type) == :polygon_edge do + if @chain_type == :polygon_edge do get("/deposits", V2.PolygonEdgeController, :deposits) get("/deposits/count", V2.PolygonEdgeController, :deposits_count) get("/withdrawals", V2.PolygonEdgeController, :withdrawals) @@ -288,7 +298,7 @@ defmodule BlockScoutWeb.Routers.ApiRouter do end scope "/scroll" do - if Application.compile_env(:explorer, :chain_type) == :scroll do + if @chain_type == :scroll do get("/batches", V2.ScrollController, :batches) get("/batches/count", V2.ScrollController, :batches_count) get("/batches/:number", V2.ScrollController, :batch) @@ -300,7 +310,7 @@ defmodule BlockScoutWeb.Routers.ApiRouter do end scope "/shibarium" do - if Application.compile_env(:explorer, :chain_type) == :shibarium do + if @chain_type == :shibarium do get("/deposits", V2.ShibariumController, :deposits) get("/deposits/count", V2.ShibariumController, :deposits_count) get("/withdrawals", V2.ShibariumController, :withdrawals) @@ -314,7 +324,7 @@ defmodule BlockScoutWeb.Routers.ApiRouter do end scope "/zkevm" do - if Application.compile_env(:explorer, :chain_type) == :polygon_zkevm do + if @chain_type == :polygon_zkevm do get("/batches", V2.PolygonZkevmController, :batches) get("/batches/count", V2.PolygonZkevmController, :batches_count) get("/batches/:batch_number", V2.PolygonZkevmController, :batch) @@ -363,13 +373,13 @@ defmodule BlockScoutWeb.Routers.ApiRouter do end scope "/blobs" do - if Application.compile_env(:explorer, :chain_type) == :ethereum do + if @chain_type == :ethereum do get("/:blob_hash_param", V2.BlobController, :blob) end end scope "/validators" do - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :stability -> scope "/stability" do get("/", V2.ValidatorController, :stability_validators_list) @@ -388,7 +398,7 @@ defmodule BlockScoutWeb.Routers.ApiRouter do end scope "/zksync" do - if Application.compile_env(:explorer, :chain_type) == :zksync do + if @chain_type == :zksync do get("/batches", V2.ZkSyncController, :batches) get("/batches/count", V2.ZkSyncController, :batches_count) get("/batches/:batch_number", V2.ZkSyncController, :batch) @@ -396,7 +406,7 @@ defmodule BlockScoutWeb.Routers.ApiRouter do end scope "/mud" do - if Application.compile_env(:explorer, Explorer.Chain.Mud)[:enabled] do + if @mud_enabled do get("/worlds", V2.MudController, :worlds) get("/worlds/count", V2.MudController, :worlds_count) get("/worlds/:world/tables", V2.MudController, :world_tables) @@ -410,7 +420,7 @@ defmodule BlockScoutWeb.Routers.ApiRouter do end scope "/arbitrum" do - if Application.compile_env(:explorer, :chain_type) == :arbitrum do + if @chain_type == :arbitrum do get("/messages/:direction", V2.ArbitrumController, :messages) get("/messages/:direction/count", V2.ArbitrumController, :messages_count) get("/batches", V2.ArbitrumController, :batches) @@ -436,12 +446,12 @@ defmodule BlockScoutWeb.Routers.ApiRouter do scope "/v1/graphql" do pipe_through(:api_v1_graphql) - if Application.compile_env(:block_scout_web, Api.GraphQL)[:enabled] do + if @graphql_enabled do forward("/", Absinthe.Plug, schema: BlockScoutWeb.GraphQL.Schema, analyze_complexity: true, - max_complexity: Application.compile_env(:block_scout_web, Api.GraphQL)[:max_complexity], - token_limit: Application.compile_env(:block_scout_web, Api.GraphQL)[:token_limit] + max_complexity: @graphql_max_complexity, + token_limit: @graphql_token_limit ) end end @@ -463,7 +473,7 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/logs-csv", AddressTransactionController, :logs_csv) - if Application.compile_env(:explorer, :chain_type) == :celo do + if @chain_type == :celo do get("/celo-election-rewards-csv", AddressTransactionController, :celo_election_rewards_csv) end @@ -475,17 +485,17 @@ defmodule BlockScoutWeb.Routers.ApiRouter do get("/gas-price-oracle", GasPriceOracleController, :gas_price_oracle) - if Application.compile_env(:block_scout_web, __MODULE__)[:reading_enabled] do + if @reading_enabled do get("/supply", V1.SupplyController, :supply) post("/eth-rpc", EthRPC.EthController, :eth_request) end - if Application.compile_env(:block_scout_web, __MODULE__)[:writing_enabled] do + if @writing_enabled do post("/decompiled_smart_contract", V1.DecompiledSmartContractController, :create) post("/verified_smart_contracts", V1.VerifiedSmartContractController, :create) end - if Application.compile_env(:block_scout_web, __MODULE__)[:reading_enabled] do + if @reading_enabled do forward("/", RPC.RPCTranslator, %{ "block" => {RPC.BlockController, []}, "account" => {RPC.AddressController, []}, @@ -503,7 +513,7 @@ defmodule BlockScoutWeb.Routers.ApiRouter do pipe_through(:api) alias BlockScoutWeb.API.{EthRPC, RPC} - if Application.compile_env(:block_scout_web, __MODULE__)[:reading_enabled] do + if @reading_enabled do post("/eth-rpc", EthRPC.EthController, :eth_request) forward("/", RPCTranslatorForwarder, %{ diff --git a/apps/block_scout_web/lib/block_scout_web/routers/smart_contracts_api_v2_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/smart_contracts_api_v2_router.ex index aee2f7363b69..22bc36dc9e31 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/smart_contracts_api_v2_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/smart_contracts_api_v2_router.ex @@ -4,6 +4,8 @@ defmodule BlockScoutWeb.Routers.SmartContractsApiV2Router do Router for /api/v2/smart-contracts. This route has separate router in order to ignore sobelow's warning about missing CSRF protection """ use BlockScoutWeb, :router + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] + alias BlockScoutWeb.API.V2 alias BlockScoutWeb.Plug.{CheckApiV2, RateLimit} @@ -71,7 +73,7 @@ defmodule BlockScoutWeb.Routers.SmartContractsApiV2Router do post("/standard-input", V2.VerificationController, :verification_via_standard_input) - if Application.compile_env(:explorer, :chain_type) !== :zksync do + if @chain_type !== :zksync do post("/flattened-code", V2.VerificationController, :verification_via_flattened_code) post("/sourcify", V2.VerificationController, :verification_via_sourcify) post("/multi-part", V2.VerificationController, :verification_via_multi_part) @@ -80,7 +82,7 @@ defmodule BlockScoutWeb.Routers.SmartContractsApiV2Router do post("/vyper-standard-input", V2.VerificationController, :verification_via_vyper_standard_input) end - if Application.compile_env(:explorer, :chain_type) === :arbitrum do + if @chain_type === :arbitrum do post("/stylus-github-repository", V2.VerificationController, :verification_via_stylus_github_repository) end end diff --git a/apps/block_scout_web/lib/block_scout_web/routers/tokens_api_v2_router.ex b/apps/block_scout_web/lib/block_scout_web/routers/tokens_api_v2_router.ex index c9506cfc84e2..3954c04cdaba 100644 --- a/apps/block_scout_web/lib/block_scout_web/routers/tokens_api_v2_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/routers/tokens_api_v2_router.ex @@ -4,6 +4,8 @@ defmodule BlockScoutWeb.Routers.TokensApiV2Router do Router for /api/v2/tokens. This route has separate router in order to ignore sobelow's warning about missing CSRF protection """ use BlockScoutWeb, :router + use Utils.CompileTimeEnvHelper, bridged_token_enabled: [:explorer, [Explorer.Chain.BridgedToken, :enabled]] + alias BlockScoutWeb.API.V2 alias BlockScoutWeb.Plug.{CheckApiV2, RateLimit} @@ -52,7 +54,7 @@ defmodule BlockScoutWeb.Routers.TokensApiV2Router do scope "/", as: :api_v2 do pipe_through(:api_v2) - if Application.compile_env(:explorer, Explorer.Chain.BridgedToken)[:enabled] do + if @bridged_token_enabled do get("/bridged", V2.TokenController, :bridged_tokens_list) end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex index 11ae92643390..bbc0d6741e12 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.API.V2.AddressView do use BlockScoutWeb, :view + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] import BlockScoutWeb.Account.AuthController, only: [current_user: 1] @@ -242,7 +243,7 @@ defmodule BlockScoutWeb.API.V2.AddressView do }) end - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :filecoin -> defp chain_type_fields(result, params) do # credo:disable-for-next-line Credo.Check.Design.AliasUsage diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex index 79f3abdd6731..35152fd16588 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.API.V2.BlockView do use BlockScoutWeb, :view + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias BlockScoutWeb.BlockView alias BlockScoutWeb.API.V2.{ApiView, Helper} @@ -103,7 +104,7 @@ defmodule BlockScoutWeb.API.V2.BlockView do def count_withdrawals(%Block{withdrawals: withdrawals}) when is_list(withdrawals), do: Enum.count(withdrawals) def count_withdrawals(_), do: nil - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :rsk -> defp chain_type_fields(result, block, single_block?) do if single_block? do diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex index 82a2270b65c4..6e26f264c8ee 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/filecoin_view.ex @@ -1,11 +1,13 @@ -if Application.compile_env(:explorer, :chain_type) == :filecoin do - defmodule BlockScoutWeb.API.V2.FilecoinView do - @moduledoc """ - View functions for rendering Filecoin-related data in JSON format. - """ +defmodule BlockScoutWeb.API.V2.FilecoinView do + @moduledoc """ + View functions for rendering Filecoin-related data in JSON format. + """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] - alias Explorer.Chain - alias Explorer.Chain.Address + if @chain_type == :filecoin do + # TODO: remove when https://github.com/elixir-lang/elixir/issues/13975 comes to elixir release + alias Explorer.Chain, warn: false + alias Explorer.Chain.Address, warn: false @api_true [api?: true] @@ -108,3 +110,5 @@ if Application.compile_env(:explorer, :chain_type) == :filecoin do end end end + +# end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex index 88b3e41f5330..b2752554f09d 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex @@ -2,6 +2,7 @@ defmodule BlockScoutWeb.API.V2.Helper do @moduledoc """ API V2 helper """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias Ecto.Association.NotLoaded alias Explorer.Chain.Address @@ -118,7 +119,7 @@ defmodule BlockScoutWeb.API.V2.Helper do } end - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :filecoin -> defp address_chain_type_fields(result, address) do # credo:disable-for-next-line Credo.Check.Design.AliasUsage 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 1134914e6248..dbda49f01570 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,5 +1,6 @@ defmodule BlockScoutWeb.API.V2.SearchView do use BlockScoutWeb, :view + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias BlockScoutWeb.{BlockView, Endpoint} alias Explorer.Chain @@ -165,7 +166,7 @@ defmodule BlockScoutWeb.API.V2.SearchView do %{"type" => "blob", "parameter" => to_string(item.hash)} end - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :filecoin -> defp chain_type_fields(result) do # credo:disable-for-next-line Credo.Check.Design.AliasUsage diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex index 70da14e7fb09..7bf5670579e5 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/smart_contract_view.ex @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do use BlockScoutWeb, :view + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] import Explorer.Helper, only: [decode_data: 2] import Explorer.SmartContract.Reader, only: [zip_tuple_values_with_types: 2] @@ -443,7 +444,7 @@ defmodule BlockScoutWeb.API.V2.SmartContractView do to_string(value) end - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :filecoin -> defp chain_type_fields(result, params) do # credo:disable-for-next-line Credo.Check.Design.AliasUsage diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex index 935a06ff8df3..72b1ad715199 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.API.V2.TokenView do use BlockScoutWeb, :view + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias BlockScoutWeb.API.V2.Helper alias BlockScoutWeb.NFTHelper @@ -141,7 +142,7 @@ defmodule BlockScoutWeb.API.V2.TokenView do end end - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :filecoin -> defp chain_type_fields(result, params) do # credo:disable-for-next-line Credo.Check.Design.AliasUsage diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex index 36511e45be37..529a83663907 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex @@ -1,5 +1,6 @@ defmodule BlockScoutWeb.API.V2.TransactionView do use BlockScoutWeb, :view + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias BlockScoutWeb.API.V2.{ApiView, Helper, InternalTransactionView, TokenTransferView, TokenView} @@ -777,7 +778,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do Map.merge(map, %{"change" => change}) end - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :polygon_edge -> defp chain_type_transformations(transactions) do transactions diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/zilliqa_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/zilliqa_view.ex index d3f7c4768776..238d2c72ab3c 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/v2/zilliqa_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/zilliqa_view.ex @@ -1,11 +1,13 @@ -if Application.compile_env(:explorer, :chain_type) == :zilliqa do - defmodule BlockScoutWeb.API.V2.ZilliqaView do - @moduledoc """ - View functions for rendering Zilliqa-related data in JSON format. - """ +defmodule BlockScoutWeb.API.V2.ZilliqaView do + @moduledoc """ + View functions for rendering Zilliqa-related data in JSON format. + """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] - alias Explorer.Chain.Block - alias Explorer.Chain.Zilliqa.{AggregateQuorumCertificate, QuorumCertificate} + if @chain_type == :zilliqa do + # TODO: remove when https://github.com/elixir-lang/elixir/issues/13975 comes to elixir release + alias Explorer.Chain.Block, warn: false + alias Explorer.Chain.Zilliqa.{AggregateQuorumCertificate, QuorumCertificate}, warn: false @doc """ Extends the JSON output with a sub-map containing information related to Zilliqa, diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs index 7ffaf8b09e0c..412358ec2388 100644 --- a/apps/block_scout_web/mix.exs +++ b/apps/block_scout_web/mix.exs @@ -87,7 +87,6 @@ defmodule BlockScoutWeb.Mixfile do {:bypass, "~> 2.1", only: :test}, # To add (CORS)(https://www.w3.org/TR/cors/) {:cors_plug, "~> 3.0"}, - {:credo, "~> 1.5", only: :test, runtime: false}, # For Absinthe to load data in batches {:dataloader, "~> 2.0.0"}, {:dialyxir, "~> 1.1", only: [:dev, :test], runtime: false}, @@ -156,6 +155,7 @@ defmodule BlockScoutWeb.Mixfile do {:ex_json_schema, "~> 0.10.1"}, {:ueberauth, "~> 0.7"}, {:ueberauth_auth0, "~> 2.0"}, + {:utils, in_umbrella: true}, {:bureaucrat, "~> 0.2.9", only: :test}, {:logger_json, "~> 5.1"} ] diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex index 1024cb806238..36c5294f5cc0 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex @@ -3,6 +3,7 @@ defmodule EthereumJSONRPC.Block do Block format as returned by [`eth_getBlockByHash`](https://github.com/ethereum/wiki/wiki/JSON-RPC/e8e0771b9f3677693649d945956bc60e886ceb2b#eth_getblockbyhash) and [`eth_getBlockByNumber`](https://github.com/ethereum/wiki/wiki/JSON-RPC/e8e0771b9f3677693649d945956bc60e886ceb2b#eth_getblockbynumber). """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] import EthereumJSONRPC, only: [quantity_to_integer: 1, timestamp_to_datetime: 1] @@ -16,7 +17,7 @@ defmodule EthereumJSONRPC.Block do # (sha3Uncles) is the RLP-encoded hash of an empty list. @sha3_uncles_empty_list "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :rsk -> @chain_type_fields quote( do: [ @@ -124,7 +125,7 @@ defmodule EthereumJSONRPC.Block do [uncles](https://bitcoin.stackexchange.com/questions/39329/in-ethereum-what-is-an-uncle-block) `t:EthereumJSONRPC.hash/0`. * `"baseFeePerGas"` - `t:EthereumJSONRPC.quantity/0` of wei to denote amount of fee burnt per unit gas used. Introduced in [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) - #{case Application.compile_env(:explorer, :chain_type) do + #{case @chain_type do :rsk -> """ * `"minimumGasPrice"` - `t:EthereumJSONRPC.quantity/0` of the minimum gas price for this block. * `"bitcoinMergedMiningHeader"` - `t:EthereumJSONRPC.data/0` of the Bitcoin merged mining header. @@ -190,7 +191,7 @@ defmodule EthereumJSONRPC.Block do ...> "totalDifficulty" => 340282366920938463463374607431465668165, ...> "transactions" => [], ...> "transactionsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",\ - #{case Application.compile_env(:explorer, :chain_type) do + #{case @chain_type do :rsk -> """ "minimumGasPrice" => 345786,\ "bitcoinMergedMiningHeader" => "0x00006d20ffd048280094a6ea0851d854036aacaa25ee0f23f0040200000000000000000078d2638fe0b4477c54601e6449051afba8228e0a88ff06b0c91f091fd34d5da57487c76402610517372c2fe9",\ @@ -241,7 +242,7 @@ defmodule EthereumJSONRPC.Block do timestamp: Timex.parse!("2017-12-15T21:03:30Z", "{ISO:Extended:Z}"), total_difficulty: 340282366920938463463374607431465668165, transactions_root: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",\ - #{case Application.compile_env(:explorer, :chain_type) do + #{case @chain_type do :rsk -> """ bitcoin_merged_mining_coinbase_transaction: "0x00000000000000805bf0dc9203da49a3b4e3ec913806e43102cc07db991272dc8b7018da57eb5abe59a32d070000ffffffff03449a4d26000000001976a914536ffa992491508dca0354e52f32a3a7a679a53a88ac00000000000000002b6a2952534b424c4f434b3ad2508d21d28c8f89d495923c0758ec3f64bd6755b4ec416f5601312600542a400000000000000000266a24aa21a9ed4ae42ea6dca2687aaed665714bf58b055c4e11f2fb038605930d630b49ad7b9d00000000",\ bitcoin_merged_mining_header: "0x00006d20ffd048280094a6ea0851d854036aacaa25ee0f23f0040200000000000000000078d2638fe0b4477c54601e6449051afba8228e0a88ff06b0c91f091fd34d5da57487c76402610517372c2fe9",\ @@ -312,7 +313,7 @@ defmodule EthereumJSONRPC.Block do timestamp: Timex.parse!("2015-07-30T15:32:07Z", "{ISO:Extended:Z}"), total_difficulty: 1039309006117, transactions_root: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",\ - #{case Application.compile_env(:explorer, :chain_type) do + #{case @chain_type do :rsk -> """ bitcoin_merged_mining_coinbase_transaction: nil,\ bitcoin_merged_mining_header: nil,\ @@ -523,7 +524,7 @@ defmodule EthereumJSONRPC.Block do end @spec chain_type_fields(params, elixir) :: params - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :rsk -> defp chain_type_fields(params, elixir) do params @@ -937,7 +938,7 @@ defmodule EthereumJSONRPC.Block do {key, Withdrawals.to_elixir(withdrawals, block_hash, quantity_to_integer(block_number))} end - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :zilliqa -> defp entry_to_elixir({"view" = key, quantity}, _block) when not is_nil(quantity) do {key, quantity_to_integer(quantity)} diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex index 9439089f6057..e02ef953a478 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex @@ -3,6 +3,7 @@ defmodule EthereumJSONRPC.Blocks do Blocks format as returned by [`eth_getBlockByHash`](https://github.com/ethereum/wiki/wiki/JSON-RPC/e8e0771b9f3677693649d945956bc60e886ceb2b#eth_getblockbyhash) and [`eth_getBlockByNumber`](https://github.com/ethereum/wiki/wiki/JSON-RPC/e8e0771b9f3677693649d945956bc60e886ceb2b#eth_getblockbynumber) from batch requests. """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias EthereumJSONRPC.{Block, Transactions, Transport, Uncles, Withdrawals} @@ -17,7 +18,7 @@ defmodule EthereumJSONRPC.Blocks do errors: [] ] - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :zilliqa -> @chain_type_fields quote( do: [ @@ -99,7 +100,7 @@ defmodule EthereumJSONRPC.Blocks do end @spec extend_with_chain_type_fields(t(), elixir()) :: t() - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :zilliqa -> defp extend_with_chain_type_fields(%__MODULE__{} = blocks, elixir_blocks) do # credo:disable-for-next-line Credo.Check.Design.AliasUsage @@ -164,7 +165,7 @@ defmodule EthereumJSONRPC.Blocks do timestamp: Timex.parse!("1970-01-01T00:00:00Z", "{ISO:Extended:Z}"), total_difficulty: 131072, transactions_root: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",\ - #{case Application.compile_env(:explorer, :chain_type) do + #{case @chain_type do :rsk -> """ bitcoin_merged_mining_coinbase_transaction: nil,\ bitcoin_merged_mining_header: nil,\ diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipt.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipt.ex index 3faa882fe025..0661af262a80 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipt.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipt.ex @@ -3,12 +3,13 @@ defmodule EthereumJSONRPC.Receipt do Receipts format as returned by [`eth_getTransactionReceipt`](https://github.com/ethereum/wiki/wiki/JSON-RPC/e8e0771b9f3677693649d945956bc60e886ceb2b#eth_gettransactionreceipt). """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] import EthereumJSONRPC, only: [quantity_to_integer: 1] alias EthereumJSONRPC.Logs - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :ethereum -> @chain_type_fields quote( do: [ @@ -117,7 +118,7 @@ defmodule EthereumJSONRPC.Receipt do status: :ok, transaction_hash: "0x3a3eb134e6792ce9403ea4188e5e79693de9e4c94e499db132be086400da79e6", transaction_index: 0,\ - #{case Application.compile_env(:explorer, :chain_type) do + #{case @chain_type do :ethereum -> """ blob_gas_price: 0,\ blob_gas_used: 0\ @@ -169,7 +170,7 @@ defmodule EthereumJSONRPC.Receipt do status: nil, transaction_hash: "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060", transaction_index: 0,\ - #{case Application.compile_env(:explorer, :chain_type) do + #{case @chain_type do :ethereum -> """ blob_gas_price: 0,\ blob_gas_used: 0\ @@ -230,7 +231,7 @@ defmodule EthereumJSONRPC.Receipt do defp maybe_append_gas_price(params, _), do: params - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :ethereum -> defp chain_type_fields(params, elixir) do params diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipts.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipts.ex index b3c2c04a4d24..25a039e9fadc 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipts.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipts.ex @@ -4,6 +4,7 @@ defmodule EthereumJSONRPC.Receipts do [`eth_getTransactionReceipt`](https://github.com/ethereum/wiki/wiki/JSON-RPC/e8e0771b9f3677693649d945956bc60e886ceb2b#eth_gettransactionreceipt) from batch requests. """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] import EthereumJSONRPC, only: [json_rpc: 2, request: 1] @@ -100,7 +101,7 @@ defmodule EthereumJSONRPC.Receipts do status: :ok, transaction_hash: "0x53bd884872de3e488692881baeec262e7b95234d3965248c39fe992fffd433e5", transaction_index: 0,\ - #{case Application.compile_env(:explorer, :chain_type) do + #{case @chain_type do :ethereum -> """ blob_gas_price: 0,\ blob_gas_used: 0\ diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex index a584ae1d5043..4fd2c31b5da8 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex @@ -7,6 +7,8 @@ defmodule EthereumJSONRPC.Transaction do [`eth_getTransactionByBlockHashAndIndex`](https://github.com/ethereum/wiki/wiki/JSON-RPC/e8e0771b9f3677693649d945956bc60e886ceb2b#eth_gettransactionbyblockhashandindex), and [`eth_getTransactionByBlockNumberAndIndex`](https://github.com/ethereum/wiki/wiki/JSON-RPC/e8e0771b9f3677693649d945956bc60e886ceb2b#eth_gettransactionbyblocknumberandindex) """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] + import EthereumJSONRPC, only: [ quantity_to_integer: 1, @@ -18,7 +20,7 @@ defmodule EthereumJSONRPC.Transaction do alias EthereumJSONRPC alias EthereumJSONRPC.SignedAuthorization - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :ethereum -> @chain_type_fields quote( do: [ @@ -122,7 +124,7 @@ defmodule EthereumJSONRPC.Transaction do * `"maxFeePerGas"` - `t:EthereumJSONRPC.quantity/0` of wei to denote max fee per unit of gas used. Introduced in [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) * `"type"` - `t:EthereumJSONRPC.quantity/0` denotes transaction type. Introduced in [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) * `"authorizationList"` - `t:list/0` of `t:EthereumJSONRPC.SignedAuthorization.t/0` authorization tuples. Introduced in [EIP-7702](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7702.md) - #{case Application.compile_env(:explorer, :chain_type) do + #{case @chain_type do :ethereum -> """ * `"maxFeePerBlobGas"` - `t:EthereumJSONRPC.quantity/0` of wei to denote max fee per unit of blob gas used. Introduced in [EIP-4844](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md) * `"blobVersionedHashes"` - `t:list/0` of `t:EthereumJSONRPC.hash/0` of included data blobs hashes. Introduced in [EIP-4844](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md) @@ -740,7 +742,7 @@ defmodule EthereumJSONRPC.Transaction do do: {key, value |> Enum.map(&SignedAuthorization.to_params/1)} # Celo-specific fields - if Application.compile_env(:explorer, :chain_type) == :celo do + if @chain_type == :celo do defp entry_to_elixir({key, value}) when key in ~w(feeCurrency gatewayFeeRecipient), do: {key, value} diff --git a/apps/ethereum_jsonrpc/mix.exs b/apps/ethereum_jsonrpc/mix.exs index b950b31b8097..0d4e757023c5 100644 --- a/apps/ethereum_jsonrpc/mix.exs +++ b/apps/ethereum_jsonrpc/mix.exs @@ -58,8 +58,6 @@ defmodule EthereumJSONRPC.MixProject do {:certifi, "~> 2.3"}, # WebSocket-server for testing `EthereumJSONRPC.WebSocket.WebSocketClient`. {:cowboy, "~> 2.0", only: [:dev, :test]}, - # Style Checking - {:credo, "~> 1.5", only: :test, runtime: false}, # Static Type Checking {:dialyxir, "~> 1.1", only: [:dev, :test], runtime: false}, {:ex_keccak, "~> 0.7.5"}, @@ -87,6 +85,7 @@ defmodule EthereumJSONRPC.MixProject do {:hackney, "~> 1.18"}, {:poolboy, "~> 1.5.2"}, {:logger_json, "~> 5.1"}, + {:utils, in_umbrella: true}, {:websockex, "~> 0.4.3"} ] end diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index 0729633b3825..690987a3eadd 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -5,6 +5,7 @@ defmodule Explorer.Chain.Address.Schema do Changes in the schema should be reflected in the bulk import module: - Explorer.Chain.Import.Runner.Addresses """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias Explorer.Chain.{ Address, @@ -24,7 +25,7 @@ defmodule Explorer.Chain.Address.Schema do alias Explorer.Chain.Cache.{Accounts, NetVersion} alias Explorer.Chain.SmartContract.Proxy.Models.Implementation - @chain_type_fields (case Application.compile_env(:explorer, :chain_type) do + @chain_type_fields (case @chain_type do :filecoin -> alias Explorer.Chain.Filecoin.{IDAddress, NativeAddress} @@ -133,6 +134,7 @@ defmodule Explorer.Chain.Address do require Explorer.Chain.Address.Schema use Explorer.Schema + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias Ecto.Association.NotLoaded alias Ecto.Changeset @@ -145,7 +147,7 @@ defmodule Explorer.Chain.Address do import Explorer.Chain.SmartContract.Proxy.Models.Implementation, only: [proxy_implementations_association: 0] @optional_attrs ~w(contract_code fetched_coin_balance fetched_coin_balance_block_number nonce decompiled verified gas_used transactions_count token_transfers_count)a - @chain_type_optional_attrs (case Application.compile_env(:explorer, :chain_type) do + @chain_type_optional_attrs (case @chain_type do :filecoin -> ~w(filecoin_id filecoin_robust filecoin_actor_type)a @@ -200,7 +202,7 @@ defmodule Explorer.Chain.Address do * `inserted_at` - when this address was inserted * `updated_at` - when this address was last updated * `ens_domain_name` - virtual field for ENS domain name passing - #{case Application.compile_env(:explorer, :chain_type) do + #{case @chain_type do :filecoin -> """ * `filecoin_native_address` - robust f0/f1/f2/f3/f4 Filecoin address * `filecoin_id_address` - short f0 Filecoin address that may change during chain reorgs diff --git a/apps/explorer/lib/explorer/chain/block.ex b/apps/explorer/lib/explorer/chain/block.ex index 311242d57cdd..97b64444ad65 100644 --- a/apps/explorer/lib/explorer/chain/block.ex +++ b/apps/explorer/lib/explorer/chain/block.ex @@ -5,6 +5,8 @@ defmodule Explorer.Chain.Block.Schema do Changes in the schema should be reflected in the bulk import module: - Explorer.Chain.Import.Runner.Blocks """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] + alias Explorer.Chain.{ Address, Block, @@ -23,7 +25,7 @@ defmodule Explorer.Chain.Block.Schema do alias Explorer.Chain.Zilliqa.QuorumCertificate, as: ZilliqaQuorumCertificate alias Explorer.Chain.ZkSync.BatchBlock, as: ZkSyncBatchBlock - @chain_type_fields (case Application.compile_env(:explorer, :chain_type) do + @chain_type_fields (case @chain_type do :ethereum -> elem( quote do @@ -185,6 +187,7 @@ defmodule Explorer.Chain.Block do require Explorer.Chain.Block.Schema use Explorer.Schema + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias Explorer.Chain.{Block, Hash, Transaction, Wei} alias Explorer.Chain.Block.{EmissionReward, Reward} @@ -193,7 +196,7 @@ defmodule Explorer.Chain.Block do @optional_attrs ~w(size refetch_needed total_difficulty difficulty base_fee_per_gas)a - @chain_type_optional_attrs (case Application.compile_env(:explorer, :chain_type) do + @chain_type_optional_attrs (case @chain_type do :rsk -> ~w(minimum_gas_price bitcoin_merged_mining_header bitcoin_merged_mining_coinbase_transaction bitcoin_merged_mining_merkle_proof hash_for_merged_mining)a @@ -246,7 +249,7 @@ defmodule Explorer.Chain.Block do * `refetch_needed` - `true` if block has missing data and has to be refetched. * `transactions` - the `t:Explorer.Chain.Transaction.t/0` in this block. * `base_fee_per_gas` - Minimum fee required per unit of gas. Fee adjusts based on network congestion. - #{case Application.compile_env(:explorer, :chain_type) do + #{case @chain_type do :rsk -> """ * `bitcoin_merged_mining_header` - Bitcoin merged mining header on Rootstock chains. * `bitcoin_merged_mining_coinbase_transaction` - Bitcoin merged mining coinbase transaction on Rootstock chains. diff --git a/apps/explorer/lib/explorer/chain/block_number_helper.ex b/apps/explorer/lib/explorer/chain/block_number_helper.ex index 48fb1a7fba63..f06a70494389 100644 --- a/apps/explorer/lib/explorer/chain/block_number_helper.ex +++ b/apps/explorer/lib/explorer/chain/block_number_helper.ex @@ -3,12 +3,13 @@ defmodule Explorer.Chain.BlockNumberHelper do @moduledoc """ Functions to operate with block numbers based on null round heights (applicable for CHAIN_TYPE=filecoin) """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] def previous_block_number(number), do: neighbor_block_number(number, :previous) def next_block_number(number), do: neighbor_block_number(number, :next) - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :filecoin -> def null_rounds_count, do: Explorer.Chain.NullRoundHeight.total() diff --git a/apps/explorer/lib/explorer/chain/cache/blackfort_validators_counters.ex b/apps/explorer/lib/explorer/chain/cache/blackfort_validators_counters.ex index cf2a77411183..0cd9e8b5e221 100644 --- a/apps/explorer/lib/explorer/chain/cache/blackfort_validators_counters.ex +++ b/apps/explorer/lib/explorer/chain/cache/blackfort_validators_counters.ex @@ -6,22 +6,20 @@ defmodule Explorer.Chain.Cache.BlackfortValidatorsCounters do """ use GenServer - - alias Explorer.Chain - alias Explorer.Chain.Blackfort.Validator, as: ValidatorBlackfort - - @validators_counter_key "blackfort_validators_counter" - @new_validators_counter_key "new_blackfort_validators_counter" - # It is undesirable to automatically start the consolidation in all environments. # Consider the test environment: if the consolidation initiates but does not # finish before a test ends, that test will fail. This way, hundreds of # tests were failing before disabling the consolidation and the scheduler in # the test env. - config = Application.compile_env(:explorer, __MODULE__) - @enable_consolidation Keyword.get(config, :enable_consolidation) + use Utils.CompileTimeEnvHelper, + enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]], + update_interval_in_milliseconds: [:explorer, [__MODULE__, :update_interval_in_milliseconds]] + + alias Explorer.Chain + alias Explorer.Chain.Blackfort.Validator, as: ValidatorBlackfort - @update_interval_in_milliseconds Keyword.get(config, :update_interval_in_milliseconds) + @validators_counter_key "blackfort_validators_counter" + @new_validators_counter_key "new_blackfort_validators_counter" @doc """ Starts a process to periodically update validators blackfort counters diff --git a/apps/explorer/lib/explorer/chain/cache/contracts_counter.ex b/apps/explorer/lib/explorer/chain/cache/contracts_counter.ex index 27700e13508b..a6ce0162ae2d 100644 --- a/apps/explorer/lib/explorer/chain/cache/contracts_counter.ex +++ b/apps/explorer/lib/explorer/chain/cache/contracts_counter.ex @@ -6,20 +6,18 @@ defmodule Explorer.Chain.Cache.ContractsCounter do """ use GenServer - - alias Explorer.Chain - - @counter_type "contracts_counter" - # It is undesirable to automatically start the consolidation in all environments. # Consider the test environment: if the consolidation initiates but does not # finish before a test ends, that test will fail. This way, hundreds of # tests were failing before disabling the consolidation and the scheduler in # the test env. - config = Application.compile_env(:explorer, Explorer.Chain.Cache.ContractsCounter) - @enable_consolidation Keyword.get(config, :enable_consolidation) + use Utils.CompileTimeEnvHelper, + enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]], + update_interval_in_milliseconds: [:explorer, [__MODULE__, :update_interval_in_milliseconds]] + + alias Explorer.Chain - @update_interval_in_milliseconds Keyword.get(config, :update_interval_in_milliseconds) + @counter_type "contracts_counter" @doc """ Starts a process to periodically update the counter of all the contracts. diff --git a/apps/explorer/lib/explorer/chain/cache/gas_usage.ex b/apps/explorer/lib/explorer/chain/cache/gas_usage.ex index ddff60dd41ab..075411521271 100644 --- a/apps/explorer/lib/explorer/chain/cache/gas_usage.ex +++ b/apps/explorer/lib/explorer/chain/cache/gas_usage.ex @@ -2,6 +2,7 @@ defmodule Explorer.Chain.Cache.GasUsage do @moduledoc """ Cache for total gas usage. """ + use Utils.CompileTimeEnvHelper, enabled: [:explorer, [__MODULE__, :enabled]] require Logger @@ -10,9 +11,6 @@ defmodule Explorer.Chain.Cache.GasUsage do from: 2 ] - config = Application.compile_env(:explorer, __MODULE__) - @enabled Keyword.get(config, :enabled) - use Explorer.Chain.MapCache, name: :gas_usage, key: :sum, diff --git a/apps/explorer/lib/explorer/chain/cache/new_contracts_counter.ex b/apps/explorer/lib/explorer/chain/cache/new_contracts_counter.ex index 8a8d27dd7b52..4c3b611808b3 100644 --- a/apps/explorer/lib/explorer/chain/cache/new_contracts_counter.ex +++ b/apps/explorer/lib/explorer/chain/cache/new_contracts_counter.ex @@ -6,20 +6,18 @@ defmodule Explorer.Chain.Cache.NewContractsCounter do """ use GenServer - - alias Explorer.Chain - - @counter_type "new_contracts_counter" - # It is undesirable to automatically start the consolidation in all environments. # Consider the test environment: if the consolidation initiates but does not # finish before a test ends, that test will fail. This way, hundreds of # tests were failing before disabling the consolidation and the scheduler in # the test env. - config = Application.compile_env(:explorer, Explorer.Chain.Cache.NewContractsCounter) - @enable_consolidation Keyword.get(config, :enable_consolidation) + use Utils.CompileTimeEnvHelper, + enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]], + update_interval_in_milliseconds: [:explorer, [__MODULE__, :update_interval_in_milliseconds]] + + alias Explorer.Chain - @update_interval_in_milliseconds Keyword.get(config, :update_interval_in_milliseconds) + @counter_type "new_contracts_counter" @doc """ Starts a process to periodically update the counter of new diff --git a/apps/explorer/lib/explorer/chain/cache/new_verified_contracts_counter.ex b/apps/explorer/lib/explorer/chain/cache/new_verified_contracts_counter.ex index 019e76ec24e5..c8aec530e580 100644 --- a/apps/explorer/lib/explorer/chain/cache/new_verified_contracts_counter.ex +++ b/apps/explorer/lib/explorer/chain/cache/new_verified_contracts_counter.ex @@ -6,20 +6,18 @@ defmodule Explorer.Chain.Cache.NewVerifiedContractsCounter do """ use GenServer - - alias Explorer.Chain - - @counter_type "new_verified_contracts_counter" - # It is undesirable to automatically start the consolidation in all environments. # Consider the test environment: if the consolidation initiates but does not # finish before a test ends, that test will fail. This way, hundreds of # tests were failing before disabling the consolidation and the scheduler in # the test env. - config = Application.compile_env(:explorer, Explorer.Chain.Cache.NewVerifiedContractsCounter) - @enable_consolidation Keyword.get(config, :enable_consolidation) + use Utils.CompileTimeEnvHelper, + enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]], + update_interval_in_milliseconds: [:explorer, [__MODULE__, :update_interval_in_milliseconds]] + + alias Explorer.Chain - @update_interval_in_milliseconds Keyword.get(config, :update_interval_in_milliseconds) + @counter_type "new_verified_contracts_counter" @doc """ Starts a process to periodically update the counter of new verified diff --git a/apps/explorer/lib/explorer/chain/cache/stability_validators_counters.ex b/apps/explorer/lib/explorer/chain/cache/stability_validators_counters.ex index 1034465848c6..816be2526acf 100644 --- a/apps/explorer/lib/explorer/chain/cache/stability_validators_counters.ex +++ b/apps/explorer/lib/explorer/chain/cache/stability_validators_counters.ex @@ -6,6 +6,14 @@ defmodule Explorer.Chain.Cache.StabilityValidatorsCounters do """ use GenServer + # It is undesirable to automatically start the consolidation in all environments. + # Consider the test environment: if the consolidation initiates but does not + # finish before a test ends, that test will fail. This way, hundreds of + # tests were failing before disabling the consolidation and the scheduler in + # the test env. + use Utils.CompileTimeEnvHelper, + enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]], + update_interval_in_milliseconds: [:explorer, [__MODULE__, :update_interval_in_milliseconds]] alias Explorer.Chain alias Explorer.Chain.Stability.Validator, as: ValidatorStability @@ -14,16 +22,6 @@ defmodule Explorer.Chain.Cache.StabilityValidatorsCounters do @new_validators_counter_key "new_stability_validators_counter" @active_validators_counter_key "active_stability_validators_counter" - # It is undesirable to automatically start the consolidation in all environments. - # Consider the test environment: if the consolidation initiates but does not - # finish before a test ends, that test will fail. This way, hundreds of - # tests were failing before disabling the consolidation and the scheduler in - # the test env. - config = Application.compile_env(:explorer, __MODULE__) - @enable_consolidation Keyword.get(config, :enable_consolidation) - - @update_interval_in_milliseconds Keyword.get(config, :update_interval_in_milliseconds) - @doc """ Starts a process to periodically update validators stability counters """ diff --git a/apps/explorer/lib/explorer/chain/cache/verified_contracts_counter.ex b/apps/explorer/lib/explorer/chain/cache/verified_contracts_counter.ex index 10c850ef10b7..6e942b6217b3 100644 --- a/apps/explorer/lib/explorer/chain/cache/verified_contracts_counter.ex +++ b/apps/explorer/lib/explorer/chain/cache/verified_contracts_counter.ex @@ -6,20 +6,18 @@ defmodule Explorer.Chain.Cache.VerifiedContractsCounter do """ use GenServer - - alias Explorer.Chain - - @counter_type "verified_contracts_counter" - # It is undesirable to automatically start the consolidation in all environments. # Consider the test environment: if the consolidation initiates but does not # finish before a test ends, that test will fail. This way, hundreds of # tests were failing before disabling the consolidation and the scheduler in # the test env. - config = Application.compile_env(:explorer, Explorer.Chain.Cache.VerifiedContractsCounter) - @enable_consolidation Keyword.get(config, :enable_consolidation) + use Utils.CompileTimeEnvHelper, + enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]], + update_interval_in_milliseconds: [:explorer, [__MODULE__, :update_interval_in_milliseconds]] + + alias Explorer.Chain - @update_interval_in_milliseconds Keyword.get(config, :update_interval_in_milliseconds) + @counter_type "verified_contracts_counter" @doc """ Starts a process to periodically update the counter of verified contracts. diff --git a/apps/explorer/lib/explorer/chain/cache/withdrawals_sum.ex b/apps/explorer/lib/explorer/chain/cache/withdrawals_sum.ex index cac2cf3a0384..e83da187aa13 100644 --- a/apps/explorer/lib/explorer/chain/cache/withdrawals_sum.ex +++ b/apps/explorer/lib/explorer/chain/cache/withdrawals_sum.ex @@ -1,16 +1,16 @@ defmodule Explorer.Chain.Cache.WithdrawalsSum do - config = Application.compile_env(:explorer, __MODULE__) - @enable_consolidation Keyword.get(config, :enable_consolidation) - @update_interval_in_milliseconds Keyword.get(config, :update_interval_in_milliseconds) - @moduledoc """ Caches the sum of all withdrawals. - It loads the sum asynchronously and in a time interval of #{@update_interval_in_milliseconds} milliseconds. + It loads the sum asynchronously and in a time interval of 30 minutes. """ use GenServer + use Utils.CompileTimeEnvHelper, + enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]], + update_interval_in_milliseconds: [:explorer, [__MODULE__, :update_interval_in_milliseconds]] + alias Explorer.Chain alias Explorer.Chain.Wei diff --git a/apps/explorer/lib/explorer/chain/events/publisher.ex b/apps/explorer/lib/explorer/chain/events/publisher.ex index a0048248cf73..97e052e0da6e 100644 --- a/apps/explorer/lib/explorer/chain/events/publisher.ex +++ b/apps/explorer/lib/explorer/chain/events/publisher.ex @@ -2,6 +2,7 @@ defmodule Explorer.Chain.Events.Publisher do @moduledoc """ Publishes events related to the Chain context. """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] @common_allowed_events ~w(addresses address_coin_balances address_token_balances address_current_token_balances blocks block_rewards internal_transactions @@ -10,7 +11,7 @@ defmodule Explorer.Chain.Events.Publisher do smart_contract_was_verified zkevm_confirmed_batches eth_bytecode_db_lookup_started smart_contract_was_not_verified)a - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :arbitrum -> @chain_type_specific_allowed_events ~w(new_arbitrum_batches new_messages_to_arbitrum_amount)a diff --git a/apps/explorer/lib/explorer/chain/events/subscriber.ex b/apps/explorer/lib/explorer/chain/events/subscriber.ex index 741422fac3f4..7f4133cd5428 100644 --- a/apps/explorer/lib/explorer/chain/events/subscriber.ex +++ b/apps/explorer/lib/explorer/chain/events/subscriber.ex @@ -2,6 +2,7 @@ defmodule Explorer.Chain.Events.Subscriber do @moduledoc """ Subscribes to events related to the Chain context. """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] @common_allowed_broadcast_events ~w(addresses address_coin_balances address_token_balances address_current_token_balances blocks block_rewards internal_transactions @@ -10,7 +11,7 @@ defmodule Explorer.Chain.Events.Subscriber do smart_contract_was_verified zkevm_confirmed_batches eth_bytecode_db_lookup_started smart_contract_was_not_verified)a - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :arbitrum -> @chain_type_specific_allowed_broadcast_events ~w(new_arbitrum_batches new_messages_to_arbitrum_amount)a diff --git a/apps/explorer/lib/explorer/chain/import/runner/tokens.ex b/apps/explorer/lib/explorer/chain/import/runner/tokens.ex index 31a1686c41aa..618f814cc964 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/tokens.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/tokens.ex @@ -2,6 +2,7 @@ defmodule Explorer.Chain.Import.Runner.Tokens do @moduledoc """ Bulk imports `t:Explorer.Chain.Token.t/0`. """ + use Utils.CompileTimeEnvHelper, bridged_tokens_enabled: [:explorer, [Explorer.Chain.BridgedToken, :enabled]] require Ecto.Query @@ -139,7 +140,7 @@ defmodule Explorer.Chain.Import.Runner.Tokens do ) end - if Application.compile_env(:explorer, Explorer.Chain.BridgedToken)[:enabled] do + if @bridged_tokens_enabled do def default_on_conflict do from( token in Token, diff --git a/apps/explorer/lib/explorer/chain/import/runner/transactions.ex b/apps/explorer/lib/explorer/chain/import/runner/transactions.ex index d16e442f176a..74ae42d19d60 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/transactions.ex @@ -2,6 +2,7 @@ defmodule Explorer.Chain.Import.Runner.Transactions do @moduledoc """ Bulk imports `t:Explorer.Chain.Transaction.t/0`. """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] require Ecto.Query @@ -108,7 +109,7 @@ defmodule Explorer.Chain.Import.Runner.Transactions do end # todo: avoid code duplication - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :suave -> defp default_on_conflict do from( diff --git a/apps/explorer/lib/explorer/chain/internal_transaction/call_type.ex b/apps/explorer/lib/explorer/chain/internal_transaction/call_type.ex index 2925fb408b9c..f10acac94448 100644 --- a/apps/explorer/lib/explorer/chain/internal_transaction/call_type.ex +++ b/apps/explorer/lib/explorer/chain/internal_transaction/call_type.ex @@ -4,9 +4,10 @@ defmodule Explorer.Chain.InternalTransaction.CallType do """ use Ecto.Type + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] @base_call_types ~w(call callcode delegatecall staticcall)a - if Application.compile_env(:explorer, :chain_type) == :arbitrum do + if @chain_type == :arbitrum do @call_types @base_call_types ++ ~w(invalid)a else @call_types @base_call_types @@ -19,7 +20,7 @@ defmodule Explorer.Chain.InternalTransaction.CallType do the current contract's context with the delegated contract's code. There's some good chances for finding bugs when fuzzing these if the memory layout differs between the current contract and the delegated contract. * `:staticcall` - #{if Application.compile_env(:explorer, :chain_type) == :arbitrum do + #{if @chain_type == :arbitrum do """ * `:invalid` """ @@ -27,7 +28,7 @@ defmodule Explorer.Chain.InternalTransaction.CallType do "" end} """ - if Application.compile_env(:explorer, :chain_type) == :arbitrum do + if @chain_type == :arbitrum do @type t :: :call | :callcode | :delegatecall | :staticcall | :invalid else @type t :: :call | :callcode | :delegatecall | :staticcall @@ -71,7 +72,7 @@ defmodule Explorer.Chain.InternalTransaction.CallType do def cast(call_type) when call_type in ["call", "callcode", "delegatecall", "staticcall"], do: {:ok, String.to_existing_atom(call_type)} - if Application.compile_env(:explorer, :chain_type) == :arbitrum do + if @chain_type == :arbitrum do def cast("invalid"), do: {:ok, :invalid} end @@ -123,7 +124,7 @@ defmodule Explorer.Chain.InternalTransaction.CallType do def load(call_type) when call_type in ["call", "callcode", "delegatecall", "staticcall"], do: {:ok, String.to_existing_atom(call_type)} - if Application.compile_env(:explorer, :chain_type) == :arbitrum do + if @chain_type == :arbitrum do def load("invalid"), do: {:ok, :invalid} end diff --git a/apps/explorer/lib/explorer/chain/internal_transaction/type.ex b/apps/explorer/lib/explorer/chain/internal_transaction/type.ex index a187d174dec3..1b07b5ff7b6b 100644 --- a/apps/explorer/lib/explorer/chain/internal_transaction/type.ex +++ b/apps/explorer/lib/explorer/chain/internal_transaction/type.ex @@ -4,6 +4,7 @@ defmodule Explorer.Chain.InternalTransaction.Type do """ use Ecto.Type + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] @typedoc """ * `:call` @@ -12,7 +13,7 @@ defmodule Explorer.Chain.InternalTransaction.Type do * `:reward` * `:selfdestruct` * `:stop` - #{if Application.compile_env(:explorer, :chain_type) == :arbitrum do + #{if @chain_type == :arbitrum do """ * `:invalid` """ @@ -20,7 +21,7 @@ defmodule Explorer.Chain.InternalTransaction.Type do "" end} """ - if Application.compile_env(:explorer, :chain_type) == :arbitrum do + if @chain_type == :arbitrum do @type t :: :call | :create | :create2 | :reward | :selfdestruct | :stop | :invalid else @type t :: :call | :create | :create2 | :reward | :selfdestruct | :stop @@ -75,7 +76,7 @@ defmodule Explorer.Chain.InternalTransaction.Type do def cast(type) when type in ["call", "create", "create2", "reward", "selfdestruct", "stop"], do: {:ok, String.to_existing_atom(type)} - if Application.compile_env(:explorer, :chain_type) == :arbitrum do + if @chain_type == :arbitrum do def cast("invalid"), do: {:ok, :invalid} end @@ -110,7 +111,7 @@ defmodule Explorer.Chain.InternalTransaction.Type do @spec dump(term()) :: {:ok, String.t()} | :error def dump(type) when type in [:call, :create, :create2, :reward, :selfdestruct, :stop], do: {:ok, Atom.to_string(type)} - if Application.compile_env(:explorer, :chain_type) == :arbitrum do + if @chain_type == :arbitrum do def dump(:invalid), do: {:ok, "invalid"} end @@ -146,7 +147,7 @@ defmodule Explorer.Chain.InternalTransaction.Type do def load(type) when type in ["call", "create", "create2", "reward", "selfdestruct", "stop"], do: {:ok, String.to_existing_atom(type)} - if Application.compile_env(:explorer, :chain_type) == :arbitrum do + if @chain_type == :arbitrum do def load("invalid"), do: {:ok, :invalid} end diff --git a/apps/explorer/lib/explorer/chain/log.ex b/apps/explorer/lib/explorer/chain/log.ex index 8f344a7e1e3a..3feeea8ae673 100644 --- a/apps/explorer/lib/explorer/chain/log.ex +++ b/apps/explorer/lib/explorer/chain/log.ex @@ -1,5 +1,6 @@ defmodule Explorer.Chain.Log.Schema do @moduledoc false + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias Explorer.Chain.{ Address, @@ -19,7 +20,7 @@ defmodule Explorer.Chain.Log.Schema do # violates the primary key constraint. To resolve this issue, we've excluded # `transaction_hash` from the composite primary key when dealing with `:celo` # chain type. - @transaction_field (case Application.compile_env(:explorer, :chain_type) do + @transaction_field (case @chain_type do :celo -> quote do [ @@ -79,6 +80,7 @@ defmodule Explorer.Chain.Log do @moduledoc "Captures a Web3 log entry generated by a transaction" use Explorer.Schema + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] require Explorer.Chain.Log.Schema require Logger @@ -90,7 +92,7 @@ defmodule Explorer.Chain.Log do alias Explorer.SmartContract.SigProviderInterface @required_attrs ~w(address_hash data block_hash index)a - |> (&(case Application.compile_env(:explorer, :chain_type) do + |> (&(case @chain_type do :celo -> &1 @@ -99,7 +101,7 @@ defmodule Explorer.Chain.Log do end)).() @optional_attrs ~w(first_topic second_topic third_topic fourth_topic block_number)a - |> (&(case Application.compile_env(:explorer, :chain_type) do + |> (&(case @chain_type do :celo -> [:transaction_hash | &1] diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index 1fc439ee344b..e0c587e8ec31 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -2,6 +2,8 @@ defmodule Explorer.Chain.SmartContract.Schema do @moduledoc """ Models smart-contract. """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] + alias Explorer.Chain.SmartContract.ExternalLibrary alias Explorer.Chain.{ @@ -11,7 +13,7 @@ defmodule Explorer.Chain.SmartContract.Schema do SmartContractAdditionalSource } - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :zksync -> @chain_type_fields quote( do: [ @@ -104,6 +106,7 @@ defmodule Explorer.Chain.SmartContract do require Explorer.Chain.SmartContract.Schema use Explorer.Schema + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias ABI.FunctionSelector alias Ecto.{Changeset, Multi} @@ -140,7 +143,7 @@ defmodule Explorer.Chain.SmartContract do @optional_changeset_attrs ~w(abi compiler_settings)a @optional_invalid_contract_changeset_attrs ~w(autodetect_constructor_args)a - @chain_type_optional_attrs (case Application.compile_env(:explorer, :chain_type) do + @chain_type_optional_attrs (case @chain_type do :zksync -> ~w(zk_compiler_version)a @@ -397,7 +400,7 @@ defmodule Explorer.Chain.SmartContract do * `name` - the human-readable name of the smart contract. * `compiler_version` - the version of the Solidity compiler used to compile `contract_source_code` with `optimization` into `address` `t:Explorer.Chain.Address.t/0` `contract_code`. - #{case Application.compile_env(:explorer, :chain_type) do + #{case @chain_type do :zksync -> """ * `zk_compiler_version` - the version of ZkSolc or ZkVyper compilers. """ diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex index 8f67e8d8f1cd..0a66653284c9 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy.ex @@ -2,6 +2,7 @@ defmodule Explorer.Chain.SmartContract.Proxy do @moduledoc """ Module for proxy smart-contract implementation detection """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias EthereumJSONRPC.Contract alias Explorer.Chain.{Address, Hash, SmartContract} @@ -584,7 +585,7 @@ defmodule Explorer.Chain.SmartContract.Proxy do end) end - if Application.compile_env(:explorer, :chain_type) == :filecoin do + if @chain_type == :filecoin do def chain_type_fields(%{"address" => address_hash} = address, implementations_info) do Map.put(address, "filecoin_robust_address", implementations_info[address_hash]) end diff --git a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex index 41e82383b0c6..f84b04ea1b6e 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract/proxy/models/implementation.ex @@ -6,6 +6,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do require Logger use Explorer.Schema + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] import Ecto.Query, only: [ @@ -427,7 +428,7 @@ defmodule Explorer.Chain.SmartContract.Proxy.Models.Implementation do def names(_, _), do: [] - if Application.compile_env(:explorer, :chain_type) == :filecoin do + if @chain_type == :filecoin do @doc """ Fetches associated addresses for Filecoin based on the provided nested IDs. diff --git a/apps/explorer/lib/explorer/chain/token.ex b/apps/explorer/lib/explorer/chain/token.ex index 83f1bd9d306c..c521288187d8 100644 --- a/apps/explorer/lib/explorer/chain/token.ex +++ b/apps/explorer/lib/explorer/chain/token.ex @@ -1,9 +1,10 @@ defmodule Explorer.Chain.Token.Schema do @moduledoc false + use Utils.CompileTimeEnvHelper, bridged_token_enabled: [:explorer, [Explorer.Chain.BridgedToken, :enabled]] alias Explorer.Chain.{Address, Hash} - if Application.compile_env(:explorer, Explorer.Chain.BridgedToken)[:enabled] do + if @bridged_token_enabled do @bridged_field [ quote do field(:bridged, :boolean) diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index 4d4aae5b98ac..bed36a7ede6a 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -5,6 +5,7 @@ defmodule Explorer.Chain.TokenTransfer.Schema do Changes in the schema should be reflected in the bulk import module: - Explorer.Chain.Import.Runner.TokenTransfers """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias Explorer.Chain.{ Address, @@ -17,7 +18,7 @@ defmodule Explorer.Chain.TokenTransfer.Schema do # Remove `transaction_hash` from primary key for `:celo` chain type. See # `Explorer.Chain.Log.Schema` for more details. - @transaction_field (case Application.compile_env(:explorer, :chain_type) do + @transaction_field (case @chain_type do :celo -> quote do [ @@ -132,6 +133,7 @@ defmodule Explorer.Chain.TokenTransfer do """ use Explorer.Schema + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] require Explorer.Chain.TokenTransfer.Schema @@ -180,7 +182,7 @@ defmodule Explorer.Chain.TokenTransfer do Explorer.Chain.TokenTransfer.Schema.generate() @required_attrs ~w(block_number log_index from_address_hash to_address_hash token_contract_address_hash block_hash token_type)a - |> (&(case Application.compile_env(:explorer, :chain_type) do + |> (&(case @chain_type do :celo -> &1 @@ -188,7 +190,7 @@ defmodule Explorer.Chain.TokenTransfer do [:transaction_hash | &1] end)).() @optional_attrs ~w(amount amounts token_ids block_consensus)a - |> (&(case Application.compile_env(:explorer, :chain_type) do + |> (&(case @chain_type do :celo -> [:transaction_hash | &1] diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index 67529bbc3a33..fac781f2c785 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -5,6 +5,7 @@ defmodule Explorer.Chain.Transaction.Schema do Changes in the schema should be reflected in the bulk import module: - Explorer.Chain.Import.Runner.Transactions """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias Explorer.Chain @@ -29,7 +30,7 @@ defmodule Explorer.Chain.Transaction.Schema do alias Explorer.Chain.Transaction.{Fork, Status} alias Explorer.Chain.ZkSync.BatchTransaction, as: ZkSyncBatchTransaction - @chain_type_fields (case Application.compile_env(:explorer, :chain_type) do + @chain_type_fields (case @chain_type do :ethereum -> # elem(quote do ... end, 2) doesn't work with a single has_one instruction quote do @@ -296,6 +297,10 @@ defmodule Explorer.Chain.Transaction do use Explorer.Schema + use Utils.CompileTimeEnvHelper, + chain_type: [:explorer, :chain_type], + decode_not_a_contract_calls: [:explorer, :decode_not_a_contract_calls] + require Logger require Explorer.Chain.Transaction.Schema @@ -328,7 +333,7 @@ defmodule Explorer.Chain.Transaction do gas_used index created_contract_code_indexed_at status to_address_hash revert_reason type has_error_in_internal_transactions r s v)a - @chain_type_optional_attrs (case Application.compile_env(:explorer, :chain_type) do + @chain_type_optional_attrs (case @chain_type do :optimism -> ~w(l1_fee l1_fee_scalar l1_gas_price l1_gas_used l1_transaction_origin l1_block_number)a @@ -810,7 +815,7 @@ defmodule Explorer.Chain.Transaction do end # skip decoding if to_address is not a contract unless DECODE_NOT_A_CONTRACT_CALLS is set - if not Application.compile_env(:explorer, :decode_not_a_contract_calls) do + if not @decode_not_a_contract_calls do def decoded_input_data( %__MODULE__{to_address: %{contract_code: nil}}, _, @@ -1853,7 +1858,7 @@ defmodule Explorer.Chain.Transaction do {:maximum, fee_calc(transaction, gas_price, gas, unit)} end - if Application.compile_env(:explorer, :chain_type) == :optimism do + if @chain_type == :optimism do def fee(%Transaction{gas_price: nil, gas_used: _gas_used}, _unit) do {:actual, nil} end diff --git a/apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex b/apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex index c8f6599c4c9b..32c956e1cbe4 100644 --- a/apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex +++ b/apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex @@ -3,6 +3,7 @@ defmodule Explorer.Counters.AddressTransactionsGasUsageCounter do Caches Address transactions gas usage counter. """ use GenServer + use Utils.CompileTimeEnvHelper, enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]] alias Ecto.Changeset alias Explorer.Chain.Address.Counters @@ -12,9 +13,6 @@ defmodule Explorer.Counters.AddressTransactionsGasUsageCounter do @cache_name :address_transactions_gas_usage_counter @last_update_key "last_update" - config = Application.compile_env(:explorer, __MODULE__) - @enable_consolidation Keyword.get(config, :enable_consolidation) - @spec start_link(term()) :: GenServer.on_start() def start_link(_) do GenServer.start_link(__MODULE__, :ok, name: __MODULE__) diff --git a/apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex b/apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex index a5a7ea300c9d..cb5a2d2b2317 100644 --- a/apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex +++ b/apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex @@ -3,6 +3,7 @@ defmodule Explorer.Counters.AddressTokenTransfersCounter do Caches Address token transfers counter. """ use GenServer + use Utils.CompileTimeEnvHelper, enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]] alias Ecto.Changeset alias Explorer.Chain.Address.Counters @@ -12,9 +13,6 @@ defmodule Explorer.Counters.AddressTokenTransfersCounter do @cache_name :address_token_transfers_counter @last_update_key "last_update" - config = Application.compile_env(:explorer, __MODULE__) - @enable_consolidation Keyword.get(config, :enable_consolidation) - @spec start_link(term()) :: GenServer.on_start() def start_link(_) do GenServer.start_link(__MODULE__, :ok, name: __MODULE__) diff --git a/apps/explorer/lib/explorer/counters/address_tokens_usd_sum.ex b/apps/explorer/lib/explorer/counters/address_tokens_usd_sum.ex index 0712be9307ec..d34d01277a4c 100644 --- a/apps/explorer/lib/explorer/counters/address_tokens_usd_sum.ex +++ b/apps/explorer/lib/explorer/counters/address_tokens_usd_sum.ex @@ -3,6 +3,7 @@ defmodule Explorer.Counters.AddressTokenUsdSum do Caches Address tokens USD value. """ use GenServer + use Utils.CompileTimeEnvHelper, enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]] alias Explorer.Chain alias Explorer.Counters.Helper @@ -10,9 +11,6 @@ defmodule Explorer.Counters.AddressTokenUsdSum do @cache_name :address_tokens_fiat_value @last_update_key "last_update" - config = Application.compile_env(:explorer, Explorer.Counters.AddressTokenUsdSum) - @enable_consolidation Keyword.get(config, :enable_consolidation) - @spec start_link(term()) :: GenServer.on_start() def start_link(_) do GenServer.start_link(__MODULE__, :ok, name: __MODULE__) diff --git a/apps/explorer/lib/explorer/counters/address_transactions_counter.ex b/apps/explorer/lib/explorer/counters/address_transactions_counter.ex index 75a7aaea2ea1..f2ffb505bfe8 100644 --- a/apps/explorer/lib/explorer/counters/address_transactions_counter.ex +++ b/apps/explorer/lib/explorer/counters/address_transactions_counter.ex @@ -3,6 +3,7 @@ defmodule Explorer.Counters.AddressTransactionsCounter do Caches Address transactions counter. """ use GenServer + use Utils.CompileTimeEnvHelper, enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]] alias Ecto.Changeset alias Explorer.Chain.Address.Counters @@ -12,9 +13,6 @@ defmodule Explorer.Counters.AddressTransactionsCounter do @cache_name :address_transactions_counter @last_update_key "last_update" - config = Application.compile_env(:explorer, __MODULE__) - @enable_consolidation Keyword.get(config, :enable_consolidation) - @spec start_link(term()) :: GenServer.on_start() def start_link(_) do GenServer.start_link(__MODULE__, :ok, name: __MODULE__) diff --git a/apps/explorer/lib/explorer/counters/addresses_counter.ex b/apps/explorer/lib/explorer/counters/addresses_counter.ex index 51fb845bb945..53633844d75a 100644 --- a/apps/explorer/lib/explorer/counters/addresses_counter.ex +++ b/apps/explorer/lib/explorer/counters/addresses_counter.ex @@ -6,6 +6,14 @@ defmodule Explorer.Counters.AddressesCounter do """ use GenServer + # It is undesirable to automatically start the consolidation in all environments. + # Consider the test environment: if the consolidation initiates but does not + # finish before a test ends, that test will fail. This way, hundreds of + # tests were failing before disabling the consolidation and the scheduler in + # the test env. + use Utils.CompileTimeEnvHelper, + enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]], + update_interval_in_milliseconds: [:explorer, [__MODULE__, :update_interval_in_milliseconds]] alias Explorer.Chain.Address.Counters @@ -21,16 +29,6 @@ defmodule Explorer.Counters.AddressesCounter do @cache_key end - # It is undesirable to automatically start the consolidation in all environments. - # Consider the test environment: if the consolidation initiates but does not - # finish before a test ends, that test will fail. This way, hundreds of - # tests were failing before disabling the consolidation and the scheduler in - # the test env. - config = Application.compile_env(:explorer, Explorer.Counters.AddressesCounter) - @enable_consolidation Keyword.get(config, :enable_consolidation) - - @update_interval_in_milliseconds Keyword.get(config, :update_interval_in_milliseconds) - @doc """ Starts a process to periodically update the counter of the token holders. """ diff --git a/apps/explorer/lib/explorer/counters/addresses_with_balance_counter.ex b/apps/explorer/lib/explorer/counters/addresses_with_balance_counter.ex index 53621029afd7..5cb090f70e4d 100644 --- a/apps/explorer/lib/explorer/counters/addresses_with_balance_counter.ex +++ b/apps/explorer/lib/explorer/counters/addresses_with_balance_counter.ex @@ -6,6 +6,14 @@ defmodule Explorer.Counters.AddressesWithBalanceCounter do """ use GenServer + # It is undesirable to automatically start the consolidation in all environments. + # Consider the test environment: if the consolidation initiates but does not + # finish before a test ends, that test will fail. This way, hundreds of + # tests were failing before disabling the consolidation and the scheduler in + # the test env. + use Utils.CompileTimeEnvHelper, + enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]], + update_interval_in_milliseconds: [:explorer, [__MODULE__, :update_interval_in_milliseconds]] alias Explorer.Chain.Address.Counters @@ -21,16 +29,6 @@ defmodule Explorer.Counters.AddressesWithBalanceCounter do @cache_key end - # It is undesirable to automatically start the consolidation in all environments. - # Consider the test environment: if the consolidation initiates but does not - # finish before a test ends, that test will fail. This way, hundreds of - # tests were failing before disabling the consolidation and the scheduler in - # the test env. - config = Application.compile_env(:explorer, Explorer.Counters.AddressesWithBalanceCounter) - @enable_consolidation Keyword.get(config, :enable_consolidation) - - @update_interval_in_milliseconds Keyword.get(config, :update_interval_in_milliseconds) - @doc """ Starts a process to periodically update the counter of the token holders. """ diff --git a/apps/explorer/lib/explorer/counters/block_burnt_fee_counter.ex b/apps/explorer/lib/explorer/counters/block_burnt_fee_counter.ex index 64c02d50fc09..ce1ea8cdf344 100644 --- a/apps/explorer/lib/explorer/counters/block_burnt_fee_counter.ex +++ b/apps/explorer/lib/explorer/counters/block_burnt_fee_counter.ex @@ -3,15 +3,13 @@ defmodule Explorer.Counters.BlockBurntFeeCounter do Caches Block Burnt Fee counter. """ use GenServer + use Utils.CompileTimeEnvHelper, enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]] alias Explorer.Chain alias Explorer.Counters.Helper @cache_name :block_burnt_fee_counter - config = Application.compile_env(:explorer, __MODULE__) - @enable_consolidation Keyword.get(config, :enable_consolidation) - @spec start_link(term()) :: GenServer.on_start() def start_link(_) do GenServer.start_link(__MODULE__, :ok, name: __MODULE__) diff --git a/apps/explorer/lib/explorer/counters/block_priority_fee_counter.ex b/apps/explorer/lib/explorer/counters/block_priority_fee_counter.ex index 79cd92fb8d3a..0a1c9cdc01a6 100644 --- a/apps/explorer/lib/explorer/counters/block_priority_fee_counter.ex +++ b/apps/explorer/lib/explorer/counters/block_priority_fee_counter.ex @@ -3,15 +3,13 @@ defmodule Explorer.Counters.BlockPriorityFeeCounter do Caches Block Priority Fee counter. """ use GenServer + use Utils.CompileTimeEnvHelper, enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]] alias Explorer.Chain alias Explorer.Counters.Helper @cache_name :block_priority_fee_counter - config = Application.compile_env(:explorer, Explorer.Counters.BlockPriorityFeeCounter) - @enable_consolidation Keyword.get(config, :enable_consolidation) - @spec start_link(term()) :: GenServer.on_start() def start_link(_) do GenServer.start_link(__MODULE__, :ok, name: __MODULE__) diff --git a/apps/explorer/lib/explorer/counters/token_holders_counter.ex b/apps/explorer/lib/explorer/counters/token_holders_counter.ex index e8e12a3aa66c..391c4ad44e35 100644 --- a/apps/explorer/lib/explorer/counters/token_holders_counter.ex +++ b/apps/explorer/lib/explorer/counters/token_holders_counter.ex @@ -3,6 +3,7 @@ defmodule Explorer.Counters.TokenHoldersCounter do Caches Token holders counter. """ use GenServer + use Utils.CompileTimeEnvHelper, enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]] alias Explorer.Chain.Address.CurrentTokenBalance alias Explorer.Chain.Token @@ -12,9 +13,6 @@ defmodule Explorer.Counters.TokenHoldersCounter do @cache_name :token_holders_count @ets_last_update_key "last_update" - config = Application.compile_env(:explorer, Explorer.Counters.TokenHoldersCounter) - @enable_consolidation Keyword.get(config, :enable_consolidation) - @spec start_link(term()) :: GenServer.on_start() def start_link(_) do GenServer.start_link(__MODULE__, :ok, name: __MODULE__) diff --git a/apps/explorer/lib/explorer/counters/token_transfers_counter.ex b/apps/explorer/lib/explorer/counters/token_transfers_counter.ex index 75fb27e5e72d..d4cf9f91b0b7 100644 --- a/apps/explorer/lib/explorer/counters/token_transfers_counter.ex +++ b/apps/explorer/lib/explorer/counters/token_transfers_counter.ex @@ -3,6 +3,7 @@ defmodule Explorer.Counters.TokenTransfersCounter do Caches Token transfers counter. """ use GenServer + use Utils.CompileTimeEnvHelper, enable_consolidation: [:explorer, [__MODULE__, :enable_consolidation]] alias Explorer.Chain alias Explorer.Counters.Helper @@ -10,9 +11,6 @@ defmodule Explorer.Counters.TokenTransfersCounter do @cache_name :token_transfers_counter @last_update_key "last_update" - config = Application.compile_env(:explorer, Explorer.Counters.TokenTransfersCounter) - @enable_consolidation Keyword.get(config, :enable_consolidation) - @spec start_link(term()) :: GenServer.on_start() def start_link(_) do GenServer.start_link(__MODULE__, :ok, name: __MODULE__) diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index f69c2a17bc7f..9a76c73d8e0c 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -67,7 +67,6 @@ defmodule Explorer.Mixfile do {:bypass, "~> 2.1", only: :test}, {:briefly, "~> 0.4", github: "CargoSense/briefly"}, {:comeonin, "~> 5.3"}, - {:credo, "~> 1.5", only: :test, runtime: false}, # For Absinthe to load data in batches {:dataloader, "~> 2.0.0"}, {:decimal, "~> 2.0"}, @@ -128,7 +127,8 @@ defmodule Explorer.Mixfile do {:ueberauth_auth0, "~> 2.0"}, {:oauth2, "~> 2.0"}, {:siwe, github: "royal-markets/siwe-ex", ref: "51c9c08240eb7eea3c35693011f8d260cd9bb3be"}, - {:joken, "~> 2.6"} + {:joken, "~> 2.6"}, + {:utils, in_umbrella: true} ] end diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index 3a78abcc6d91..ddbc1d2d8524 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -1,5 +1,6 @@ defmodule Explorer.Factory do use ExMachina.Ecto, repo: Explorer.Repo + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] require Ecto.Query @@ -63,7 +64,7 @@ defmodule Explorer.Factory do alias Ueberauth.Auth.{Extra, Info} alias Ueberauth.Auth - if Application.compile_env(:explorer, :chain_type) == :zksync do + if @chain_type == :zksync do @optimization_runs "1" else @optimization_runs 1 @@ -268,7 +269,7 @@ defmodule Explorer.Factory do |> Map.merge(address_factory_chain_type_fields()) end - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :zksync -> defp address_factory_chain_type_fields() do %{ @@ -581,7 +582,7 @@ defmodule Explorer.Factory do |> Map.merge(block_factory_chain_type_fields()) end - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :arbitrum -> defp block_factory_chain_type_fields() do %{ @@ -946,7 +947,7 @@ defmodule Explorer.Factory do |> Map.merge(transaction_factory_chain_type_fields()) end - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :arbitrum -> defp transaction_factory_chain_type_fields() do %{ diff --git a/apps/indexer/lib/indexer/block/catchup/missing_ranges_collector.ex b/apps/indexer/lib/indexer/block/catchup/missing_ranges_collector.ex index ebd10dc41d6b..fc97210c4215 100644 --- a/apps/indexer/lib/indexer/block/catchup/missing_ranges_collector.ex +++ b/apps/indexer/lib/indexer/block/catchup/missing_ranges_collector.ex @@ -4,6 +4,7 @@ defmodule Indexer.Block.Catchup.MissingRangesCollector do """ use GenServer + use Utils.CompileTimeEnvHelper, future_check_interval: [:indexer, [__MODULE__, :future_check_interval]] alias EthereumJSONRPC.Utility.RangesHelper alias Explorer.{Chain, Helper, Repo} @@ -11,7 +12,6 @@ defmodule Indexer.Block.Catchup.MissingRangesCollector do alias Explorer.Utility.{MissingBlockRange, MissingRangesManipulator} @default_missing_ranges_batch_size 100_000 - @future_check_interval Application.compile_env(:indexer, __MODULE__)[:future_check_interval] @past_check_interval 10 @increased_past_check_interval :timer.minutes(1) diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 4105e4382520..afc5f4f676fa 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -4,6 +4,7 @@ defmodule Indexer.Block.Fetcher do """ use Spandex.Decorators + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] require Logger @@ -289,7 +290,7 @@ defmodule Indexer.Block.Fetcher do end end - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :ethereum -> defp import_options(basic_import_options, %{transactions_with_receipts: transactions_with_receipts}) do basic_import_options diff --git a/apps/indexer/lib/indexer/block/realtime/fetcher.ex b/apps/indexer/lib/indexer/block/realtime/fetcher.ex index c0d4650efc39..59fef6de6256 100644 --- a/apps/indexer/lib/indexer/block/realtime/fetcher.ex +++ b/apps/indexer/lib/indexer/block/realtime/fetcher.ex @@ -5,6 +5,7 @@ defmodule Indexer.Block.Realtime.Fetcher do use GenServer use Spandex.Decorators + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] require Indexer.Tracer require Logger @@ -165,7 +166,7 @@ defmodule Indexer.Block.Realtime.Fetcher do Process.cancel_timer(timer) end - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :stability -> defp fetch_validators_async do GenServer.cast(Indexer.Fetcher.Stability.Validator, :update_validators_list) @@ -307,7 +308,7 @@ defmodule Indexer.Block.Realtime.Fetcher do @spec remove_assets_by_number(non_neg_integer()) :: any() - case Application.compile_env(:explorer, :chain_type) do + case @chain_type do :optimism -> # Removes all rows from `op_transaction_batches` and `op_withdrawals` tables # previously written starting from the reorg block number diff --git a/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex b/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex index 0860c65c93db..772440833db9 100644 --- a/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex +++ b/apps/indexer/lib/indexer/fetcher/rollup_l1_reorg_monitor.ex @@ -9,6 +9,7 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do use GenServer use Indexer.Fetcher + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] require Logger @@ -18,7 +19,7 @@ defmodule Indexer.Fetcher.RollupL1ReorgMonitor do @fetcher_name :rollup_l1_reorg_monitor @start_recheck_period_seconds 3 - @modules_can_use_reorg_monitor (case Application.compile_env(:explorer, :chain_type) do + @modules_can_use_reorg_monitor (case @chain_type do :optimism -> [ Indexer.Fetcher.Optimism.Deposit, diff --git a/apps/indexer/lib/indexer/prometheus/collector/filecoin_pending_address_operations_collector.ex b/apps/indexer/lib/indexer/prometheus/collector/filecoin_pending_address_operations_collector.ex index 207990ca456b..b21677173168 100644 --- a/apps/indexer/lib/indexer/prometheus/collector/filecoin_pending_address_operations_collector.ex +++ b/apps/indexer/lib/indexer/prometheus/collector/filecoin_pending_address_operations_collector.ex @@ -1,14 +1,16 @@ -if Application.compile_env(:explorer, :chain_type) == :filecoin do - defmodule Indexer.Prometheus.Collector.FilecoinPendingAddressOperations do - @moduledoc """ - Custom collector to count number of records in filecoin_pending_address_operations table. - """ +defmodule Indexer.Prometheus.Collector.FilecoinPendingAddressOperations do + @moduledoc """ + Custom collector to count number of records in filecoin_pending_address_operations table. + """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] + if @chain_type == :filecoin do use Prometheus.Collector - alias Explorer.Chain.Filecoin.PendingAddressOperation - alias Explorer.Repo - alias Prometheus.Model + # TODO: remove when https://github.com/elixir-lang/elixir/issues/13975 comes to elixir release + alias Explorer.Chain.Filecoin.PendingAddressOperation, warn: false + alias Explorer.Repo, warn: false + alias Prometheus.Model, warn: false def collect_mf(_registry, callback) do callback.( diff --git a/apps/indexer/lib/indexer/transform/address_coin_balances.ex b/apps/indexer/lib/indexer/transform/address_coin_balances.ex index 84d692c5caeb..14882d5682d0 100644 --- a/apps/indexer/lib/indexer/transform/address_coin_balances.ex +++ b/apps/indexer/lib/indexer/transform/address_coin_balances.ex @@ -2,6 +2,7 @@ defmodule Indexer.Transform.AddressCoinBalances do @moduledoc """ Extracts `Explorer.Chain.Address.CoinBalance` params from other schema's params. """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias Explorer.Chain.TokenTransfer @@ -114,7 +115,7 @@ defmodule Indexer.Transform.AddressCoinBalances do |> (&transactions_params_chain_type_fields_reducer(transaction_params, &1)).() end - if Application.compile_env(:explorer, :chain_type) == :celo do + if @chain_type == :celo do import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0] @burn_address_hash_string burn_address_hash_string() diff --git a/apps/indexer/lib/indexer/transform/addresses.ex b/apps/indexer/lib/indexer/transform/addresses.ex index 59a9e0b8376a..ca4e4939ebff 100644 --- a/apps/indexer/lib/indexer/transform/addresses.ex +++ b/apps/indexer/lib/indexer/transform/addresses.ex @@ -47,6 +47,7 @@ defmodule Indexer.Transform.Addresses do ] } """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] alias Indexer.Helper @@ -73,7 +74,7 @@ defmodule Indexer.Transform.Addresses do %{from: :block_number, to: :fetched_coin_balance_block_number}, %{from: :to_address_hash, to: :hash} ], - if Application.compile_env(:explorer, :chain_type) == :zksync do + if @chain_type == :zksync do [ %{from: :block_number, to: :fetched_coin_balance_block_number}, %{from: :created_contract_address_hash, to: :hash} diff --git a/apps/indexer/lib/indexer/transform/scroll/l1_fee_params.ex b/apps/indexer/lib/indexer/transform/scroll/l1_fee_params.ex index 12875255817c..4615266a7519 100644 --- a/apps/indexer/lib/indexer/transform/scroll/l1_fee_params.ex +++ b/apps/indexer/lib/indexer/transform/scroll/l1_fee_params.ex @@ -3,6 +3,7 @@ defmodule Indexer.Transform.Scroll.L1FeeParams do Helper functions for transforming data for Scroll L1 fee parameters in realtime block fetcher. """ + use Utils.CompileTimeEnvHelper, chain_type: [:explorer, :chain_type] require Logger @@ -22,7 +23,7 @@ defmodule Indexer.Transform.Scroll.L1FeeParams do @spec parse([map()]) :: [Explorer.Chain.Scroll.L1FeeParam.to_import()] def parse(logs) - if Application.compile_env(:explorer, :chain_type) == :scroll do + if @chain_type == :scroll do def parse(logs) do prev_metadata = Logger.metadata() Logger.metadata(fetcher: :scroll_l1_fee_params_realtime) diff --git a/apps/indexer/mix.exs b/apps/indexer/mix.exs index c84d313fc024..17331a8bbdb8 100644 --- a/apps/indexer/mix.exs +++ b/apps/indexer/mix.exs @@ -70,7 +70,8 @@ defmodule Indexer.MixProject do # `:spandex` integration with Datadog {:spandex_datadog, "~> 1.0"}, {:logger_json, "~> 5.1"}, - {:varint, "~> 1.4"} + {:varint, "~> 1.4"}, + {:utils, in_umbrella: true} ] end diff --git a/apps/utils/.gitignore b/apps/utils/.gitignore new file mode 100644 index 000000000000..7f38206dd8d0 --- /dev/null +++ b/apps/utils/.gitignore @@ -0,0 +1,26 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +utils-*.tar + +# Temporary files, for example, from tests. +/tmp/ diff --git a/apps/utils/lib/credo/checks/compile_env_usage.ex b/apps/utils/lib/credo/checks/compile_env_usage.ex new file mode 100644 index 000000000000..b17505011edc --- /dev/null +++ b/apps/utils/lib/credo/checks/compile_env_usage.ex @@ -0,0 +1,54 @@ +defmodule Utils.Credo.Checks.CompileEnvUsage do + @moduledoc """ + Disallows usage of Application.compile_env throughout the codebase, + except in Utils.CompileTimeEnvHelper module. + + Application.compile_env should generally be avoided as it makes the code + harder to test and configure dynamically. + """ + + @explanation [ + check: @moduledoc, + params: [] + ] + + use Credo.Check, base_priority: :high, category: :warning + + alias Credo.Code + + @doc false + @impl true + def run(%SourceFile{} = source_file, params \\ []) do + issue_meta = IssueMeta.for(source_file, params) + + # Skip check for Utils.CompileTimeEnvHelper module + if String.ends_with?(source_file.filename, "utils/compile_time_env_helper.ex") do + [] + else + Code.prewalk(source_file, &traverse(&1, &2, issue_meta)) + end + end + + defp traverse({:., _, [{:__aliases__, _, [:Application]}, :compile_env]} = ast, issues, issue_meta) do + {ast, [issue_for(issue_meta, Macro.to_string(ast)) | issues]} + end + + defp traverse({:., _, [{:__aliases__, _, [:Application]}, :compile_env!]} = ast, issues, issue_meta) do + {ast, [issue_for(issue_meta, Macro.to_string(ast)) | issues]} + end + + defp traverse(ast, issues, _issue_meta) do + {ast, issues} + end + + defp issue_for(issue_meta, trigger) do + format_issue( + issue_meta, + message: """ + Avoid using Application.compile_env, use runtime configuration instead. If you need compile-time config, use Utils.CompileTimeEnvHelper. + More details: https://github.com/blockscout/blockscout/tree/master/CONTRIBUTING.md#compile-time-environment-variables + """, + trigger: trigger + ) + end +end diff --git a/apps/utils/lib/utils/compile_time_env_helper.ex b/apps/utils/lib/utils/compile_time_env_helper.ex new file mode 100644 index 000000000000..d5f50191cd1b --- /dev/null +++ b/apps/utils/lib/utils/compile_time_env_helper.ex @@ -0,0 +1,170 @@ +defmodule Utils.CompileTimeEnvHelper do + @moduledoc """ + A module that helps with compile time environment variable handling and automatic + module recompilation when environment variables change. + + ## Motivation + + Direct use of `Application.compile_env/3` causes error when runtime value + of environment variable value do not match compile time value, + this error halts the compilation and requires recompilation of the whole app. + This module prevents this since module is being recompiled automatically when + environment variable value changes. So, this module solves two issues: + + 1. Compile time error is avoided, so the app can be run without whole recompilation. + 2. No need to recompile the whole app when environment variable value changes, that + speed up compilation of different app versions. + + ## How It Works + + The module implements the `__mix_recompile__?/0` callback which Mix uses to determine + if a module needs recompilation. It tracks the values of specified environment variables + at compile time and triggers recompilation when these values change. + + ## Configuration + + Each key-value pair in the options represents: + - Key: The desired module attribute name + - Value: A list containing two elements: + - First element: The application name (atom) + - Second element: The configuration key or list of nested keys + + ## Examples + + Simple configuration: + + use Utils.CompileTimeEnvHelper, + api_url: [:my_app, :api_url] + + Nested configuration: + + use Utils.CompileTimeEnvHelper, + db_config: [:my_app, [:database, :config]], + api_key: [:my_app, [:api, :credentials, :key]] + + ## Technical Details + + 1. **Compile Time Value Tracking** + The module stores the initial values of environment variables during compilation + in a module attribute. These values are used as a reference point for the + `__mix_recompile__?/0` function. + + 2. **Recompilation Logic** + When Mix checks if recompilation is needed, the module compares the current + environment variable values with the stored ones. If any value has changed, + it triggers recompilation of the module. + """ + + # A macro that sets up compile-time environment variable handling. + # + # ## How it works under the hood + # + # 1. When you `use Utils.CompileTimeEnvHelper`, it triggers this macro + # 2. The macro processes your environment configuration and generates necessary code + # using metaprogramming (the `quote` block) + # + # ## Example of generated code + # + # When you write: + # use Utils.CompileTimeEnvHelper, + # api_url: [:my_app, :api_url] + # + # It generates code similar to: + # Module.register_attribute(__MODULE__, :__compile_time_env_vars, accumulate: true) + # + # # Creates @api_url attribute with the compile-time value + # Module.put_attribute( + # __MODULE__, + # :api_url, + # Application.compile_env(:my_app, :api_url) + # ) + # + # # Stores the value for recompilation checking + # Module.put_attribute( + # __MODULE__, + # :__compile_time_env_vars, + # {Application.compile_env(:my_app, :api_url), {:my_app, :api_url}} + # ) + defmacro __using__(env_vars) do + alias Utils.CompileTimeEnvHelper + CompileTimeEnvHelper.__generate_attributes_and_recompile_functions__(env_vars) + end + + @doc """ + Generates the code needed for compile-time environment variable handling. + + ## Technical Details + + This function uses `quote` to create an Abstract Syntax Tree (AST) that will be + injected into the module using this helper. The generated code: + + 1. Creates a module attribute to accumulate environment variables: + ``` + Module.register_attribute(__MODULE__, :__compile_time_env_vars, accumulate: true) + ``` + + 2. For each environment variable in the configuration: + - Creates a module attribute with the compile-time value + - Stores the value and path for recompilation checking + + 3. Generates the `__mix_recompile__?/0` function that Mix uses to determine + if the module needs recompilation + + ## Example + + Given configuration: + api_url: [:my_app, :api_url] + db_host: [:my_app, [:database, :host]] + + This function generates: + # Module attributes for direct access + @api_url Application.compile_env(:my_app, :api_url) + @db_host Application.compile_env(:my_app, [:database, :host]) + + # Storage for recompilation checking + @__compile_time_env_vars [ + {, {:my_app, :api_url}}, + {, {:my_app, [:database, :host]}} + ] + + # Recompilation check function + def __mix_recompile__? do + # Check if any values changed + end + + ## Understanding the Quote Block + + The `quote do ... end` block is Elixir's metaprogramming feature that: + 1. Creates a template of code instead of executing it immediately + 2. This template will be injected into the module that uses this helper + 3. The code inside `quote` is executed when the module is compiled + """ + def __generate_attributes_and_recompile_functions__(env_vars) do + quote do + Module.register_attribute(__MODULE__, :__compile_time_env_vars, accumulate: true) + + for {attribute_name, [app, key_or_path]} <- unquote(env_vars) do + Module.put_attribute( + __MODULE__, + attribute_name, + Application.compile_env(app, key_or_path) + ) + + Module.put_attribute( + __MODULE__, + :__compile_time_env_vars, + {Application.compile_env(app, key_or_path), {app, key_or_path}} + ) + end + + def __mix_recompile__? do + @__compile_time_env_vars + |> Enum.map(fn + {value, {app, [key | path]}} -> value != get_in(Application.get_env(app, key), path) + {value, {app, key}} -> value != Application.get_env(app, key) + end) + |> Enum.any?() + end + end + end +end diff --git a/apps/utils/mix.exs b/apps/utils/mix.exs new file mode 100644 index 000000000000..a4eae3988c0d --- /dev/null +++ b/apps/utils/mix.exs @@ -0,0 +1,39 @@ +defmodule Utils.MixProject do + use Mix.Project + + def project do + [ + app: :utils, + version: "6.9.2", + build_path: "../../_build", + # config_path: "../../config/config.exs", + deps_path: "../../deps", + lockfile: "../../mix.lock", + elixir: "~> 1.17", + elixirc_paths: elixirc_paths(Mix.env()), + start_permanent: Mix.env() == :prod, + deps: deps(), + preferred_cli_env: [ + credo: :test, + dialyzer: :test + ] + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + {:credo, "~> 1.5", only: [:test, :dev], runtime: false} + ] + end + + defp elixirc_paths(:prod), do: ["lib/utils", "lib/*"] + defp elixirc_paths(_), do: ["lib"] +end diff --git a/apps/utils/test/checks/compile_env_usage_test.exs b/apps/utils/test/checks/compile_env_usage_test.exs new file mode 100644 index 000000000000..3422a8ccb3e3 --- /dev/null +++ b/apps/utils/test/checks/compile_env_usage_test.exs @@ -0,0 +1,38 @@ +defmodule Utils.Credo.Checks.CompileEnvUsageTest do + use Credo.Test.Case + + alias Utils.Credo.Checks.CompileEnvUsage + + test "finds violations" do + """ + defmodule CredoSampleModule do + @test Application.compile_env(:blockscout, :test) + end + """ + |> to_source_file() + |> run_check(CompileEnvUsage) + |> assert_issue() + end + + test "ignores compile_time_env_helper.ex" do + """ + defmodule CredoSampleModule do + @test Application.compile_env(:blockscout, :test) + end + """ + |> to_source_file("utils/compile_time_env_helper.ex") + |> run_check(CompileEnvUsage) + |> refute_issues() + end + + test "no false positives" do + """ + defmodule CredoSampleModule do + use Utils.CompileTimeEnvHelper, test: [:blockscout, :test] + end + """ + |> to_source_file() + |> run_check(CompileEnvUsage) + |> refute_issues() + end +end diff --git a/apps/utils/test/test_helper.exs b/apps/utils/test/test_helper.exs new file mode 100644 index 000000000000..97950157e27c --- /dev/null +++ b/apps/utils/test/test_helper.exs @@ -0,0 +1 @@ +{:ok, _} = Application.ensure_all_started(:credo) diff --git a/bin/version_bump.sh b/bin/version_bump.sh index 97583f4a1f89..78f8fa576c4d 100755 --- a/bin/version_bump.sh +++ b/bin/version_bump.sh @@ -7,6 +7,7 @@ MIX_FILES=( "$(pwd)/apps/explorer/mix.exs" "$(pwd)/apps/indexer/mix.exs" "$(pwd)/apps/ethereum_jsonrpc/mix.exs" + "$(pwd)/apps/utils/mix.exs" ) CONFIG_FILE="$(pwd)/rel/config.exs" DOCKER_COMPOSE_FILE="$(pwd)/docker-compose/docker-compose.yml" diff --git a/cspell.json b/cspell.json index cbe17cb92673..9ca6de7bae94 100644 --- a/cspell.json +++ b/cspell.json @@ -337,6 +337,7 @@ "Menlo", "mergeable", "Merkle", + "metaprogramming", "metatags", "microsecs", "millis", @@ -435,6 +436,7 @@ "Postrge", "prederive", "prederived", + "prewalk", "progressbar", "proxiable", "proxying", diff --git a/mix.exs b/mix.exs index 8cb5633b0ca7..aeb99f19332f 100644 --- a/mix.exs +++ b/mix.exs @@ -23,7 +23,8 @@ defmodule BlockScout.Mixfile do block_scout_web: :permanent, ethereum_jsonrpc: :permanent, explorer: :permanent, - indexer: :permanent + indexer: :permanent, + utils: :permanent ], steps: [:assemble, ©_prod_runtime_config/1], validate_compile_env: false @@ -53,7 +54,7 @@ defmodule BlockScout.Mixfile do defp dialyzer() do [ plt_add_deps: :app_tree, - plt_add_apps: ~w(ex_unit mix wallaby)a, + plt_add_apps: ~w(credo ex_unit mix wallaby)a, ignore_warnings: ".dialyzer-ignore", plt_core_path: "priv/plts", plt_file: {:no_warn, "priv/plts/dialyzer.plt"} From a40d37956b7fa7100e454a5f509dafbd46f90c9d Mon Sep 17 00:00:00 2001 From: nikitosing <32202610+nikitosing@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:38:45 +0300 Subject: [PATCH 362/363] feat: Add is_banned to token_instances table (#11235) * Init * some changes * Add is_banned filling logic * Add tests * Set null: true in migration * Process review comments --- apps/explorer/lib/explorer/chain.ex | 25 ++- .../lib/explorer/chain/token/instance.ex | 47 +++- ...25432_add_is_banned_to_token_instances.exs | 9 + .../indexer/fetcher/token_instance/helper.ex | 7 +- .../fetcher/token_instance/helper_test.exs | 203 +++++++++++++++++- 5 files changed, 273 insertions(+), 18 deletions(-) create mode 100644 apps/explorer/priv/repo/migrations/20241002125432_add_is_banned_to_token_instances.exs diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 24c19b1d39e1..b75ca90b9fa0 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -3740,11 +3740,12 @@ defmodule Explorer.Chain do when accumulator: term() def stream_token_instances_with_error(initial, reducer, limited? \\ false) when is_function(reducer, 2) do # likely to get valid metadata - high_priority = ["request error: 429", ":checkout_timeout", ":econnrefused", ":timeout"] + high_priority = ["request error: 429", ":checkout_timeout"] # almost impossible to get valid metadata negative_priority = ["VM execution error", "no uri", "invalid json"] Instance + |> where([instance], is_nil(instance.is_banned) or not instance.is_banned) |> where([instance], not is_nil(instance.error)) |> where([instance], is_nil(instance.refetch_after) or instance.refetch_after < ^DateTime.utc_now()) |> select([instance], %{ @@ -3947,14 +3948,15 @@ defmodule Explorer.Chain do @spec upsert_token_instance(map()) :: {:ok, Instance.t()} | {:error, Ecto.Changeset.t()} def upsert_token_instance(params) do changeset = Instance.changeset(%Instance{}, params) + max_retries_count_before_ban = Instance.error_to_max_retries_count_before_ban(params[:error]) Repo.insert(changeset, - on_conflict: token_instance_metadata_on_conflict(), + on_conflict: token_instance_metadata_on_conflict(max_retries_count_before_ban), conflict_target: [:token_id, :token_contract_address_hash] ) end - defp token_instance_metadata_on_conflict do + defp token_instance_metadata_on_conflict(max_retries_count_before_ban) do config = Application.get_env(:indexer, Indexer.Fetcher.TokenInstance.Retry) coef = config[:exp_timeout_coeff] @@ -3978,16 +3980,29 @@ defmodule Explorer.Chain do refetch_after: fragment( """ - CASE WHEN EXCLUDED.metadata IS NULL THEN - NOW() AT TIME ZONE 'UTC' + interval '1 seconds' * (? * ? ^ LEAST(? + 1.0, ?)) + CASE + WHEN ? > ? THEN + NULL + WHEN EXCLUDED.metadata IS NULL THEN + NOW() AT TIME ZONE 'UTC' + interval '1 seconds' * (? * ? ^ LEAST(? + 1.0, ?)) ELSE NULL END """, + token_instance.retries_count + 1, + ^max_retries_count_before_ban, ^coef, ^base, token_instance.retries_count, ^max_retry_count + ), + is_banned: + fragment( + """ + CASE WHEN ? > ? THEN TRUE ELSE FALSE END + """, + token_instance.retries_count + 1, + ^max_retries_count_before_ban ) ] ], diff --git a/apps/explorer/lib/explorer/chain/token/instance.ex b/apps/explorer/lib/explorer/chain/token/instance.ex index ee3422dfff80..4dc9d44aae9c 100644 --- a/apps/explorer/lib/explorer/chain/token/instance.ex +++ b/apps/explorer/lib/explorer/chain/token/instance.ex @@ -20,6 +20,7 @@ defmodule Explorer.Chain.Token.Instance do * `error` - error fetching token instance * `refetch_after` - when to refetch the token instance * `retries_count` - number of times the token instance has been retried + * `is_banned` - if the token instance is banned """ @primary_key false typed_schema "token_instances" do @@ -32,6 +33,7 @@ defmodule Explorer.Chain.Token.Instance do field(:is_unique, :boolean, virtual: true) field(:refetch_after, :utc_datetime_usec) field(:retries_count, :integer) + field(:is_banned, :boolean, default: false) belongs_to(:owner, Address, foreign_key: :owner_address_hash, references: :hash, type: Hash.Address) @@ -59,7 +61,8 @@ defmodule Explorer.Chain.Token.Instance do :owner_updated_at_block, :owner_updated_at_log_index, :refetch_after, - :retries_count + :retries_count, + :is_banned ]) |> validate_required([:token_id, :token_contract_address_hash]) |> foreign_key_constraint(:token_contract_address_hash) @@ -636,4 +639,46 @@ defmodule Explorer.Chain.Token.Instance do timeout: @timeout ) end + + @max_retries_count_value 32767 + @error_to_ban_interval %{ + 9 => [ + "VM execution error", + "request error: 404", + "no uri", + "ignored host", + "(-32000)", + "invalid ", + "{:max_redirect_overflow, ", + "{:invalid_redirection, ", + "nxdomain", + ":nxdomain", + "econnrefused", + ":econnrefused" + ], + # 32767 is the maximum value for retries_count (smallint) + @max_retries_count_value => ["request error: 429"] + } + + @doc """ + Determines the maximum number of retries allowed before banning based on the given error. + + ## Parameters + - error: The error encountered that may trigger retries. + + ## Returns + - An integer representing the maximum number of retries allowed before a ban is enforced. + """ + @spec error_to_max_retries_count_before_ban(String.t() | nil) :: non_neg_integer() + def error_to_max_retries_count_before_ban(nil) do + @max_retries_count_value + end + + def error_to_max_retries_count_before_ban(error) do + Enum.find_value(@error_to_ban_interval, fn {interval, errors} -> + Enum.any?(errors, fn error_pattern -> + String.starts_with?(error, error_pattern) + end) && interval + end) || 13 + end end diff --git a/apps/explorer/priv/repo/migrations/20241002125432_add_is_banned_to_token_instances.exs b/apps/explorer/priv/repo/migrations/20241002125432_add_is_banned_to_token_instances.exs new file mode 100644 index 000000000000..2e67556f2089 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20241002125432_add_is_banned_to_token_instances.exs @@ -0,0 +1,9 @@ +defmodule Explorer.Repo.Migrations.AddIsBannedToTokenInstances do + use Ecto.Migration + + def change do + alter table(:token_instances) do + add(:is_banned, :boolean, default: false, null: true) + end + end +end diff --git a/apps/indexer/lib/indexer/fetcher/token_instance/helper.ex b/apps/indexer/lib/indexer/fetcher/token_instance/helper.ex index 0987055f3328..cb6a9b4bc3dc 100644 --- a/apps/indexer/lib/indexer/fetcher/token_instance/helper.ex +++ b/apps/indexer/lib/indexer/fetcher/token_instance/helper.ex @@ -252,11 +252,12 @@ defmodule Indexer.Fetcher.TokenInstance.Helper do end end - def normalize_token_id("ERC-721", _token_id), do: nil - - def normalize_token_id(_token_type, token_id), + @spec normalize_token_id(binary(), integer()) :: nil | binary() + defp normalize_token_id("ERC-1155", token_id), do: token_id |> Integer.to_string(16) |> String.downcase() |> String.pad_leading(64, "0") + defp normalize_token_id(_token_type, _token_id), do: nil + defp result_to_insert_params({:ok, %{metadata: metadata}}, token_contract_address_hash, token_id) do %{ token_id: token_id, diff --git a/apps/indexer/test/indexer/fetcher/token_instance/helper_test.exs b/apps/indexer/test/indexer/fetcher/token_instance/helper_test.exs index 03e6f27c1f2f..d30b3753eea7 100644 --- a/apps/indexer/test/indexer/fetcher/token_instance/helper_test.exs +++ b/apps/indexer/test/indexer/fetcher/token_instance/helper_test.exs @@ -407,6 +407,7 @@ defmodule Indexer.Fetcher.TokenInstance.HelperTest do assert instance.retries_count == 0 assert DateTime.diff(refetch_after, instance.refetch_after) < 1 assert !is_nil(instance.error) + assert not instance.is_banned end test "proper updates retries count and refetch after on retry" do @@ -438,6 +439,7 @@ defmodule Indexer.Fetcher.TokenInstance.HelperTest do assert instance.retries_count == 1 assert DateTime.diff(refetch_after, instance.refetch_after) < 1 assert !is_nil(instance.error) + assert not instance.is_banned end test "success insert after retry" do @@ -468,6 +470,7 @@ defmodule Indexer.Fetcher.TokenInstance.HelperTest do assert instance.retries_count == 1 assert DateTime.diff(refetch_after, instance.refetch_after) < 1 assert !is_nil(instance.error) + assert not instance.is_banned token_address = to_string(erc_721_token.contract_address_hash) @@ -511,6 +514,7 @@ defmodule Indexer.Fetcher.TokenInstance.HelperTest do assert instance.retries_count == 2 assert is_nil(instance.refetch_after) assert is_nil(instance.error) + assert not instance.is_banned assert instance.metadata == %{ "name" => "OMNI404 #300067000000000000", @@ -522,12 +526,29 @@ defmodule Indexer.Fetcher.TokenInstance.HelperTest do end test "Don't fail on high retries count" do - config = Application.get_env(:indexer, Indexer.Fetcher.TokenInstance.Retry) + erc_721_token = insert(:token, type: "ERC-721") - coef = config[:exp_timeout_coeff] - base = config[:exp_timeout_base] - max_refetch_interval = config[:max_refetch_interval] + token_instance = + insert(:token_instance, + token_contract_address_hash: erc_721_token.contract_address_hash, + error: "error", + metadata: nil, + retries_count: 50 + ) + + Helper.batch_fetch_instances([ + %{contract_address_hash: token_instance.token_contract_address_hash, token_id: token_instance.token_id} + ]) + + [instance] = Repo.all(Instance) + + assert instance.retries_count == 51 + assert is_nil(instance.refetch_after) + assert !is_nil(instance.error) + assert instance.is_banned + end + test "set is_banned (VM execution error) if retries_count > 9" do erc_721_token = insert(:token, type: "ERC-721") token_instance = @@ -535,21 +556,185 @@ defmodule Indexer.Fetcher.TokenInstance.HelperTest do token_contract_address_hash: erc_721_token.contract_address_hash, error: "error", metadata: nil, - retries_count: 50 + retries_count: 9 ) + token_address = to_string(erc_721_token.contract_address_hash) + + data = + "0xc87b56dd" <> + (ABI.TypeEncoder.encode([Decimal.to_integer(token_instance.token_id)], [{:uint, 256}]) + |> Base.encode16(case: :lower)) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: id, + jsonrpc: "2.0", + method: "eth_call", + params: [ + %{ + data: ^data, + to: ^token_address + }, + "latest" + ] + } + ], + _options -> + {:ok, + [ + %{ + error: %{code: -32015, data: "Reverted 0x", message: "execution reverted"}, + id: id, + jsonrpc: "2.0" + } + ]} + end) + Helper.batch_fetch_instances([ %{contract_address_hash: token_instance.token_contract_address_hash, token_id: token_instance.token_id} ]) - now = DateTime.utc_now() - refetch_after = DateTime.add(now, max_refetch_interval, :millisecond) + [instance] = Repo.all(Instance) + assert instance.error == "VM execution error" + assert instance.is_banned + end + + test "don't set is_banned (VM execution error) if retries_count < 9" do + erc_721_token = insert(:token, type: "ERC-721") + + token_instance = + insert(:token_instance, + token_contract_address_hash: erc_721_token.contract_address_hash, + error: "error", + metadata: nil, + retries_count: 8 + ) + + token_address = to_string(erc_721_token.contract_address_hash) + + data = + "0xc87b56dd" <> + (ABI.TypeEncoder.encode([Decimal.to_integer(token_instance.token_id)], [{:uint, 256}]) + |> Base.encode16(case: :lower)) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: id, + jsonrpc: "2.0", + method: "eth_call", + params: [ + %{ + data: ^data, + to: ^token_address + }, + "latest" + ] + } + ], + _options -> + {:ok, + [ + %{ + error: %{code: -32015, data: "Reverted 0x", message: "execution reverted"}, + id: id, + jsonrpc: "2.0" + } + ]} + end) + + Helper.batch_fetch_instances([ + %{contract_address_hash: token_instance.token_contract_address_hash, token_id: token_instance.token_id} + ]) [instance] = Repo.all(Instance) + assert instance.error =~ "VM execution error" + assert not instance.is_banned + end - assert instance.retries_count == 51 + test "don't set is_banned (429 error)", %{bypass: bypass} do + erc_721_token = insert(:token, type: "ERC-721") + + token_instance = + insert(:token_instance, + token_contract_address_hash: erc_721_token.contract_address_hash, + error: "error", + metadata: nil, + retries_count: 1000 + ) + + token_address = to_string(erc_721_token.contract_address_hash) + + data = + "0xc87b56dd" <> + (ABI.TypeEncoder.encode([Decimal.to_integer(token_instance.token_id)], [{:uint, 256}]) + |> Base.encode16(case: :lower)) + + encoded_url = + "0x" <> + (ABI.TypeEncoder.encode(["http://localhost:#{bypass.port}/api/card"], %ABI.FunctionSelector{ + function: nil, + types: [ + :string + ] + }) + |> Base.encode16(case: :lower)) + + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_call", + params: [ + %{ + data: ^data, + to: ^token_address + }, + "latest" + ] + } + ], + _options -> + {:ok, + [ + %{ + id: 0, + jsonrpc: "2.0", + result: encoded_url + } + ]} + end) + + Bypass.expect( + bypass, + "GET", + "/api/card", + fn conn -> + Conn.resp(conn, 429, "429 Too many requests") + end + ) + + Helper.batch_fetch_instances([ + %{contract_address_hash: token_instance.token_contract_address_hash, token_id: token_instance.token_id} + ]) + + now = DateTime.utc_now() + + refetch_after = + DateTime.add( + now, + Application.get_env(:indexer, Indexer.Fetcher.TokenInstance.Retry)[:max_refetch_interval], + :millisecond + ) + + [instance] = Repo.all(Instance) assert DateTime.diff(refetch_after, instance.refetch_after) < 1 - assert !is_nil(instance.error) + assert instance.error =~ "request error: 429" + assert instance.retries_count == 1001 + assert not instance.is_banned end end end From 228b8bfc13f598820b82c67f6c60e4f5cc7f86dc Mon Sep 17 00:00:00 2001 From: Maxim Filonov <53992153+sl1depengwyn@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:17:07 +0300 Subject: [PATCH 363/363] fix: add utils to dockerfile (#11345) --- docker/Dockerfile | 1 + docker/oldUI.Dockerfile | 1 + 2 files changed, 2 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 3a1e0858f10a..79fe70743d08 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -11,6 +11,7 @@ COPY apps/block_scout_web/mix.exs ./apps/block_scout_web/ COPY apps/explorer/mix.exs ./apps/explorer/ COPY apps/ethereum_jsonrpc/mix.exs ./apps/ethereum_jsonrpc/ COPY apps/indexer/mix.exs ./apps/indexer/ +COPY apps/utils/mix.exs ./apps/utils/ ENV MIX_ENV="prod" ENV MIX_HOME=/opt/mix diff --git a/docker/oldUI.Dockerfile b/docker/oldUI.Dockerfile index a98878763bfd..95c4a1f9b519 100644 --- a/docker/oldUI.Dockerfile +++ b/docker/oldUI.Dockerfile @@ -11,6 +11,7 @@ COPY apps/block_scout_web/mix.exs ./apps/block_scout_web/ COPY apps/explorer/mix.exs ./apps/explorer/ COPY apps/ethereum_jsonrpc/mix.exs ./apps/ethereum_jsonrpc/ COPY apps/indexer/mix.exs ./apps/indexer/ +COPY apps/utils/mix.exs ./apps/utils/ ENV MIX_ENV="prod" ENV MIX_HOME=/opt/mix