diff --git a/.gitignore b/.gitignore index 480f7002b9..bdb24a89a4 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,7 @@ screenshots/ # Sobelow .sobelow -# osx +# osx .DS_Store # mix phx.gen.cert self-signed certs for dev @@ -45,3 +45,6 @@ screenshots/ /docker-compose/postgres-data /docker-compose/tmp + +.idea/ +*.iml diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index e1c414f3e7..57afcf2a70 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -800,6 +800,53 @@ defmodule Explorer.Chain do end end + @uncle_reward_coef 1 / 32 + def block_reward_by_parts(block, transactions) do + %{hash: block_hash, number: block_number} = block + base_fee_per_gas = Map.get(block, :base_fee_per_gas) + + txn_fees = + Enum.reduce(transactions, Decimal.new(0), fn %{gas_used: gas_used, gas_price: gas_price}, acc -> + gas_used + |> Decimal.new() + |> Decimal.mult(Decimal.new(gas_price)) + |> Decimal.add(acc) + end) + + static_reward = + Repo.one( + from( + er in EmissionReward, + where: fragment("int8range(?, ?) <@ ?", ^block_number, ^block_number, er.block_range), + select: er.reward + ) + ) || %Wei{value: Decimal.new(0)} + + burned_fee_counter = + transactions + |> Enum.filter(fn tx -> not is_nil(Map.get(tx, :max_priority_fee_per_gas)) end) + |> Enum.reduce(Decimal.new(0), fn %{gas_used: gas_used}, acc -> + gas_used + |> Decimal.new() + |> Decimal.add(acc) + end) + + has_uncles? = is_list(block.uncles) and not Enum.empty?(block.uncles) + + burned_fees = base_fee_per_gas && Wei.mult(base_fee_per_gas, burned_fee_counter) + uncle_reward = (has_uncles? && Wei.mult(static_reward, Decimal.from_float(@uncle_reward_coef))) || nil + + %{ + block_number: block_number, + block_hash: block_hash, + miner_hash: block.miner_hash, + static_reward: static_reward, + txn_fees: %Wei{value: txn_fees}, + burned_fees: burned_fees || %Wei{value: Decimal.new(0)}, + uncle_reward: uncle_reward || %Wei{value: Decimal.new(0)} + } + end + @doc """ The `t:Explorer.Chain.Wei.t/0` paid to the miners of the `t:Explorer.Chain.Block.t/0`s with `hash` `Explorer.Chain.Hash.Full.t/0` by the signers of the transactions in those blocks to cover the gas fee @@ -893,7 +940,7 @@ defmodule Explorer.Chain do select: sum( fragment( - "CASE + "CASE WHEN COALESCE(?,?) = 0 THEN 0 WHEN COALESCE(?,?) - ? < COALESCE(?,?) THEN (COALESCE(?,?) - ?) * ? ELSE COALESCE(?,?) * ? END", @@ -2951,7 +2998,7 @@ defmodule Explorer.Chain do right_join: missing_range in fragment( """ - (SELECT b1.number + (SELECT b1.number FROM generate_series(0, (?)::integer) AS b1(number) WHERE NOT EXISTS (SELECT 1 FROM blocks b2 WHERE b2.number=b1.number AND b2.consensus)) @@ -3038,7 +3085,7 @@ defmodule Explorer.Chain do right_join: missing_range in fragment( """ - (SELECT distinct b1.number + (SELECT distinct b1.number FROM generate_series((?)::integer, (?)::integer) AS b1(number) WHERE NOT EXISTS (SELECT 1 FROM blocks b2 WHERE b2.number=b1.number AND b2.consensus)) @@ -3976,7 +4023,7 @@ defmodule Explorer.Chain do Updates a `t:SmartContract.t/0`. Has the similar logic as create_smart_contract/1. - Used in cases when you need to update row in DB contains SmartContract, e.g. in case of changing + Used in cases when you need to update row in DB contains SmartContract, e.g. in case of changing status `partially verified` to `fully verified` (re-verify). """ @spec update_smart_contract(map()) :: {:ok, SmartContract.t()} | {:error, Ecto.Changeset.t()} @@ -5039,7 +5086,7 @@ defmodule Explorer.Chain do # Fetches custom metadata for bridged tokens from the node. # Currently, gets Balancer token composite tokens with their weights - # from foreign chain + # from foreign chain defp get_bridged_token_custom_metadata(foreign_token_address_hash, json_rpc_named_arguments, foreign_json_rpc) when not is_nil(foreign_json_rpc) and foreign_json_rpc !== "" do eth_call_foreign_json_rpc_named_arguments = diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 6d6b030439..e8baeca32e 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -3415,6 +3415,48 @@ defmodule Explorer.ChainTest do end end + describe "block_reward_by_parts/1" do + setup do + {:ok, emission_reward: insert(:emission_reward)} + end + + test "without uncles", %{emission_reward: %{reward: reward, block_range: range}} do + block = build(:block, number: range.from, base_fee_per_gas: %Wei{value: Decimal.new(5)}, uncles: []) + + tx1 = build(:transaction, gas_price: 1, gas_used: 1, block_number: block.number, block_hash: block.hash) + tx2 = build(:transaction, gas_price: 1, gas_used: 2, block_number: block.number, block_hash: block.hash) + + tx3 = + build(:transaction, + gas_price: 1, + gas_used: 3, + block_number: block.number, + block_hash: block.hash, + max_priority_fee_per_gas: 1 + ) + + expected_txn_fees = %Wei{value: Decimal.new(6)} + expected_burned_fees = %Wei{value: Decimal.new(15)} + expected_uncle_reward = %Wei{value: Decimal.new(0)} + + assert %{ + static_reward: ^reward, + txn_fees: ^expected_txn_fees, + burned_fees: ^expected_burned_fees, + uncle_reward: ^expected_uncle_reward + } = Chain.block_reward_by_parts(block, [tx1, tx2, tx3]) + end + + test "with uncles", %{emission_reward: %{reward: reward, block_range: range}} do + block = + build(:block, number: range.from, uncles: ["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273311"]) + + expected_uncle_reward = Wei.mult(reward, Decimal.from_float(1 / 32)) + + assert %{uncle_reward: ^expected_uncle_reward} = Chain.block_reward_by_parts(block, []) + end + end + describe "gas_payment_by_block_hash/1" do setup do number = 1 diff --git a/apps/indexer/config/config.exs b/apps/indexer/config/config.exs index 80330b9022..49bb7f0e7d 100644 --- a/apps/indexer/config/config.exs +++ b/apps/indexer/config/config.exs @@ -34,7 +34,8 @@ config :indexer, first_block: System.get_env("FIRST_BLOCK") || "", last_block: System.get_env("LAST_BLOCK") || "", trace_first_block: System.get_env("TRACE_FIRST_BLOCK") || "", - trace_last_block: System.get_env("TRACE_LAST_BLOCK") || "" + trace_last_block: System.get_env("TRACE_LAST_BLOCK") || "", + fetch_rewards_way: System.get_env("FETCH_REWARDS_WAY", "trace_block") config :indexer, Indexer.Fetcher.PendingTransaction.Supervisor, disabled?: diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 3a5e537d42..8f241dbfaf 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -11,7 +11,7 @@ defmodule Indexer.Block.Fetcher do alias EthereumJSONRPC.{Blocks, FetchedBeneficiaries} alias Explorer.Chain - alias Explorer.Chain.{Address, Block, Hash, Import, Transaction} + alias Explorer.Chain.{Address, Block, Hash, Import, Transaction, Wei} alias Explorer.Chain.Block.Reward alias Explorer.Chain.Cache.Blocks, as: BlocksCache alias Explorer.Chain.Cache.{Accounts, BlockNumber, Transactions, Uncles} @@ -136,7 +136,7 @@ defmodule Indexer.Block.Fetcher do %{token_transfers: token_transfers, tokens: tokens} = TokenTransfers.parse(logs), %{mint_transfers: mint_transfers} = MintTransfers.parse(logs), %FetchedBeneficiaries{params_set: beneficiary_params_set, errors: beneficiaries_errors} = - fetch_beneficiaries(blocks, json_rpc_named_arguments), + fetch_beneficiaries(blocks, transactions_with_receipts, json_rpc_named_arguments), addresses = Addresses.extract_addresses(%{ block_reward_contract_beneficiaries: MapSet.to_list(beneficiary_params_set), @@ -160,10 +160,8 @@ defmodule Indexer.Block.Fetcher do blocks: blocks } |> AddressCoinBalancesDaily.params_set(), - beneficiaries_with_gas_payment <- - beneficiary_params_set - |> add_gas_payments(transactions_with_receipts, blocks) - |> BlockReward.reduce_uncle_rewards(), + beneficiaries_with_gas_payment = + beneficiaries_with_gas_payment(blocks, beneficiary_params_set, transactions_with_receipts), address_token_balances = AddressTokenBalances.params_set(%{token_transfers_params: token_transfers}), {:ok, inserted} <- __MODULE__.import( @@ -336,7 +334,48 @@ defmodule Indexer.Block.Fetcher do quantity_to_integer(block_quantity) end - defp fetch_beneficiaries(blocks, json_rpc_named_arguments) do + defp fetch_beneficiaries(blocks, all_transactions, json_rpc_named_arguments) do + case Application.get_env(:indexer, :fetch_rewards_way) do + "manual" -> fetch_beneficiaries_manual(blocks, all_transactions) + _ -> fetch_beneficiaries_by_trace_block(blocks, json_rpc_named_arguments) + end + end + + def fetch_beneficiaries_manual(blocks, all_transactions) when is_list(blocks) do + block_transactions_map = Enum.group_by(all_transactions, & &1.block_number) + + blocks + |> Enum.map(fn block -> fetch_beneficiaries_manual(block, block_transactions_map[block.number] || []) end) + |> Enum.reduce(%FetchedBeneficiaries{}, fn params_set, %{params_set: acc_params_set} = acc -> + %FetchedBeneficiaries{acc | params_set: MapSet.union(acc_params_set, params_set)} + end) + end + + def fetch_beneficiaries_manual(block, transactions) do + block + |> Chain.block_reward_by_parts(transactions) + |> reward_parts_to_beneficiaries() + end + + defp reward_parts_to_beneficiaries(reward_parts) do + reward = + reward_parts.static_reward + |> Wei.sum(reward_parts.txn_fees) + |> Wei.sub(reward_parts.burned_fees) + |> Wei.sum(reward_parts.uncle_reward) + + MapSet.new([ + %{ + address_hash: reward_parts.miner_hash, + block_hash: reward_parts.block_hash, + block_number: reward_parts.block_number, + reward: reward, + address_type: :validator + } + ]) + end + + defp fetch_beneficiaries_by_trace_block(blocks, json_rpc_named_arguments) do hash_string_by_number = Enum.into(blocks, %{}, fn %{number: number, hash: hash_string} when is_integer(number) and is_binary(hash_string) -> @@ -400,6 +439,18 @@ defmodule Indexer.Block.Fetcher do |> Enum.into(MapSet.new()) end + defp beneficiaries_with_gas_payment(blocks, beneficiary_params_set, transactions_with_receipts) do + case Application.get_env(:indexer, :fetch_rewards_way) do + "manual" -> + beneficiary_params_set + + _ -> + beneficiary_params_set + |> add_gas_payments(transactions_with_receipts, blocks) + |> BlockReward.reduce_uncle_rewards() + end + end + defp add_gas_payments(beneficiaries, transactions, blocks) do transactions_by_block_number = Enum.group_by(transactions, & &1.block_number) diff --git a/apps/indexer/test/indexer/block/realtime/fetcher_test.exs b/apps/indexer/test/indexer/block/realtime/fetcher_test.exs index 491dfa7386..6cf066632e 100644 --- a/apps/indexer/test/indexer/block/realtime/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/realtime/fetcher_test.exs @@ -5,7 +5,7 @@ defmodule Indexer.Block.Realtime.FetcherTest do import Mox alias Explorer.Chain - alias Explorer.Chain.{Address, Transaction} + alias Explorer.Chain.{Address, Transaction, Wei} alias Indexer.Block.Catchup.Sequence alias Indexer.Block.Realtime alias Indexer.Fetcher.{ContractCode, InternalTransaction, ReplacedTransaction, Token, TokenBalance, UncleBlock} @@ -536,10 +536,519 @@ defmodule Indexer.Block.Realtime.FetcherTest do } ], blocks: [%Chain.Block{number: 3_946_079}, %Chain.Block{number: 3_946_080}], - transactions: [%Transaction{hash: transaction_hash}] + transactions: [%Transaction{hash: _transaction_hash}] }, errors: [] }} = Indexer.Block.Fetcher.fetch_and_import_range(block_fetcher, 3_946_079..3_946_080) end + + @tag :no_geth + test "in range with internal transactions and manual reward fetching", %{ + block_fetcher: %Indexer.Block.Fetcher{} = block_fetcher, + json_rpc_named_arguments: json_rpc_named_arguments + } do + Application.put_env(:indexer, :fetch_rewards_way, "manual") + + {:ok, sequence} = Sequence.start_link(ranges: [], step: 2) + Sequence.cap(sequence) + + Token.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + + ContractCode.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + + InternalTransaction.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments) + + UncleBlock.Supervisor.Case.start_supervised!( + block_fetcher: %Indexer.Block.Fetcher{json_rpc_named_arguments: json_rpc_named_arguments} + ) + + ReplacedTransaction.Supervisor.Case.start_supervised!() + + if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: ["0x3C365F", true] + }, + %{ + id: 1, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: ["0x3C3660", true] + } + ], + _ -> + {:ok, + [ + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "author" => "0x5ee341ac44d344ade1ca3a771c59b98eb2a77df2", + "difficulty" => "0xfffffffffffffffffffffffffffffffe", + "extraData" => "0xd583010b088650617269747986312e32372e32826c69", + "gasLimit" => "0x7a1200", + "gasUsed" => "0x2886e", + "hash" => "0xa4ec735cabe1510b5ae081b30f17222580b4588dbec52830529753a688b046cc", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0x5ee341ac44d344ade1ca3a771c59b98eb2a77df2", + "number" => "0x3c365f", + "parentHash" => "0x57f6d66e07488defccd5216c4d2968dd6afd3bd32415e284de3b02af6535e8dc", + "receiptsRoot" => "0x111be72e682cea9c93e02f1ef503fb64aa821b2ef510fd9177c49b37d0af98b5", + "sealFields" => [ + "0x841246c63f", + "0xb841ba3d11db672fd7893d1b7906275fa7c4c7f4fbcc8fa29eab0331480332361516545ef10a36d800ad2be2b449dde8d5703125156a9cf8a035f5a8623463e051b700" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "ba3d11db672fd7893d1b7906275fa7c4c7f4fbcc8fa29eab0331480332361516545ef10a36d800ad2be2b449dde8d5703125156a9cf8a035f5a8623463e051b700", + "size" => "0x33e", + "stateRoot" => "0x7f73f5fb9f891213b671356126c31e9795d038844392c7aa8800ed4f52307209", + "step" => "306628159", + "timestamp" => "0x5b61df3b", + "totalDifficulty" => "0x3c365effffffffffffffffffffffffed7f0362", + "transactions" => [ + %{ + "blockHash" => "0xa4ec735cabe1510b5ae081b30f17222580b4588dbec52830529753a688b046cc", + "blockNumber" => "0x3c365f", + "chainId" => "0x63", + "condition" => nil, + "creates" => nil, + "from" => "0x40b18103537c0f15d5e137dd8ddd019b84949d16", + "gas" => "0x3d9c5", + "gasPrice" => "0x3b9aca00", + "hash" => "0xd3937e70fab3fb2bfe8feefac36815408bf07de3b9e09fe81114b9a6b17f55c8", + "input" => + "0x8841ac11000000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005", + "nonce" => "0x65b", + "publicKey" => + "0x89c2123ed4b5d141cf1f4b6f5f3d754418f03aea2e870a1c50888d94bf5531f74237e2fea72d0bc198ef213272b62c6869615720757255e6cba087f9db6e759f", + "r" => "0x55a1a93541d7f782f97f6699437bb60fa4606d63760b30c1ee317e648f93995", + "raw" => + "0xf8f582065b843b9aca008303d9c594698bf6943bab687b2756394624aa183f434f65da8901158e4f216242a000b8848841ac11000000000000000000000000000000000000000000000000000000000000006c00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000581eaa0055a1a93541d7f782f97f6699437bb60fa4606d63760b30c1ee317e648f93995a06affd4da5eca84fbca2b016c980f861e0af1f8d6535e2fe29d8f96dc0ce358f7", + "s" => "0x6affd4da5eca84fbca2b016c980f861e0af1f8d6535e2fe29d8f96dc0ce358f7", + "standardV" => "0x1", + "to" => "0x698bf6943bab687b2756394624aa183f434f65da", + "transactionIndex" => "0x0", + "v" => "0xea", + "value" => "0x1158e4f216242a000" + } + ], + "transactionsRoot" => "0xd7c39a93eafe0bdcbd1324c13dcd674bed8c9fa8adbf8f95bf6a59788985da6f", + "uncles" => ["0xa4ec735cabe1510b5ae081b30f17222580b4588dbec52830529753a688b046cd"] + } + }, + %{ + id: 1, + jsonrpc: "2.0", + result: %{ + "author" => "0x66c9343c7e8ca673a1fedf9dbf2cd7936dbbf7e3", + "difficulty" => "0xfffffffffffffffffffffffffffffffe", + "extraData" => "0xd583010a068650617269747986312e32362e32826c69", + "gasLimit" => "0x7a1200", + "gasUsed" => "0x0", + "hash" => "0xfb483e511d316fa4072694da3f7abc94b06286406af45061e5e681395bdc6815", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0x66c9343c7e8ca673a1fedf9dbf2cd7936dbbf7e3", + "number" => "0x3c3660", + "parentHash" => "0xa4ec735cabe1510b5ae081b30f17222580b4588dbec52830529753a688b046cc", + "receiptsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sealFields" => [ + "0x841246c640", + "0xb84114db3fd7526b7ea3635f5c85c30dd8a645453aa2f8afe5fd33fe0ec663c9c7b653b0fb5d8dc7d0b809674fa9dca9887d1636a586bf62191da22255eb068bf20800" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "14db3fd7526b7ea3635f5c85c30dd8a645453aa2f8afe5fd33fe0ec663c9c7b653b0fb5d8dc7d0b809674fa9dca9887d1636a586bf62191da22255eb068bf20800", + "size" => "0x243", + "stateRoot" => "0x3174c461989e9f99e08fa9b4ffb8bce8d9a281c8fc9f80694bb9d3acd4f15559", + "step" => "306628160", + "timestamp" => "0x5b61df40", + "totalDifficulty" => "0x3c365fffffffffffffffffffffffffed7f0360", + "transactions" => [], + "transactionsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles" => [] + } + } + ]} + end) + |> expect(:json_rpc, fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getTransactionReceipt", + params: ["0xd3937e70fab3fb2bfe8feefac36815408bf07de3b9e09fe81114b9a6b17f55c8"] + } + ], + _ -> + {:ok, + [ + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "blockHash" => "0xa4ec735cabe1510b5ae081b30f17222580b4588dbec52830529753a688b046cc", + "blockNumber" => "0x3c365f", + "contractAddress" => nil, + "cumulativeGasUsed" => "0x2886e", + "gasUsed" => "0x2886e", + "logs" => [], + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "root" => nil, + "status" => "0x1", + "transactionHash" => "0xd3937e70fab3fb2bfe8feefac36815408bf07de3b9e09fe81114b9a6b17f55c8", + "transactionIndex" => "0x0" + } + } + ]} + end) + |> expect(:json_rpc, 3, fn + [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: ["0x3C365F", true] + } + ], + _ -> + {:ok, + [ + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "author" => "0x5ee341ac44d344ade1ca3a771c59b98eb2a77df2", + "difficulty" => "0xfffffffffffffffffffffffffffffffe", + "extraData" => "0xd583010b088650617269747986312e32372e32826c69", + "gasLimit" => "0x7a1200", + "gasUsed" => "0x2886e", + "hash" => "0xa4ec735cabe1510b5ae081b30f17222580b4588dbec52830529753a688b046cc", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0x5ee341ac44d344ade1ca3a771c59b98eb2a77df2", + "number" => "0x3c365f", + "parentHash" => "0x57f6d66e07488defccd5216c4d2968dd6afd3bd32415e284de3b02af6535e8dc", + "receiptsRoot" => "0x111be72e682cea9c93e02f1ef503fb64aa821b2ef510fd9177c49b37d0af98b5", + "sealFields" => [ + "0x841246c63f", + "0xb841ba3d11db672fd7893d1b7906275fa7c4c7f4fbcc8fa29eab0331480332361516545ef10a36d800ad2be2b449dde8d5703125156a9cf8a035f5a8623463e051b700" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "ba3d11db672fd7893d1b7906275fa7c4c7f4fbcc8fa29eab0331480332361516545ef10a36d800ad2be2b449dde8d5703125156a9cf8a035f5a8623463e051b700", + "size" => "0x33e", + "stateRoot" => "0x7f73f5fb9f891213b671356126c31e9795d038844392c7aa8800ed4f52307209", + "step" => "306628159", + "timestamp" => "0x5b61df3b", + "totalDifficulty" => "0x3c365effffffffffffffffffffffffed7f0362", + "transactions" => [ + %{ + "blockHash" => "0xa4ec735cabe1510b5ae081b30f17222580b4588dbec52830529753a688b046cc", + "blockNumber" => "0x3c365f", + "chainId" => "0x63", + "condition" => nil, + "creates" => nil, + "from" => "0x40b18103537c0f15d5e137dd8ddd019b84949d16", + "gas" => "0x3d9c5", + "gasPrice" => "0x3b9aca00", + "hash" => "0xd3937e70fab3fb2bfe8feefac36815408bf07de3b9e09fe81114b9a6b17f55c8", + "input" => + "0x8841ac11000000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005", + "nonce" => "0x65b", + "publicKey" => + "0x89c2123ed4b5d141cf1f4b6f5f3d754418f03aea2e870a1c50888d94bf5531f74237e2fea72d0bc198ef213272b62c6869615720757255e6cba087f9db6e759f", + "r" => "0x55a1a93541d7f782f97f6699437bb60fa4606d63760b30c1ee317e648f93995", + "raw" => + "0xf8f582065b843b9aca008303d9c594698bf6943bab687b2756394624aa183f434f65da8901158e4f216242a000b8848841ac11000000000000000000000000000000000000000000000000000000000000006c00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000581eaa0055a1a93541d7f782f97f6699437bb60fa4606d63760b30c1ee317e648f93995a06affd4da5eca84fbca2b016c980f861e0af1f8d6535e2fe29d8f96dc0ce358f7", + "s" => "0x6affd4da5eca84fbca2b016c980f861e0af1f8d6535e2fe29d8f96dc0ce358f7", + "standardV" => "0x1", + "to" => "0x698bf6943bab687b2756394624aa183f434f65da", + "transactionIndex" => "0x0", + "v" => "0xea", + "value" => "0x1158e4f216242a000" + } + ], + "transactionsRoot" => "0xd7c39a93eafe0bdcbd1324c13dcd674bed8c9fa8adbf8f95bf6a59788985da6f", + "uncles" => ["0xa4ec735cabe1510b5ae081b30f17222580b4588dbec52830529753a688b046cd"] + } + } + ]} + + [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBlockByNumber", + params: ["0x3C3660", true] + } + ], + _ -> + {:ok, + [ + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "author" => "0x66c9343c7e8ca673a1fedf9dbf2cd7936dbbf7e3", + "difficulty" => "0xfffffffffffffffffffffffffffffffe", + "extraData" => "0xd583010a068650617269747986312e32362e32826c69", + "gasLimit" => "0x7a1200", + "gasUsed" => "0x0", + "hash" => "0xfb483e511d316fa4072694da3f7abc94b06286406af45061e5e681395bdc6815", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0x66c9343c7e8ca673a1fedf9dbf2cd7936dbbf7e3", + "number" => "0x3c3660", + "parentHash" => "0xa4ec735cabe1510b5ae081b30f17222580b4588dbec52830529753a688b046cc", + "receiptsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sealFields" => [ + "0x841246c640", + "0xb84114db3fd7526b7ea3635f5c85c30dd8a645453aa2f8afe5fd33fe0ec663c9c7b653b0fb5d8dc7d0b809674fa9dca9887d1636a586bf62191da22255eb068bf20800" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "14db3fd7526b7ea3635f5c85c30dd8a645453aa2f8afe5fd33fe0ec663c9c7b653b0fb5d8dc7d0b809674fa9dca9887d1636a586bf62191da22255eb068bf20800", + "size" => "0x243", + "stateRoot" => "0x3174c461989e9f99e08fa9b4ffb8bce8d9a281c8fc9f80694bb9d3acd4f15559", + "step" => "306628160", + "timestamp" => "0x5b61df40", + "totalDifficulty" => "0x3c365fffffffffffffffffffffffffed7f0360", + "transactions" => [], + "transactionsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles" => [] + } + } + ]} + + [ + %{ + id: 0, + jsonrpc: "2.0", + method: "trace_replayBlockTransactions", + params: [ + "0x3C3660", + ["trace"] + ] + }, + %{ + id: 1, + jsonrpc: "2.0", + method: "trace_replayBlockTransactions", + params: [ + "0x3C365F", + ["trace"] + ] + } + ], + _ -> + {:ok, + [ + %{id: 0, jsonrpc: "2.0", result: []}, + %{ + id: 1, + jsonrpc: "2.0", + result: [ + %{ + "output" => "0x", + "stateDiff" => nil, + "trace" => [ + %{ + "action" => %{ + "callType" => "call", + "from" => "0x40b18103537c0f15d5e137dd8ddd019b84949d16", + "gas" => "0x383ad", + "input" => + "0x8841ac11000000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000005", + "to" => "0x698bf6943bab687b2756394624aa183f434f65da", + "value" => "0x1158e4f216242a000" + }, + "result" => %{"gasUsed" => "0x23256", "output" => "0x"}, + "subtraces" => 5, + "traceAddress" => [], + "type" => "call" + }, + %{ + "action" => %{ + "callType" => "call", + "from" => "0x698bf6943bab687b2756394624aa183f434f65da", + "gas" => "0x36771", + "input" => "0x6352211e000000000000000000000000000000000000000000000000000000000000006c", + "to" => "0x11c4469d974f8af5ba9ec99f3c42c07c848c861c", + "value" => "0x0" + }, + "result" => %{ + "gasUsed" => "0x495", + "output" => "0x00000000000000000000000040b18103537c0f15d5e137dd8ddd019b84949d16" + }, + "subtraces" => 0, + "traceAddress" => [0], + "type" => "call" + }, + %{ + "action" => %{ + "callType" => "call", + "from" => "0x698bf6943bab687b2756394624aa183f434f65da", + "gas" => "0x35acb", + "input" => "0x33f30a43000000000000000000000000000000000000000000000000000000000000006c", + "to" => "0x11c4469d974f8af5ba9ec99f3c42c07c848c861c", + "value" => "0x0" + }, + "result" => %{ + "gasUsed" => "0x52d2", + "output" => + "0x00000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000058000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000004e000000000000000000000000000000000000000000000000000000000000004f000000000000000000000000000000000000000000000000000000000000004d000000000000000000000000000000000000000000000000000000000000004b000000000000000000000000000000000000000000000000000000000000004f00000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000078000000000000000000000000000000000000000000000000000000005b61df09000000000000000000000000000000000000000000000000000000005b61df5e000000000000000000000000000000000000000000000000000000005b61df8b000000000000000000000000000000000000000000000000000000005b61df2c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006c00000000000000000000000000000000000000000000000000000000000000fd000000000000000000000000000000000000000000000000000000000000004e000000000000000000000000000000000000000000000000000000000000007a000000000000000000000000000000000000000000000000000000000000004e0000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000189000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054c65696c61000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002566303430313037303331343330303332333036303933333235303131323036303730373131000000000000000000000000000000000000000000000000000000" + }, + "subtraces" => 0, + "traceAddress" => [1], + "type" => "call" + }, + %{ + "action" => %{ + "callType" => "call", + "from" => "0x698bf6943bab687b2756394624aa183f434f65da", + "gas" => "0x2fc79", + "input" => "0x1b8ef0bb000000000000000000000000000000000000000000000000000000000000006c", + "to" => "0x11c4469d974f8af5ba9ec99f3c42c07c848c861c", + "value" => "0x0" + }, + "result" => %{ + "gasUsed" => "0x10f2", + "output" => "0x0000000000000000000000000000000000000000000000000000000000000013" + }, + "subtraces" => 0, + "traceAddress" => [2], + "type" => "call" + }, + %{ + "action" => %{ + "callType" => "call", + "from" => "0x698bf6943bab687b2756394624aa183f434f65da", + "gas" => "0x2e21f", + "input" => + "0xcf5f87d0000000000000000000000000000000000000000000000000000000000000006c0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000a", + "to" => "0x11c4469d974f8af5ba9ec99f3c42c07c848c861c", + "value" => "0x0" + }, + "result" => %{"gasUsed" => "0x1ca1", "output" => "0x"}, + "subtraces" => 0, + "traceAddress" => [3], + "type" => "call" + }, + %{ + "action" => %{ + "callType" => "call", + "from" => "0x698bf6943bab687b2756394624aa183f434f65da", + "gas" => "0x8fc", + "input" => "0x", + "to" => "0x40b18103537c0f15d5e137dd8ddd019b84949d16", + "value" => "0x9184e72a000" + }, + "result" => %{"gasUsed" => "0x0", "output" => "0x"}, + "subtraces" => 0, + "traceAddress" => [4], + "type" => "call" + } + ], + "transactionHash" => "0xd3937e70fab3fb2bfe8feefac36815408bf07de3b9e09fe81114b9a6b17f55c8", + "vmTrace" => nil + } + ] + } + ]} + + [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_getBalance", + params: ["0x40b18103537c0f15d5e137dd8ddd019b84949d16", "0x3C365F"] + }, + %{ + id: 1, + jsonrpc: "2.0", + method: "eth_getBalance", + params: ["0x5ee341ac44d344ade1ca3a771c59b98eb2a77df2", "0x3C365F"] + }, + %{ + id: 2, + jsonrpc: "2.0", + method: "eth_getBalance", + params: ["0x66c9343c7e8ca673a1fedf9dbf2cd7936dbbf7e3", "0x3C3660"] + }, + %{ + id: 3, + jsonrpc: "2.0", + method: "eth_getBalance", + params: ["0x698bf6943bab687b2756394624aa183f434f65da", "0x3C365F"] + } + ], + _ -> + {:ok, + [ + %{id: 0, jsonrpc: "2.0", result: "0x148adc763b603291685"}, + %{id: 1, jsonrpc: "2.0", result: "0x53474fa377a46000"}, + %{id: 2, jsonrpc: "2.0", result: "0x53507afe51f28000"}, + %{id: 3, jsonrpc: "2.0", result: "0x3e1a95d7517dc197108"} + ]} + end) + end + + first_expected_reward = %Wei{value: Decimal.new(165_998_000_000_000)} + second_expected_reward = %Wei{value: Decimal.new(0)} + + assert {:ok, + %{ + inserted: %{ + addresses: [ + %Address{hash: first_address_hash, fetched_coin_balance_block_number: 3_946_079}, + %Address{hash: second_address_hash, fetched_coin_balance_block_number: 3_946_079}, + %Address{hash: third_address_hash, fetched_coin_balance_block_number: 3_946_080}, + %Address{hash: fourth_address_hash, fetched_coin_balance_block_number: 3_946_079} + ], + address_coin_balances: [ + %{ + address_hash: first_address_hash, + block_number: 3_946_079 + }, + %{ + address_hash: second_address_hash, + block_number: 3_946_079 + }, + %{ + address_hash: third_address_hash, + block_number: 3_946_080 + }, + %{ + address_hash: fourth_address_hash, + block_number: 3_946_079 + } + ], + block_rewards: [ + %{ + address_hash: second_address_hash, + address_type: :validator, + reward: ^first_expected_reward + }, + %{ + address_hash: third_address_hash, + address_type: :validator, + reward: ^second_expected_reward + } + ], + blocks: [%Chain.Block{number: 3_946_079}, %Chain.Block{number: 3_946_080}], + transactions: [%Transaction{hash: _transaction_hash}] + }, + errors: [] + }} = Indexer.Block.Fetcher.fetch_and_import_range(block_fetcher, 3_946_079..3_946_080) + + Application.put_env(:indexer, :fetch_rewards_way, nil) + end end end