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
np-fix-forever-pending-verification
Fedor Ivanov 1 month ago committed by GitHub
parent e897070d84
commit fae80f7501
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 9
      apps/block_scout_web/lib/block_scout_web/views/api/v2/celo_view.ex
  2. 45
      apps/explorer/lib/explorer/chain/cache/celo_core_contracts.ex
  3. 73
      apps/explorer/lib/explorer/chain/celo/election_reward.ex
  4. 1
      apps/explorer/lib/explorer/chain/celo/reader.ex

@ -221,7 +221,7 @@ defmodule BlockScoutWeb.API.V2.CeloView do
} }
end end
defp prepare_election_reward(%ElectionReward{} = reward) do defp prepare_election_reward(%ElectionReward{token: %Token{}} = reward) do
%{ %{
amount: reward.amount, amount: reward.amount,
block_number: reward.block.number, block_number: reward.block.number,
@ -237,7 +237,12 @@ defmodule BlockScoutWeb.API.V2.CeloView do
reward.associated_account_address, reward.associated_account_address,
reward.associated_account_address_hash 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 end

@ -161,7 +161,33 @@ defmodule Explorer.Chain.Cache.CeloCoreContracts do
end end
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}} <- with {:atom, {:ok, contract_name}} <-
{:atom, Map.fetch(@atom_to_contract_name, contract_atom)}, {:atom, Map.fetch(@atom_to_contract_name, contract_atom)},
{:addresses, {:ok, contract_name_to_addresses}} <- {:addresses, {:ok, contract_name_to_addresses}} <-
@ -223,6 +249,23 @@ defmodule Explorer.Chain.Cache.CeloCoreContracts do
end end
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 def get_first_update_block_number(contract_atom) do
with {:ok, address_updates} <- get_address_updates(contract_atom), with {:ok, address_updates} <- get_address_updates(contract_atom),
%{"updated_at_block_number" => updated_at_block_number} <- %{"updated_at_block_number" => updated_at_block_number} <-

@ -32,8 +32,9 @@ defmodule Explorer.Chain.Celo.ElectionReward do
import Ecto.Query, only: [from: 2, where: 3] import Ecto.Query, only: [from: 2, where: 3]
import Explorer.Helper, only: [safe_parse_non_negative_integer: 1] import Explorer.Helper, only: [safe_parse_non_negative_integer: 1]
alias Explorer.Chain.Cache.CeloCoreContracts
alias Explorer.{Chain, PagingOptions} 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 @type type :: :voter | :validator | :group | :delegated_payment
@types_enum ~w(voter validator group delegated_payment)a @types_enum ~w(voter validator group delegated_payment)a
@ -96,6 +97,8 @@ defmodule Explorer.Chain.Celo.ElectionReward do
null: false null: false
) )
field(:token, :any, virtual: true) :: Token.t() | nil
timestamps() timestamps()
end end
@ -243,10 +246,76 @@ defmodule Explorer.Chain.Celo.ElectionReward do
) )
end 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 """ @doc """
Makes Explorer.PagingOptions map for election rewards. 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 def block_paging_options(params) do
with %{ with %{
"amount" => amount_string, "amount" => amount_string,

@ -52,6 +52,7 @@ defmodule Explorer.Chain.Celo.Reader do
address_hash address_hash
|> ElectionReward.address_hash_to_ordered_rewards_query() |> ElectionReward.address_hash_to_ordered_rewards_query()
|> ElectionReward.join_token()
|> ElectionReward.paginate(paging_options) |> ElectionReward.paginate(paging_options)
|> limit(^paging_options.page_size) |> limit(^paging_options.page_size)
|> join_associations(necessity_by_association) |> join_associations(necessity_by_association)

Loading…
Cancel
Save