Take into account EIP-1559 in gas price oracle

pull/6190/head
sl1depengwyn 2 years ago committed by Maxim Filonov
parent b86f1408c6
commit a3322e693d
  1. 9
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex
  2. 5
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block/by_tag.ex
  3. 11
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc/mox_test.exs
  4. 277
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs
  5. 167
      apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex
  6. 118
      apps/explorer/test/explorer/chain/cache/gas_price_oracle_test.exs
  7. 9
      apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs

@ -278,9 +278,9 @@ defmodule EthereumJSONRPC do
@doc """
Fetches block by "t:tag/0".
"""
@spec fetch_blocks_by_tag(tag(), json_rpc_named_arguments) ::
@spec fetch_block_by_tag(tag(), json_rpc_named_arguments) ::
{:ok, Blocks.t()} | {:error, reason :: :invalid_tag | :not_found | term()}
def fetch_blocks_by_tag(tag, json_rpc_named_arguments) when tag in ~w(earliest latest pending) do
def fetch_block_by_tag(tag, json_rpc_named_arguments) when tag in ~w(earliest latest pending) do
[%{tag: tag}]
|> fetch_blocks_by_params(&Block.ByTag.request/1, json_rpc_named_arguments)
end
@ -320,9 +320,8 @@ defmodule EthereumJSONRPC do
@spec fetch_block_number_by_tag(tag(), json_rpc_named_arguments) ::
{:ok, non_neg_integer()} | {:error, reason :: :invalid_tag | :not_found | term()}
def fetch_block_number_by_tag(tag, json_rpc_named_arguments) when tag in ~w(earliest latest pending) do
%{id: 0, tag: tag}
|> Block.ByTag.request()
|> json_rpc(json_rpc_named_arguments)
tag
|> fetch_block_by_tag(json_rpc_named_arguments)
|> Block.ByTag.number_from_result()
end

@ -5,6 +5,7 @@ defmodule EthereumJSONRPC.Block.ByTag do
"""
import EthereumJSONRPC, only: [quantity_to_integer: 1]
alias EthereumJSONRPC.Blocks
def request(%{id: id, tag: tag}) when is_binary(tag) do
EthereumJSONRPC.request(%{id: id, method: "eth_getBlockByNumber", params: [tag, false]})
@ -16,6 +17,10 @@ defmodule EthereumJSONRPC.Block.ByTag do
{:ok, quantity_to_integer(quantity)}
end
def number_from_result({:ok, %Blocks{blocks_params: []}}), do: {:error, :not_found}
def number_from_result({:ok, %Blocks{blocks_params: [%{number: number}]}}), do: {:ok, number}
def number_from_result({:ok, nil}), do: {:error, :not_found}
def number_from_result({:error, %{"code" => -32602}}), do: {:error, :invalid_tag}

@ -21,8 +21,15 @@ defmodule EthereumJSONRPC.MoxTest do
describe "fetch_block_number_by_tag/2" do
test "with pending with null result", %{json_rpc_named_arguments: json_rpc_named_arguments} do
expect(EthereumJSONRPC.Mox, :json_rpc, fn _json, _options ->
{:ok, nil}
expect(EthereumJSONRPC.Mox, :json_rpc, fn [
%{
id: id,
method: "eth_getBlockByNumber",
params: ["pending", false]
}
],
_options ->
{:ok, [%{id: id, result: nil}]}
end)
assert {:error, :not_found} = EthereumJSONRPC.fetch_block_number_by_tag("pending", json_rpc_named_arguments)

@ -2,6 +2,7 @@ defmodule EthereumJSONRPCTest do
use EthereumJSONRPC.Case, async: true
import EthereumJSONRPC.Case
import EthereumJSONRPC, only: [quantity_to_integer: 1]
import Mox
alias EthereumJSONRPC.{Blocks, FetchedBalances, FetchedBeneficiaries, FetchedCodes, Subscription}
@ -543,191 +544,32 @@ defmodule EthereumJSONRPCTest do
end
end
describe "fetch_blocks_by_tag/2" do
@tag capture_log: false
test "with earliest", %{json_rpc_named_arguments: json_rpc_named_arguments} do
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options ->
block_number = "0x0"
block_hash = "0x29c850324e357f3c0c836d79860c5af55f7b651e5d7ee253c1af1b14908af49c"
transaction_hash = "0xa2e81bb56b55ba3dab2daf76501b50dfaad240cccb905dbf89d65c7a84a4a48e"
{:ok,
[
%{
id: id,
result: %{
"difficulty" => "0x0",
"gasLimit" => "0x0",
"gasUsed" => "0x0",
"hash" => block_hash,
"extraData" => "0x0",
"logsBloom" => "0x0",
"miner" => "0x0",
"number" => block_number,
"parentHash" => "0x0",
"receiptsRoot" => "0x0",
"size" => "0x0",
"sha3Uncles" => "0x0",
"stateRoot" => "0x0",
"timestamp" => "0x0",
"totalDifficulty" => "0x0",
"transactions" => [
%{
"blockHash" => block_hash,
"blockNumber" => block_number,
"from" => "0x0",
"gas" => "0x0",
"gasPrice" => "0x0",
"hash" => transaction_hash,
"input" => "0x",
"nonce" => "0x0",
"r" => "0x0",
"s" => "0x0",
"to" => "0x0",
"transactionIndex" => "0x0",
"v" => "0x0",
"value" => "0x0"
}
],
"transactionsRoot" => "0x0",
"uncles" => []
}
}
]}
end)
end
log_bad_gateway(
fn -> EthereumJSONRPC.fetch_blocks_by_tag("earliest", json_rpc_named_arguments) end,
fn result ->
assert {:ok, %Blocks{blocks_params: [_ | _], transactions_params: [_ | _]}} = result
end
)
end
describe "fetch_block_by_tag/2" do
@supported_tags ~w(earliest latest pending)
@tag capture_log: false
test "with latest", %{json_rpc_named_arguments: json_rpc_named_arguments} do
test "with all supported tags", %{json_rpc_named_arguments: json_rpc_named_arguments} do
for tag <- @supported_tags do
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options ->
block_number = "0x1"
block_hash = "0x29c850324e357f3c0c836d79860c5af55f7b651e5d7ee253c1af1b14908af49c"
transaction_hash = "0xa2e81bb56b55ba3dab2daf76501b50dfaad240cccb905dbf89d65c7a84a4a48e"
{:ok,
[
expect(EthereumJSONRPC.Mox, :json_rpc, fn [
%{
id: id,
result: %{
"difficulty" => "0x0",
"gasLimit" => "0x0",
"gasUsed" => "0x0",
"hash" => block_hash,
"extraData" => "0x0",
"logsBloom" => "0x0",
"miner" => "0x0",
"number" => block_number,
"parentHash" => "0x0",
"receiptsRoot" => "0x0",
"size" => "0x0",
"sha3Uncles" => "0x0",
"stateRoot" => "0x0",
"timestamp" => "0x0",
"totalDifficulty" => "0x0",
"transactions" => [
%{
"blockHash" => block_hash,
"blockNumber" => block_number,
"from" => "0x0",
"gas" => "0x0",
"gasPrice" => "0x0",
"hash" => transaction_hash,
"input" => "0x",
"nonce" => "0x0",
"r" => "0x0",
"s" => "0x0",
"to" => "0x0",
"transactionIndex" => "0x0",
"v" => "0x0",
"value" => "0x0"
method: "eth_getBlockByNumber",
params: [^tag, false]
}
],
"transactionsRoot" => "0x0",
"uncles" => []
}
}
]}
_options ->
block_response(id, tag == "pending", "0x1")
end)
end
log_bad_gateway(
fn -> EthereumJSONRPC.fetch_blocks_by_tag("latest", json_rpc_named_arguments) end,
fn -> EthereumJSONRPC.fetch_block_by_tag(tag, json_rpc_named_arguments) end,
fn result ->
{:ok, %Blocks{blocks_params: [_ | _], transactions_params: [_ | _]}} = result
{:ok, %Blocks{blocks_params: [_ | _], transactions_params: []}} = result
end
)
end
@tag capture_log: false
test "with pending", %{json_rpc_named_arguments: json_rpc_named_arguments} do
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options ->
block_number = "0x1"
block_hash = "0x29c850324e357f3c0c836d79860c5af55f7b651e5d7ee253c1af1b14908af49c"
transaction_hash = "0xa2e81bb56b55ba3dab2daf76501b50dfaad240cccb905dbf89d65c7a84a4a48e"
{:ok,
[
%{
id: id,
result: %{
"difficulty" => "0x0",
"gasLimit" => "0x0",
"gasUsed" => "0x0",
"hash" => block_hash,
"extraData" => "0x0",
"logsBloom" => "0x0",
"miner" => "0x0",
"number" => block_number,
"parentHash" => "0x0",
"receiptsRoot" => "0x0",
"size" => "0x0",
"sha3Uncles" => "0x0",
"stateRoot" => "0x0",
"timestamp" => "0x0",
"totalDifficulty" => "0x0",
"transactions" => [
%{
"blockHash" => block_hash,
"blockNumber" => block_number,
"from" => "0x0",
"gas" => "0x0",
"gasPrice" => "0x0",
"hash" => transaction_hash,
"input" => "0x",
"nonce" => "0x0",
"r" => "0x0",
"s" => "0x0",
"to" => "0x0",
"transactionIndex" => "0x0",
"v" => "0x0",
"value" => "0x0"
}
],
"transactionsRoot" => "0x0",
"uncles" => []
}
}
]}
end)
end
log_bad_gateway(
fn -> EthereumJSONRPC.fetch_blocks_by_tag("pending", json_rpc_named_arguments) end,
fn result ->
{:ok, %Blocks{blocks_params: [_ | _], transactions_params: [_ | _]}} = result
end
)
end
test "unknown errors are returned", %{json_rpc_named_arguments: json_rpc_named_arguments} do
@ -740,55 +582,36 @@ defmodule EthereumJSONRPCTest do
{:error, unknown_error}
end)
assert {:error, ^unknown_error} =
EthereumJSONRPC.fetch_block_number_by_tag("latest", moxed_json_rpc_named_arguments)
assert {:error, ^unknown_error} = EthereumJSONRPC.fetch_block_by_tag("latest", moxed_json_rpc_named_arguments)
end
end
describe "fetch_block_number_by_tag" do
@tag capture_log: false
test "with earliest", %{json_rpc_named_arguments: json_rpc_named_arguments} do
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
expect(EthereumJSONRPC.Mox, :json_rpc, fn _json, _options ->
{:ok, %{"number" => "0x0"}}
end)
end
log_bad_gateway(
fn -> EthereumJSONRPC.fetch_block_number_by_tag("earliest", json_rpc_named_arguments) end,
fn result ->
assert {:ok, 0} = result
end
)
end
@supported_tags %{"earliest" => "0x0", "latest" => "0x1", "pending" => nil}
@tag capture_log: false
test "with latest", %{json_rpc_named_arguments: json_rpc_named_arguments} do
test "with all supported tags", %{json_rpc_named_arguments: json_rpc_named_arguments} do
for {tag, expected_result} <- @supported_tags do
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
expect(EthereumJSONRPC.Mox, :json_rpc, fn _json, _options ->
{:ok, %{"number" => "0x1"}}
end)
end
log_bad_gateway(
fn -> EthereumJSONRPC.fetch_block_number_by_tag("latest", json_rpc_named_arguments) end,
fn result ->
assert {:ok, number} = result
assert number > 0
end
)
expect(EthereumJSONRPC.Mox, :json_rpc, fn [
%{
id: id,
method: "eth_getBlockByNumber",
params: [^tag, false]
}
],
_options ->
if tag == "pending" do
{:ok, [%{id: id, result: nil}]}
else
block_response(id, false, expected_result)
end
@tag capture_log: false
test "with pending", %{json_rpc_named_arguments: json_rpc_named_arguments} do
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
expect(EthereumJSONRPC.Mox, :json_rpc, fn _json, _options ->
{:ok, nil}
end)
end
log_bad_gateway(
fn -> EthereumJSONRPC.fetch_block_number_by_tag("pending", json_rpc_named_arguments) end,
fn -> EthereumJSONRPC.fetch_block_number_by_tag(tag, json_rpc_named_arguments) end,
if tag == "pending" do
fn
# Parity after https://github.com/paritytech/parity-ethereum/pull/8281 and anything spec-compliant
{:error, reason} ->
@ -799,8 +622,15 @@ defmodule EthereumJSONRPCTest do
assert is_integer(number)
assert number > 0
end
else
fn result ->
integer_result = expected_result && quantity_to_integer(expected_result)
assert {:ok, ^integer_result} = result
end
end
)
end
end
test "unknown errors are returned", %{json_rpc_named_arguments: json_rpc_named_arguments} do
# Can't be faked reliably on real chain
@ -1114,12 +944,43 @@ defmodule EthereumJSONRPCTest do
:ok
end
end
defp block_response(id, pending, block_number) do
block_hash = "0x29c850324e357f3c0c836d79860c5af55f7b651e5d7ee253c1af1b14908af49c"
transaction_hash = "0xa2e81bb56b55ba3dab2daf76501b50dfaad240cccb905dbf89d65c7a84a4a48e"
{:ok,
[
%{
id: id,
result: %{
"difficulty" => "0x0",
"gasLimit" => "0x0",
"gasUsed" => "0x0",
"hash" => if(pending, do: nil, else: block_hash),
"extraData" => "0x0",
"logsBloom" => "0x0",
"miner" => "0x0",
"number" => block_number,
"parentHash" => "0x0",
"receiptsRoot" => "0x0",
"size" => "0x0",
"sha3Uncles" => "0x0",
"stateRoot" => "0x0",
"timestamp" => "0x0",
"totalDifficulty" => "0x0",
"transactions" => [transaction_hash],
"transactionsRoot" => "0x0",
"uncles" => []
}
}
]}
end
end
defmodule EthereumJSONRPCSyncTest do
use EthereumJSONRPC.Case, async: false
import EthereumJSONRPC.Case
import Mox
alias EthereumJSONRPC.FetchedBalances

@ -10,6 +10,8 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do
from: 2
]
alias EthereumJSONRPC.Blocks
alias Explorer.Chain.{
Block,
Wei
@ -25,8 +27,17 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do
ttl_check_interval: :timer.seconds(1),
callback: &async_task_on_deletion(&1)
@doc """
Get `safelow`, `average` and `fast` percentile of transactions gas prices among the last `num_of_blocks` blocks
"""
@spec get_average_gas_price(pos_integer(), pos_integer(), pos_integer(), pos_integer()) ::
{:error, any} | {:ok, %{String.t() => nil | float, String.t() => nil | float, String.t() => nil | float}}
def get_average_gas_price(num_of_blocks, safelow_percentile, average_percentile, fast_percentile) do
latest_gas_price_query =
safelow_percentile_fraction = safelow_percentile / 100
average_percentile_fraction = average_percentile / 100
fast_percentile_fraction = fast_percentile / 100
fee_query =
from(
block in Block,
left_join: transaction in assoc(block, :transactions),
@ -35,27 +46,48 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do
where: transaction.gas_price > ^0,
group_by: block.number,
order_by: [desc: block.number],
select: min(transaction.gas_price),
select: %{
slow_gas_price:
fragment(
"percentile_disc(?) within group ( order by ? )",
^safelow_percentile_fraction,
transaction.gas_price
),
average_gas_price:
fragment(
"percentile_disc(?) within group ( order by ? )",
^average_percentile_fraction,
transaction.gas_price
),
fast_gas_price:
fragment(
"percentile_disc(?) within group ( order by ? )",
^fast_percentile_fraction,
transaction.gas_price
),
slow:
fragment(
"percentile_disc(?) within group ( order by ? )",
^safelow_percentile_fraction,
transaction.max_priority_fee_per_gas
),
average:
fragment(
"percentile_disc(?) within group ( order by ? )",
^average_percentile_fraction,
transaction.max_priority_fee_per_gas
),
fast:
fragment(
"percentile_disc(?) within group ( order by ? )",
^fast_percentile_fraction,
transaction.max_priority_fee_per_gas
)
},
limit: ^num_of_blocks
)
latest_gas_prices =
latest_gas_price_query
|> Repo.all(timeout: :infinity)
latest_ordered_gas_prices =
latest_gas_prices
|> Enum.map(fn %Wei{value: gas_price} -> Decimal.to_integer(gas_price) end)
safelow_gas_price = gas_price_percentile_to_gwei(latest_ordered_gas_prices, safelow_percentile)
average_gas_price = gas_price_percentile_to_gwei(latest_ordered_gas_prices, average_percentile)
fast_gas_price = gas_price_percentile_to_gwei(latest_ordered_gas_prices, fast_percentile)
gas_prices = %{
"slow" => safelow_gas_price,
"average" => average_gas_price,
"fast" => fast_gas_price
}
gas_prices = fee_query |> Repo.all(timeout: :infinity) |> process_fee_data_from_db()
{:ok, gas_prices}
catch
@ -63,6 +95,69 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do
{:error, error}
end
defp process_fee_data_from_db([]) do
%{
"slow" => nil,
"average" => nil,
"fast" => nil
}
end
defp process_fee_data_from_db(fees) do
fees_length = Enum.count(fees)
%{
slow_gas_price: slow_gas_price,
average_gas_price: average_gas_price,
fast_gas_price: fast_gas_price,
slow: slow,
average: average,
fast: fast
} =
fees
|> Enum.reduce(
&Map.merge(&1, &2, fn
_, v1, v2 when nil not in [v1, v2] -> Decimal.add(v1, v2)
_, v1, v2 -> v1 || v2
end)
)
|> Map.new(fn
{key, nil} -> {key, nil}
{key, value} -> {key, Decimal.div(value, fees_length)}
end)
json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments)
{slow_fee, average_fee, fast_fee} =
case {nil not in [slow, average, fast], EthereumJSONRPC.fetch_block_by_tag("pending", json_rpc_named_arguments)} do
{true, {:ok, %Blocks{blocks_params: [%{base_fee_per_gas: base_fee}]}}} when not is_nil(base_fee) ->
base_fee_wei = base_fee |> Decimal.new() |> Wei.from(:wei)
{
priority_with_base_fee(slow, base_fee_wei),
priority_with_base_fee(average, base_fee_wei),
priority_with_base_fee(fast, base_fee_wei)
}
_ ->
{gas_price(slow_gas_price), gas_price(average_gas_price), gas_price(fast_gas_price)}
end
%{
"slow" => slow_fee,
"average" => average_fee,
"fast" => fast_fee
}
end
defp priority_with_base_fee(priority, base_fee) do
priority |> Wei.from(:wei) |> Wei.sum(base_fee) |> Wei.to(:gwei) |> Decimal.to_float() |> Float.ceil(2)
end
defp gas_price(value) do
value |> Wei.from(:wei) |> Wei.to(:gwei) |> Decimal.to_float() |> Float.ceil(2)
end
defp num_of_blocks, do: Application.get_env(:explorer, __MODULE__)[:num_of_blocks]
defp safelow, do: Application.get_env(:explorer, __MODULE__)[:safelow_percentile]
@ -102,40 +197,6 @@ defmodule Explorer.Chain.Cache.GasPriceOracle do
{:update, task}
end
defp gas_price_percentile_to_gwei(gas_prices, percentile) do
gas_price_wei = percentile(gas_prices, percentile)
if gas_price_wei do
gas_price_gwei = Wei.to(%Wei{value: Decimal.from_float(gas_price_wei)}, :gwei)
gas_price_gwei_float = gas_price_gwei |> Decimal.to_float()
if gas_price_gwei_float > 0.01 do
gas_price_gwei_float
|> Float.ceil(2)
else
gas_price_gwei_float
end
else
nil
end
end
@spec percentile(list, number) :: number | nil
defp percentile([], _), do: nil
defp percentile([x], _), do: x
defp percentile(list, 0), do: Enum.min(list)
defp percentile(list, 100), do: Enum.max(list)
defp percentile(list, n) when is_list(list) and is_number(n) do
s = Enum.sort(list)
r = n / 100.0 * (length(list) - 1)
f = :erlang.trunc(r)
lower = Enum.at(s, f)
upper = Enum.at(s, f + 1)
lower + (upper - lower) * (r - f)
end
# By setting this as a `callback` an async task will be started each time the
# `gas_prices` expires (unless there is one already running)
defp async_task_on_deletion({:delete, _, :gas_prices}), do: get_async_task()

@ -1,10 +1,53 @@
defmodule Explorer.Chain.Cache.GasPriceOracleTest do
use Explorer.DataCase
import Mox
alias Explorer.Chain.Cache.GasPriceOracle
@block %{
"difficulty" => "0x0",
"gasLimit" => "0x0",
"gasUsed" => "0x0",
"hash" => "0x29c850324e357f3c0c836d79860c5af55f7b651e5d7ee253c1af1b14908af49c",
"extraData" => "0x0",
"logsBloom" => "0x0",
"miner" => "0x0",
"number" => "0x1",
"parentHash" => "0x0",
"receiptsRoot" => "0x0",
"size" => "0x0",
"sha3Uncles" => "0x0",
"stateRoot" => "0x0",
"timestamp" => "0x0",
"baseFeePerGas" => "0x1DCD6500",
"totalDifficulty" => "0x0",
"transactions" => [
%{
"blockHash" => "0x29c850324e357f3c0c836d79860c5af55f7b651e5d7ee253c1af1b14908af49c",
"blockNumber" => "0x1",
"from" => "0x0",
"gas" => "0x0",
"gasPrice" => "0x0",
"hash" => "0xa2e81bb56b55ba3dab2daf76501b50dfaad240cccb905dbf89d65c7a84a4a48e",
"input" => "0x",
"nonce" => "0x0",
"r" => "0x0",
"s" => "0x0",
"to" => "0x0",
"transactionIndex" => "0x0",
"v" => "0x0",
"value" => "0x0"
}
],
"transactionsRoot" => "0x0",
"uncles" => []
}
describe "get_average_gas_price/4" do
test "returns nil percentile values if no blocks in the DB" do
expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end)
assert {:ok,
%{
"slow" => nil,
@ -14,6 +57,8 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do
end
test "returns nil percentile values if blocks are empty in the DB" do
expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end)
insert(:block)
insert(:block)
insert(:block)
@ -27,6 +72,8 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do
end
test "returns nil percentile values for blocks with failed txs in the DB" do
expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end)
block = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391")
:transaction
@ -51,6 +98,8 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do
end
test "returns nil percentile values for transactions with 0 gas price aka 'whitelisted transactions' in the DB" do
expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end)
block1 = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391")
block2 = insert(:block, number: 101, hash: "0x76c3da57334fffdc66c0d954dce1a910fcff13ec889a13b2d8b0b6e9440ce729")
@ -87,6 +136,8 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do
end
test "returns the same percentile values if gas price is the same over transactions" do
expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end)
block1 = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391")
block2 = insert(:block, number: 101, hash: "0x76c3da57334fffdc66c0d954dce1a910fcff13ec889a13b2d8b0b6e9440ce729")
@ -123,6 +174,8 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do
end
test "returns correct min gas price from the block" do
expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end)
block1 = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391")
block2 = insert(:block, number: 101, hash: "0x76c3da57334fffdc66c0d954dce1a910fcff13ec889a13b2d8b0b6e9440ce729")
@ -165,12 +218,14 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do
assert {:ok,
%{
"slow" => 1.0,
"average" => 1.0,
"fast" => 1.0
"average" => 2.0,
"fast" => 2.0
}} = GasPriceOracle.get_average_gas_price(3, 35, 60, 90)
end
test "returns correct average percentile" do
expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end)
block1 = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391")
block2 = insert(:block, number: 101, hash: "0x76c3da57334fffdc66c0d954dce1a910fcff13ec889a13b2d8b0b6e9440ce729")
block3 = insert(:block, number: 102, hash: "0x659b2a1cc4dd1a5729900cf0c81c471d1c7891b2517bf9466f7fba56ead2fca0")
@ -213,7 +268,64 @@ defmodule Explorer.Chain.Cache.GasPriceOracleTest do
assert {:ok,
%{
"average" => 4.0
"average" => 3.34
}} = GasPriceOracle.get_average_gas_price(3, 35, 60, 90)
end
test "returns correct gas price for EIP-1559 transactions" do
expect(EthereumJSONRPC.Mox, :json_rpc, fn [%{id: id}], _options -> {:ok, [%{id: id, result: @block}]} end)
block1 = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391")
block2 = insert(:block, number: 101, hash: "0x76c3da57334fffdc66c0d954dce1a910fcff13ec889a13b2d8b0b6e9440ce729")
:transaction
|> insert(
status: :ok,
block_hash: block1.hash,
block_number: block1.number,
cumulative_gas_used: 884_322,
gas_used: 106_025,
index: 0,
gas_price: 1_000_000_000,
max_priority_fee_per_gas: 1_000_000_000,
max_fee_per_gas: 1_000_000_000,
hash: "0xac2a7dab94d965893199e7ee01649e2d66f0787a4c558b3118c09e80d4df8269"
)
:transaction
|> insert(
status: :ok,
block_hash: block2.hash,
block_number: block2.number,
cumulative_gas_used: 884_322,
gas_used: 106_025,
index: 0,
gas_price: 1_000_000_000,
max_priority_fee_per_gas: 1_000_000_000,
max_fee_per_gas: 1_000_000_000,
hash: "0x5d5c2776f96704e7845f7d3c1fbba6685ab6efd6f82b6cd11d549f3b3a46bd03"
)
:transaction
|> insert(
status: :ok,
block_hash: block2.hash,
block_number: block2.number,
cumulative_gas_used: 884_322,
gas_used: 106_025,
index: 1,
gas_price: 3_000_000_000,
max_priority_fee_per_gas: 3_000_000_000,
max_fee_per_gas: 3_000_000_000,
hash: "0x906b80861b4a0921acfbb91a7b527227b0d32adabc88bc73e8c52ff714e55016"
)
assert {:ok,
%{
# including base fee
"slow" => 1.5,
"average" => 2.5,
"fast" => 2.5
}} = GasPriceOracle.get_average_gas_price(3, 35, 60, 90)
end
end

@ -50,9 +50,12 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisorTest do
EthereumJSONRPC.Mox
|> stub(:json_rpc, fn
# latest block number to seed starting block number for genesis and realtime tasks
%{method: "eth_getBlockByNumber", params: ["latest", false]}, _options ->
[%{id: id, method: "eth_getBlockByNumber", params: ["latest", false]}], _options ->
{:ok,
[
%{
id: id,
result: %{
"author" => "0xe2ac1c6843a33f81ae4935e5ef1277a392990381",
"difficulty" => "0xfffffffffffffffffffffffffffffffe",
"extraData" => "0xd583010a068650617269747986312e32362e32826c69",
@ -80,7 +83,9 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisorTest do
"transactions" => [],
"transactionsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncles" => []
}}
}
}
]}
[%{method: "trace_block"} | _] = requests, _options ->
{:ok, Enum.map(requests, fn %{id: id} -> %{id: id, result: []} end)}

Loading…
Cancel
Save