Merge branch 'master' into 623-live-reload-pending-transaction-details

pull/760/head
Andrew Cravenho 6 years ago committed by GitHub
commit 3d1c2ecc2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      apps/block_scout_web/assets/css/components/_tile.scss
  2. 2
      apps/block_scout_web/config/test.exs
  3. 3
      apps/block_scout_web/lib/block_scout_web/chain.ex
  4. 13
      apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex
  5. 4
      apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex
  6. 3
      apps/block_scout_web/lib/block_scout_web/notifier.ex
  7. 2
      apps/block_scout_web/lib/block_scout_web/router.ex
  8. 56
      apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex
  9. 2
      apps/block_scout_web/lib/block_scout_web/templates/block/_tile.html.eex
  10. 2
      apps/block_scout_web/lib/block_scout_web/templates/chain/_block.html.eex
  11. 5
      apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex
  12. 2
      apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex
  13. 45
      apps/block_scout_web/lib/block_scout_web/views/address_view.ex
  14. 42
      apps/block_scout_web/priv/gettext/default.pot
  15. 44
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  16. 2
      apps/block_scout_web/test/block_scout_web/channels/address_channel_test.exs
  17. 13
      apps/block_scout_web/test/block_scout_web/controllers/address_controller_test.exs
  18. 20
      apps/block_scout_web/test/block_scout_web/features/pages/address_page.ex
  19. 24
      apps/block_scout_web/test/block_scout_web/features/viewing_addresses_test.exs
  20. 91
      apps/block_scout_web/test/block_scout_web/views/address_view_test.exs
  21. 8
      apps/block_scout_web/test/support/fake_adapter.ex
  22. 33
      apps/explorer/lib/explorer/chain.ex
  23. 15
      apps/explorer/priv/repo/migrations/20180919175123_alter_token_decimals_to_bigint.exs
  24. 55
      apps/explorer/test/explorer/chain_test.exs

@ -3,7 +3,6 @@
color: $text-muted;
line-height: 1.4rem;
border: 1px solid $border-color;
border-left: 4px solid $primary;
border-radius: 2px;
padding: 1rem;
box-shadow: 0 1px 5px rgba($black, 0.15);
@ -18,6 +17,10 @@
&-type {
&-block {
border-left: 4px solid $primary;
}
&-transaction {
border-left: 4px solid $blue;

@ -15,5 +15,3 @@ config :logger, :block_scout_web,
# Configure wallaby
config :wallaby, screenshot_on_failure: true
config :block_scout_web, :fake_adapter, BlockScoutWeb.FakeAdapter

@ -16,7 +16,6 @@ defmodule BlockScoutWeb.Chain do
Address,
Address.TokenBalance,
Block,
Hash,
InternalTransaction,
Log,
Token,
@ -195,7 +194,7 @@ defmodule BlockScoutWeb.Chain do
end
defp paging_params(%TokenBalance{address_hash: address_hash, value: value}) do
%{"address_hash" => Hash.to_string(address_hash), "value" => Decimal.to_integer(value)}
%{"address_hash" => to_string(address_hash), "value" => Decimal.to_integer(value)}
end
defp transaction_from_param(param) do

@ -1,14 +1,23 @@
defmodule BlockScoutWeb.AddressController do
use BlockScoutWeb, :controller
alias Explorer.Chain
alias Explorer.{Chain, Market}
alias Explorer.Chain.Address
alias Explorer.ExchangeRates.Token
def index(conn, _params) do
render(conn, "index.html",
addresses: Chain.list_top_addresses(),
address_estimated_count: Chain.address_estimated_count(),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null()
)
end
def show(conn, %{"id" => id}) do
redirect(conn, to: address_transaction_path(conn, :index, id))
end
def transaction_count(%Address{} = address) do
Chain.address_to_transaction_count_estimate(address)
Chain.address_to_transactions_estimated_count(address)
end
end

@ -7,8 +7,6 @@ defmodule BlockScoutWeb.ChainController do
alias Explorer.Market
def show(conn, _params) do
address_count_module = Application.get_env(:block_scout_web, :fake_adapter) || Chain
blocks =
[paging_options: %PagingOptions{page_size: 4}]
|> Chain.list_blocks()
@ -38,7 +36,7 @@ defmodule BlockScoutWeb.ChainController do
render(
conn,
"show.html",
address_estimated_count: address_count_module.address_estimated_count(),
address_estimated_count: Chain.address_estimated_count(),
average_block_time: Chain.average_block_time(),
blocks: blocks,
exchange_rate: exchange_rate,

@ -9,8 +9,7 @@ defmodule BlockScoutWeb.Notifier do
alias BlockScoutWeb.Endpoint
def handle_event({:chain_event, :addresses, addresses}) do
address_count_module = Application.get_env(:block_scout_web, :fake_adapter) || Chain
Endpoint.broadcast("addresses:new_address", "count", %{count: address_count_module.address_estimated_count()})
Endpoint.broadcast("addresses:new_address", "count", %{count: Chain.address_estimated_count()})
addresses
|> Stream.reject(fn %Address{fetched_coin_balance: fetched_coin_balance} -> is_nil(fetched_coin_balance) end)

@ -61,6 +61,8 @@ defmodule BlockScoutWeb.Router do
resources("/token_transfers", TransactionTokenTransferController, only: [:index], as: :token_transfer)
end
resources("/accounts", AddressController, only: [:index])
resources "/address", AddressController, only: [:show] do
resources("/transactions", AddressTransactionController, only: [:index], as: :transaction)

@ -0,0 +1,56 @@
<section class="container">
<div class="card">
<div class="card-body">
<h1><%= gettext "Addresses" %></h1>
<p>
<%= gettext "Showing 250 addresses of" %>
<%= Cldr.Number.to_string!(@address_estimated_count, format: "#,###") %>
<%= gettext "total addresses with a balance" %>
</p>
<span data-selector="top-addresses-list">
<%= for {address, index} <- Enum.with_index(@addresses, 1) do %>
<div class="tile">
<div class="row">
<!-- rank -->
<div class="col-2 col-md-1 d-flex justify-content-center align-items-center">
<!-- incremented number by order in the list -->
<span><%= index %></span>
</div>
<div class="col-10 col-md-11">
<div class="row">
<div class="col-md-7 d-flex flex-column mt-3 mt-md-0">
<%= address |> BlockScoutWeb.AddressView.address_partial_selector(nil, nil) |> BlockScoutWeb.AddressView.render_partial() %>
<!-- number of txns for this address -->
<span>
<span data-test="transaction_count"><%= transaction_count(address) %></span>
<%= gettext "Transactions" %>
</span>
</div>
<!-- balance and percentage -->
<div class="col-md-5 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 %>"
data-usd-exchange-rate="<%= @exchange_rate.usd_value %>">
</span>
<!-- percentage of coins from total supply -->
<span class="ml-0 ml-md-2">(<%= balance_percentage(address) %>)</span>
</div>
</div>
</div>
</div>
</div>
</div>
<% end %>
</span>
</div>
</div>
</section>

@ -1,4 +1,4 @@
<div class="tile fade-up">
<div class="tile tile-type-block fade-up">
<div class="row">
<div class="col-md-8 col-lg-9">
<!-- block height -->

@ -1,5 +1,5 @@
<div class="col-sm-3 fade-up-blocks-chain mb-3 mb-sm-0" data-selector="chain-block" data-block-number="<%= @block.number %>">
<div class="tile d-flex flex-column">
<div class="tile tile-type-block d-flex flex-column">
<%= link(@block, to: block_path(BlockScoutWeb.Endpoint, :show, @block), class: "tile-title") %>
<div>
<span class="mr-2"> <%= Enum.count(@block.transactions) %> Transactions </span>

@ -18,6 +18,11 @@
<%= gettext("Transactions") %>
<% end %>
</li>
<li class="nav-item">
<%= link to: address_path(@conn, :index), class: "nav-link topnav-nav-link" do %>
<%= gettext("Accounts") %>
<% end %>
</li>
<li class="nav-item">
<%= link to: api_docs_path(@conn, :index), class: "nav-link topnav-nav-link" do %>
<%= gettext("API") %>

@ -53,7 +53,7 @@
<a href="#" class="alert-link"><%= gettext "Connection Lost, click to load newer transactions" %></a>
</div>
</div>
<h2 class="card-title mb-0"><%= gettext "Transactions" %></h2>
<h1><%= gettext "Transactions" %></h1>
<p><%= gettext("Showing") %> <span data-selector="transaction-count"><%= Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###") %></span> <%= gettext("Validated Transactions") %></p>
<span data-selector="transactions-list">
<%= for transaction <- @transactions do %>

@ -1,12 +1,17 @@
defmodule BlockScoutWeb.AddressView do
use BlockScoutWeb, :view
alias Explorer.Chain.{Address, Hash, InternalTransaction, SmartContract, Token, TokenTransfer, Transaction}
alias Explorer.Chain
alias Explorer.Chain.{Address, Hash, InternalTransaction, SmartContract, Token, TokenTransfer, Transaction, Wei}
@dialyzer :no_match
def address_partial_selector(struct_to_render_from, direction, current_address, truncate \\ false)
def address_partial_selector(%Address{} = address, _, current_address, truncate) do
matching_address_check(current_address, address, contract?(address), truncate)
end
def address_partial_selector(
%InternalTransaction{to_address_hash: nil, created_contract_address_hash: nil},
:to,
@ -84,6 +89,16 @@ defmodule BlockScoutWeb.AddressView do
format_wei_value(balance, :ether)
end
def balance_percentage(%Address{fetched_coin_balance: balance}) do
balance
|> Wei.to(:ether)
|> Decimal.div(Decimal.new(Chain.total_supply()))
|> Decimal.mult(100)
|> Decimal.round(4)
|> Decimal.to_string(:normal)
|> Kernel.<>("% #{gettext("Market Cap")}")
end
def balance_block_number(%Address{fetched_coin_balance_block_number: nil}), do: ""
def balance_block_number(%Address{fetched_coin_balance_block_number: fetched_coin_balance_block_number}) do
@ -100,6 +115,18 @@ defmodule BlockScoutWeb.AddressView do
to_string(hash)
end
@doc """
Returns the primary name of an address if available.
"""
def primary_name(%Address{names: [_ | _] = address_names}) do
case Enum.find(address_names, &(&1.primary == true)) do
nil -> nil
%Address.Name{name: name} -> name
end
end
def primary_name(%Address{names: _}), do: nil
def qr_code(%Address{hash: hash}) do
hash
|> to_string()
@ -136,6 +163,10 @@ defmodule BlockScoutWeb.AddressView do
def token_title(%Token{name: name, symbol: symbol}), do: "#{name} (#{symbol})"
def transaction_count(%Address{} = address) do
Chain.address_to_transactions_estimated_count(address)
end
def trimmed_hash(%Hash{} = hash) do
string_hash = to_string(hash)
"#{String.slice(string_hash, 0..5)}#{String.slice(string_hash, -6..-1)}"
@ -160,16 +191,4 @@ defmodule BlockScoutWeb.AddressView do
truncate: truncate
}
end
@doc """
Returns the primary name of an address if available.
"""
def primary_name(%Address{names: [_ | _] = address_names}) do
case Enum.find(address_names, &(&1.primary == true)) do
nil -> nil
%Address.Name{name: name} -> name
end
end
def primary_name(%Address{names: _}), do: nil
end

@ -33,6 +33,7 @@ msgstr ""
msgid "BlockScout"
msgstr ""
#: lib/block_scout_web/templates/address/index.html.eex:28
#: lib/block_scout_web/templates/address/overview.html.eex:28
#: lib/block_scout_web/templates/address_contract/index.html.eex:12
#: lib/block_scout_web/templates/address_contract/index.html.eex:65
@ -138,7 +139,7 @@ msgid "%{count} transactions in this block"
msgstr ""
#: lib/block_scout_web/templates/transaction_log/index.html.eex:70
#: lib/block_scout_web/views/address_view.ex:74
#: lib/block_scout_web/views/address_view.ex:79
msgid "Address"
msgstr ""
@ -331,7 +332,7 @@ msgstr ""
msgid "Internal Transactions"
msgstr ""
#: lib/block_scout_web/templates/layout/_topnav.html.eex:29
#: lib/block_scout_web/templates/layout/_topnav.html.eex:34
msgid "Search by address, transaction hash, or block number"
msgstr ""
@ -404,6 +405,7 @@ msgstr ""
#: lib/block_scout_web/templates/chain/show.html.eex:17
#: lib/block_scout_web/templates/layout/app.html.eex:24
#: lib/block_scout_web/views/address_view.ex:99
msgid "Market Cap"
msgstr ""
@ -487,7 +489,7 @@ msgid "Contract"
msgstr ""
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:12
#: lib/block_scout_web/views/address_view.ex:72
#: lib/block_scout_web/views/address_view.ex:77
msgid "Contract Address"
msgstr ""
@ -559,17 +561,17 @@ msgid "Contract Creation"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:39
#: lib/block_scout_web/templates/layout/_topnav.html.eex:44
msgid "Mainnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:43
#: lib/block_scout_web/templates/layout/_topnav.html.eex:48
msgid "POA Core"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:42
#: lib/block_scout_web/templates/layout/_topnav.html.eex:47
msgid "POA Sokol"
msgstr ""
@ -649,8 +651,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:64
#: lib/block_scout_web/views/address_view.ex:16
#: lib/block_scout_web/views/address_view.ex:50
#: lib/block_scout_web/views/address_view.ex:21
#: lib/block_scout_web/views/address_view.ex:55
msgid "Contract Address Pending"
msgstr ""
@ -846,7 +848,7 @@ msgid "Total Supply"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:23
#: lib/block_scout_web/templates/layout/_topnav.html.eex:28
msgid "API"
msgstr ""
@ -1169,8 +1171,8 @@ msgid "Reset"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:29
#: lib/block_scout_web/templates/layout/_topnav.html.eex:34
#: lib/block_scout_web/templates/layout/_topnav.html.eex:39
msgid "Search"
msgstr ""
@ -1235,3 +1237,23 @@ msgstr ""
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:59
msgid "Verify & publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:23
msgid "Accounts"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:4
msgid "Addresses"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:6
msgid "Showing 250 addresses of"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:8
msgid "total addresses with a balance"
msgstr ""

@ -45,6 +45,7 @@ msgstr "Height"
msgid "BlockScout"
msgstr "BlockScout"
#: lib/block_scout_web/templates/address/index.html.eex:28
#: lib/block_scout_web/templates/address/overview.html.eex:28
#: lib/block_scout_web/templates/address_contract/index.html.eex:12
#: lib/block_scout_web/templates/address_contract/index.html.eex:65
@ -150,7 +151,7 @@ msgid "%{count} transactions in this block"
msgstr "%{count} transactions in this block"
#: lib/block_scout_web/templates/transaction_log/index.html.eex:70
#: lib/block_scout_web/views/address_view.ex:74
#: lib/block_scout_web/views/address_view.ex:79
msgid "Address"
msgstr "Address"
@ -343,7 +344,7 @@ msgstr ""
msgid "Internal Transactions"
msgstr ""
#: lib/block_scout_web/templates/layout/_topnav.html.eex:29
#: lib/block_scout_web/templates/layout/_topnav.html.eex:34
msgid "Search by address, transaction hash, or block number"
msgstr ""
@ -416,6 +417,7 @@ msgstr ""
#: lib/block_scout_web/templates/chain/show.html.eex:17
#: lib/block_scout_web/templates/layout/app.html.eex:24
#: lib/block_scout_web/views/address_view.ex:99
msgid "Market Cap"
msgstr ""
@ -499,7 +501,7 @@ msgid "Contract"
msgstr ""
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:12
#: lib/block_scout_web/views/address_view.ex:72
#: lib/block_scout_web/views/address_view.ex:77
msgid "Contract Address"
msgstr ""
@ -571,17 +573,17 @@ msgid "Contract Creation"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:39
#: lib/block_scout_web/templates/layout/_topnav.html.eex:44
msgid "Mainnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:43
#: lib/block_scout_web/templates/layout/_topnav.html.eex:48
msgid "POA Core"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:42
#: lib/block_scout_web/templates/layout/_topnav.html.eex:47
msgid "POA Sokol"
msgstr ""
@ -661,8 +663,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:64
#: lib/block_scout_web/views/address_view.ex:16
#: lib/block_scout_web/views/address_view.ex:50
#: lib/block_scout_web/views/address_view.ex:21
#: lib/block_scout_web/views/address_view.ex:55
msgid "Contract Address Pending"
msgstr ""
@ -858,7 +860,7 @@ msgid "Total Supply"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:23
#: lib/block_scout_web/templates/layout/_topnav.html.eex:28
msgid "API"
msgstr ""
@ -1181,8 +1183,8 @@ msgid "Reset"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:29
#: lib/block_scout_web/templates/layout/_topnav.html.eex:34
#: lib/block_scout_web/templates/layout/_topnav.html.eex:39
msgid "Search"
msgstr ""
@ -1238,7 +1240,7 @@ msgstr ""
msgid "Copy Code"
msgstr ""
#, elixir-format, fuzzy
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:103
msgid "Verify & Publish"
msgstr ""
@ -1247,3 +1249,23 @@ msgstr ""
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:59
msgid "Verify & publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:23
msgid "Accounts"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:4
msgid "Addresses"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:6
msgid "Showing 250 addresses of"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:8
msgid "total addresses with a balance"
msgstr ""

@ -12,7 +12,7 @@ defmodule BlockScoutWeb.AddressChannelTest do
Notifier.handle_event({:chain_event, :addresses, [address]})
receive do
%Phoenix.Socket.Broadcast{topic: ^topic, event: "count", payload: %{count: 1}} ->
%Phoenix.Socket.Broadcast{topic: ^topic, event: "count", payload: %{count: _}} ->
assert true
after
5_000 ->

@ -1,6 +1,19 @@
defmodule BlockScoutWeb.AddressControllerTest do
use BlockScoutWeb.ConnCase
describe "GET index/2" do
test "returns top addresses", %{conn: conn} do
address_hashes =
4..1
|> Enum.map(&insert(:address, fetched_coin_balance: &1))
|> Enum.map(& &1.hash)
conn = get(conn, address_path(conn, :index))
assert conn.assigns.addresses |> Enum.map(& &1.hash) == address_hashes
end
end
describe "GET show/3" do
test "redirects to address/:address_id/transactions", %{conn: conn} do
insert(:address, hash: "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed")

@ -15,6 +15,10 @@ defmodule BlockScoutWeb.AddressPage do
css("[data-test='address_balance']")
end
def address(%Address{hash: hash}) do
css("[data-address-hash='#{hash}']", text: to_string(hash))
end
def contract_creator do
css("[data-test='address_contract_creator']")
end
@ -75,12 +79,6 @@ defmodule BlockScoutWeb.AddressPage do
css("[data-transaction-hash='#{transaction_hash}'] [data-test='transaction_status']")
end
def visit_page(session, %Address{hash: address_hash}), do: visit_page(session, address_hash)
def visit_page(session, address_hash) do
visit(session, "/address/#{address_hash}")
end
def token_transfer(%Transaction{hash: transaction_hash}, %Address{hash: address_hash}, count: count) do
css(
"[data-transaction-hash='#{transaction_hash}'] [data-test='token_transfer'] [data-address-hash='#{address_hash}']",
@ -95,4 +93,14 @@ defmodule BlockScoutWeb.AddressPage do
def token_transfers_expansion(%Transaction{hash: transaction_hash}) do
css("[data-transaction-hash='#{transaction_hash}'] [data-test='token_transfers_expansion']")
end
def visit_page(session, %Address{hash: address_hash}), do: visit_page(session, address_hash)
def visit_page(session, address_hash) do
visit(session, "/address/#{address_hash}")
end
def visit_page(session) do
visit(session, "/accounts")
end
end

@ -1,15 +1,13 @@
defmodule BlockScoutWeb.ViewingAddressesTest do
use BlockScoutWeb.FeatureCase, async: true
alias Explorer.Chain.Wei
alias BlockScoutWeb.{AddressPage, AddressView, Notifier}
setup do
block = insert(:block)
{:ok, balance} = Wei.cast(5)
lincoln = insert(:address, fetched_coin_balance: balance)
taft = insert(:address)
lincoln = insert(:address, fetched_coin_balance: 5)
taft = insert(:address, fetched_coin_balance: 5)
from_taft =
:transaction
@ -29,6 +27,24 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
}}
end
describe "viewing top addresses" do
setup do
addresses = Enum.map(150..101, &insert(:address, fetched_coin_balance: &1))
{:ok, %{addresses: addresses}}
end
test "lists top addresses", %{session: session, addresses: addresses} do
[first_address | _] = addresses
[last_address | _] = Enum.reverse(addresses)
session
|> AddressPage.visit_page()
|> assert_has(AddressPage.address(first_address))
|> assert_has(AddressPage.address(last_address))
end
end
test "viewing address overview information", %{session: session} do
address = insert(:address, fetched_coin_balance: 500)

@ -115,6 +115,21 @@ defmodule BlockScoutWeb.AddressViewTest do
end
end
describe "balance_block_number/1" do
test "gives empty string with no fetched balance block number present" do
assert AddressView.balance_block_number(%Address{}) == ""
end
test "gives block number when fetched balance block number is non-nil" do
assert AddressView.balance_block_number(%Address{fetched_coin_balance_block_number: 1_000_000}) == "1000000"
end
end
test "balance_percentage/1" do
address = insert(:address, fetched_coin_balance: 2_524_608_000_000_000_000_000_000)
assert "1.0000% Market Cap" = AddressView.balance_percentage(address)
end
describe "contract?/1" do
test "with a smart contract" do
{:ok, code} = Data.cast("0x000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef")
@ -132,6 +147,39 @@ defmodule BlockScoutWeb.AddressViewTest do
end
end
describe "hash/1" do
test "gives a string version of an address's hash" do
address = %Address{
hash: %Hash{
byte_count: 20,
bytes: <<139, 243, 141, 71, 100, 146, 144, 100, 242, 212, 211, 165, 101, 32, 167, 106, 179, 223, 65, 91>>
}
}
assert AddressView.hash(address) == "0x8bf38d4764929064f2d4d3a56520a76ab3df415b"
end
end
describe "primary_name/1" do
test "gives an address's primary name when present" do
address = insert(:address)
address_name = insert(:address_name, address: address, primary: true, name: "POA Foundation Wallet")
insert(:address_name, address: address, name: "POA Wallet")
preloaded_address = Explorer.Repo.preload(address, :names)
assert AddressView.primary_name(preloaded_address) == address_name.name
end
test "returns nil when no primary available" do
address_name = insert(:address_name, name: "POA Wallet")
preloaded_address = Explorer.Repo.preload(address_name.address, :names)
refute AddressView.primary_name(preloaded_address)
end
end
describe "qr_code/1" do
test "it returns an encoded value" do
address = build(:address)
@ -241,47 +289,4 @@ defmodule BlockScoutWeb.AddressViewTest do
assert AddressView.token_title(token) == "super token money (ST$)"
end
end
describe "hash/1" do
test "gives a string version of an address's hash" do
address = %Address{
hash: %Hash{
byte_count: 20,
bytes: <<139, 243, 141, 71, 100, 146, 144, 100, 242, 212, 211, 165, 101, 32, 167, 106, 179, 223, 65, 91>>
}
}
assert AddressView.hash(address) == "0x8bf38d4764929064f2d4d3a56520a76ab3df415b"
end
end
describe "balance_block_number/1" do
test "gives empty string with no fetched balance block number present" do
assert AddressView.balance_block_number(%Address{}) == ""
end
test "gives block number when fetched balance block number is non-nil" do
assert AddressView.balance_block_number(%Address{fetched_coin_balance_block_number: 1_000_000}) == "1000000"
end
end
describe "primary_name/1" do
test "gives an address's primary name when present" do
address = insert(:address)
address_name = insert(:address_name, address: address, primary: true, name: "POA Foundation Wallet")
insert(:address_name, address: address, name: "POA Wallet")
preloaded_address = Explorer.Repo.preload(address, :names)
assert AddressView.primary_name(preloaded_address) == address_name.name
end
test "returns nil when no primary available" do
address_name = insert(:address_name, name: "POA Wallet")
preloaded_address = Explorer.Repo.preload(address_name.address, :names)
refute AddressView.primary_name(preloaded_address)
end
end
end

@ -1,8 +0,0 @@
defmodule BlockScoutWeb.FakeAdapter do
alias Explorer.Chain.Address
alias Explorer.Repo
def address_estimated_count do
Repo.aggregate(Address, :count, :hash)
end
end

@ -69,16 +69,18 @@ defmodule Explorer.Chain do
@typep paging_options :: {:paging_options, PagingOptions.t()}
@doc """
Estimated count of `t:Explorer.Chain.Address.t/0`.
Estimated count of addresses
Gets an estimated count of `t:Explorer.Chain.Address.t/0`'s where the `fetched_coin_balance` is > 0
"""
@spec address_estimated_count :: non_neg_integer()
def address_estimated_count do
%Postgrex.Result{rows: [[rows]]} =
SQL.query!(Repo, "SELECT reltuples::BIGINT AS estimate FROM pg_class WHERE relname='addresses'")
{:ok, %Postgrex.Result{rows: result}} =
Repo.query("""
EXPLAIN SELECT COUNT(a0.hash) FROM addresses AS a0 WHERE (a0.fetched_coin_balance > 0)
""")
rows
{[explain], _} = List.pop_at(result, 1)
[[_ | [rows]]] = Regex.scan(~r/rows=(\d+)/, explain)
String.to_integer(rows)
end
@doc """
@ -135,8 +137,8 @@ defmodule Explorer.Chain do
Gets an estimated count of `t:Explorer.Chain.Transaction.t/0` to or from the `address` based on the estimated rows
resulting in an EXPLAIN of the query plan for the count query.
"""
@spec address_to_transaction_count_estimate(Address.t()) :: non_neg_integer()
def address_to_transaction_count_estimate(%Address{hash: address_hash}) do
@spec address_to_transactions_estimated_count(Address.t()) :: non_neg_integer()
def address_to_transactions_estimated_count(%Address{hash: address_hash}) do
{:ok, %Postgrex.Result{rows: result}} =
Repo.query(
"""
@ -740,7 +742,7 @@ defmodule Explorer.Chain do
`:required`, and the `t:Explorer.Chain.Block.t/0` has no associated record for that association, then the
`t:Explorer.Chain.Block.t/0` will not be included in the page `entries`.
* `:paging_options` - a `t:Explorer.PagingOptions.t/0` used to specify the `:page_size` and
`:key` (a tuple of the lowest/oldest `{block_number}`) and. Results will be the internal
`:key` (a tuple of the lowest/oldest `{block_number}`). Results will be the internal
transactions older than the `block_number` that are passed.
"""
@ -757,6 +759,19 @@ defmodule Explorer.Chain do
|> Repo.all()
end
@doc """
Lists the top 250 `t:Explorer.Chain.Address.t/0`'s' in descending order based on coin balance.
"""
@spec list_top_addresses :: [Address.t()]
def list_top_addresses do
Address
|> limit(250)
|> order_by(desc: :fetched_coin_balance, asc: :hash)
|> where([address], address.fetched_coin_balance > ^0)
|> Repo.all()
end
@doc """
Returns a stream of unfetched `t:Explorer.Chain.Address.CoinBalance.t/0`.

@ -0,0 +1,15 @@
defmodule Explorer.Repo.Migrations.AlterTokenDecimalsToBigint do
use Ecto.Migration
def up do
alter table("tokens") do
modify(:decimals, :bigint)
end
end
def down do
alter table("tokens") do
modify(:decimals, :smallint)
end
end
end

@ -277,6 +277,12 @@ defmodule Explorer.ChainTest do
end
end
describe "address_to_transactions_estimated_count/1" do
test "returns integer" do
assert is_integer(Chain.address_to_transactions_estimated_count(build(:address)))
end
end
describe "average_block_time/0" do
test "without blocks duration is 0" do
assert Chain.average_block_time() == Timex.Duration.parse!("PT0S")
@ -979,6 +985,41 @@ defmodule Explorer.ChainTest do
end
end
describe "list_top_addresses/0" do
test "without addresses with balance > 0" do
insert(:address, fetched_coin_balance: 0)
assert [] = Chain.list_top_addresses()
end
test "with top addresses in order" do
address_hashes =
4..1
|> Enum.map(&insert(:address, fetched_coin_balance: &1))
|> Enum.map(& &1.hash)
assert address_hashes == Enum.map(Chain.list_top_addresses(), & &1.hash)
end
test "with top addresses in order with matching value" do
test_hashes =
4..0
|> Enum.map(&Explorer.Chain.Hash.cast(Explorer.Chain.Hash.Address, &1))
|> Enum.map(&elem(&1, 1))
tail =
4..1
|> Enum.map(&insert(:address, fetched_coin_balance: &1, hash: Enum.fetch!(test_hashes, &1 - 1)))
|> Enum.map(& &1.hash)
first_result_hash =
:address
|> insert(fetched_coin_balance: 4, hash: Enum.fetch!(test_hashes, 4))
|> Map.fetch!(:hash)
assert [first_result_hash | tail] == Enum.map(Chain.list_top_addresses(), & &1.hash)
end
end
describe "number_to_block/1" do
test "without block" do
assert {:error, :not_found} = Chain.number_to_block(-1)
@ -2602,6 +2643,20 @@ defmodule Explorer.ChainTest do
Chain.update_token(token, update_params)
refute Repo.get_by(Address.Name, address_hash: token.contract_address_hash)
end
test "stores token with big 'decimals' values" do
token = insert(:token, name: nil, symbol: nil, total_supply: nil, decimals: nil, cataloged: false)
update_params = %{
name: "Hodl Token",
symbol: "HT",
total_supply: 10,
decimals: 1_000_000_000_000_000_000,
cataloged: true
}
assert {:ok, updated_token} = Chain.update_token(token, update_params)
end
end
describe "fetch_last_token_balances/1" do

Loading…
Cancel
Save