commit
55a5c651d5
@ -0,0 +1,49 @@ |
||||
defmodule BlockScoutWeb.API.V1.HealthController do |
||||
use BlockScoutWeb, :controller |
||||
|
||||
alias Explorer.Chain |
||||
|
||||
def health(conn, _) do |
||||
with {:ok, number, timestamp} <- Chain.last_block_status() do |
||||
send_resp(conn, :ok, result(number, timestamp)) |
||||
else |
||||
status -> send_resp(conn, :internal_server_error, error(status)) |
||||
end |
||||
end |
||||
|
||||
def result(number, timestamp) do |
||||
%{ |
||||
"healthy" => true, |
||||
"data" => %{ |
||||
"latest_block_number" => to_string(number), |
||||
"latest_block_inserted_at" => to_string(timestamp) |
||||
} |
||||
} |
||||
|> Jason.encode!() |
||||
end |
||||
|
||||
def error({:error, :no_blocks}) do |
||||
%{ |
||||
"healthy" => false, |
||||
"error_code" => 5002, |
||||
"error_title" => "no blocks in db", |
||||
"error_description" => "There are no blocks in the DB" |
||||
} |
||||
|> Jason.encode!() |
||||
end |
||||
|
||||
def error({:error, number, timestamp}) do |
||||
%{ |
||||
"healthy" => false, |
||||
"error_code" => 5001, |
||||
"error_title" => "blocks fetching is stuck", |
||||
"error_description" => |
||||
"There are no new blocks in the DB for the last 5 mins. Check the healthiness of Ethereum archive node or the Blockscout DB instance", |
||||
"data" => %{ |
||||
"latest_block_number" => to_string(number), |
||||
"latest_block_inserted_at" => to_string(timestamp) |
||||
} |
||||
} |
||||
|> Jason.encode!() |
||||
end |
||||
end |
@ -1,51 +1,39 @@ |
||||
<div class="tile"> |
||||
<div class="row"> |
||||
<!-- rank --> |
||||
<div class="col-2 col-md-1 d-flex justify-content-center align-items-center"> |
||||
<tr> |
||||
<td class="stakes-td"> |
||||
<!-- incremented number by order in the list --> |
||||
<span> |
||||
<span class="color-lighten"> |
||||
<%= @index %> |
||||
</span> |
||||
</div> |
||||
|
||||
<div class="col-10 col-md-11"> |
||||
<div class="row"> |
||||
<div class="col-md-6 d-flex flex-column"> |
||||
<%= @address |> BlockScoutWeb.AddressView.address_partial_selector(nil, nil) |> BlockScoutWeb.RenderHelpers.render_partial() %> |
||||
<!-- number of txns for this address --> |
||||
<span class="mr-4"> |
||||
<span data-test="transaction_count"> |
||||
<%= @tx_count %> |
||||
</span> <%= gettext "Transactions sent" %> |
||||
<% if validator?(@address) do %> |
||||
<span data-test="validation_count"> |
||||
<%= @validation_count %> |
||||
</span> <%= gettext "Validations" %> |
||||
<% end %> |
||||
</span> |
||||
</div> |
||||
|
||||
<!-- balance and percentage --> |
||||
<div class="col-md-6 d-flex flex-column text-md-right mt-3 mt-md-0"> |
||||
<!-- address coin balance --> |
||||
<span class="tile-title" data-test="address_balance"><%= balance(@address) %></span> |
||||
<div class="d-flex flex-column flex-md-row justify-content-md-end"> |
||||
<!-- USD value of the balance --> |
||||
<span |
||||
data-wei-value="<%= if @address.fetched_coin_balance, do: @address.fetched_coin_balance.value %>" |
||||
<% if !empty_exchange_rate?(@exchange_rate) do %> |
||||
data-usd-exchange-rate="<%= @exchange_rate.usd_value %>"> |
||||
<% end %> |
||||
</span> |
||||
<!-- percentage of coins from total supply --> |
||||
<span class="ml-0 ml-md-2"> |
||||
<%= if @total_supply do %> |
||||
(<%= balance_percentage(@address, @total_supply) %>) |
||||
<% end %> |
||||
</span> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</td> |
||||
<td class="stakes-td"> |
||||
<%= @address |> BlockScoutWeb.AddressView.address_partial_selector(nil, nil) |> BlockScoutWeb.RenderHelpers.render_partial() %> |
||||
</td> |
||||
<td class="stakes-td"> |
||||
<span data-test="address_balance"><%= balance(@address) %></span> |
||||
<!-- USD value of the balance --> |
||||
<span |
||||
data-wei-value="<%= if @address.fetched_coin_balance, do: @address.fetched_coin_balance.value %>" |
||||
<% if !empty_exchange_rate?(@exchange_rate) do %> |
||||
data-usd-exchange-rate="<%= @exchange_rate.usd_value %>"> |
||||
<% end %> |
||||
</span> |
||||
</td> |
||||
<td class="stakes-td color-lighten"> |
||||
<!-- percentage of coins from total supply --> |
||||
<%= if @total_supply do %> |
||||
(<%= balance_percentage(@address, @total_supply) %>) |
||||
<% end %> |
||||
</td> |
||||
<td class="stakes-td"> |
||||
<span class="mr-4"> |
||||
<span data-test="transaction_count"> |
||||
<%= @tx_count %> |
||||
</span> <%= gettext "Transactions sent" %> |
||||
<% if validator?(@address) do %> |
||||
<span data-test="validation_count"> |
||||
<%= @validation_count %> |
||||
</span> <%= gettext "Validations" %> |
||||
<% end %> |
||||
</span> |
||||
</td> |
||||
</tr> |
||||
|
@ -0,0 +1,50 @@ |
||||
defmodule BlockScoutWeb.API.V1.HealthControllerTest do |
||||
use BlockScoutWeb.ConnCase |
||||
|
||||
describe "GET last_block_status/0" do |
||||
test "returns error when there are no blocks in db", %{conn: conn} do |
||||
request = get(conn, api_v1_health_path(conn, :health)) |
||||
|
||||
assert request.status == 500 |
||||
|
||||
assert request.resp_body == |
||||
"{\"error_code\":5002,\"error_description\":\"There are no blocks in the DB\",\"error_title\":\"no blocks in db\",\"healthy\":false}" |
||||
end |
||||
|
||||
test "returns error when last block is stale", %{conn: conn} do |
||||
insert(:block, consensus: true, timestamp: Timex.shift(DateTime.utc_now(), hours: -50)) |
||||
|
||||
request = get(conn, api_v1_health_path(conn, :health)) |
||||
|
||||
assert request.status == 500 |
||||
|
||||
assert %{ |
||||
"healthy" => false, |
||||
"error_code" => 5001, |
||||
"error_title" => "blocks fetching is stuck", |
||||
"error_description" => |
||||
"There are no new blocks in the DB for the last 5 mins. Check the healthiness of Ethereum archive node or the Blockscout DB instance", |
||||
"data" => %{ |
||||
"latest_block_number" => _, |
||||
"latest_block_inserted_at" => _ |
||||
} |
||||
} = Poison.decode!(request.resp_body) |
||||
end |
||||
|
||||
test "returns ok when last block is not stale", %{conn: conn} do |
||||
insert(:block, consensus: true, timestamp: DateTime.utc_now()) |
||||
|
||||
request = get(conn, api_v1_health_path(conn, :health)) |
||||
|
||||
assert request.status == 200 |
||||
|
||||
assert %{ |
||||
"healthy" => true, |
||||
"data" => %{ |
||||
"latest_block_number" => _, |
||||
"latest_block_inserted_at" => _ |
||||
} |
||||
} = Poison.decode!(request.resp_body) |
||||
end |
||||
end |
||||
end |
@ -1,78 +0,0 @@ |
||||
defmodule Explorer.Chain.Supply.TransactionAndLog do |
||||
@moduledoc """ |
||||
Defines the supply API for calculating the supply for smaller chains with |
||||
specific mint and burn events |
||||
""" |
||||
use Explorer.Chain.Supply |
||||
|
||||
alias Explorer.Chain.{InternalTransaction, Log, Wei} |
||||
alias Explorer.{Chain, Repo} |
||||
|
||||
{:ok, base_wei} = Wei.cast(0) |
||||
@base_wei base_wei |
||||
|
||||
{:ok, burn_address} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") |
||||
|
||||
@burn_address burn_address |
||||
@bridge_edge "0x3c798bbcf33115b42c728b8504cff11dd58736e9fa789f1cda2738db7d696b2a" |
||||
|
||||
import Ecto.Query, only: [from: 2] |
||||
|
||||
def circulating, do: total(Timex.now()) |
||||
|
||||
def total, do: total(Timex.now()) |
||||
|
||||
@doc false |
||||
@spec total(DateTime.t()) :: %Decimal{sign: 1} |
||||
def total(on_date) do |
||||
on_date |
||||
|> minted_value |
||||
|> Wei.sub(burned_value(on_date)) |
||||
|> Wei.to(:ether) |
||||
end |
||||
|
||||
def supply_for_days(days_count) when is_integer(days_count) and days_count > 0 do |
||||
past_days = -(days_count - 1) |
||||
|
||||
result = |
||||
for i <- past_days..0, into: %{} do |
||||
datetime = Timex.shift(Timex.now(), days: i) |
||||
{DateTime.to_date(datetime), total(datetime)} |
||||
end |
||||
|
||||
{:ok, result} |
||||
end |
||||
|
||||
defp minted_value(on_date) do |
||||
query = |
||||
from( |
||||
l in Log, |
||||
join: t in assoc(l, :transaction), |
||||
join: b in assoc(t, :block), |
||||
where: b.timestamp <= ^on_date and l.first_topic == @bridge_edge, |
||||
select: fragment("concat('0x', encode(?, 'hex'))", l.data) |
||||
) |
||||
|
||||
query |
||||
|> Repo.all() |
||||
|> Enum.reduce(@base_wei, fn data, acc -> |
||||
{:ok, wei_value} = Wei.cast(data) |
||||
Wei.sum(wei_value, acc) |
||||
end) |
||||
end |
||||
|
||||
defp burned_value(on_date) do |
||||
query = |
||||
from( |
||||
it in InternalTransaction, |
||||
join: t in assoc(it, :transaction), |
||||
join: b in assoc(t, :block), |
||||
where: b.timestamp <= ^on_date and it.to_address_hash == ^@burn_address, |
||||
select: it.value |
||||
) |
||||
|
||||
query |
||||
|> Repo.all() |
||||
|> Enum.reduce(@base_wei, fn data, acc -> Wei.sum(data, acc) end) |
||||
end |
||||
end |
@ -0,0 +1,143 @@ |
||||
defmodule Explorer.Chain.TransactionsCache do |
||||
@moduledoc """ |
||||
Caches the latest imported transactions |
||||
""" |
||||
|
||||
alias Explorer.Chain.Transaction |
||||
alias Explorer.Repo |
||||
|
||||
@transactions_ids_key "transactions_ids" |
||||
@cache_name :transactions |
||||
@max_size 51 |
||||
@preloads [ |
||||
:block, |
||||
created_contract_address: :names, |
||||
from_address: :names, |
||||
to_address: :names, |
||||
token_transfers: :token, |
||||
token_transfers: :from_address, |
||||
token_transfers: :to_address |
||||
] |
||||
|
||||
@spec cache_name :: atom() |
||||
def cache_name, do: @cache_name |
||||
|
||||
@doc """ |
||||
Fetches a transaction from its id ({block_number, index}), returns nil if not found |
||||
""" |
||||
@spec get({non_neg_integer(), non_neg_integer()}) :: Transaction.t() | nil |
||||
def get(id), do: ConCache.get(@cache_name, id) |
||||
|
||||
@doc """ |
||||
Return the current number of transactions stored |
||||
""" |
||||
@spec size :: non_neg_integer() |
||||
def size, do: Enum.count(transactions_ids()) |
||||
|
||||
@doc """ |
||||
Checks if there are enough transactions stored |
||||
""" |
||||
@spec enough?(non_neg_integer()) :: boolean() |
||||
def enough?(amount) do |
||||
amount <= size() |
||||
end |
||||
|
||||
@doc """ |
||||
Checks if the number of transactions stored is already the max allowed |
||||
""" |
||||
@spec full? :: boolean() |
||||
def full? do |
||||
@max_size <= size() |
||||
end |
||||
|
||||
@doc "Returns the list ids of the transactions currently stored" |
||||
@spec transactions_ids :: [{non_neg_integer(), non_neg_integer()}] |
||||
def transactions_ids do |
||||
ConCache.get(@cache_name, @transactions_ids_key) || [] |
||||
end |
||||
|
||||
@doc "Returns all the stored transactions" |
||||
@spec all :: [Transaction.t()] |
||||
def all, do: Enum.map(transactions_ids(), &get(&1)) |
||||
|
||||
@doc "Returns the `n` most recent transactions stored" |
||||
@spec take(integer()) :: [Transaction.t()] |
||||
def take(amount) do |
||||
transactions_ids() |
||||
|> Enum.take(amount) |
||||
|> Enum.map(&get(&1)) |
||||
end |
||||
|
||||
@doc """ |
||||
Returns the `n` most recent transactions, unless there are not as many stored, |
||||
in which case returns `nil` |
||||
""" |
||||
@spec take_enough(integer()) :: [Transaction.t()] | nil |
||||
def take_enough(amount) do |
||||
if enough?(amount), do: take(amount) |
||||
end |
||||
|
||||
@doc """ |
||||
Adds a transaction (or a list of transactions). |
||||
If the cache is already full, the transaction will be only stored if it can take |
||||
the place of a less recent one. |
||||
NOTE: each transaction is inserted atomically |
||||
""" |
||||
@spec update([Transaction.t()] | Transaction.t() | nil) :: :ok |
||||
def update(transactions) when is_nil(transactions), do: :ok |
||||
|
||||
def update(transactions) when is_list(transactions) do |
||||
Enum.map(transactions, &update(&1)) |
||||
end |
||||
|
||||
def update(transaction) do |
||||
ConCache.isolated(@cache_name, @transactions_ids_key, fn -> |
||||
transaction_id = {transaction.block_number, transaction.index} |
||||
ids = transactions_ids() |
||||
|
||||
if full?() do |
||||
{init, [min]} = Enum.split(ids, -1) |
||||
|
||||
cond do |
||||
transaction_id < min -> |
||||
:ok |
||||
|
||||
transaction_id > min -> |
||||
insert_transaction(transaction_id, transaction, init) |
||||
ConCache.delete(@cache_name, min) |
||||
|
||||
transaction_id == min -> |
||||
put_transaction(transaction_id, transaction) |
||||
end |
||||
else |
||||
insert_transaction(transaction_id, transaction, ids) |
||||
end |
||||
end) |
||||
end |
||||
|
||||
defp insert_transaction(transaction_id, transaction, ids) do |
||||
put_transaction(transaction_id, transaction) |
||||
|
||||
ConCache.put(@cache_name, @transactions_ids_key, insert_sorted(transaction_id, ids)) |
||||
end |
||||
|
||||
defp put_transaction(transaction_id, transaction) do |
||||
full_transaction = Repo.preload(transaction, @preloads) |
||||
|
||||
ConCache.put(@cache_name, transaction_id, full_transaction) |
||||
end |
||||
|
||||
defp insert_sorted(id, ids) do |
||||
case ids do |
||||
[] -> |
||||
[id] |
||||
|
||||
[head | tail] -> |
||||
cond do |
||||
head > id -> [head | insert_sorted(id, tail)] |
||||
head < id -> [id | ids] |
||||
head == id -> ids |
||||
end |
||||
end |
||||
end |
||||
end |
@ -1,6 +1,6 @@ |
||||
defmodule Explorer.ExchangeRates.Source.TransactionAndLog do |
||||
defmodule Explorer.ExchangeRates.Source.TokenBridge do |
||||
@moduledoc """ |
||||
Adapter for calculating the market cap and total supply from logs and transactions |
||||
Adapter for calculating the market cap and total supply from token bridge |
||||
while still getting other info like price in dollars and bitcoin from a secondary source |
||||
""" |
||||
|
@ -1,122 +0,0 @@ |
||||
defmodule Explorer.Chain.Supply.TransactionAndLogTest do |
||||
use Explorer.DataCase |
||||
alias Explorer.Chain |
||||
alias Explorer.Chain.Supply.TransactionAndLog |
||||
|
||||
setup do |
||||
{:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") |
||||
|
||||
burn_address = |
||||
case Chain.hash_to_address(burn_address_hash) do |
||||
{:ok, burn_address} -> burn_address |
||||
{:error, :not_found} -> insert(:address, hash: "0x0000000000000000000000000000000000000000") |
||||
end |
||||
|
||||
{:ok, %{burn_address: burn_address}} |
||||
end |
||||
|
||||
describe "total/1" do |
||||
test "today with no mints or burns brings zero" do |
||||
assert TransactionAndLog.total(Timex.now()) == Decimal.new(0) |
||||
end |
||||
|
||||
test "today with mints and burns calculates a value", %{burn_address: burn_address} do |
||||
old_block = insert(:block, timestamp: Timex.shift(Timex.now(), days: -1), number: 1000) |
||||
|
||||
insert(:log, |
||||
transaction: |
||||
insert(:transaction, block: old_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 2), |
||||
first_topic: "0x3c798bbcf33115b42c728b8504cff11dd58736e9fa789f1cda2738db7d696b2a", |
||||
data: "0x0000000000000000000000000000000000000000000000008ac7230489e80000" |
||||
) |
||||
|
||||
insert(:internal_transaction, |
||||
index: 527, |
||||
transaction: |
||||
insert(:transaction, block: old_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 3), |
||||
to_address: burn_address, |
||||
value: "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000" |
||||
) |
||||
|
||||
assert TransactionAndLog.total(Timex.now()) == Decimal.new(9) |
||||
end |
||||
|
||||
test "yesterday with mints and burns calculates a value ignoring whatever happened today", %{ |
||||
burn_address: burn_address |
||||
} do |
||||
old_block = insert(:block, timestamp: Timex.shift(Timex.now(), days: -1), number: 1000) |
||||
|
||||
insert(:log, |
||||
transaction: |
||||
insert(:transaction, block: old_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 2), |
||||
first_topic: "0x3c798bbcf33115b42c728b8504cff11dd58736e9fa789f1cda2738db7d696b2a", |
||||
data: "0x0000000000000000000000000000000000000000000000008ac7230489e80000" |
||||
) |
||||
|
||||
new_block = insert(:block, timestamp: Timex.now(), number: 1001) |
||||
|
||||
insert(:internal_transaction, |
||||
index: 527, |
||||
transaction: |
||||
insert(:transaction, block: new_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 3), |
||||
to_address: burn_address, |
||||
value: "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000" |
||||
) |
||||
|
||||
assert TransactionAndLog.total(Timex.shift(Timex.now(), days: -1)) == Decimal.new(10) |
||||
end |
||||
end |
||||
|
||||
describe "total/0" do |
||||
test "calculates the same value as total/1 receiving today's date", %{burn_address: burn_address} do |
||||
old_block = insert(:block, timestamp: Timex.shift(Timex.now(), days: -1), number: 1000) |
||||
|
||||
insert(:log, |
||||
transaction: |
||||
insert(:transaction, block: old_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 2), |
||||
first_topic: "0x3c798bbcf33115b42c728b8504cff11dd58736e9fa789f1cda2738db7d696b2a", |
||||
data: "0x0000000000000000000000000000000000000000000000008ac7230489e80000" |
||||
) |
||||
|
||||
insert(:internal_transaction, |
||||
index: 527, |
||||
transaction: |
||||
insert(:transaction, block: old_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 3), |
||||
to_address: burn_address, |
||||
value: "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000" |
||||
) |
||||
|
||||
assert TransactionAndLog.total() == TransactionAndLog.total(Timex.now()) |
||||
end |
||||
end |
||||
|
||||
describe "supply_for_days/1" do |
||||
test "bring the supply of today and yesterday when receiving 2", %{burn_address: burn_address} do |
||||
old_block = insert(:block, timestamp: Timex.shift(Timex.now(), days: -1), number: 1000) |
||||
|
||||
insert(:log, |
||||
transaction: |
||||
insert(:transaction, block: old_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 2), |
||||
first_topic: "0x3c798bbcf33115b42c728b8504cff11dd58736e9fa789f1cda2738db7d696b2a", |
||||
data: "0x0000000000000000000000000000000000000000000000008ac7230489e80000" |
||||
) |
||||
|
||||
new_block = insert(:block, timestamp: Timex.now(), number: 1001) |
||||
|
||||
insert(:internal_transaction, |
||||
index: 527, |
||||
transaction: |
||||
insert(:transaction, block: new_block, block_number: 1000, cumulative_gas_used: 1, gas_used: 1, index: 3), |
||||
to_address: burn_address, |
||||
value: "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000" |
||||
) |
||||
|
||||
expected_result = %{ |
||||
Timex.shift(Timex.today(), days: -1) => Decimal.new(10), |
||||
Timex.today() => Decimal.new(9) |
||||
} |
||||
|
||||
assert TransactionAndLog.supply_for_days(2) == {:ok, expected_result} |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,95 @@ |
||||
defmodule Explorer.Chain.TransactionsCacheTest do |
||||
use Explorer.DataCase |
||||
|
||||
alias Explorer.Chain.TransactionsCache |
||||
alias Explorer.Repo |
||||
|
||||
@size 51 |
||||
|
||||
describe "update/1" do |
||||
test "adds a new value to a new cache with preloads" do |
||||
transaction = insert(:transaction) |> preload_all() |
||||
|
||||
TransactionsCache.update(transaction) |
||||
|
||||
assert TransactionsCache.take(1) == [transaction] |
||||
end |
||||
|
||||
test "adds several elements, removing the oldest when necessary" do |
||||
transactions = |
||||
1..@size |
||||
|> Enum.map(fn n -> |
||||
block = insert(:block, number: n) |
||||
insert(:transaction) |> with_block(block) |
||||
end) |
||||
|
||||
TransactionsCache.update(transactions) |
||||
|
||||
assert TransactionsCache.all() == Enum.reverse(preload_all(transactions)) |
||||
|
||||
more_transactions = |
||||
(@size + 1)..(@size + 10) |
||||
|> Enum.map(fn n -> |
||||
block = insert(:block, number: n) |
||||
insert(:transaction) |> with_block(block) |
||||
end) |
||||
|
||||
TransactionsCache.update(more_transactions) |
||||
|
||||
kept_transactions = |
||||
Enum.reverse(transactions ++ more_transactions) |
||||
|> Enum.take(@size) |
||||
|> preload_all() |
||||
|
||||
assert TransactionsCache.take(@size) == kept_transactions |
||||
end |
||||
|
||||
test "does not add a transaction too old when full" do |
||||
transactions = |
||||
10..(@size + 9) |
||||
|> Enum.map(fn n -> |
||||
block = insert(:block, number: n) |
||||
insert(:transaction) |> with_block(block) |
||||
end) |
||||
|
||||
TransactionsCache.update(transactions) |
||||
|
||||
loaded_transactions = Enum.reverse(preload_all(transactions)) |
||||
assert TransactionsCache.all() == loaded_transactions |
||||
|
||||
block = insert(:block, number: 1) |
||||
insert(:transaction) |> with_block(block) |> TransactionsCache.update() |
||||
|
||||
assert TransactionsCache.all() == loaded_transactions |
||||
end |
||||
|
||||
test "adds intermediate transactions" do |
||||
blocks = 1..10 |> Map.new(fn n -> {n, insert(:block, number: n)} end) |
||||
|
||||
insert(:transaction) |> with_block(blocks[1]) |> TransactionsCache.update() |
||||
insert(:transaction) |> with_block(blocks[10]) |> TransactionsCache.update() |
||||
|
||||
assert TransactionsCache.size() == 2 |
||||
|
||||
insert(:transaction) |> with_block(blocks[5]) |> TransactionsCache.update() |
||||
|
||||
assert TransactionsCache.size() == 3 |
||||
end |
||||
end |
||||
|
||||
defp preload_all(transactions) when is_list(transactions) do |
||||
Enum.map(transactions, &preload_all(&1)) |
||||
end |
||||
|
||||
defp preload_all(transaction) do |
||||
Repo.preload(transaction, [ |
||||
:block, |
||||
created_contract_address: :names, |
||||
from_address: :names, |
||||
to_address: :names, |
||||
token_transfers: :token, |
||||
token_transfers: :from_address, |
||||
token_transfers: :to_address |
||||
]) |
||||
end |
||||
end |
Loading…
Reference in new issue