From 126816af9bf3ebe0a5c47b1691961ddfc68f2c27 Mon Sep 17 00:00:00 2001 From: obasilakis <33531476+obasilakis@users.noreply.github.com> Date: Tue, 30 Nov 2021 15:36:42 +0100 Subject: [PATCH 1/3] Index epoch data (#472) * Fetch data from calculateTargetEpochRewards * Add ABIs for reserve, goldtoken and stabletoken * Ability to give custom name to request * Get rest of necessary epoch data from blockchain * Add celo_pending_epoch_operations table, schema and factory * Add method to insert epoch block to index in the db * Implement celo epoch rewards fetcher's init method * Block fetcher now inserts epoch blocks into celo_pending_epoch_operations table * Format * Rename celo_voter_rewards to celo_epoch_rewards and clean up * Add missing columns to celo_epoch_rewards * Insert celo pending epoch operations as part of the blocks runner instead of fetcher * Insert epoch rewards to table and delete celo pending ops * Format * Credo * Get rid of warnings * Amend migration module's name * Fix tests * Improve error handling * DRYer code * Create new epoch rewards table instead of altering the existing one * Typos * Remove Ecto dependency in the fetcher * Format and dialyze * Add migration that adds initial celo pending epoch operations * Format * Improve test readability slightly * Don't call calculateTargetEpochRewards for the first 10 epoch blocks * Format * Typo * Make sure errors are logged * Start epoch blocks from the 17280th block --- .../explorer/lib/explorer/celo/abi_handler.ex | 5 +- .../lib/explorer/celo/account_reader.ex | 126 +++++- apps/explorer/lib/explorer/celo/util.ex | 6 + apps/explorer/lib/explorer/chain.ex | 39 ++ apps/explorer/lib/explorer/chain/block.ex | 2 + .../lib/explorer/chain/celo_epoch_rewards.ex | 88 ++++ .../chain/celo_pending_epoch_operation.ex | 52 +++ .../lib/explorer/chain/celo_voter_rewards.ex | 64 --- .../explorer/chain/import/runner/blocks.ex | 31 +- .../chain/import/runner/celo_epoch_rewards.ex | 106 +++++ .../chain/import/runner/celo_voter_rewards.ex | 88 ---- .../chain/import/stage/address_referencing.ex | 2 +- apps/explorer/lib/mix/tasks/core_contracts.ex | 11 +- .../priv/contracts_abi/celo/goldtoken.json | 1 + .../priv/contracts_abi/celo/reserve.json | 1 + .../priv/contracts_abi/celo/stabletoken.json | 1 + ...5_create_celo_pending_epoch_operations.exs | 16 + ...183251_create_celo_epoch_rewards_table.exs | 36 ++ ..._initial_celo_pending_epoch_operations.exs | 19 + apps/explorer/test/explorer/chain_test.exs | 12 + apps/explorer/test/support/factory.ex | 13 +- apps/indexer/lib/indexer/block/fetcher.ex | 9 +- .../lib/indexer/fetcher/celo_epoch_rewards.ex | 102 +++++ .../lib/indexer/fetcher/celo_voter_rewards.ex | 118 ------ apps/indexer/lib/indexer/supervisor.ex | 4 +- .../lib/indexer/transform/celo_accounts.ex | 28 -- .../bound_interval_supervisor_test.exs | 4 +- .../indexer/block/catchup/fetcher_test.exs | 4 +- .../test/indexer/block/fetcher_test.exs | 6 +- .../indexer/block/realtime/fetcher_test.exs | 4 +- .../fetcher/celo_epoch_rewards_test.exs | 391 ++++++++++++++++++ ...rds_case.ex => celo_epoch_rewards_case.ex} | 6 +- 32 files changed, 1043 insertions(+), 352 deletions(-) create mode 100644 apps/explorer/lib/explorer/chain/celo_epoch_rewards.ex create mode 100644 apps/explorer/lib/explorer/chain/celo_pending_epoch_operation.ex delete mode 100644 apps/explorer/lib/explorer/chain/celo_voter_rewards.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/celo_epoch_rewards.ex delete mode 100644 apps/explorer/lib/explorer/chain/import/runner/celo_voter_rewards.ex create mode 100644 apps/explorer/priv/contracts_abi/celo/goldtoken.json create mode 100644 apps/explorer/priv/contracts_abi/celo/reserve.json create mode 100644 apps/explorer/priv/contracts_abi/celo/stabletoken.json create mode 100644 apps/explorer/priv/repo/migrations/20211109155555_create_celo_pending_epoch_operations.exs create mode 100644 apps/explorer/priv/repo/migrations/20211111183251_create_celo_epoch_rewards_table.exs create mode 100644 apps/explorer/priv/repo/migrations/20211122160654_add_initial_celo_pending_epoch_operations.exs create mode 100644 apps/indexer/lib/indexer/fetcher/celo_epoch_rewards.ex delete mode 100644 apps/indexer/lib/indexer/fetcher/celo_voter_rewards.ex create mode 100644 apps/indexer/test/indexer/fetcher/celo_epoch_rewards_test.exs rename apps/indexer/test/support/indexer/fetcher/{celo_voter_rewards_case.ex => celo_epoch_rewards_case.ex} (69%) diff --git a/apps/explorer/lib/explorer/celo/abi_handler.ex b/apps/explorer/lib/explorer/celo/abi_handler.ex index 6fc76f55a069..3577bb1a3efd 100644 --- a/apps/explorer/lib/explorer/celo/abi_handler.ex +++ b/apps/explorer/lib/explorer/celo/abi_handler.ex @@ -20,7 +20,10 @@ defmodule Explorer.Celo.AbiHandler do abi("blockchainparameters.json") ++ abi("epochrewards.json") ++ abi("registry.json") ++ - abi("erc20.json") + abi("erc20.json") ++ + abi("reserve.json") ++ + abi("goldtoken.json") ++ + abi("stabletoken.json") {:ok, contract_abi} end diff --git a/apps/explorer/lib/explorer/celo/account_reader.ex b/apps/explorer/lib/explorer/celo/account_reader.ex index a640c65f6546..099bba1199f9 100644 --- a/apps/explorer/lib/explorer/celo/account_reader.ex +++ b/apps/explorer/lib/explorer/celo/account_reader.ex @@ -21,16 +21,18 @@ defmodule Explorer.Celo.AccountReader do account_type = determine_account_type(is_validator, is_validator_group), {:ok, [gold]} <- data["getAccountTotalLockedGold"], {:ok, [nonvoting_gold]} <- data["getAccountNonvotingLockedGold"] do - {:ok, - %{ - address: account_address, - name: name, - url: url, - usd: usd, - locked_gold: gold, - nonvoting_locked_gold: nonvoting_gold, - account_type: account_type - }} + { + :ok, + %{ + address: account_address, + name: name, + url: url, + usd: usd, + locked_gold: gold, + nonvoting_locked_gold: nonvoting_gold, + account_type: account_type + } + } else _ -> :error @@ -55,20 +57,102 @@ defmodule Explorer.Celo.AccountReader do end end - def validator_group_reward_data(address, bn) do + def validator_group_reward_data(%{block_number: bn, block_hash: block_hash}) do + methods = [ + {:epochrewards, "calculateTargetEpochRewards", [], bn}, + {:epochrewards, "getTargetGoldTotalSupply", [], bn}, + {:epochrewards, "getRewardsMultiplier", [], bn}, + {:epochrewards, "getRewardsMultiplierParameters", [], bn}, + {:epochrewards, "getTargetVotingYieldParameters", [], bn}, + {:epochrewards, "getTargetVotingGoldFraction", [], bn}, + {:epochrewards, "getVotingGoldFraction", [], bn}, + {:lockedgold, "getTotalLockedGold", [], bn}, + {:lockedgold, "getNonvotingLockedGold", [], bn}, + {:election, "getTotalVotes", [], bn}, + {:election, "getElectableValidators", [], bn}, + {:reserve, "getReserveGoldBalance", [], bn}, + {:gold, "totalSupply", [], bn, "goldTotalSupply"}, + {:usd, "totalSupply", [], bn, "stableUSDTotalSupply"} + ] + data = - call_methods([ - {:election, "getActiveVotesForGroup", [address], bn - 1}, - {:epochrewards, "calculateTargetEpochRewards", [], bn - 1}, - {:election, "getActiveVotes", [], bn - 1} - ]) + if bn <= 155_520 do + call_methods(Enum.reject(methods, &match?({_, "calculateTargetEpochRewards", _, _}, &1))) + else + call_methods(methods) + end + + with {:ok, [target_total_supply]} <- data["getTargetGoldTotalSupply"], + {:ok, [rewards_multiplier]} <- data["getRewardsMultiplier"], + { + :ok, + [ + rewards_multiplier_max, + rewards_multiplier_under, + rewards_multiplier_over + ] + } <- data["getRewardsMultiplierParameters"], + { + :ok, + [ + target_voting_yield, + target_voting_yield_max, + target_voting_yield_adjustment_factor + ] + } <- data["getTargetVotingYieldParameters"], + {:ok, [target_voting_fraction]} <- data["getTargetVotingGoldFraction"], + {:ok, [voting_fraction]} <- data["getVotingGoldFraction"], + {:ok, [total_locked_gold]} <- data["getTotalLockedGold"], + {:ok, [total_non_voting]} <- data["getNonvotingLockedGold"], + {:ok, [total_votes]} <- data["getTotalVotes"], + {:ok, [_, electable_validators_max]} <- data["getElectableValidators"], + {:ok, [reserve_gold_balance]} <- data["getReserveGoldBalance"], + {:ok, [gold_total_supply]} <- data["goldTotalSupply"], + {:ok, [stable_usd_total_supply]} <- data["stableUSDTotalSupply"] do + response = %{ + target_total_supply: target_total_supply, + rewards_multiplier: rewards_multiplier, + rewards_multiplier_max: rewards_multiplier_max, + rewards_multiplier_under: rewards_multiplier_under, + rewards_multiplier_over: rewards_multiplier_over, + target_voting_yield: target_voting_yield, + target_voting_yield_max: target_voting_yield_max, + target_voting_yield_adjustment_factor: target_voting_yield_adjustment_factor, + target_voting_fraction: target_voting_fraction, + voting_fraction: voting_fraction, + total_locked_gold: total_locked_gold, + total_non_voting: total_non_voting, + total_votes: total_votes, + electable_validators_max: electable_validators_max, + reserve_gold_balance: reserve_gold_balance, + gold_total_supply: gold_total_supply, + stable_usd_total_supply: stable_usd_total_supply, + block_hash: block_hash, + block_number: bn, + epoch_number: div(bn, 17280) + } + + if bn <= 155_520 do + {:ok, response} + else + {:ok, + [ + validator_target_epoch_rewards, + voter_target_epoch_rewards, + community_target_epoch_rewards, + carbon_offsetting_target_epoch_rewards + ]} = data["calculateTargetEpochRewards"] - with {:ok, [active_votes]} <- data["getActiveVotesForGroup"], - {:ok, [total_active_votes]} <- data["getActiveVotes"], - {:ok, [_ | [total_reward | _]]} <- data["calculateTargetEpochRewards"] do - {:ok, %{active_votes: active_votes, total_active_votes: total_active_votes, total_reward: total_reward}} + {:ok, + Map.merge(response, %{ + validator_target_epoch_rewards: validator_target_epoch_rewards, + voter_target_epoch_rewards: voter_target_epoch_rewards, + community_target_epoch_rewards: community_target_epoch_rewards, + carbon_offsetting_target_epoch_rewards: carbon_offsetting_target_epoch_rewards + })} + end else - _ -> :error + error -> error end end diff --git a/apps/explorer/lib/explorer/celo/util.ex b/apps/explorer/lib/explorer/celo/util.ex index d01cba9bb232..73a1c04c5ffa 100644 --- a/apps/explorer/lib/explorer/celo/util.ex +++ b/apps/explorer/lib/explorer/celo/util.ex @@ -27,6 +27,7 @@ defmodule Explorer.Celo.Util do |> Enum.into(%{}, fn {response, {_, function_name, _}} -> {function_name, response} {response, {_, function_name, _, _}} -> {function_name, response} + {response, {_, _, _, _, custom_name}} -> {custom_name, response} end) end @@ -47,6 +48,10 @@ defmodule Explorer.Celo.Util do } end + defp format_request({contract_name, function_name, params, bn, _}) do + format_request({contract_name, function_name, params, bn}) + end + defp contract(:blockchainparameters), do: get_address("BlockchainParameters") defp contract(:lockedgold), do: get_address("LockedGold") defp contract(:validators), do: get_address("Validators") @@ -56,6 +61,7 @@ defmodule Explorer.Celo.Util do defp contract(:gold), do: get_address("GoldToken") defp contract(:usd), do: get_address("StableToken") defp contract(:eur), do: get_address("StableTokenEUR") + defp contract(:reserve), do: get_address("Reserve") defp contract(:real), do: get_address("StableTokenREAL") def get_address(name) do diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 597c7c927dba..306119312b13 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -47,6 +47,7 @@ defmodule Explorer.Chain do CeloAccount, CeloClaims, CeloParams, + CeloPendingEpochOperation, CeloSigners, CeloValidator, CeloValidatorGroup, @@ -2805,6 +2806,23 @@ defmodule Explorer.Chain do Repo.stream_reduce(query, initial, reducer) end + @spec stream_blocks_with_unfetched_epoch_rewards( + initial :: accumulator, + reducer :: (entry :: term(), accumulator -> accumulator) + ) :: {:ok, accumulator} + when accumulator: term() + def stream_blocks_with_unfetched_epoch_rewards(initial, reducer) when is_function(reducer, 2) do + query = + from( + b in Block, + join: celo_pending_ops in assoc(b, :celo_pending_epoch_operations), + where: celo_pending_ops.fetch_epoch_rewards, + select: %{block_number: b.number, block_hash: b.hash} + ) + + Repo.stream_reduce(query, initial, reducer) + end + def remove_nonconsensus_blocks_from_pending_ops(block_hashes) do query = from( @@ -7788,4 +7806,25 @@ defmodule Explorer.Chain do query |> Repo.one() end + + @spec delete_celo_pending_epoch_operation(Hash.Full.t()) :: CeloPendingEpochOperation.t() + def delete_celo_pending_epoch_operation(block_hash) do + celo_pending_operation = Repo.get(CeloPendingEpochOperation, block_hash) + Repo.delete(celo_pending_operation) + end + + def import_epoch_rewards_and_delete_pending_celo_epoch_operations(import_params, success) do + Multi.new() + |> Multi.run(:import_rewards, fn _, _ -> + result = Chain.import(import_params) + {:ok, result} + end) + |> Multi.run(:delete_celo_pending, fn _, _ -> + success + |> Enum.each(fn reward -> Chain.delete_celo_pending_epoch_operation(reward.block_hash) end) + + {:ok, success} + end) + |> Explorer.Repo.transaction() + end end diff --git a/apps/explorer/lib/explorer/chain/block.ex b/apps/explorer/lib/explorer/chain/block.ex index dbffbd9adbf7..44ad0d5a5a35 100644 --- a/apps/explorer/lib/explorer/chain/block.ex +++ b/apps/explorer/lib/explorer/chain/block.ex @@ -9,6 +9,7 @@ defmodule Explorer.Chain.Block do alias Explorer.Chain.{ Address, + CeloPendingEpochOperation, CeloSigners, CeloValidatorHistory, Data, @@ -118,6 +119,7 @@ defmodule Explorer.Chain.Block do has_many(:rewards, Reward, foreign_key: :block_hash) has_one(:pending_operations, PendingBlockOperation, foreign_key: :block_hash) + has_one(:celo_pending_epoch_operations, CeloPendingEpochOperation, foreign_key: :block_hash) has_one(:celo_delegator, CeloSigners, foreign_key: :signer, references: :miner_hash) has_one(:online, CeloValidatorHistory, foreign_key: :block_number, references: :number) has_many(:signers, CeloValidatorHistory, foreign_key: :block_number, references: :number) diff --git a/apps/explorer/lib/explorer/chain/celo_epoch_rewards.ex b/apps/explorer/lib/explorer/chain/celo_epoch_rewards.ex new file mode 100644 index 000000000000..d9058251529f --- /dev/null +++ b/apps/explorer/lib/explorer/chain/celo_epoch_rewards.ex @@ -0,0 +1,88 @@ +defmodule Explorer.Chain.CeloEpochRewards do + @moduledoc """ + Datatype for storing Celo epoch rewards + """ + + require Logger + + use Explorer.Schema + + alias Explorer.Chain.{Block, Hash, Wei} + + @typedoc """ + * `block_hash` - block where this reward was paid. + """ + + @type t :: %__MODULE__{ + block_hash: Hash.Full.t(), + block_number: non_neg_integer(), + epoch_number: non_neg_integer(), + validator_target_epoch_rewards: Wei.t(), + voter_target_epoch_rewards: Wei.t(), + community_target_epoch_rewards: Wei.t(), + carbon_offsetting_target_epoch_rewards: Wei.t(), + target_total_supply: Wei.t(), + rewards_multiplier: float(), + rewards_multiplier_max: float(), + rewards_multiplier_under: float(), + rewards_multiplier_over: float(), + target_voting_yield: float(), + target_voting_yield_max: float(), + target_voting_yield_adjustment_factor: float(), + target_voting_fraction: float(), + voting_fraction: float(), + total_locked_gold: Wei.t(), + total_non_voting: Wei.t(), + total_votes: Wei.t(), + electable_validators_max: non_neg_integer(), + reserve_gold_balance: Wei.t(), + gold_total_supply: Wei.t(), + stable_usd_total_supply: Wei.t() + } + + @attrs ~w( block_hash block_number epoch_number validator_target_epoch_rewards voter_target_epoch_rewards community_target_epoch_rewards carbon_offsetting_target_epoch_rewards target_total_supply rewards_multiplier rewards_multiplier_max rewards_multiplier_under rewards_multiplier_over target_voting_yield target_voting_yield_max target_voting_yield_adjustment_factor target_voting_fraction voting_fraction total_locked_gold total_non_voting total_votes electable_validators_max reserve_gold_balance gold_total_supply stable_usd_total_supply )a + + @required_attrs ~w( block_hash )a + + schema "celo_epoch_rewards" do + field(:block_number, :integer) + field(:epoch_number, :integer) + field(:validator_target_epoch_rewards, Wei) + field(:voter_target_epoch_rewards, Wei) + field(:community_target_epoch_rewards, Wei) + field(:carbon_offsetting_target_epoch_rewards, Wei) + field(:target_total_supply, Wei) + field(:rewards_multiplier, :decimal) + field(:rewards_multiplier_max, :decimal) + field(:rewards_multiplier_under, :decimal) + field(:rewards_multiplier_over, :decimal) + field(:target_voting_yield, :decimal) + field(:target_voting_yield_max, :decimal) + field(:target_voting_yield_adjustment_factor, :decimal) + field(:target_voting_fraction, :decimal) + field(:voting_fraction, :decimal) + field(:total_locked_gold, Wei) + field(:total_non_voting, Wei) + field(:total_votes, Wei) + field(:electable_validators_max, :integer) + field(:reserve_gold_balance, Wei) + field(:gold_total_supply, Wei) + field(:stable_usd_total_supply, Wei) + + belongs_to(:block, Block, + foreign_key: :block_hash, + primary_key: true, + references: :hash, + type: Hash.Full + ) + + timestamps(null: false, type: :utc_datetime_usec) + end + + def changeset(%__MODULE__{} = item, attrs) do + item + |> cast(attrs, @attrs) + |> validate_required(@required_attrs) + |> unique_constraint(:celo_epoch_rewards_key, name: :celo_epoch_rewards_block_hash_index) + end +end diff --git a/apps/explorer/lib/explorer/chain/celo_pending_epoch_operation.ex b/apps/explorer/lib/explorer/chain/celo_pending_epoch_operation.ex new file mode 100644 index 000000000000..5a1a25445618 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/celo_pending_epoch_operation.ex @@ -0,0 +1,52 @@ +defmodule Explorer.Chain.CeloPendingEpochOperation do + @moduledoc """ + Tracks epoch blocks that have pending operations. + """ + + use Explorer.Schema + + alias Explorer.Chain.{Block, Hash} + + @required_attrs ~w(block_hash fetch_epoch_rewards)a + + @typedoc """ + * `block_hash` - the hash of the epoch block that has pending operations. + * `fetch_epoch_rewards` - if the epoch rewards should be fetched (or not) + """ + @type t :: %__MODULE__{ + block_hash: Hash.Full.t(), + fetch_epoch_rewards: boolean() + } + + @primary_key false + schema "celo_pending_epoch_operations" do + field(:fetch_epoch_rewards, :boolean) + + timestamps() + + belongs_to(:block, Block, foreign_key: :block_hash, primary_key: true, references: :hash, type: Hash.Full) + end + + def changeset(%__MODULE__{} = celo_epoch_pending_ops, attrs) do + celo_epoch_pending_ops + |> cast(attrs, @required_attrs) + |> validate_required(@required_attrs) + |> foreign_key_constraint(:block_hash) + |> unique_constraint(:block_hash, name: :celo_pending_epoch_operations_pkey) + end + + def default_on_conflict do + from( + celo_epoch_pending_ops in __MODULE__, + update: [ + set: [ + fetch_epoch_rewards: celo_epoch_pending_ops.fetch_epoch_rewards or fragment("EXCLUDED.fetch_epoch_rewards"), + # Don't update `block_hash` as it is used for the conflict target + inserted_at: celo_epoch_pending_ops.inserted_at, + updated_at: fragment("EXCLUDED.updated_at") + ] + ], + where: fragment("EXCLUDED.fetch_epoch_rewards <> ?", celo_epoch_pending_ops.fetch_epoch_rewards) + ) + end +end diff --git a/apps/explorer/lib/explorer/chain/celo_voter_rewards.ex b/apps/explorer/lib/explorer/chain/celo_voter_rewards.ex deleted file mode 100644 index 363f3a4f21fa..000000000000 --- a/apps/explorer/lib/explorer/chain/celo_voter_rewards.ex +++ /dev/null @@ -1,64 +0,0 @@ -defmodule Explorer.Chain.CeloVoterRewards do - @moduledoc """ - Datatype for storing Celo voter rewards - """ - - require Logger - - use Explorer.Schema - - alias Explorer.Chain.{Address, Block, Hash, Wei} - - @typedoc """ - * `block_hash` - block where this reward was paid. - * `log_index` - Log index for the associated event - """ - - @type t :: %__MODULE__{ - block_hash: Hash.Full.t(), - log_index: non_neg_integer(), - block_number: non_neg_integer(), - address_hash: Hash.Address.t(), - active_votes: Wei.t(), - total_active_votes: Wei.t(), - total_reward: Wei.t(), - reward: Wei.t() - } - - @attrs ~w( block_hash log_index address_hash active_votes reward total_active_votes total_reward block_number )a - - @required_attrs ~w( block_hash log_index )a - - schema "celo_voter_rewards" do - field(:reward, Wei) - field(:active_votes, Wei) - field(:total_reward, Wei) - field(:total_active_votes, Wei) - field(:log_index, :integer) - field(:block_number, :integer) - - belongs_to( - :group_address, - Address, - foreign_key: :address_hash, - references: :hash, - type: Hash.Address - ) - - belongs_to(:block, Block, - foreign_key: :block_hash, - primary_key: true, - references: :hash, - type: Hash.Full - ) - - timestamps(null: false, type: :utc_datetime_usec) - end - - def changeset(%__MODULE__{} = item, attrs) do - item - |> cast(attrs, @attrs) - |> validate_required(@required_attrs) - |> unique_constraint(:celo_voter_rewards_key, name: :celo_voter_rewards_block_hash_log_index_index) - 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 811680ac2be6..4b9fdb60a798 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/blocks.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/blocks.ex @@ -9,7 +9,7 @@ defmodule Explorer.Chain.Import.Runner.Blocks do import Ecto.Query, only: [from: 2] alias Ecto.{Changeset, Multi, Repo} - alias Explorer.Chain.{Block, Import, PendingBlockOperation, Transaction} + alias Explorer.Chain.{Block, CeloPendingEpochOperation, Import, PendingBlockOperation, Transaction} alias Explorer.Chain.Block.Reward alias Explorer.Chain.Import.Runner @@ -64,6 +64,9 @@ defmodule Explorer.Chain.Import.Runner.Blocks do |> Multi.run(:new_pending_operations, fn repo, %{lose_consensus: nonconsensus_hashes} -> new_pending_operations(repo, nonconsensus_hashes, hashes, insert_options) end) + |> Multi.run(:new_celo_pending_operations, fn repo, _ -> + new_celo_pending_operations(repo, changes_list, insert_options) + end) |> Multi.run(:uncle_fetched_block_second_degree_relations, fn repo, _ -> update_block_second_degree_relations(repo, hashes, %{ timeout: @@ -345,6 +348,32 @@ defmodule Explorer.Chain.Import.Runner.Blocks do end end + def new_celo_pending_operations(repo, changes_list, %{timeout: timeout, timestamps: timestamps}) do + if Application.get_env(:explorer, :json_rpc_named_arguments)[:variant] == EthereumJSONRPC.RSK do + {:ok, []} + else + celo_pending_ops = + changes_list + |> Enum.map(& &1.number) + |> MapSet.new() + |> MapSet.to_list() + |> Enum.filter(&(rem(&1, 17280) == 0)) + |> Enum.map(&Enum.find(changes_list, fn block -> block.number == &1 end)) + |> Enum.map(&%{block_hash: &1.hash, fetch_epoch_rewards: true}) + + Import.insert_changes_list( + repo, + celo_pending_ops, + conflict_target: :block_hash, + on_conflict: CeloPendingEpochOperation.default_on_conflict(), + for: CeloPendingEpochOperation, + returning: true, + timeout: timeout, + timestamps: timestamps + ) + end + end + # defp delete_address_token_balances(_, [], _), do: {:ok, []} # # defp delete_address_token_balances(repo, consensus_block_numbers, %{timeout: timeout}) do 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..2ecbdfb4d26e --- /dev/null +++ b/apps/explorer/lib/explorer/chain/import/runner/celo_epoch_rewards.ex @@ -0,0 +1,106 @@ +defmodule Explorer.Chain.Import.Runner.CeloEpochRewards do + @moduledoc """ + Bulk imports Celo voter rewards to the DB table. + """ + + require Ecto.Query + + alias Ecto.{Changeset, Multi, Repo} + alias Explorer.Chain.{CeloEpochRewards, Import} + alias Explorer.Chain.Import.Runner.Util + + import Ecto.Query, only: [from: 2] + + @behaviour Import.Runner + + # milliseconds + @timeout 60_000 + + @type imported :: [CeloEpochRewards.t()] + + @impl Import.Runner + def ecto_schema_module, do: CeloEpochRewards + + @impl Import.Runner + def option_key, do: :celo_epoch_rewards + + @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, options) do + insert_options = Util.make_insert_options(option_key(), @timeout, options) + + # Enforce ShareLocks tables order (see docs: sharelocks.md) + Multi.run(multi, :insert_voter_reward_items, fn repo, _ -> + insert(repo, changes_list, insert_options) + end) + end + + @impl Import.Runner + def timeout, do: @timeout + + @spec insert(Repo.t(), [map()], Util.insert_options()) :: + {:ok, [CeloEpochRewards.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) + + # Enforce ShareLocks order (see docs: sharelocks.md) + uniq_changes_list = + changes_list + |> Enum.sort_by(&{&1.block_hash}) + |> Enum.dedup_by(&{&1.block_hash}) + + {:ok, _} = + Import.insert_changes_list( + repo, + uniq_changes_list, + conflict_target: [:block_hash], + on_conflict: on_conflict, + for: CeloEpochRewards, + returning: true, + timeout: timeout, + timestamps: timestamps + ) + end + + defp default_on_conflict do + from( + account in CeloEpochRewards, + update: [ + set: [ + block_number: fragment("EXCLUDED.block_number"), + epoch_number: fragment("EXCLUDED.epoch_number"), + validator_target_epoch_rewards: fragment("EXCLUDED.validator_target_epoch_rewards"), + voter_target_epoch_rewards: fragment("EXCLUDED.voter_target_epoch_rewards"), + community_target_epoch_rewards: fragment("EXCLUDED.community_target_epoch_rewards"), + carbon_offsetting_target_epoch_rewards: fragment("EXCLUDED.carbon_offsetting_target_epoch_rewards"), + target_total_supply: fragment("EXCLUDED.target_total_supply"), + rewards_multiplier: fragment("EXCLUDED.rewards_multiplier"), + rewards_multiplier_max: fragment("EXCLUDED.rewards_multiplier_max"), + rewards_multiplier_under: fragment("EXCLUDED.rewards_multiplier_under"), + rewards_multiplier_over: fragment("EXCLUDED.rewards_multiplier_over"), + target_voting_yield: fragment("EXCLUDED.target_voting_yield"), + target_voting_yield_max: fragment("EXCLUDED.target_voting_yield_max"), + target_voting_yield_adjustment_factor: fragment("EXCLUDED.target_voting_yield_adjustment_factor"), + target_voting_fraction: fragment("EXCLUDED.target_voting_fraction"), + voting_fraction: fragment("EXCLUDED.voting_fraction"), + total_locked_gold: fragment("EXCLUDED.total_locked_gold"), + total_non_voting: fragment("EXCLUDED.total_non_voting"), + total_votes: fragment("EXCLUDED.total_votes"), + electable_validators_max: fragment("EXCLUDED.electable_validators_max"), + reserve_gold_balance: fragment("EXCLUDED.reserve_gold_balance"), + gold_total_supply: fragment("EXCLUDED.gold_total_supply"), + stable_usd_total_supply: fragment("EXCLUDED.stable_usd_total_supply"), + inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", account.inserted_at), + updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", account.updated_at) + ] + ] + ) + end +end diff --git a/apps/explorer/lib/explorer/chain/import/runner/celo_voter_rewards.ex b/apps/explorer/lib/explorer/chain/import/runner/celo_voter_rewards.ex deleted file mode 100644 index 5508390265c3..000000000000 --- a/apps/explorer/lib/explorer/chain/import/runner/celo_voter_rewards.ex +++ /dev/null @@ -1,88 +0,0 @@ -defmodule Explorer.Chain.Import.Runner.CeloVoterRewards do - @moduledoc """ - Bulk imports Celo voter rewards to the DB table. - """ - - require Ecto.Query - - alias Ecto.{Changeset, Multi, Repo} - alias Explorer.Chain.{CeloVoterRewards, Import} - alias Explorer.Chain.Import.Runner.Util - - import Ecto.Query, only: [from: 2] - - @behaviour Import.Runner - - # milliseconds - @timeout 60_000 - - @type imported :: [CeloVoterRewards.t()] - - @impl Import.Runner - def ecto_schema_module, do: CeloVoterRewards - - @impl Import.Runner - def option_key, do: :celo_voter_rewards - - @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, options) do - insert_options = Util.make_insert_options(option_key(), @timeout, options) - - # Enforce ShareLocks tables order (see docs: sharelocks.md) - Multi.run(multi, :insert_voter_reward_items, fn repo, _ -> - insert(repo, changes_list, insert_options) - end) - end - - @impl Import.Runner - def timeout, do: @timeout - - @spec insert(Repo.t(), [map()], Util.insert_options()) :: - {:ok, [CeloVoterRewards.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) - - # Enforce ShareLocks order (see docs: sharelocks.md) - uniq_changes_list = - changes_list - |> Enum.sort_by(&{&1.block_hash, &1.log_index}) - |> Enum.dedup_by(&{&1.block_hash, &1.log_index}) - - {:ok, _} = - Import.insert_changes_list( - repo, - uniq_changes_list, - conflict_target: [:block_hash, :log_index], - on_conflict: on_conflict, - for: CeloVoterRewards, - returning: true, - timeout: timeout, - timestamps: timestamps - ) - end - - defp default_on_conflict do - from( - account in CeloVoterRewards, - update: [ - set: [ - address_hash: fragment("EXCLUDED.address_hash"), - reward: fragment("EXCLUDED.reward"), - active_votes: fragment("EXCLUDED.active_votes"), - total_reward: fragment("EXCLUDED.total_reward"), - total_active_votes: fragment("EXCLUDED.total_active_votes"), - inserted_at: fragment("LEAST(?, EXCLUDED.inserted_at)", account.inserted_at), - updated_at: fragment("GREATEST(?, EXCLUDED.updated_at)", account.updated_at) - ] - ] - ) - end -end diff --git a/apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex b/apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex index 14057366ece9..0447c959a1b2 100644 --- a/apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex +++ b/apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex @@ -21,7 +21,7 @@ defmodule Explorer.Chain.Import.Stage.AddressReferencing do Runner.CeloValidatorGroups, Runner.CeloValidatorHistory, Runner.CeloValidatorStatus, - Runner.CeloVoterRewards, + Runner.CeloEpochRewards, Runner.CeloVoters, Runner.PendingCelo, Runner.CeloWallets, diff --git a/apps/explorer/lib/mix/tasks/core_contracts.ex b/apps/explorer/lib/mix/tasks/core_contracts.ex index 7da8017bca81..e4948c655709 100644 --- a/apps/explorer/lib/mix/tasks/core_contracts.ex +++ b/apps/explorer/lib/mix/tasks/core_contracts.ex @@ -34,11 +34,10 @@ defmodule Mix.Tasks.CoreContracts do end def query_registry(name, registry_url) do - result = - name - |> request_for_name() - |> perform_request(registry_url) - |> transform_result() + name + |> request_for_name() + |> perform_request(registry_url) + |> transform_result() end defp perform_request(json_body, source_url) do @@ -51,7 +50,7 @@ defmodule Mix.Tasks.CoreContracts do end end - defp transform_result({:ok, %{id: id, result: address}}) do + defp transform_result({:ok, %{result: address}}) do address # last 40 characters of response |> String.slice(-40..-1) diff --git a/apps/explorer/priv/contracts_abi/celo/goldtoken.json b/apps/explorer/priv/contracts_abi/celo/goldtoken.json new file mode 100644 index 000000000000..9ec56839c86e --- /dev/null +++ b/apps/explorer/priv/contracts_abi/celo/goldtoken.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"bool","name":"test","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"registryAddress","type":"address"}],"name":"RegistrySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"comment","type":"string"}],"name":"TransferComment","type":"event"},{"constant":true,"inputs":[],"name":"initialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"registry","outputs":[{"internalType":"contractIRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"registryAddress","type":"address"}],"name":"setRegistry","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getVersionNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"registryAddress","type":"address"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"string","name":"comment","type":"string"}],"name":"transferWithComment","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"increaseSupply","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}] diff --git a/apps/explorer/priv/contracts_abi/celo/reserve.json b/apps/explorer/priv/contracts_abi/celo/reserve.json new file mode 100644 index 000000000000..935d0e761b4b --- /dev/null +++ b/apps/explorer/priv/contracts_abi/celo/reserve.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"bool","name":"test","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32[]","name":"symbols","type":"bytes32[]"},{"indexed":false,"internalType":"uint256[]","name":"weights","type":"uint256[]"}],"name":"AssetAllocationSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"ratio","type":"uint256"}],"name":"DailySpendingRatioSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"exchangeSpender","type":"address"}],"name":"ExchangeSpenderAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"exchangeSpender","type":"address"}],"name":"ExchangeSpenderRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"otherReserveAddress","type":"address"}],"name":"OtherReserveAddressAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"otherReserveAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"OtherReserveAddressRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"registryAddress","type":"address"}],"name":"RegistrySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"ReserveGoldTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"spender","type":"address"}],"name":"SpenderAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"spender","type":"address"}],"name":"SpenderRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TobinTaxReserveRatioSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TobinTaxSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TobinTaxStalenessThresholdSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"TokenAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"TokenRemoved","type":"event"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"assetAllocationSymbols","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"assetAllocationWeights","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"exchangeSpenderAddresses","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"frozenReserveGoldDays","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"frozenReserveGoldStartBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"frozenReserveGoldStartDay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"initialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isExchangeSpender","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isOtherReserveAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isSpender","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lastSpendingDay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"otherReserveAddresses","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"registry","outputs":[{"internalType":"contractIRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"registryAddress","type":"address"}],"name":"setRegistry","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"spendingLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tobinTax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tobinTaxCache","outputs":[{"internalType":"uint128","name":"numerator","type":"uint128"},{"internalType":"uint128","name":"timestamp","type":"uint128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tobinTaxReserveRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tobinTaxStalenessThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getVersionNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"registryAddress","type":"address"},{"internalType":"uint256","name":"_tobinTaxStalenessThreshold","type":"uint256"},{"internalType":"uint256","name":"_spendingRatio","type":"uint256"},{"internalType":"uint256","name":"_frozenGold","type":"uint256"},{"internalType":"uint256","name":"_frozenDays","type":"uint256"},{"internalType":"bytes32[]","name":"_assetAllocationSymbols","type":"bytes32[]"},{"internalType":"uint256[]","name":"_assetAllocationWeights","type":"uint256[]"},{"internalType":"uint256","name":"_tobinTax","type":"uint256"},{"internalType":"uint256","name":"_tobinTaxReserveRatio","type":"uint256"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setTobinTaxStalenessThreshold","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setTobinTax","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setTobinTaxReserveRatio","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"ratio","type":"uint256"}],"name":"setDailySpendingRatio","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getDailySpendingRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"frozenGold","type":"uint256"},{"internalType":"uint256","name":"frozenDays","type":"uint256"}],"name":"setFrozenGold","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32[]","name":"symbols","type":"bytes32[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"}],"name":"setAssetAllocations","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"addToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"removeToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"reserveAddress","type":"address"}],"name":"addOtherReserveAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"reserveAddress","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"removeOtherReserveAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"addSpender","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"removeSpender","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"addExchangeSpender","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"removeExchangeSpender","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getExchangeSpenders","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"addresspayable","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferGold","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"addresspayable","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferExchangeGold","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"getOrComputeTobinTax","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getOtherReserveAddresses","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getAssetAllocationSymbols","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getAssetAllocationWeights","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getUnfrozenBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getReserveGoldBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getOtherReserveAddressesGoldBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getUnfrozenReserveGoldBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getFrozenReserveGoldBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getReserveRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}] diff --git a/apps/explorer/priv/contracts_abi/celo/stabletoken.json b/apps/explorer/priv/contracts_abi/celo/stabletoken.json new file mode 100644 index 000000000000..918ad6373bea --- /dev/null +++ b/apps/explorer/priv/contracts_abi/celo/stabletoken.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"bool","name":"test","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"factor","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lastUpdated","type":"uint256"}],"name":"InflationFactorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"updatePeriod","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lastUpdated","type":"uint256"}],"name":"InflationParametersUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"registryAddress","type":"address"}],"name":"RegistrySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"comment","type":"string"}],"name":"TransferComment","type":"event"},{"constant":true,"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"bytes","name":"blsKey","type":"bytes"},{"internalType":"bytes","name":"blsPop","type":"bytes"}],"name":"checkProofOfPossession","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"aNumerator","type":"uint256"},{"internalType":"uint256","name":"aDenominator","type":"uint256"},{"internalType":"uint256","name":"bNumerator","type":"uint256"},{"internalType":"uint256","name":"bDenominator","type":"uint256"},{"internalType":"uint256","name":"exponent","type":"uint256"},{"internalType":"uint256","name":"_decimals","type":"uint256"}],"name":"fractionMulExp","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes","name":"header","type":"bytes"}],"name":"getBlockNumberFromHeader","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getEpochNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getEpochNumberOfBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getEpochSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getParentSealBitmap","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes","name":"header","type":"bytes"}],"name":"getVerifiedSealBitmapFromHeader","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes","name":"header","type":"bytes"}],"name":"hashHeader","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"initialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"minQuorumSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"minQuorumSizeInCurrentSet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"numberValidatorsInCurrentSet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"numberValidatorsInSet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"registry","outputs":[{"internalType":"contractIRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"registryAddress","type":"address"}],"name":"setRegistry","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"validatorSignerAddressFromCurrentSet","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"validatorSignerAddressFromSet","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getVersionNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"},{"internalType":"address","name":"registryAddress","type":"address"},{"internalType":"uint256","name":"inflationRate","type":"uint256"},{"internalType":"uint256","name":"inflationFactorUpdatePeriod","type":"uint256"},{"internalType":"address[]","name":"initialBalanceAddresses","type":"address[]"},{"internalType":"uint256[]","name":"initialBalanceValues","type":"uint256[]"},{"internalType":"string","name":"exchangeIdentifier","type":"string"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"rate","type":"uint256"},{"internalType":"uint256","name":"updatePeriod","type":"uint256"}],"name":"setInflationParameters","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"string","name":"comment","type":"string"}],"name":"transferWithComment","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"burn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"accountOwner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"accountOwner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getInflationParameters","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"valueToUnits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getExchangeRegistryId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"units","type":"uint256"}],"name":"unitsToValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"debitGasFees","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"feeRecipient","type":"address"},{"internalType":"address","name":"gatewayFeeRecipient","type":"address"},{"internalType":"address","name":"communityFund","type":"address"},{"internalType":"uint256","name":"refund","type":"uint256"},{"internalType":"uint256","name":"tipTxFee","type":"uint256"},{"internalType":"uint256","name":"gatewayFee","type":"uint256"},{"internalType":"uint256","name":"baseTxFee","type":"uint256"}],"name":"creditGasFees","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}] diff --git a/apps/explorer/priv/repo/migrations/20211109155555_create_celo_pending_epoch_operations.exs b/apps/explorer/priv/repo/migrations/20211109155555_create_celo_pending_epoch_operations.exs new file mode 100644 index 000000000000..ac82d516bbe1 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20211109155555_create_celo_pending_epoch_operations.exs @@ -0,0 +1,16 @@ +defmodule Explorer.Repo.Migrations.CreateCeloPendingEpochOperations do + use Ecto.Migration + + def change do + create table(:celo_pending_epoch_operations, primary_key: false) do + add(:block_hash, references(:blocks, column: :hash, type: :bytea, on_delete: :delete_all), + null: false, + primary_key: true + ) + + add(:fetch_epoch_rewards, :boolean, null: false) + + timestamps(null: false, type: :utc_datetime_usec) + end + end +end diff --git a/apps/explorer/priv/repo/migrations/20211111183251_create_celo_epoch_rewards_table.exs b/apps/explorer/priv/repo/migrations/20211111183251_create_celo_epoch_rewards_table.exs new file mode 100644 index 000000000000..baa205e4b06c --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20211111183251_create_celo_epoch_rewards_table.exs @@ -0,0 +1,36 @@ +defmodule Explorer.Repo.Migrations.AddMissingColumnsToCeloEpochRewards do + use Ecto.Migration + + def change do + create table(:celo_epoch_rewards) do + add(:block_hash, :bytea, null: false) + add(:block_number, :integer, null: false) + add(:epoch_number, :integer) + add(:validator_target_epoch_rewards, :numeric, precision: 100) + add(:voter_target_epoch_rewards, :numeric, precision: 100) + add(:community_target_epoch_rewards, :numeric, precision: 100) + add(:carbon_offsetting_target_epoch_rewards, :numeric, precision: 100) + add(:target_total_supply, :numeric, precision: 100) + add(:rewards_multiplier, :numeric, precision: 100) + add(:rewards_multiplier_max, :numeric, precision: 100) + add(:rewards_multiplier_under, :numeric, precision: 100) + add(:rewards_multiplier_over, :numeric, precision: 100) + add(:target_voting_yield, :numeric, precision: 100) + add(:target_voting_yield_max, :numeric, precision: 100) + add(:target_voting_yield_adjustment_factor, :numeric, precision: 100) + add(:target_voting_fraction, :numeric, precision: 100) + add(:voting_fraction, :numeric, precision: 100) + add(:total_locked_gold, :numeric, precision: 100) + add(:total_non_voting, :numeric, precision: 100) + add(:total_votes, :numeric, precision: 100) + add(:electable_validators_max, :integer) + add(:reserve_gold_balance, :numeric, precision: 100) + add(:gold_total_supply, :numeric, precision: 100) + add(:stable_usd_total_supply, :numeric, precision: 100) + + timestamps(null: false, type: :utc_datetime_usec) + end + + create(index(:celo_epoch_rewards, [:block_hash], unique: true)) + end +end diff --git a/apps/explorer/priv/repo/migrations/20211122160654_add_initial_celo_pending_epoch_operations.exs b/apps/explorer/priv/repo/migrations/20211122160654_add_initial_celo_pending_epoch_operations.exs new file mode 100644 index 000000000000..63e46f9e2ae0 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20211122160654_add_initial_celo_pending_epoch_operations.exs @@ -0,0 +1,19 @@ +defmodule Explorer.Repo.Migrations.AddInitialCeloPendingEpochOperations do + use Ecto.Migration + + def up do + execute(""" + WITH epoch_blocks AS ( + SELECT i * 17280 as block_number FROM generate_series(1, (SELECT (MAX(number)/17280) FROM blocks)) as i + ), epoch_block_hashes AS + (SELECT b.hash, true as fetch FROM epoch_blocks eb LEFT JOIN blocks b ON b.number = eb.block_number) + INSERT INTO celo_pending_epoch_operations ( + block_hash, fetch_epoch_rewards, inserted_at, updated_at + ) SELECT *, NOW(), NOW() FROM epoch_block_hashes; + """) + end + + def down do + execute("delete from celo_pending_epoch_operations;") + end +end diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index edf5cd2e461a..2f980f9976bd 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -14,6 +14,7 @@ defmodule Explorer.ChainTest do alias Explorer.Chain.{ Address, Block, + CeloPendingEpochOperation, PendingCelo, Data, DecompiledSmartContract, @@ -5941,4 +5942,15 @@ defmodule Explorer.ChainTest do assert Repo.aggregate(PendingCelo, :count, :index) == 2 end end + + describe "delete_celo_pending_epoch_operation/1" do + test "deletes an epoch block hash that was indexed from celo_pending_epoch_operations" do + block = insert(:block) + insert(:celo_pending_epoch_operations, block_hash: block.hash, fetch_epoch_rewards: true) + + Chain.delete_celo_pending_epoch_operation(block.hash) + + assert Repo.one!(select(CeloPendingEpochOperation, fragment("COUNT(*)"))) == 0 + end + end end diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index 2502db9fecf7..b37173779b93 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -18,7 +18,8 @@ defmodule Explorer.Factory do Address.CoinBalance, Address.CoinBalanceDaily, Block, - PendingCelo, + CeloAccount, + CeloPendingEpochOperation, ContractMethod, Data, DecompiledSmartContract, @@ -26,12 +27,12 @@ defmodule Explorer.Factory do InternalTransaction, Log, PendingBlockOperation, + PendingCelo, SmartContract, Token, TokenTransfer, Token.Instance, Transaction, - CeloAccount, StakingPool, StakingPoolsDelegator } @@ -313,6 +314,14 @@ defmodule Explorer.Factory do } end + def celo_pending_epoch_operations_factory do + %CeloPendingEpochOperation{ + # caller MUST supply block + # all operations will default to false + fetch_epoch_rewards: false + } + end + def internal_transaction_factory() do gas = Enum.random(21_000..100_000) gas_used = Enum.random(0..gas) diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 72edc1845c84..2e85b492fd7f 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -25,7 +25,6 @@ defmodule Indexer.Block.Fetcher do CeloValidator, CeloValidatorGroup, CeloValidatorHistory, - CeloVoterRewards, CeloVoters, CoinBalance, ContractCode, @@ -198,7 +197,7 @@ defmodule Indexer.Block.Fetcher do %{ celo: celo_token, cusd: stable_token_usd, - creal: stable_token_real, + creal: _, ceur: _ }, oracle_address, celo_token_enabled} <- @@ -224,7 +223,6 @@ defmodule Indexer.Block.Fetcher do attestations_requested: attestations_requested, exchange_rates: exchange_rates, account_names: account_names, - voter_rewards: celo_voter_rewards, wallets: celo_wallets, withdrawals: celo_withdrawals } = CeloAccounts.parse(logs, oracle_address), @@ -334,7 +332,6 @@ defmodule Indexer.Block.Fetcher do Market.bulk_insert_history(market_history) async_import_celo_validators(%{celo_validators: %{params: celo_validators}}) - async_import_celo_voter_rewards(%{celo_voter_rewards: %{params: celo_voter_rewards}}) async_import_celo_validator_groups(%{celo_validator_groups: %{params: celo_validator_groups}}) async_import_celo_voters(%{celo_voters: %{params: celo_voters}}) async_import_celo_validator_history(range) @@ -499,10 +496,6 @@ defmodule Indexer.Block.Fetcher do def async_import_celo_validator_groups(_), do: :ok - def async_import_celo_voter_rewards(%{celo_voter_rewards: accounts}) do - CeloVoterRewards.async_fetch(accounts) - end - def async_import_celo_voters(%{celo_voters: accounts}) do CeloVoters.async_fetch(accounts) end diff --git a/apps/indexer/lib/indexer/fetcher/celo_epoch_rewards.ex b/apps/indexer/lib/indexer/fetcher/celo_epoch_rewards.ex new file mode 100644 index 000000000000..6b29fd2e2294 --- /dev/null +++ b/apps/indexer/lib/indexer/fetcher/celo_epoch_rewards.ex @@ -0,0 +1,102 @@ +defmodule Indexer.Fetcher.CeloEpochRewards do + @moduledoc """ + Fetches Celo voter rewards for groups. + """ + use Indexer.Fetcher + use Spandex.Decorators + + require Logger + + alias Explorer.Celo.AccountReader + alias Explorer.Chain + alias Explorer.Chain.CeloEpochRewards + + alias Indexer.BufferedTask + alias Indexer.Fetcher.Util + + @behaviour BufferedTask + + @doc false + def child_spec([init_options, gen_server_options]) do + Util.default_child_spec(init_options, gen_server_options, __MODULE__) + end + + @impl BufferedTask + def init(initial, reducer, _json_rpc_named_arguments) do + {:ok, final} = + Chain.stream_blocks_with_unfetched_epoch_rewards(initial, fn block, acc -> + block + |> reducer.(acc) + end) + + final + end + + @impl BufferedTask + def run(entries, _json_rpc_named_arguments) do + failed_list = + entries + |> fetch_from_blockchain() + |> import_items() + + if failed_list == [] do + :ok + else + {:retry, failed_list} + end + end + + def fetch_from_blockchain(blocks) do + blocks + |> Enum.map(fn block -> + case AccountReader.validator_group_reward_data(block) do + {:ok, data} -> + data + + error -> + Logger.debug(inspect(error)) + Map.put(block, :error, error) + end + end) + end + + def import_items(rewards) do + {failed, success} = + Enum.reduce(rewards, {[], []}, fn + %{error: _error} = reward, {failed, success} -> + {[reward | failed], success} + + reward, {failed, success} -> + changeset = CeloEpochRewards.changeset(%CeloEpochRewards{}, reward) + + if changeset.valid? do + {failed, [changeset.changes | success]} + else + {[reward | failed], success} + end + end) + + import_params = %{ + celo_epoch_rewards: %{params: success}, + timeout: :infinity + } + + if failed != [] do + Logger.error(fn -> "requeuing rewards" end, + block_numbers: Enum.map(failed, fn rew -> rew.block_number end) + ) + end + + case Chain.import_epoch_rewards_and_delete_pending_celo_epoch_operations(import_params, success) do + {:ok, _} -> + :ok + + {:error, reason} -> + Logger.error(fn -> ["failed to import Celo voter reward data: ", inspect(reason)] end, + error_count: Enum.count(rewards) + ) + end + + failed + end +end diff --git a/apps/indexer/lib/indexer/fetcher/celo_voter_rewards.ex b/apps/indexer/lib/indexer/fetcher/celo_voter_rewards.ex deleted file mode 100644 index cbb0f8ca195d..000000000000 --- a/apps/indexer/lib/indexer/fetcher/celo_voter_rewards.ex +++ /dev/null @@ -1,118 +0,0 @@ -defmodule Indexer.Fetcher.CeloVoterRewards do - @moduledoc """ - Fetches Celo voter rewards for groups. - """ - use Indexer.Fetcher - use Spandex.Decorators - - require Logger - - alias Indexer.Fetcher.CeloVoterRewards.Supervisor, as: CeloVoterRewardsSupervisor - - alias Explorer.Celo.AccountReader - alias Explorer.Chain - alias Explorer.Chain.CeloVoterRewards - - alias Indexer.BufferedTask - alias Indexer.Fetcher.Util - - @behaviour BufferedTask - - @max_retries 3 - - def async_fetch(accounts) do - if CeloVoterRewardsSupervisor.disabled?() do - :ok - else - params = - accounts.params - |> Enum.map(&entry/1) - - BufferedTask.buffer(__MODULE__, params, :infinity) - end - end - - def entry(elem) do - %{ - address_hash: elem.address_hash, - block_hash: elem.block_hash, - log_index: elem.log_index, - reward: elem.reward, - block_number: elem.block_number, - retries_count: 0 - } - end - - @doc false - def child_spec([init_options, gen_server_options]) do - Util.default_child_spec(init_options, gen_server_options, __MODULE__) - end - - @impl BufferedTask - def init(initial, _, _) do - initial - end - - @impl BufferedTask - def run(accounts, _json_rpc_named_arguments) do - failed_list = - accounts - |> Enum.map(&Map.put(&1, :retries_count, &1.retries_count + 1)) - |> fetch_from_blockchain() - |> import_items() - - if failed_list == [] do - :ok - else - {:retry, failed_list} - end - end - - defp fetch_from_blockchain(addresses) do - addresses - |> Enum.filter(&(&1.retries_count <= @max_retries)) - |> Enum.map(fn %{address_hash: address, block_number: bn} = account -> - case AccountReader.validator_group_reward_data(address, bn) do - {:ok, data} -> - Map.merge(account, data) - - error -> - Map.put(account, :error, error) - end - end) - end - - defp import_items(accounts) do - {failed, success} = - Enum.reduce(accounts, {[], []}, fn - %{error: _error} = account, {failed, success} -> - {[account | failed], success} - - account, {failed, success} -> - changeset = CeloVoterRewards.changeset(%CeloVoterRewards{}, account) - - if changeset.valid? do - {failed, [changeset.changes | success]} - else - {[account | failed], success} - end - end) - - import_params = %{ - celo_voter_rewards: %{params: success}, - timeout: :infinity - } - - case Chain.import(import_params) do - {:ok, _} -> - :ok - - {:error, reason} -> - Logger.debug(fn -> ["failed to import Celo voter reward data: ", inspect(reason)] end, - error_count: Enum.count(accounts) - ) - end - - failed - end -end diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex index e9b5d1bbbda2..9636451d6b16 100644 --- a/apps/indexer/lib/indexer/supervisor.ex +++ b/apps/indexer/lib/indexer/supervisor.ex @@ -21,11 +21,11 @@ defmodule Indexer.Supervisor do alias Indexer.Fetcher.{ BlockReward, CeloAccount, + CeloEpochRewards, CeloMaterializedViewRefresh, CeloValidator, CeloValidatorGroup, CeloValidatorHistory, - CeloVoterRewards, CeloVoters, CoinBalance, CoinBalanceOnDemand, @@ -155,7 +155,7 @@ defmodule Indexer.Supervisor do [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, {CeloValidatorHistory.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, - {CeloVoterRewards.Supervisor, + {CeloEpochRewards.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, {PendingCelo.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, {CeloVoters.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, diff --git a/apps/indexer/lib/indexer/transform/celo_accounts.ex b/apps/indexer/lib/indexer/transform/celo_accounts.ex index 51fd920d2447..17aee646626b 100644 --- a/apps/indexer/lib/indexer/transform/celo_accounts.ex +++ b/apps/indexer/lib/indexer/transform/celo_accounts.ex @@ -26,7 +26,6 @@ defmodule Indexer.Transform.CeloAccounts do get_addresses(logs, Events.vote_events(), fn a -> a.third_topic end), withdrawals: [get_withdrawals(logs, Events.withdrawal_events())], signers: get_signers(logs, Events.signer_events()), - voter_rewards: get_rewards(logs, Events.validator_group_voter_reward_events()), voters: get_voters(logs, Events.voter_events()), attestations_fulfilled: get_addresses(logs, [Events.attestation_completed_event()], fn a -> a.fourth_topic end), attestations_requested: @@ -69,12 +68,6 @@ defmodule Indexer.Transform.CeloAccounts do |> Enum.reduce([], fn log, accounts -> do_parse_signers(log, accounts) end) end - def get_rewards(logs, topics) do - logs - |> Enum.filter(fn log -> Enum.member?(topics, log.first_topic) end) - |> Enum.reduce([], fn log, accounts -> do_parse_rewards(log, accounts) end) - end - def get_wallets(logs) do logs |> Enum.filter(fn log -> log.first_topic == Events.account_wallet_address_set_event() end) @@ -110,14 +103,6 @@ defmodule Indexer.Transform.CeloAccounts do accounts end - defp do_parse_rewards(log, accounts) do - reward = parse_reward_params(log) - [reward | accounts] - rescue - _ in [FunctionClauseError, MatchError] -> - Logger.error(fn -> "Unknown group voter reward event format: #{inspect(log)}" end) - end - defp do_parse_wallets(log, wallets) do wallet = parse_wallet_params(log) [wallet | wallets] @@ -190,19 +175,6 @@ defmodule Indexer.Transform.CeloAccounts do %{address: address, signer: signer} end - defp parse_reward_params(log) do - address = truncate_address_hash(log.second_topic) - [value] = decode_data(log.data, [{:uint, 256}]) - - %{ - address_hash: address, - reward: value, - log_index: log.index, - block_hash: log.block_hash, - block_number: log.block_number - } - end - defp parse_wallet_params(log) do account = truncate_address_hash(log.second_topic) wallet = truncate_address_hash(log.data) 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 658a0ef9ebcb..31c8a693a772 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 @@ -24,7 +24,7 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisorTest do CeloValidator, CeloValidatorHistory, CeloValidatorGroup, - CeloVoterRewards, + CeloEpochRewards, CeloVoters } @@ -234,7 +234,7 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisorTest do CeloValidator.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) CeloValidatorHistory.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) CeloValidatorGroup.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) - CeloVoterRewards.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + CeloEpochRewards.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) CeloVoters.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) ReplacedTransaction.Supervisor.Case.start_supervised!() diff --git a/apps/indexer/test/indexer/block/catchup/fetcher_test.exs b/apps/indexer/test/indexer/block/catchup/fetcher_test.exs index 9c5c8786e4d3..5589c215728b 100644 --- a/apps/indexer/test/indexer/block/catchup/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/catchup/fetcher_test.exs @@ -23,7 +23,7 @@ defmodule Indexer.Block.Catchup.FetcherTest do CeloValidator, CeloValidatorHistory, CeloValidatorGroup, - CeloVoterRewards, + CeloEpochRewards, CeloVoters } @@ -37,7 +37,7 @@ defmodule Indexer.Block.Catchup.FetcherTest do CeloValidatorHistory.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) CeloValidatorGroup.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) CeloVoters.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) - CeloVoterRewards.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + CeloEpochRewards.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) end @moduletag capture_log: true diff --git a/apps/indexer/test/indexer/block/fetcher_test.exs b/apps/indexer/test/indexer/block/fetcher_test.exs index c6062c47ccdc..9e0b09f62c75 100644 --- a/apps/indexer/test/indexer/block/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/fetcher_test.exs @@ -8,7 +8,7 @@ defmodule Indexer.Block.FetcherTest do import Explorer.Celo.CacheHelper alias Explorer.Chain - alias Explorer.Chain.{Address, PendingCelo, Log, Transaction, Wei} + alias Explorer.Chain.{Address, CeloPendingEpochOperation, PendingCelo, Log, Transaction, Wei} alias Indexer.Block.Fetcher alias Indexer.BufferedTask @@ -24,7 +24,7 @@ defmodule Indexer.Block.FetcherTest do CeloValidator, CeloValidatorHistory, CeloValidatorGroup, - CeloVoterRewards, + CeloEpochRewards, CeloVoters } @@ -66,7 +66,7 @@ defmodule Indexer.Block.FetcherTest do CeloValidatorHistory.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) CeloValidatorGroup.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) CeloVoters.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) - CeloVoterRewards.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + CeloEpochRewards.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) ReplacedTransaction.Supervisor.Case.start_supervised!() UncleBlock.Supervisor.Case.start_supervised!( diff --git a/apps/indexer/test/indexer/block/realtime/fetcher_test.exs b/apps/indexer/test/indexer/block/realtime/fetcher_test.exs index c9ad36f6a8c9..f4cfe4593f73 100644 --- a/apps/indexer/test/indexer/block/realtime/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/realtime/fetcher_test.exs @@ -22,7 +22,7 @@ defmodule Indexer.Block.Realtime.FetcherTest do CeloValidator, CeloValidatorGroup, CeloValidatorHistory, - CeloVoterRewards, + CeloEpochRewards, CeloVoters } @@ -76,7 +76,7 @@ defmodule Indexer.Block.Realtime.FetcherTest do CeloAccount.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) CeloValidatorGroup.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) CeloValidatorHistory.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) - CeloVoterRewards.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + CeloEpochRewards.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) CeloVoters.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) diff --git a/apps/indexer/test/indexer/fetcher/celo_epoch_rewards_test.exs b/apps/indexer/test/indexer/fetcher/celo_epoch_rewards_test.exs new file mode 100644 index 000000000000..77cc9c4b1c1f --- /dev/null +++ b/apps/indexer/test/indexer/fetcher/celo_epoch_rewards_test.exs @@ -0,0 +1,391 @@ +defmodule Indexer.Fetcher.CeloEpochRewardsTest 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 Explorer.DataCase + + import EthereumJSONRPC, only: [integer_to_quantity: 1] + import Explorer.Celo.CacheHelper + import Mox + + alias Explorer.Chain + alias Explorer.Chain.{Block, CeloEpochRewards, CeloPendingEpochOperation, Hash} + alias Indexer.BufferedTask + alias Indexer.Fetcher.CeloEpochRewards, as: CeloEpochRewardsFetcher + + @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! + + setup do + start_supervised!({Task.Supervisor, name: Indexer.TaskSupervisor}) + + # Need to always mock to allow consensus switches to happen on demand and protect from them happening when we don't + # want them to. + %{ + json_rpc_named_arguments: [ + transport: EthereumJSONRPC.Mox, + transport_options: [], + # Which one does not matter, so pick one + variant: EthereumJSONRPC.Parity + ] + } + end + + describe "init/2" do + test "buffers unindexed epoch blocks", %{ + json_rpc_named_arguments: json_rpc_named_arguments + } do + block = insert(:block) + insert(:celo_pending_epoch_operations, block_hash: block.hash, fetch_epoch_rewards: true) + + assert CeloEpochRewardsFetcher.init( + [], + fn block_number, acc -> [block_number | acc] end, + json_rpc_named_arguments + ) == [%{block_number: block.number, block_hash: block.hash}] + end + + @tag :no_geth + test "does not buffer blocks with fetched epoch rewards", %{ + json_rpc_named_arguments: json_rpc_named_arguments + } do + block = insert(:block) + insert(:celo_pending_epoch_operations, block_hash: block.hash, fetch_epoch_rewards: false) + + assert CeloEpochRewardsFetcher.init( + [], + fn block_number, acc -> [block_number | acc] end, + json_rpc_named_arguments + ) == [] + end + end + + describe "fetch_from_blockchain/1" do + setup do + block = insert(:block, number: 172_800) + + %{block: block} + end + + test "fetches epoch data from blockchain", %{ + block: %Block{ + hash: block_hash, + number: block_number + }, + json_rpc_named_arguments: json_rpc_named_arguments + } do + setup_mox(%{ + id: 0, + jsonrpc: "2.0", + result: + "0x00000000000000000000000000000000000000000000000b25b7389d6e6f8233000000000000000000000000000000000000000000000583d67889a223c1b9ab00000000000000000000000000000000000000000000034b50882b7adf687bd70000000000000000000000000000000000000000000000035f8ddb4f56e8ddad" + }) + + fetched = + CeloEpochRewardsFetcher.fetch_from_blockchain([ + %{block_number: block_number, block_hash: block_hash} + ]) + + assert [ + %{ + block_number: block_number, + carbon_offsetting_target_epoch_rewards: 62_225_632_760_255_012_269, + community_target_epoch_rewards: 15_556_408_190_063_753_067_479, + validator_target_epoch_rewards: 205_631_887_959_760_273_971, + voter_target_epoch_rewards: 26_043_810_141_454_976_793_003, + target_total_supply: 601_017_204_041_941_484_863_859_293, + rewards_multiplier: 1_000_741_854_737_500_000_000_000, + rewards_multiplier_max: 2_000_000_000_000_000_000_000_000, + rewards_multiplier_under: 500_000_000_000_000_000_000_000, + rewards_multiplier_over: 5_000_000_000_000_000_000_000_000, + target_voting_yield: 160_000_000_000_000_000_000, + target_voting_yield_adjustment_factor: 0, + target_voting_yield_max: 500_000_000_000_000_000_000, + target_voting_fraction: 500_000_000_000_000_000_000_000, + voting_fraction: 410_303_431_329_291_024_629_586, + total_locked_gold: 316_279_462_377_767_975_674_883_803, + total_non_voting: 22_643_903_944_557_354_402_445_358, + total_votes: 293_635_558_433_210_621_272_438_445, + electable_validators_max: 110, + reserve_gold_balance: 115_255_226_249_038_379_930_471_272, + gold_total_supply: 600_363_049_982_598_326_620_386_513, + stable_usd_total_supply: 5_182_985_086_049_091_467_996_121, + block_hash: block_hash, + epoch_number: 10 + } + ] == fetched + end + end + + describe "import_items/1" do + test "saves epoch rewards and deletes celo pending epoch operations" do + block = + insert(:block, + hash: %Hash{ + byte_count: 32, + bytes: + <<252, 154, 78, 156, 195, 203, 115, 134, 25, 196, 0, 181, 189, 239, 174, 127, 27, 61, 98, 208, 104, 72, + 127, 167, 112, 119, 204, 138, 81, 255, 5, 91>> + }, + number: 9_434_880 + ) + + insert(:celo_pending_epoch_operations, block_hash: block.hash, fetch_epoch_rewards: true) + + rewards = [ + %{ + address_hash: %Hash{ + byte_count: 20, + bytes: <<42, 57, 230, 201, 63, 231, 229, 237, 228, 165, 179, 126, 139, 187, 19, 165, 70, 44, 201, 123>> + }, + block_hash: block.hash, + block_number: block.number, + carbon_offsetting_target_epoch_rewards: 55_094_655_441_694_756_188, + community_target_epoch_rewards: 13_773_663_860_423_689_047_089, + electable_validators_max: 110, + epoch_number: 546, + gold_total_supply: 632_725_491_274_706_367_854_422_889, + log_index: 0, + reserve_gold_balance: 115_257_993_782_506_057_885_594_247, + rewards_multiplier: 830_935_429_083_244_762_116_865, + rewards_multiplier_max: 2_000_000_000_000_000_000_000_000, + rewards_multiplier_over: 5_000_000_000_000_000_000_000_000, + rewards_multiplier_under: 500_000_000_000_000_000_000_000, + stable_usd_total_supply: 102_072_732_704_065_987_635_855_047, + target_total_supply: 619_940_889_565_364_451_209_200_067, + target_voting_fraction: 600_000_000_000_000_000_000_000, + target_voting_yield: 161_241_419_224_794_107_230, + target_voting_yield_adjustment_factor: 1_127_990_000_000_000_000, + target_voting_yield_max: 500_000_000_000_000_000_000, + total_locked_gold: 316_316_894_443_027_811_324_534_950, + total_non_voting: 22_643_903_944_557_354_402_445_358, + total_votes: 293_672_990_498_470_456_922_089_592, + validator_target_epoch_rewards: 170_740_156_660_940_704_543, + voter_target_epoch_rewards: 38_399_789_501_591_793_730_548, + voting_fraction: 567_519_683_693_557_844_261_489 + } + ] + + CeloEpochRewardsFetcher.import_items(rewards) + + assert count(CeloPendingEpochOperation) == 0 + assert count(CeloEpochRewards) == 1 + end + end + + defp count(schema) do + Repo.one!(select(schema, fragment("COUNT(*)"))) + end + + defp wait_for_tasks(buffered_task) do + wait_until(:timer.seconds(10), fn -> + counts = BufferedTask.debug_count(buffered_task) + counts.buffer == 0 and counts.tasks == 0 + end) + end + + defp wait_until(timeout, producer) do + parent = self() + ref = make_ref() + + spawn(fn -> do_wait_until(parent, ref, producer) end) + + receive do + {^ref, :ok} -> :ok + after + timeout -> exit(:timeout) + end + end + + defp do_wait_until(parent, ref, producer) do + if producer.() do + send(parent, {ref, :ok}) + else + :timer.sleep(100) + do_wait_until(parent, ref, producer) + end + end + + defp setup_mox(calculate_target_epoch_rewards_response) do + set_test_addresses(%{ + "EpochRewards" => "0x07f007d389883622ef8d4d347b3f78007f28d8b7", + "LockedGold" => "0x6cc083aed9e3ebe302a6336dbc7c921c9f03349e", + "Election" => "0x8d6677192144292870907e3fa8a5527fe55a7ff6", + "Reserve" => "0x9380fa34fd9e4fd14c06305fd7b6199089ed4eb9", + "GoldToken" => "0x471ece3750da237f93b8e339c536989b8978a438", + "StableToken" => "0x765de816845861e75a25fca122bb6898b8b1282a" + }) + + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn [ + %{ + id: _calculateTargetEpochRewards, + jsonrpc: "2.0", + method: "eth_call", + params: [%{data: "0x64347043", to: _}, _] + }, + %{ + id: getTargetGoldTotalSupply, + jsonrpc: "2.0", + method: "eth_call", + params: [%{data: "0x5049890f", to: _}, _] + }, + %{ + id: getRewardsMultiplier, + jsonrpc: "2.0", + method: "eth_call", + params: [%{data: "0x0203ab24", to: _}, _] + }, + %{ + id: getRewardsMultiplierParameters, + jsonrpc: "2.0", + method: "eth_call", + params: [%{data: "0x5f396e48", to: _}, _] + }, + %{ + id: getTargetVotingYieldParameters, + jsonrpc: "2.0", + method: "eth_call", + params: [%{data: "0x171af90f", to: _}, _] + }, + %{ + id: getTargetVotingGoldFraction, + jsonrpc: "2.0", + method: "eth_call", + params: [%{data: "0xae098de2", to: _}, _] + }, + %{ + id: getVotingGoldFraction, + jsonrpc: "2.0", + method: "eth_call", + params: [%{data: "0xa1b95962", to: _}, _] + }, + %{ + id: getTotalLockedGold, + jsonrpc: "2.0", + method: "eth_call", + params: [%{data: "0x30a61d59", to: _}, _] + }, + %{ + id: getNonvotingLockedGold, + jsonrpc: "2.0", + method: "eth_call", + params: [%{data: "0x807876b7", to: _}, _] + }, + %{ + id: getTotalVotes, + jsonrpc: "2.0", + method: "eth_call", + params: [%{data: "0x9a0e7d66", to: _}, _] + }, + %{ + id: getElectableValidators, + jsonrpc: "2.0", + method: "eth_call", + params: [%{data: "0xf9f41a7a", to: _}, _] + }, + %{ + id: getReserveGoldBalance, + jsonrpc: "2.0", + method: "eth_call", + params: [%{data: "0x8d9a5e6f", to: _}, _] + }, + %{ + id: goldTotalSupply, + jsonrpc: "2.0", + method: "eth_call", + params: [%{data: "0x18160ddd", to: "0x471ece3750da237f93b8e339c536989b8978a438"}, _] + }, + %{ + id: stableUSDTotalSupply, + jsonrpc: "2.0", + method: "eth_call", + params: [%{data: "0x18160ddd", to: "0x765de816845861e75a25fca122bb6898b8b1282a"}, _] + } + ], + _ -> + { + :ok, + [ + calculate_target_epoch_rewards_response, + %{ + id: getTargetGoldTotalSupply, + jsonrpc: "2.0", + result: "0x000000000000000000000000000000000000000001f12657ea8a3cbb0ff9aa5d" + }, + %{ + id: getRewardsMultiplier, + jsonrpc: "2.0", + result: "0x00000000000000000000000000000000000000000000d3ea531c462b6d289800" + }, + %{ + id: getRewardsMultiplierParameters, + jsonrpc: "2.0", + result: + "0x00000000000000000000000000000000000000000001a784379d99db420000000000000000000000000000000000000000000000000069e10de76676d08000000000000000000000000000000000000000000000000422ca8b0a00a425000000" + }, + %{ + id: getTargetVotingYieldParameters, + jsonrpc: "2.0", + result: + "0x000000000000000000000000000000000000000000000008ac7230489e80000000000000000000000000000000000000000000000000001b1ae4d6e2ef5000000000000000000000000000000000000000000000000000000000000000000000" + }, + %{ + id: getTargetVotingGoldFraction, + jsonrpc: "2.0", + result: "0x0000000000000000000000000000000000000000000069e10de76676d0800000" + }, + %{ + id: getVotingGoldFraction, + jsonrpc: "2.0", + result: "0x0000000000000000000000000000000000000000000056e297f4f13e205a7f52" + }, + %{ + id: getTotalLockedGold, + jsonrpc: "2.0", + result: "0x000000000000000000000000000000000000000001059ec802d92a296076aedb" + }, + %{ + id: getNonvotingLockedGold, + jsonrpc: "2.0", + result: "0x00000000000000000000000000000000000000000012bb087e1546063ebff82e" + }, + %{ + id: getTotalVotes, + jsonrpc: "2.0", + result: "0x000000000000000000000000000000000000000000f2e3bf84c3e42321b6b6ad" + }, + %{ + id: getElectableValidators, + jsonrpc: "2.0", + result: + "0x0000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000006e" + }, + %{ + id: getReserveGoldBalance, + jsonrpc: "2.0", + result: "0x0000000000000000000000000000000000000000005f563e55a0348825d9cb68" + }, + %{ + id: goldTotalSupply, + jsonrpc: "2.0", + result: "0x000000000000000000000000000000000000000001f09bd2274f90dfe61df4d1" + }, + %{ + id: stableUSDTotalSupply, + jsonrpc: "2.0", + result: "0x00000000000000000000000000000000000000000004498a2f3c39c0d4b5ebd9" + } + ] + } + end + ) + end +end diff --git a/apps/indexer/test/support/indexer/fetcher/celo_voter_rewards_case.ex b/apps/indexer/test/support/indexer/fetcher/celo_epoch_rewards_case.ex similarity index 69% rename from apps/indexer/test/support/indexer/fetcher/celo_voter_rewards_case.ex rename to apps/indexer/test/support/indexer/fetcher/celo_epoch_rewards_case.ex index bec7bd324eb2..f5c6bc6f6c6e 100644 --- a/apps/indexer/test/support/indexer/fetcher/celo_voter_rewards_case.ex +++ b/apps/indexer/test/support/indexer/fetcher/celo_epoch_rewards_case.ex @@ -1,5 +1,5 @@ -defmodule Indexer.Fetcher.CeloVoterRewards.Supervisor.Case do - alias Indexer.Fetcher.CeloVoterRewards +defmodule Indexer.Fetcher.CeloEpochRewards.Supervisor.Case do + alias Indexer.Fetcher.CeloEpochRewards def start_supervised!(fetcher_arguments \\ []) when is_list(fetcher_arguments) do merged_fetcher_arguments = @@ -11,7 +11,7 @@ defmodule Indexer.Fetcher.CeloVoterRewards.Supervisor.Case do ) [merged_fetcher_arguments] - |> CeloVoterRewards.Supervisor.child_spec() + |> CeloEpochRewards.Supervisor.child_spec() |> ExUnit.Callbacks.start_supervised!() end end From 745924ef229934808685ff70330be06ebe4b88a5 Mon Sep 17 00:00:00 2001 From: Snyk bot Date: Wed, 1 Dec 2021 15:05:03 +0100 Subject: [PATCH 2/3] fix: upgrade solc from 0.8.9 to 0.8.10 (#488) Snyk has created this PR to upgrade solc from 0.8.9 to 0.8.10. See this package in npm: https://www.npmjs.com/package/solc See this project in Snyk: https://app.snyk.io/org/bspeckien-clabs/project/dccc69d1-8109-4abe-a2af-b8385187ca86?utm_source=github&utm_medium=referral&page=upgrade-pr --- apps/explorer/package-lock.json | 12 ++++++------ apps/explorer/package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/explorer/package-lock.json b/apps/explorer/package-lock.json index c4f6835897e2..a56446b608dc 100644 --- a/apps/explorer/package-lock.json +++ b/apps/explorer/package-lock.json @@ -33,9 +33,9 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "follow-redirects": { - "version": "1.14.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", - "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==" + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", + "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" }, "fs-extra": { "version": "0.30.0", @@ -157,9 +157,9 @@ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "solc": { - "version": "0.8.9", - "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.9.tgz", - "integrity": "sha512-dD8tQgGCrdWQPpbDTbQF048S3JAcpytOax2r5qPgQluKJPCRFT6c/fec0ZkbrRwRSeYT/qiKz0OKBDOinnGeWw==", + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.10.tgz", + "integrity": "sha512-I/Mcn6J5bEtJqveNLplQm9IRrhemm6v+qkw5S2+wM4x9HItJ1sYdrqVTN3j4DMhFDM3ZbvM0QywVzpPx666PHw==", "requires": { "command-exists": "^1.2.8", "commander": "^8.1.0", diff --git a/apps/explorer/package.json b/apps/explorer/package.json index 7ad6bc0993b3..1c9a2260d7bf 100644 --- a/apps/explorer/package.json +++ b/apps/explorer/package.json @@ -13,6 +13,6 @@ }, "scripts": {}, "dependencies": { - "solc": "^0.8.9" + "solc": "^0.8.10" } } From 399e52114ab40b69aa589f436a38c284ebbea4b4 Mon Sep 17 00:00:00 2001 From: Donald Hutchison Date: Wed, 1 Dec 2021 17:56:09 +0100 Subject: [PATCH 3/3] Set csv export paging to 500 for transactions and token transfers. (#490) --- .../lib/explorer/chain/address_token_transfer_csv_exporter.ex | 2 +- .../lib/explorer/chain/address_transaction_csv_exporter.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/explorer/lib/explorer/chain/address_token_transfer_csv_exporter.ex b/apps/explorer/lib/explorer/chain/address_token_transfer_csv_exporter.ex index 63e79aa1597f..c0a9c9d41970 100644 --- a/apps/explorer/lib/explorer/chain/address_token_transfer_csv_exporter.ex +++ b/apps/explorer/lib/explorer/chain/address_token_transfer_csv_exporter.ex @@ -7,7 +7,7 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporter do alias Explorer.Chain.{Address, AddressTransactionCsvExporter, TokenTransfer, Transaction} alias NimbleCSV.RFC4180 - @page_size 150 + @page_size 500 @paging_options %PagingOptions{page_size: @page_size + 1} @spec export(Address.t(), String.t(), String.t()) :: Enumerable.t() diff --git a/apps/explorer/lib/explorer/chain/address_transaction_csv_exporter.ex b/apps/explorer/lib/explorer/chain/address_transaction_csv_exporter.ex index 37d1d94456a8..a57a21eb4d0c 100644 --- a/apps/explorer/lib/explorer/chain/address_transaction_csv_exporter.ex +++ b/apps/explorer/lib/explorer/chain/address_transaction_csv_exporter.ex @@ -21,7 +21,7 @@ defmodule Explorer.Chain.AddressTransactionCsvExporter do } ] - @page_size 150 + @page_size 500 @paging_options %PagingOptions{page_size: @page_size + 1}