diff --git a/apps/block_scout_web/lib/block_scout_web/api_router.ex b/apps/block_scout_web/lib/block_scout_web/api_router.ex index 7cd8dfb601..167c848ee0 100644 --- a/apps/block_scout_web/lib/block_scout_web/api_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/api_router.ex @@ -209,6 +209,10 @@ defmodule BlockScoutWeb.ApiRouter do get("/", V2.WithdrawalController, :withdrawals_list) get("/counters", V2.WithdrawalController, :withdrawals_counters) end + + scope "/zkevm" do + get("/batches/:batch_number", V2.ZkevmController, :batch) + end end scope "/v1", as: :api_v1 do diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/zkevm_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/zkevm_controller.ex new file mode 100644 index 0000000000..119be3e004 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/zkevm_controller.ex @@ -0,0 +1,26 @@ +defmodule BlockScoutWeb.API.V2.ZkevmController do + use BlockScoutWeb, :controller + + alias Explorer.Chain + + action_fallback(BlockScoutWeb.API.V2.FallbackController) + + @batch_necessity_by_association %{ + :sequence_transaction => :optional, + :verify_transaction => :optional, + :l2_transactions => :required + } + + def batch(conn, %{"batch_number" => batch_number} = _params) do + {:ok, batch} = + Chain.zkevm_batch( + batch_number, + necessity_by_association: @batch_necessity_by_association, + api?: true + ) + + conn + |> put_status(200) + |> render(:zkevm_batch, %{batch: batch}) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/zkevm_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/zkevm_view.ex new file mode 100644 index 0000000000..f1207d1527 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/zkevm_view.ex @@ -0,0 +1,22 @@ +defmodule BlockScoutWeb.API.V2.ZkevmView do + use BlockScoutWeb, :view + + def render("zkevm_batch.json", %{batch: batch}) do + %{ + "number" => batch.number, + "status" => batch_status(batch), + "timestamp" => batch.timestamp + } + end + + defp batch_status(batch) do + sequence_id = Map.get(batch, :sequence_id) + verify_id = Map.get(batch, :verify_id) + + cond do + is_nil(sequence_id) && is_nil(verify_id) -> "Unfinalized" + !is_nil(sequence_id) && is_nil(verify_id) -> "L1 Sequence Confirmed" + !is_nil(verify_id) -> "Finalized" + end + end +end diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 9e013ec9a7..a61ad88d54 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -64,7 +64,8 @@ defmodule Explorer.Chain do TokenTransfer, Transaction, Wei, - Withdrawal + Withdrawal, + ZkevmTxnBatch } alias Explorer.Chain.Block.{EmissionReward, Reward} @@ -6389,6 +6390,18 @@ defmodule Explorer.Chain do ) } ) + + def zkevm_batch(number, options \\ []) when is_list(options) do + necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) + + ZkevmTxnBatch + |> where(number: ^number) + |> join_associations(necessity_by_association) + |> select_repo(options).one() + |> case do + nil -> {:error, :not_found} + batch -> {:ok, batch} + end end @spec verified_contracts_top(non_neg_integer()) :: [Hash.Address.t()]