Merge pull request #738 from Lokraan/validated-blocks-address-page-overview

Add mined blocks count to miner (validator) address page overview.
pull/826/head
Andrew Cravenho 6 years ago committed by GitHub
commit 32ce2f9289
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      apps/block_scout_web/assets/js/pages/address.js
  2. 3
      apps/block_scout_web/lib/block_scout_web/channels/block_channel.ex
  3. 5
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_controller.ex
  4. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex
  5. 6
      apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex
  6. 5
      apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex
  7. 3
      apps/block_scout_web/lib/block_scout_web/controllers/address_token_controller.ex
  8. 5
      apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex
  9. 5
      apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex
  10. 43
      apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex
  11. 7
      apps/block_scout_web/lib/block_scout_web/router.ex
  12. 12
      apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex
  13. 52
      apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex
  14. 89
      apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex
  15. 41
      apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex
  16. 87
      apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex
  17. 85
      apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex
  18. 137
      apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex
  19. 2
      apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
  20. 94
      apps/block_scout_web/lib/block_scout_web/templates/tokens/token/show.html.eex
  21. 4
      apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex
  22. 2
      apps/block_scout_web/lib/block_scout_web/views/address_internal_transaction_view.ex
  23. 2
      apps/block_scout_web/lib/block_scout_web/views/address_read_contract_view.ex
  24. 2
      apps/block_scout_web/lib/block_scout_web/views/address_transaction_view.ex
  25. 6
      apps/block_scout_web/lib/block_scout_web/views/address_validation_view.ex
  26. 4
      apps/block_scout_web/lib/block_scout_web/views/address_view.ex
  27. 1457
      apps/block_scout_web/priv/gettext/default.pot
  28. 1479
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  29. 15
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipt.ex
  30. 44
      apps/explorer/lib/explorer/chain.ex
  31. 3
      apps/explorer/lib/explorer/etherscan.ex
  32. 62
      apps/explorer/test/explorer/chain_test.exs
  33. 3
      apps/explorer/test/support/factory.ex

@ -93,6 +93,14 @@ export function reducer (state = initialState, action) {
}) })
} }
} }
case 'RECEIVED_NEW_BLOCK': {
if (state.channelDisconnected || state.beyondPageOne) return state
return Object.assign({}, state, {
newBlock: action.msg.blockHtml,
minerHash: action.msg.blockMinerHash
})
}
default: default:
return state return state
} }
@ -116,6 +124,12 @@ if ($addressDetailsPage.length) {
addressChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) addressChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' }))
addressChannel.on('balance', (msg) => store.dispatch({ type: 'RECEIVED_UPDATED_BALANCE', msg })) addressChannel.on('balance', (msg) => store.dispatch({ type: 'RECEIVED_UPDATED_BALANCE', msg }))
if (!state.beyondPageOne) { if (!state.beyondPageOne) {
const blocksChannel = socket.channel(`blocks:new_block`, {})
blocksChannel.join()
blocksChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' }))
blocksChannel.on('new_block', (msg) => {
store.dispatch({ type: 'RECEIVED_NEW_BLOCK', msg: humps.camelizeKeys(msg) })
})
addressChannel.on('transaction', batchChannel((msgs) => addressChannel.on('transaction', batchChannel((msgs) =>
store.dispatch({ type: 'RECEIVED_NEW_TRANSACTION_BATCH', msgs }) store.dispatch({ type: 'RECEIVED_NEW_TRANSACTION_BATCH', msgs })
)) ))
@ -135,6 +149,7 @@ if ($addressDetailsPage.length) {
const $internalTransactionsList = $('[data-selector="internal-transactions-list"]') const $internalTransactionsList = $('[data-selector="internal-transactions-list"]')
const $transactionCount = $('[data-selector="transaction-count"]') const $transactionCount = $('[data-selector="transaction-count"]')
const $transactionsList = $('[data-selector="transactions-list"]') const $transactionsList = $('[data-selector="transactions-list"]')
const $validationsList = $('[data-selector="validations-list"]')
if ($emptyInternalTransactionsList.length && state.newInternalTransactions.length) window.location.reload() if ($emptyInternalTransactionsList.length && state.newInternalTransactions.length) window.location.reload()
if ($emptyTransactionsList.length && state.newTransactions.length) window.location.reload() if ($emptyTransactionsList.length && state.newTransactions.length) window.location.reload()
@ -159,6 +174,15 @@ if ($addressDetailsPage.length) {
prependWithClingBottom($transactionsList, state.newTransactions.slice(oldState.newTransactions.length).reverse().join('')) prependWithClingBottom($transactionsList, state.newTransactions.slice(oldState.newTransactions.length).reverse().join(''))
updateAllAges() updateAllAges()
} }
if (oldState.newBlock !== state.newBlock && state.minerHash === state.addressHash) {
const len = $validationsList.children().length
$validationsList
.children()
.slice(len - 1, len)
.remove()
$validationsList.prepend(state.newBlock)
}
} }
}) })
} }

@ -34,7 +34,8 @@ defmodule BlockScoutWeb.BlockChannel do
average_block_time: Timex.format_duration(average_block_time, :humanized), average_block_time: Timex.format_duration(average_block_time, :humanized),
chain_block_html: rendered_chain_block, chain_block_html: rendered_chain_block,
block_html: rendered_block, block_html: rendered_block,
block_number: block.number block_number: block.number,
block_miner_hash: to_string(block.miner_hash)
}) })
{:noreply, socket} {:noreply, socket}

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.AddressContractController do defmodule BlockScoutWeb.AddressContractController do
use BlockScoutWeb, :controller use BlockScoutWeb, :controller
import BlockScoutWeb.AddressController, only: [transaction_count: 1] import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
alias Explorer.{Chain, Market} alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token
@ -14,7 +14,8 @@ defmodule BlockScoutWeb.AddressContractController do
"index.html", "index.html",
address: address, address: address,
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address) transaction_count: transaction_count(address),
validation_count: validation_count(address)
) )
else else
:error -> :error ->

@ -20,4 +20,8 @@ defmodule BlockScoutWeb.AddressController do
def transaction_count(%Address{} = address) do def transaction_count(%Address{} = address) do
Chain.address_to_transactions_estimated_count(address) Chain.address_to_transactions_estimated_count(address)
end end
def validation_count(%Address{} = address) do
Chain.address_to_validation_count(address)
end
end end

@ -5,7 +5,7 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
use BlockScoutWeb, :controller use BlockScoutWeb, :controller
import BlockScoutWeb.AddressController, only: [transaction_count: 1] import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.Chain, only: [current_filter: 1, paging_options: 1, next_page_params: 3, split_list_by_page: 1] import BlockScoutWeb.Chain, only: [current_filter: 1, paging_options: 1, next_page_params: 3, split_list_by_page: 1]
alias Explorer.{Chain, Market} alias Explorer.{Chain, Market}
@ -26,7 +26,6 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
|> Keyword.merge(current_filter(params)) |> Keyword.merge(current_filter(params))
internal_transactions_plus_one = Chain.address_to_internal_transactions(address, full_options) internal_transactions_plus_one = Chain.address_to_internal_transactions(address, full_options)
{internal_transactions, next_page} = split_list_by_page(internal_transactions_plus_one) {internal_transactions, next_page} = split_list_by_page(internal_transactions_plus_one)
render( render(
@ -37,7 +36,8 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
filter: params["filter"], filter: params["filter"],
internal_transactions: internal_transactions, internal_transactions: internal_transactions,
transaction_count: transaction_count(address) transaction_count: transaction_count(address),
validation_count: validation_count(address)
) )
else else
:error -> :error ->

@ -11,7 +11,7 @@ defmodule BlockScoutWeb.AddressReadContractController do
alias Explorer.{Chain, Market} alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token
import BlockScoutWeb.AddressController, only: [transaction_count: 1] import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
def index(conn, %{"address_id" => address_hash_string}) do def index(conn, %{"address_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
@ -21,7 +21,8 @@ defmodule BlockScoutWeb.AddressReadContractController do
"index.html", "index.html",
address: address, address: address,
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address) transaction_count: transaction_count(address),
validation_count: validation_count(address)
) )
else else
:error -> :error ->

@ -4,7 +4,7 @@ defmodule BlockScoutWeb.AddressTokenController do
alias Explorer.{Chain, Market} alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token
import BlockScoutWeb.AddressController, only: [transaction_count: 1] import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.Chain, only: [next_page_params: 3, paging_options: 1, split_list_by_page: 1] import BlockScoutWeb.Chain, only: [next_page_params: 3, paging_options: 1, split_list_by_page: 1]
def index(conn, %{"address_id" => address_hash_string} = params) do def index(conn, %{"address_id" => address_hash_string} = params) do
@ -19,6 +19,7 @@ defmodule BlockScoutWeb.AddressTokenController do
address: address, address: address,
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address), transaction_count: transaction_count(address),
validation_count: validation_count(address),
next_page_params: next_page_params(next_page, tokens, params), next_page_params: next_page_params(next_page, tokens, params),
tokens: tokens tokens: tokens
) )

@ -4,7 +4,7 @@ defmodule BlockScoutWeb.AddressTokenTransferController do
alias Explorer.{Chain, Market} alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token
import BlockScoutWeb.AddressController, only: [transaction_count: 1] import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.Chain, import BlockScoutWeb.Chain,
only: [next_page_params: 3, paging_options: 1, split_list_by_page: 1] only: [next_page_params: 3, paging_options: 1, split_list_by_page: 1]
@ -34,7 +34,8 @@ defmodule BlockScoutWeb.AddressTokenTransferController do
next_page_params: next_page_params(next_page, transactions_paginated, params), next_page_params: next_page_params(next_page, transactions_paginated, params),
token: token, token: token,
transaction_count: transaction_count(address), transaction_count: transaction_count(address),
transactions: transactions_paginated transactions: transactions_paginated,
validation_count: validation_count(address)
) )
else else
:error -> :error ->

@ -5,7 +5,7 @@ defmodule BlockScoutWeb.AddressTransactionController do
use BlockScoutWeb, :controller use BlockScoutWeb, :controller
import BlockScoutWeb.AddressController, only: [transaction_count: 1] import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.Chain, only: [current_filter: 1, paging_options: 1, next_page_params: 3, split_list_by_page: 1] import BlockScoutWeb.Chain, only: [current_filter: 1, paging_options: 1, next_page_params: 3, split_list_by_page: 1]
alias Explorer.{Chain, Market} alias Explorer.{Chain, Market}
@ -37,7 +37,8 @@ defmodule BlockScoutWeb.AddressTransactionController do
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
filter: params["filter"], filter: params["filter"],
transactions: transactions, transactions: transactions,
transaction_count: transaction_count(address) transaction_count: transaction_count(address),
validation_count: validation_count(address)
) )
else else
:error -> :error ->

@ -0,0 +1,43 @@
defmodule BlockScoutWeb.AddressValidationController do
@moduledoc """
Display all the blocks that this address validates.
"""
use BlockScoutWeb, :controller
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1]
alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token
def index(conn, %{"address_id" => address_hash_string} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash) do
full_options =
Keyword.merge(
[necessity_by_association: %{miner: :required, transactions: :optional}],
paging_options(params)
)
blocks_plus_one = Chain.get_blocks_validated_by_address(full_options, address)
{blocks, next_page} = split_list_by_page(blocks_plus_one)
render(
conn,
"index.html",
address: address,
blocks: blocks,
transaction_count: transaction_count(address),
validation_count: validation_count(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
next_page_params: next_page_params(next_page, blocks, params)
)
else
:error ->
unprocessable_entity(conn)
{:error, :not_found} ->
not_found(conn)
end
end
end

@ -78,6 +78,13 @@ defmodule BlockScoutWeb.Router do
as: :internal_transaction as: :internal_transaction
) )
resources(
"/validations",
AddressValidationController,
only: [:index],
as: :validation
)
resources( resources(
"/contracts", "/contracts",
AddressContractController, AddressContractController,

@ -17,15 +17,19 @@
</div> </div>
<h1 class="card-title"><%= address_title(@address) %> <%= gettext "Details" %> </h1> <h1 class="card-title"><%= address_title(@address) %> <%= gettext "Details" %> </h1>
<h3 class="<%= if BlockScoutWeb.AddressView.contract?(@address) do %>contract-address<% end %>" data-test="address_detail_hash"><%= @address.hash %></h3> <h3 class="<%= if BlockScoutWeb.AddressView.contract?(@address) do %>contract-address<% end %>" data-test="address_detail_hash"><%= @address.hash %></h3>
<div class="d-flex flex-column flex-lg-row justify-content-start text-muted"> <div class="d-flex flex-column flex-lg-row justify-content-start text-muted">
<%= if address_name = primary_name(@address) do %> <%= if address_name = primary_name(@address) do %>
<strong class="mr-4 mb-2 text-primary"><%= address_name %></strong> <strong class="mr-4 mb-2 text-primary"><%= address_name %></strong>
<% end %> <% end %>
<span class="mr-4 mb-2"> <span class="mr-4 mb-2">
<span data-selector="transaction-count"><%= Cldr.Number.to_string!(@transaction_count, format: "#,###") %></span> <span data-selector="transaction-count">
<%= Cldr.Number.to_string!(@transaction_count, format: "#,###") %>
<%= gettext "Transactions" %> </span> <%= gettext("Transactions") %>
<%= if validator?(@validation_count) do %>
<span data-selector="validation-count">
<%= Cldr.Number.to_string!(@validation_count, format: "#,###") %>
</span> <%= gettext("Blocks Validated") %>
<% end %>
</span> </span>
<%= if @address.token do %> <%= if @address.token do %>
<span class="mr-4 mb-2"> <span class="mr-4 mb-2">

@ -9,26 +9,36 @@
<ul class="nav nav-tabs card-header-tabs d-none d-lg-inline-flex"> <ul class="nav nav-tabs card-header-tabs d-none d-lg-inline-flex">
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Transactions"), gettext("Transactions"),
class: "nav-link", class: "nav-link",
to: address_transaction_path(@conn, :index, @address.hash) to: address_transaction_path(@conn, :index, @address.hash)
) %> ) %>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Tokens"), gettext("Tokens"),
class: "nav-link", class: "nav-link",
to: address_token_path(@conn, :index, @address.hash) to: address_token_path(@conn, :index, @address.hash)
) %> ) %>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Internal Transactions"), gettext("Internal Transactions"),
class: "nav-link", class: "nav-link",
"data-test": "internal_transactions_tab_link", "data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @address.hash) to: address_internal_transaction_path(@conn, :index, @address.hash)
) %> ) %>
</li> </li>
<%= if BlockScoutWeb.AddressView.validator?(@validation_count) do %>
<li class="nav-item">
<%= link(
gettext("Blocks Validated"),
class: "nav-link",
"data-test": "validations_tab_link",
to: address_validation_path(@conn, :index, @address.hash)
) %>
</li>
<% end %>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
to: address_contract_path(@conn, :index, @address.hash), to: address_contract_path(@conn, :index, @address.hash),
@ -43,9 +53,10 @@
<%= if BlockScoutWeb.AddressView.smart_contract_with_read_only_functions?(@address) do %> <%= if BlockScoutWeb.AddressView.smart_contract_with_read_only_functions?(@address) do %>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Read Contract"), gettext("Read Contract"),
to: address_read_contract_path(@conn, :index, @address.hash), to: address_read_contract_path(@conn, :index, @address.hash),
class: "nav-link")%> class: "nav-link"
)%>
</li> </li>
<% end %> <% end %>
</ul> </ul>
@ -77,6 +88,14 @@
"data-test": "internal_transactions_tab_link", "data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @address.hash) to: address_internal_transaction_path(@conn, :index, @address.hash)
) %> ) %>
<%= if BlockScoutWeb.AddressView.validator?(@validation_count) do %>
<%= link(
gettext("Blocks Validated"),
class: "dropdown-item",
"data-test": "validations_tab_link",
to: address_validation_path(@conn, :index, @address.hash)
) %>
<% end %>
<%= link( <%= link(
to: address_contract_path(@conn, :index, @address.hash), to: address_contract_path(@conn, :index, @address.hash),
class: "dropdown-item active") do %> class: "dropdown-item active") do %>
@ -155,7 +174,6 @@
</div> </div>
</section> </section>
<% end %> <% end %>
<section> <section>
<div class="d-flex justify-content-between align-items-baseline"> <div class="d-flex justify-content-between align-items-baseline">

@ -10,26 +10,36 @@
<ul class="nav nav-tabs card-header-tabs d-none d-lg-inline-flex"> <ul class="nav nav-tabs card-header-tabs d-none d-lg-inline-flex">
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Transactions"), gettext("Transactions"),
class: "nav-link", class: "nav-link",
to: address_transaction_path(@conn, :index, @address.hash) to: address_transaction_path(@conn, :index, @address.hash)
) %> ) %>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Tokens"), gettext("Tokens"),
class: "nav-link", class: "nav-link",
to: address_token_path(@conn, :index, @address.hash) to: address_token_path(@conn, :index, @address.hash)
) %> ) %>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Internal Transactions"), gettext("Internal Transactions"),
class: "nav-link active", class: "nav-link active",
"data-test": "internal_transactions_tab_link", "data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @address.hash) to: address_internal_transaction_path(@conn, :index, @address.hash)
) %> ) %>
</li> </li>
<%= if validator?(@validation_count) do %>
<li class="nav-item">
<%= link(
gettext("Blocks Validated"),
class: "nav-link",
"data-test": "validations_tab_link",
to: address_validation_path(@conn, :index, @address.hash)
) %>
</li>
<% end %>
<%= if contract?(@address) do %> <%= if contract?(@address) do %>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
@ -61,28 +71,38 @@
</a> </a>
<div class="dropdown-menu"> <div class="dropdown-menu">
<%= link( <%= link(
gettext("Transactions"), gettext("Transactions"),
class: "dropdown-item", class: "dropdown-item",
to: address_transaction_path(@conn, :index, @address.hash) to: address_transaction_path(@conn, :index, @address.hash)
) %> ) %>
<%= link(
gettext("Tokens"),
class: "dropdown-item",
to: address_token_path(@conn, :index, @address.hash)
) %>
<%= link( <%= link(
gettext("Internal Transactions"), gettext("Tokens"),
class: "dropdown-item active", class: "dropdown-item",
"data-test": "internal_transactions_tab_link", to: address_token_path(@conn, :index, @address.hash)
to: address_internal_transaction_path(@conn, :index, @address.hash) ) %>
) %>
<%= link( <%= link(
to: address_contract_path(@conn, :index, @address.hash), gettext("Internal Transactions"),
class: "dropdown-item") do %> class: "dropdown-item active",
<%= gettext("Code") %> "data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @address.hash)
) %>
<%= if validator?(@validation_count) do %>
<%= link(
gettext("Blocks Validated"),
class: "dropdown-item",
"data-test": "validations_tab_link",
to: address_validation_path(@conn, :index, @address.hash)
) %>
<% end %>
<%= if contract?(@address) do %>
<%= link(
to: address_contract_path(@conn, :index, @address.hash),
class: "dropdown-item") do %>
<%= gettext("Code") %>
<%= if smart_contract_verified?(@address) do %> <%= if smart_contract_verified?(@address) do %>
<i class="far fa-check-circle"></i> <i class="far fa-check-circle"></i>
<% end %>
<% end %> <% end %>
<% end %> <% end %>
<%= link( <%= link(
@ -94,12 +114,12 @@
</ul> </ul>
</div> </div>
<div class="card-body"> <div class="card-body">
<div data-selector="channel-batching-message" style="display:none;"> <div data-selector="channel-batching-message" class="d-none">
<div data-selector="reload-button" class="alert alert-info"> <div data-selector="reload-button" class="alert alert-info">
<a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More internal transactions have come in" %></a> <a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More internal transactions have come in" %></a>
</div> </div>
</div> </div>
<div data-selector="channel-disconnected-message" style="display:none;"> <div data-selector="channel-disconnected-message" class="d-none">
<div data-selector="reload-button" class="alert alert-danger"> <div data-selector="reload-button" class="alert alert-danger">
<a href="#" class="alert-link"><%= gettext "Connection Lost, click to load newer internal transactions" %></a> <a href="#" class="alert-link"><%= gettext "Connection Lost, click to load newer internal transactions" %></a>
</div> </div>
@ -168,6 +188,5 @@
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</section> </section>

@ -9,26 +9,36 @@
<ul class="nav nav-tabs card-header-tabs d-none d-lg-inline-flex"> <ul class="nav nav-tabs card-header-tabs d-none d-lg-inline-flex">
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Transactions"), gettext("Transactions"),
class: "nav-link", class: "nav-link",
to: address_transaction_path(@conn, :index, @address.hash) to: address_transaction_path(@conn, :index, @address.hash)
) %> ) %>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Tokens"), gettext("Tokens"),
class: "nav-link", class: "nav-link",
to: address_token_path(@conn, :index, @address.hash) to: address_token_path(@conn, :index, @address.hash)
) %> ) %>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Internal Transactions"), gettext("Internal Transactions"),
class: "nav-link",
"data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @address.hash)
) %>
</li>
<%= if validator?(@validation_count) do %>
<li class="nav-item">
<%= link(
gettext("Blocks Validated"),
class: "nav-link", class: "nav-link",
"data-test": "internal_transactions_tab_link", "data-test": "validations_tab_link",
to: address_internal_transaction_path(@conn, :index, @address.hash) to: address_validation_path(@conn, :index, @address.hash)
) %> ) %>
</li> </li>
<% end %>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
to: address_contract_path(@conn, :index, @address.hash), to: address_contract_path(@conn, :index, @address.hash),
@ -42,9 +52,10 @@
</li> </li>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Read Contract"), gettext("Read Contract"),
to: address_read_contract_path(@conn, :index, @address.hash), to: address_read_contract_path(@conn, :index, @address.hash),
class: "nav-link active")%> class: "nav-link active"
)%>
</li> </li>
</ul> </ul>

@ -8,28 +8,36 @@
<ul class="nav nav-tabs card-header-tabs d-none d-lg-inline-flex"> <ul class="nav nav-tabs card-header-tabs d-none d-lg-inline-flex">
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Transactions"), gettext("Transactions"),
class: "nav-link", class: "nav-link",
to: address_transaction_path(@conn, :index, @address.hash) to: address_transaction_path(@conn, :index, @address.hash)
) %> ) %>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Tokens"), gettext("Tokens"),
class: "nav-link active", class: "nav-link active",
to: address_token_path(@conn, :index, @address.hash) to: address_token_path(@conn, :index, @address.hash)
) %> ) %>
</li> </li>
<li class="nav-item">
<li class="nav-item"> <%= link( <%= link(
gettext("Internal Transactions"), gettext("Internal Transactions"),
class: "nav-link", class: "nav-link",
"data-test": "internal_transactions_tab_link", "data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @address.hash) to: address_internal_transaction_path(@conn, :index, @address.hash)
) %> ) %>
</li> </li>
<%= if AddressView.validator?(@validation_count) do %>
<li class="nav-item">
<%= link(
gettext("Blocks Validated"),
class: "nav-link",
"data-test": "validations_tab_link",
to: address_validation_path(@conn, :index, @address.hash)
) %>
</li>
<% end %>
<%= if AddressView.contract?(@address) do %> <%= if AddressView.contract?(@address) do %>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
@ -43,7 +51,6 @@
<% end %> <% end %>
</li> </li>
<% end %> <% end %>
<%= if AddressView.smart_contract_with_read_only_functions?(@address) do %> <%= if AddressView.smart_contract_with_read_only_functions?(@address) do %>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
@ -70,28 +77,38 @@
</a> </a>
<div class="dropdown-menu"> <div class="dropdown-menu">
<%= link( <%= link(
gettext("Transactions"), gettext("Transactions"),
class: "dropdown-item", class: "dropdown-item",
to: address_transaction_path(@conn, :index, @address.hash) to: address_transaction_path(@conn, :index, @address.hash)
) %> ) %>
<%= link( <%= link(
gettext("Tokens"), gettext("Tokens"),
class: "dropdown-item active", class: "dropdown-item active",
to: address_token_path(@conn, :index, @address.hash) to: address_token_path(@conn, :index, @address.hash)
) %> ) %>
<%= link(
gettext("Internal Transactions"),
class: "dropdown-item",
"data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @address.hash)
) %>
<%= link( <%= link(
gettext("Internal Transactions"),
class: "dropdown-item",
"data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @address.hash)
) %>
<%= if AddressView.validator?(@validation_count) do %>
<%= link(
gettext("Blocks Validated"),
class: "dropdown-item",
"data-test": "validations_tab_link",
to: address_validation_path(@conn, :index, @address.hash)
) %>
<% end %>
<%= if AddressView.contract?(@address) do %>
<%= link(
to: address_contract_path(@conn, :index, @address.hash), to: address_contract_path(@conn, :index, @address.hash),
class: "dropdown-item") do %> class: "dropdown-item") do %>
<%= gettext("Code") %> <%= gettext("Code") %>
<%= if AddressView.smart_contract_verified?(@address) do %> <%= if AddressView.smart_contract_verified?(@address) do %>
<i class="far fa-check-circle"></i> <i class="far fa-check-circle"></i>
<% end %>
<% end %> <% end %>
<% end %> <% end %>
<%= if AddressView.smart_contract_with_read_only_functions?(@address) do %> <%= if AddressView.smart_contract_with_read_only_functions?(@address) do %>

@ -10,27 +10,37 @@
<ul class="nav nav-tabs card-header-tabs d-none d-lg-inline-flex"> <ul class="nav nav-tabs card-header-tabs d-none d-lg-inline-flex">
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Transactions"), gettext("Transactions"),
class: "nav-link active", class: "nav-link active",
to: address_transaction_path(@conn, :index, @address.hash) to: address_transaction_path(@conn, :index, @address.hash)
) %> ) %>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Tokens"), gettext("Tokens"),
class: "nav-link", class: "nav-link",
to: address_token_path(@conn, :index, @address.hash), to: address_token_path(@conn, :index, @address.hash),
"data-test": "tokens_tab_link" "data-test": "tokens_tab_link"
) %> ) %>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Internal Transactions"), gettext("Internal Transactions"),
class: "nav-link", class: "nav-link",
"data-test": "internal_transactions_tab_link", "data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @address.hash) to: address_internal_transaction_path(@conn, :index, @address.hash)
) %> ) %>
</li> </li>
<%= if validator?(@validation_count) do %>
<li class="nav-item">
<%= link(
gettext("Blocks Validated"),
class: "nav-link",
"data-test": "validations_tab_link",
to: address_validation_path(@conn, :index, @address.hash)
) %>
</li>
<% end %>
<%= if contract?(@address) do %> <%= if contract?(@address) do %>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
@ -47,9 +57,10 @@
<%= if smart_contract_with_read_only_functions?(@address) do %> <%= if smart_contract_with_read_only_functions?(@address) do %>
<li class="nav-item"> <li class="nav-item">
<%= link( <%= link(
gettext("Read Contract"), gettext("Read Contract"),
to: address_read_contract_path(@conn, :index, @address.hash), to: address_read_contract_path(@conn, :index, @address.hash),
class: "nav-link")%> class: "nav-link"
)%>
</li> </li>
<% end %> <% end %>
</ul> </ul>
@ -60,21 +71,29 @@
<a class="nav-link active dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false"><%= gettext "Transactions" %></a> <a class="nav-link active dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false"><%= gettext "Transactions" %></a>
<div class="dropdown-menu"> <div class="dropdown-menu">
<%= link( <%= link(
gettext("Transactions"), gettext("Transactions"),
class: "dropdown-item active", class: "dropdown-item active",
to: address_transaction_path(@conn, :index, @address.hash) to: address_transaction_path(@conn, :index, @address.hash)
) %> ) %>
<%= link( <%= link(
gettext("Tokens"), gettext("Tokens"),
class: "dropdown-item", class: "dropdown-item",
to: address_token_path(@conn, :index, @address.hash) to: address_token_path(@conn, :index, @address.hash)
) %> ) %>
<%= link( <%= link(
gettext("Internal Transactions"), gettext("Internal Transactions"),
class: "dropdown-item", class: "dropdown-item",
"data-test": "internal_transactions_tab_link", "data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @address.hash) to: address_internal_transaction_path(@conn, :index, @address.hash)
) %> ) %>
<%= if validator?(@validation_count) do %>
<%= link(
gettext("Blocks Validated"),
class: "dropdown-item",
"data-test": "validations_tab_link",
to: address_validation_path(@conn, :index, @address.hash)
) %>
<% end %>
<%= if contract?(@address) do %> <%= if contract?(@address) do %>
<%= link( <%= link(
to: address_contract_path(@conn, :index, @address.hash), to: address_contract_path(@conn, :index, @address.hash),
@ -90,7 +109,7 @@
<%= link( <%= link(
gettext("Read Contract"), gettext("Read Contract"),
to: address_read_contract_path(@conn, :index, @address.hash), to: address_read_contract_path(@conn, :index, @address.hash),
class: "dropdown-item")%> class: "dropdown-item") %>
<% end %> <% end %>
</div> </div>
</li> </li>
@ -98,12 +117,12 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<div data-selector="channel-batching-message" style="display:none;"> <div data-selector="channel-batching-message" class="d-none">
<div data-selector="reload-button" class="alert alert-info"> <div data-selector="reload-button" class="alert alert-info">
<a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More transactions have come in" %></a> <a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More transactions have come in" %></a>
</div> </div>
</div> </div>
<div data-selector="channel-disconnected-message" style="display:none;"> <div data-selector="channel-disconnected-message" class="d-none">
<div data-selector="reload-button" class="alert alert-danger"> <div data-selector="reload-button" class="alert alert-danger">
<a href="#" class="alert-link"><%= gettext "Connection Lost, click to load newer transactions" %></a> <a href="#" class="alert-link"><%= gettext "Connection Lost, click to load newer transactions" %></a>
</div> </div>

@ -0,0 +1,137 @@
<section class="container">
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<section>
<div class="card">
<div class="card-header">
<!-- DESKTOP TAB NAV -->
<ul class="nav nav-tabs card-header-tabs d-none d-md-inline-flex">
<li class="nav-item">
<%= link(
gettext("Transactions"),
class: "nav-link",
to: address_transaction_path(@conn, :index, @address.hash)
) %>
</li>
<li class="nav-item">
<%= link(
gettext("Tokens"),
class: "nav-link",
to: address_token_path(@conn, :index, @address.hash)
) %>
</li>
<li class="nav-item"> <%= link(
gettext("Internal Transactions"),
class: "nav-link",
"data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @address.hash)
) %>
</li>
<li class="nav-item">
<%= link(
gettext("Blocks Validated"),
class: "nav-link active",
"data-test": "validations_tab_link",
to: address_validation_path(@conn, :index, @address.hash)
) %>
</li>
<%= if contract?(@address) do %>
<li class="nav-item">
<%= link(
to: address_contract_path(@conn, :index, @address.hash),
class: "nav-link") do %>
<%= gettext("Code") %>
<%= if smart_contract_verified?(@address) do %>
<i class="far fa-check-circle"></i>
<% end %>
<% end %>
</li>
<% end %>
<%= if smart_contract_with_read_only_functions?(@address) do %>
<li class="nav-item">
<%= link(
gettext("Read Contract"),
to: address_read_contract_path(@conn, :index, @address.hash),
class: "nav-link")%>
</li>
<% end %>
</ul>
<!-- MOBILE DROPDOWN NAV -->
<ul class="nav nav-tabs card-header-tabs d-md-none">
<li class="nav-item dropdown flex-fill text-center">
<a class="nav-link active dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false"><%= gettext "Tokens" %></a>
<div class="dropdown-menu">
<%= link(
gettext("Transactions"),
class: "dropdown-item",
to: address_transaction_path(@conn, :index, @address.hash)
) %>
<%= link(
gettext("Tokens"),
class: "dropdown-item",
to: address_token_path(@conn, :index, @address.hash)
) %>
<%= link(
gettext("Internal Transactions"),
class: "dropdown-item",
"data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @address.hash)
) %>
<%= link(
gettext("Blocks Validated"),
class: "dropdown-item active",
"data-test": "validations_tab_link",
to: address_validation_path(@conn, :index, @address.hash)
) %>
<%= if contract?(@address) do %>
<%= link(
to: address_contract_path(@conn, :index, @address.hash),
class: "dropdown-item") do %>
<%= gettext("Code") %>
<%= if smart_contract_verified?(@address) do %>
<i class="far fa-check-circle"></i>
<% end %>
<% end %>
<% end %>
</div>
</li>
</ul>
</div>
<div class="card-body">
<div data-selector="channel-batching-message">
<div data-selector="reload-button" class="alert alert-info" class="d-none">
<a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More validations have come in." %></a>
</div>
</div>
<div data-selector="channel-disconnected-message" class="d-none">
<div data-selector="reload-button" class="alert alert-danger">
<a href="#" class="alert-link"><%= gettext "Connection Lost, click to load newer validations" %></a>
</div>
</div>
<h2 class="card-title"><%=gettext("Blocks Validated")%></h2>
<span data-selector="validations-list">
<%= for block <- @blocks do %>
<%= render BlockScoutWeb.BlockView, "_tile.html", block: block %>
<% end %>
</span>
<div>
<%= if @next_page_params do %>
<%= link(
gettext("Older"),
class: "button button-secondary button-sm float-right mt-3",
to: address_validation_path(
@conn,
:index,
@address,
@next_page_params
)
) %>
<% end %>
</div>
</div> <!-- Card Body -->
</div> <!-- Card -->
<section>
</section>

@ -64,7 +64,7 @@
<div class="card card-chain-transactions"> <div class="card card-chain-transactions">
<div class="card-body"> <div class="card-body">
<div data-selector="channel-batching-message" style="display:none;"> <div data-selector="channel-batching-message" class="d-none">
<div data-selector="reload-button" class="alert alert-info"> <div data-selector="reload-button" class="alert alert-info">
<a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More transactions have come in" %></a> <a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More transactions have come in" %></a>
</div> </div>

@ -0,0 +1,94 @@
<section class="container">
<%= render(
OverviewView,
"_details.html",
token: @token,
total_token_transfers: @total_token_transfers,
total_token_holders: @total_token_holders,
conn: @conn
) %>
<section>
<div class="card">
<div class="card-header">
<!-- DESKTOP TAB NAV -->
<ul class="nav nav-tabs card-header-tabs d-none d-md-inline-flex">
<li class="nav-item">
<%= link(
gettext("Token Transfers"),
class: "nav-link active",
to: token_path(@conn, :show, @token.contract_address_hash)
) %>
</li>
<%= if smart_contract_with_read_only_functions?(@token) do %>
<li class="nav-item">
<%= link(
gettext("Read Contract"),
to: token_read_contract_path(@conn, :index, @conn.params["id"]),
class: "nav-link")%>
</li>
<% end %>
<li class="nav-item"i>
<%= link(
gettext("Token Holders"),
class: "nav-link",
"data-test": "token_holders_tab",
to: token_holder_path(@conn, :index, @token.contract_address_hash)
) %>
</li>
</ul>
<!-- MOBILE DROPDOWN NAV -->
<ul class="nav nav-tabs card-header-tabs d-md-none">
<li class="nav-item dropdown flex-fill text-center">
<a class="nav-link active dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false"><%= gettext("Token Transfers") %></a>
<div class="dropdown-menu">
<%= link(
gettext("Token Transfers"),
class: "dropdown-item active",
to: token_path(@conn, :show, @token.contract_address_hash)
) %>
<%= if smart_contract_with_read_only_functions?(@token) do %>
<%= link(
gettext("Read Contract"),
to: "#",
class: "dropdown-item")%>
<% end %>
<%= link(
gettext("Token Holders"),
class: "dropdown-item",
to: token_holder_path(@conn, :index, @token.contract_address_hash)
) %>
</div>
</li>
</ul>
</div>
<div class="card-body">
<h2 class="card-title"><%= gettext "Token Transfers" %></h2>
<%= if Enum.any?(@transfers) do %>
<%= for transfer <- @transfers do %>
<%= render("_token_transfer.html", token: @token, transfer: transfer) %>
<% end %>
<% else %>
<div class="tile tile-muted text-center">
<span data-selector="empty-transactions-list">
<%= gettext "There are no transfers for this Token." %>
</span>
</div>
<% end %>
<%= if @next_page_params do %>
<%= link(
gettext("Older"),
class: "button button-secondary button-small float-right mt-4",
to: token_path(@conn, :show, @token.contract_address_hash, @next_page_params)
) %>
<% end %>
</div>
</div>
</section>
</section>

@ -43,12 +43,12 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<div data-selector="channel-batching-message" style="display:none;"> <div data-selector="channel-batching-message" class="d-none">
<div data-selector="reload-button" class="alert alert-info"> <div data-selector="reload-button" class="alert alert-info">
<a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More transactions have come in" %></a> <a href="#" class="alert-link"><span data-selector="channel-batching-count"></span> <%= gettext "More transactions have come in" %></a>
</div> </div>
</div> </div>
<div data-selector="channel-disconnected-message" style="display:none;"> <div data-selector="channel-disconnected-message" class="d-none">
<div data-selector="reload-button" class="alert alert-danger"> <div data-selector="reload-button" class="alert alert-danger">
<a href="#" class="alert-link"><%= gettext "Connection Lost, click to load newer transactions" %></a> <a href="#" class="alert-link"><%= gettext "Connection Lost, click to load newer transactions" %></a>
</div> </div>

@ -2,7 +2,7 @@ defmodule BlockScoutWeb.AddressInternalTransactionView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
import BlockScoutWeb.AddressView, import BlockScoutWeb.AddressView,
only: [contract?: 1, smart_contract_verified?: 1, smart_contract_with_read_only_functions?: 1] only: [contract?: 1, smart_contract_verified?: 1, smart_contract_with_read_only_functions?: 1, validator?: 1]
def format_current_filter(filter) do def format_current_filter(filter) do
case filter do case filter do

@ -1,7 +1,7 @@
defmodule BlockScoutWeb.AddressReadContractView do defmodule BlockScoutWeb.AddressReadContractView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
import BlockScoutWeb.AddressView, only: [smart_contract_verified?: 1] import BlockScoutWeb.AddressView, only: [smart_contract_verified?: 1, validator?: 1]
def queryable?(inputs), do: Enum.any?(inputs) def queryable?(inputs), do: Enum.any?(inputs)

@ -2,7 +2,7 @@ defmodule BlockScoutWeb.AddressTransactionView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
import BlockScoutWeb.AddressView, import BlockScoutWeb.AddressView,
only: [contract?: 1, smart_contract_verified?: 1, smart_contract_with_read_only_functions?: 1] only: [contract?: 1, smart_contract_verified?: 1, smart_contract_with_read_only_functions?: 1, validator?: 1]
def format_current_filter(filter) do def format_current_filter(filter) do
case filter do case filter do

@ -0,0 +1,6 @@
defmodule BlockScoutWeb.AddressValidationView do
use BlockScoutWeb, :view
import BlockScoutWeb.AddressView,
only: [contract?: 1, smart_contract_verified?: 1, smart_contract_with_read_only_functions?: 1]
end

@ -111,6 +111,10 @@ defmodule BlockScoutWeb.AddressView do
def contract?(nil), do: true def contract?(nil), do: true
def validator?(val) when val > 0, do: true
def validator?(_), do: false
def hash(%Address{hash: hash}) do def hash(%Address{hash: hash}) do
to_string(hash) to_string(hash)
end end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -258,11 +258,18 @@ defmodule EthereumJSONRPC.Receipt do
defp entry_to_elixir({"status" = key, status}) do defp entry_to_elixir({"status" = key, status}) do
case status do case status do
"0x0" -> {:ok, {key, :error}} "0x0" ->
"0x1" -> {:ok, {key, :ok}} {:ok, {key, :error}}
"0x1" ->
{:ok, {key, :ok}}
# pre-Byzantium / Ethereum Classic on Parity # pre-Byzantium / Ethereum Classic on Parity
nil -> :ignore nil ->
other -> {:error, {:unknown_value, %{key: key, value: other}}} :ignore
other ->
{:error, {:unknown_value, %{key: key, value: other}}}
end end
end end

@ -12,7 +12,8 @@ defmodule Explorer.Chain do
order_by: 3, order_by: 3,
preload: 2, preload: 2,
where: 2, where: 2,
where: 3 where: 3,
select: 3
] ]
alias Ecto.Adapters.SQL alias Ecto.Adapters.SQL
@ -837,6 +838,47 @@ defmodule Explorer.Chain do
|> Repo.all() |> Repo.all()
end end
@doc """
Finds all Blocks validated by the address given.
## Options
* `:necessity_by_association` - use to load `t:association/0` as `:required` or `:optional`. If an association is
`: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
transactions older than the `block_number` that are passed.
Returns all blocks validated by the address given.
"""
@spec get_blocks_validated_by_address(
[paging_options | necessity_by_association_option],
Address.t()
) :: [Block.t()]
def get_blocks_validated_by_address(options \\ [], %Address{hash: hash}) when is_list(options) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
Block
|> join_associations(necessity_by_association)
|> where(miner_hash: ^hash)
|> page_blocks(paging_options)
|> limit(^paging_options.page_size)
|> order_by(desc: :number)
|> Repo.all()
end
@doc """
Counts the number of `t:Explorer.Chain.Block.t/0` validated by the `address`.
"""
@spec address_to_validation_count(Address.t()) :: non_neg_integer()
def address_to_validation_count(%Address{hash: hash}) do
Block
|> where(miner_hash: ^hash)
|> select([b], count(b.hash))
|> Repo.one()
end
@doc """ @doc """
Returns a stream of unfetched `t:Explorer.Chain.Address.CoinBalance.t/0`. Returns a stream of unfetched `t:Explorer.Chain.Address.CoinBalance.t/0`.

@ -172,7 +172,8 @@ defmodule Explorer.Etherscan do
%Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash %Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash
) do ) do
query = query =
from(tb in TokenBalance, from(
tb in TokenBalance,
where: tb.token_contract_address_hash == ^contract_address_hash, where: tb.token_contract_address_hash == ^contract_address_hash,
where: tb.address_hash == ^address_hash, where: tb.address_hash == ^address_hash,
order_by: [desc: :block_number], order_by: [desc: :block_number],

@ -1033,6 +1033,68 @@ defmodule Explorer.ChainTest do
end end
end end
describe "get_blocks_validated_by_address/2" do
test "returns nothing when there are no blocks" do
address = insert(:address)
assert [] = Chain.get_blocks_validated_by_address(address)
end
test "returns the blocks validated by a specified address" do
address = insert(:address)
another_address = insert(:address)
block = insert(:block, miner: address, miner_hash: address.hash)
insert(:block, miner: another_address, miner_hash: another_address.hash)
results =
address
|> Chain.get_blocks_validated_by_address()
|> Enum.map(& &1.hash)
assert results == [block.hash]
end
test "with blocks can be paginated" do
address = insert(:address)
first_page_block = insert(:block, miner: address, miner_hash: address.hash, number: 0)
second_page_block = insert(:block, miner: address, miner_hash: address.hash, number: 2)
assert [first_page_block.number] ==
[paging_options: %PagingOptions{key: {1}, page_size: 1}]
|> Chain.get_blocks_validated_by_address(address)
|> Enum.map(& &1.number)
|> Enum.reverse()
assert [second_page_block.number] ==
[paging_options: %PagingOptions{key: {3}, page_size: 1}]
|> Chain.get_blocks_validated_by_address(address)
|> Enum.map(& &1.number)
|> Enum.reverse()
end
end
describe "address_to_validation_count/1" do
test "returns 0 when there aren't any blocks" do
address = insert(:address)
assert 0 = Chain.address_to_validation_count(address)
end
test "returns the number of blocks mined by addres" do
address = insert(:address)
another_address = insert(:address)
insert(:block, miner: address, miner_hash: address.hash)
insert(:block, miner: another_address, miner_hash: another_address.hash)
insert(:block, miner: another_address, miner_hash: another_address.hash)
assert 1 = Chain.address_to_validation_count(address)
assert 2 = Chain.address_to_validation_count(another_address)
end
end
describe "number_to_block/1" do describe "number_to_block/1" do
test "without block" do test "without block" do
assert {:error, :not_found} = Chain.number_to_block(-1) assert {:error, :not_found} = Chain.number_to_block(-1)

@ -50,7 +50,8 @@ defmodule Explorer.Factory do
def update_balance_value(%CoinBalance{address_hash: address_hash, block_number: block_number}, value) do def update_balance_value(%CoinBalance{address_hash: address_hash, block_number: block_number}, value) do
Repo.update_all( Repo.update_all(
from(balance in CoinBalance, from(
balance in CoinBalance,
where: balance.address_hash == ^address_hash and balance.block_number == ^block_number where: balance.address_hash == ^address_hash and balance.block_number == ^block_number
), ),
set: [value: value, value_fetched_at: DateTime.utc_now()] set: [value: value, value_fetched_at: DateTime.utc_now()]

Loading…
Cancel
Save