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 be905edcd0..804974f88e 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 eab95a7559..ac22f8ae13 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 c5f6d2d93e..2b40f4d879 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 d1c2dfff92..35a534e9e5 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