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

pull/760/head
Andrew Cravenho 6 years ago committed by GitHub
commit 046c6789a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      apps/block_scout_web/assets/css/_elements.scss
  2. 63
      apps/block_scout_web/assets/css/components/_navbar.scss
  3. 6
      apps/block_scout_web/assets/css/theme/_base_variables.scss
  4. 6
      apps/block_scout_web/assets/css/theme/_ethereum_classic_variables.scss
  5. 4
      apps/block_scout_web/assets/css/theme/_ethereum_variables.scss
  6. 6
      apps/block_scout_web/assets/css/theme/_kovan_variables.scss
  7. 2
      apps/block_scout_web/assets/css/theme/_poa_variables.scss
  8. 4
      apps/block_scout_web/assets/css/theme/_ropsten_variables.scss
  9. 4
      apps/block_scout_web/assets/css/theme/_sokol_variables.scss
  10. 3
      apps/block_scout_web/assets/static/images/icons/accounts.svg
  11. 3
      apps/block_scout_web/assets/static/images/icons/api.svg
  12. 3
      apps/block_scout_web/assets/static/images/icons/blocks.svg
  13. 3
      apps/block_scout_web/assets/static/images/icons/network.svg
  14. 3
      apps/block_scout_web/assets/static/images/icons/search.svg
  15. 3
      apps/block_scout_web/assets/static/images/icons/test-network.svg
  16. 3
      apps/block_scout_web/assets/static/images/icons/transactions.svg
  17. 47
      apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex
  18. 19
      apps/block_scout_web/lib/block_scout_web/router.ex
  19. 6
      apps/block_scout_web/lib/block_scout_web/templates/address_token/_tokens.html.eex
  20. 4
      apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex
  21. 148
      apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex
  22. 3
      apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex
  23. 3
      apps/block_scout_web/lib/block_scout_web/templates/icons/_accounts_icon.html.eex
  24. 3
      apps/block_scout_web/lib/block_scout_web/templates/icons/_api_icon.html.eex
  25. 4
      apps/block_scout_web/lib/block_scout_web/templates/icons/_block_icon.html.eex
  26. 3
      apps/block_scout_web/lib/block_scout_web/templates/icons/_network_icon.html.eex
  27. 3
      apps/block_scout_web/lib/block_scout_web/templates/icons/_search_icon.html.eex
  28. 3
      apps/block_scout_web/lib/block_scout_web/templates/icons/_test_network_icon.html.eex
  29. 4
      apps/block_scout_web/lib/block_scout_web/templates/icons/_transaction_icon.html.eex
  30. 26
      apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex
  31. 24
      apps/block_scout_web/lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex
  32. 2
      apps/block_scout_web/lib/block_scout_web/templates/tokens/token/show.html.eex
  33. 5
      apps/block_scout_web/lib/block_scout_web/views/address_token_transfer_view.ex
  34. 79
      apps/block_scout_web/priv/gettext/default.pot
  35. 79
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  36. 114
      apps/block_scout_web/test/block_scout_web/controllers/address_token_transfer_controller_test.exs
  37. 10
      apps/block_scout_web/test/block_scout_web/features/pages/address_page.ex
  38. 38
      apps/block_scout_web/test/block_scout_web/features/viewing_addresses_test.exs
  39. 21
      apps/explorer/lib/explorer/chain.ex
  40. 29
      apps/explorer/lib/explorer/chain/transaction.ex
  41. 140
      apps/explorer/test/explorer/chain/transaction_test.exs
  42. 76
      apps/explorer/test/explorer/chain_test.exs

@ -3,3 +3,13 @@ hr {
border-style: solid;
border-color: $gray-300 transparent transparent transparent;
}
svg {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
display: inline-block;
font-style: normal;
font-variant: normal;
text-rendering: auto;
line-height: 1;
}

@ -8,17 +8,17 @@
}
.navbar-brand {
margin-left: 10px;
margin-left: 0;
}
.navbar-logo {
height: 36px;
height: 1.5em;
}
@include media-breakpoint-up(md) {
.navbar-expand-lg .navbar-nav .nav-link {
padding-left: 1.25rem;
padding-right: 1.25rem;
padding-left: 0.75rem;
padding-right: 0.75rem;
}
}
@ -26,10 +26,29 @@
font-size: 14px;
}
.navbar .nav-link {
.navbar-dark .navbar-nav .nav-link {
color: $white;
}
.navbar-nav .nav-link {
display: flex;
align-items: center;
}
.nav-link-icon {
display: flex;
align-items: center;
position: relative;
top: -2px;
height: 1em;
width: 1em;
margin-right: 0.5em;
path {
fill: $secondary;
}
}
.navbar .form-control {
background: transparent;
width: auto;
@ -70,10 +89,10 @@
.input-group-append {
margin-left: 0px;
}
.fa-search {
color: $white;
path {
fill: $secondary;
}
}
.topnav-nav-link {
@ -84,32 +103,26 @@
content: "";
position: absolute;
display: block;
bottom: 0;
bottom: -10px;
left: 50%;
width: 100%;
width: 75%;
height: 0.2rem;
background-color: $white;
background-color: $secondary;
border-radius: 4px 4px 0 0;
opacity: 0;
transform: translateX(-50%) translateY(-0.5rem);
transition: all 0.3s ease;
transform: translateX(-50%);
transition: all 0.2s ease;
}
&:hover {
color: $white;
&:before {
transform: translateX(-50%) translateY(-0.25rem);
&:before{
opacity: 1;
}
}
}
&:active {
&:before {
transition: all 0.1s ease-out;
transform: translateX(-50%) translateY(-0.5rem);
}
}
.dropdown-toggle {
padding-right: 0 !important;
}
.dropdown-menu {
@ -120,7 +133,7 @@
padding: 10px 20px;
&:hover {
background-color: $secondary;
background-color: $tertiary;
color: $white;
}
}

@ -30,8 +30,8 @@ $grays: map-merge((
), $grays);
$blue: #4786ff !default;
$indigo: #5b33a1 !default;
$purple: #997fdc !default;
$indigo: #5b389f !default;
$purple: #997fdc !default;
$pink: #e83e8c !default;
$red: #c74d39 !default;
$orange: #ef9a60 !default;
@ -58,7 +58,7 @@ $colors: map-merge((
), $colors);
$primary: $indigo !default;
$secondary: $teal !default;
$secondary: #7dd79f !default;
$tertiary: $purple !default;
$success: $green !default;
$info: $cyan !default;

@ -1,3 +1,3 @@
$primary: #1b1b3a;
$secondary: #40ed9e;
$tertiary: #40ed9e;
$primary: #1b1b39;
$secondary: #4beba0;
$tertiary: #4beba0;

@ -1,3 +1,3 @@
$primary: #12455b;
$secondary: #4786cb;
$primary: #16465b;
$secondary: #5ab3ff;
$tertiary: #77a4c5;

@ -1,3 +1,3 @@
$primary: #1BACA4;
$secondary: #6435c9;
$tertiary: #6435c9;
$primary: #28aca4;
$secondary: #89edda;
$tertiary: $purple;

@ -1,3 +1,3 @@
$primary: $indigo;
$secondary: $teal;
$secondary: #7dd79f;
$tertiary: $purple;

@ -1,3 +1,3 @@
$primary: #24a7fb;
$secondary: #f4c500;
$primary: #2fa8f8;
$secondary: #a2daff;
$tertiary: #006aa7;

@ -1,3 +1,3 @@
$primary: #539387;
$secondary: #77a4c5;
$primary: #559387;
$secondary: #add7cf;
$tertiary: #38533d;

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
<path fill="#7DD79F" fill-rule="evenodd" d="M15 16H1a1 1 0 0 1-1-1v-4a3 3 0 0 1 3-3h1V3a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v5h1a3 3 0 0 1 3 3v4a1 1 0 0 1-1 1zM10 4a2 2 0 1 0-4 0v3a2 2 0 1 0 4 0V4zm4 8c0-1.105-1.075-2-2.4-2h-.379c-.549.61-1.336 1-2.221 1H7c-.885 0-1.672-.39-2.221-1H4.4c-1.325 0-2.4.895-2.4 2v2h12v-2z"/>
</svg>

After

Width:  |  Height:  |  Size: 410 B

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
<path fill="#7DD79F" fill-rule="evenodd" d="M15 9h-1.277c-.347.595-.985 1-1.723 1a1.994 1.994 0 0 1-1.723-1H1a1 1 0 0 1 0-2h9.277c.347-.595.985-1 1.723-1 .738 0 1.376.405 1.723 1H15a1 1 0 0 1 0 2zm0-6H5.723C5.376 3.595 4.738 4 4 4a1.994 1.994 0 0 1-1.723-1H1a1 1 0 0 1 0-2h1.277C2.624.405 3.262 0 4 0c.738 0 1.376.405 1.723 1H15a1 1 0 0 1 0 2zM1 13h1.277c.347-.595.985-1 1.723-1 .738 0 1.376.405 1.723 1H15a1 1 0 0 1 0 2H5.723c-.347.595-.985 1-1.723 1a1.994 1.994 0 0 1-1.723-1H1a1 1 0 0 1 0-2z"/>
</svg>

After

Width:  |  Height:  |  Size: 593 B

@ -0,0 +1,3 @@
<svg viewBox="0 0 14 16" xmlns="http://www.w3.org/2000/svg" width="14" height="16">
<path fill="#7DD79F" fill-rule="evenodd" d="M14 11c0 .026-.013.049-.015.074.004.382-.159.757-.481.962l-5.897 3.743-.004.002-.099.063a.983.983 0 0 1-.299.115c-.01.002-.019.009-.03.011-.013.002-.027.001-.04.003-.046.006-.087.027-.135.027-.048 0-.089-.021-.135-.027-.013-.002-.027-.001-.04-.003-.011-.002-.02-.009-.03-.011a.983.983 0 0 1-.299-.115l-.099-.063-.004-.002-5.897-3.743c-.322-.205-.485-.58-.481-.962C.013 11.049 0 11.026 0 11V5a.98.98 0 0 1 .085-.398c.066-.256.184-.494.411-.638L6.393.221l.004-.002.114-.073c.022-.013.046-.013.068-.025A.924.924 0 0 1 6.825.03.86.86 0 0 1 7 .017a.86.86 0 0 1 .175.013c.087.017.166.05.246.091.022.012.046.012.068.025l.114.073.004.002 5.897 3.743c.227.144.345.382.411.638A.98.98 0 0 1 14 5v6zm-6 2.01l4-2.539V6.99L8 9.529v3.481zm-6-2.539l4 2.539V9.529L2 6.99v3.481zm5-8.116L2.834 5 7 7.645 11.166 5 7 2.355z"/>
</svg>

After

Width:  |  Height:  |  Size: 945 B

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
<path fill="#7DD79F" fill-rule="evenodd" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm5.906-7h-1.951c-.098 1.559-.408 2.985-.888 4.131A5.996 5.996 0 0 0 13.906 9zM8 14.013c.988 0 1.803-2.171 1.961-5.013H6.039c.158 2.842.973 5.013 1.961 5.013zm-3.067-.882c-.48-1.146-.79-2.572-.888-4.131H2.094a5.996 5.996 0 0 0 2.839 4.131zM2.094 7h1.951c.098-1.559.408-2.985.888-4.131A5.996 5.996 0 0 0 2.094 7zM8 1.987c-.988 0-1.803 2.171-1.961 5.013h3.922C9.803 4.158 8.988 1.987 8 1.987zm3.067.882c.48 1.146.79 2.572.888 4.131h1.951a5.996 5.996 0 0 0-2.839-4.131z"/>
</svg>

After

Width:  |  Height:  |  Size: 643 B

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 17" xmlns="http://www.w3.org/2000/svg" width="16" height="17">
<path fill="#7DD79F" fill-rule="evenodd" d="M15.713 15.727a.982.982 0 0 1-1.388 0l-2.289-2.29C10.773 14.403 9.213 15 7.5 15A7.5 7.5 0 1 1 15 7.5c0 1.719-.602 3.284-1.575 4.55l2.288 2.288a.983.983 0 0 1 0 1.389zM7.5 2a5.5 5.5 0 1 0 0 11 5.5 5.5 0 1 0 0-11z"/>
</svg>

After

Width:  |  Height:  |  Size: 354 B

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
<path fill="#89EDDA" fill-rule="evenodd" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zM8 2a6 6 0 1 0 0 12A6 6 0 0 0 8 2zm2 5H9v4a1 1 0 0 1-2 0V7H6a1 1 0 0 1 0-2h4a1 1 0 0 1 0 2z"/>
</svg>

After

Width:  |  Height:  |  Size: 269 B

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 14" xmlns="http://www.w3.org/2000/svg" width="16" height="14">
<path fill="#7DD79F" fill-rule="evenodd" d="M15 8H7a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2zm0-6H7a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2zM3 14H1a1 1 0 0 1 0-2h2a1 1 0 0 1 0 2zm0-6H1a1 1 0 0 1 0-2h2a1 1 0 0 1 0 2zm0-6H1a1 1 0 0 1 0-2h2a1 1 0 0 1 0 2zm4 10h8a1 1 0 0 1 0 2H7a1 1 0 0 1 0-2z"/>
</svg>

After

Width:  |  Height:  |  Size: 368 B

@ -0,0 +1,47 @@
defmodule BlockScoutWeb.AddressTokenTransferController do
use BlockScoutWeb, :controller
alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token
import BlockScoutWeb.AddressController, only: [transaction_count: 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, "address_token_id" => token_hash_string} = params
) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token_hash} <- Chain.string_to_address_hash(token_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash),
{:ok, token} <- Chain.token_from_address_hash(token_hash) do
transactions =
Chain.address_to_transactions_with_token_tranfers(
address_hash,
token_hash,
paging_options(params)
)
{transactions_paginated, next_page} = split_list_by_page(transactions)
render(
conn,
"index.html",
address: address,
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
next_page_params: next_page_params(next_page, transactions_paginated, params),
token: token,
transaction_count: transaction_count(address),
transactions: transactions_paginated
)
else
:error ->
unprocessable_entity(conn)
{:error, :not_found} ->
not_found(conn)
end
end
end

@ -58,7 +58,10 @@ defmodule BlockScoutWeb.Router do
resources("/logs", TransactionLogController, only: [:index], as: :log)
resources("/token_transfers", TransactionTokenTransferController, only: [:index], as: :token_transfer)
resources("/token_transfers", TransactionTokenTransferController,
only: [:index],
as: :token_transfer
)
end
resources("/accounts", AddressController, only: [:index])
@ -94,12 +97,14 @@ defmodule BlockScoutWeb.Router do
as: :read_contract
)
resources(
"/tokens",
AddressTokenController,
only: [:index],
as: :token
)
resources("/tokens", AddressTokenController, only: [:index], as: :token) do
resources(
"/token_transfers",
AddressTokenTransferController,
only: [:index],
as: :transfers
)
end
resources(
"/token_balances",

@ -1,7 +1,11 @@
<div class="tile tile-type-token">
<div class="row justify-content align-items-center">
<div class="col-md-7 d-flex flex-column mt-3 mt-md-0">
<%= link(to: token_path(@conn, :show, @token.contract_address_hash), class: "tile-title-lg") do %>
<%= link(
to: address_token_transfers_path(@conn, :index, @address.hash, @token.contract_address_hash),
class: "tile-title-lg",
"data-test": "token_transfers_#{@token.contract_address_hash}"
) do %>
<%= token_name(@token) %>
<% end %>
<span><%= @token.type %> - <%= number_of_transfers(@token) %></span>

@ -109,7 +109,7 @@
<h2 class="card-title"><%= gettext "Tokens" %></h2>
<%= if Enum.any?(@tokens) do %>
<%= for token <- @tokens do %>
<%= render "_tokens.html", conn: @conn, token: token %>
<%= render "_tokens.html", conn: @conn, token: token, address: @address %>
<% end %>
<% else %>
<div class="tile tile-muted text-center">
@ -121,7 +121,7 @@
<%= if @next_page_params do %>
<%= link(
gettext("Next"),
class: "button button-secondary button-sm float-right",
class: "button button-secondary button-sm float-right mt-3",
to: address_token_path(
@conn,
:index,

@ -0,0 +1,148 @@
<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-lg-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 active",
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>
<%= if AddressView.contract?(@address) do %>
<li class="nav-item">
<%= link(
to: address_contract_path(@conn, :index, @address.hash),
class: "nav-link") do %>
<%= gettext("Code") %>
<%= if AddressView.smart_contract_verified?(@address) do %>
<i class="far fa-check-circle"></i>
<% end %>
<% end %>
</li>
<% end %>
<%= if AddressView.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 %>
<%= if AddressView.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-lg-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 active",
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(
to: address_contract_path(@conn, :index, @address.hash),
class: "dropdown-item") do %>
<%= gettext("Code") %>
<%= if AddressView.smart_contract_verified?(@address) do %>
<i class="far fa-check-circle"></i>
<% end %>
<% end %>
<%= if AddressView.smart_contract_with_read_only_functions?(@address) do %>
<%= link(
gettext("Read Contract"),
to: address_read_contract_path(@conn, :index, @address.hash),
class: "dropdown-item"
)%>
<% end %>
</div>
</li>
</ul>
</div>
<div class="card-body">
<h2 class="card-title">
<span class="text-muted"><%= gettext "Tokens" %></span> / <%= token_name(@token) %>
</h2>
<%= if Enum.any?(@transactions) do %>
<span data-selector="transactions-list">
<%= for transaction <- @transactions do %>
<%= render(
BlockScoutWeb.TransactionView,
"_tile.html",
transaction: transaction,
current_address: @address
) %>
<% end %>
</span>
<% else %>
<div class="tile tile-muted text-center">
<span><%= gettext "There are no token transfers for this address." %></span>
</div>
<% end %>
<%= if @next_page_params do %>
<%= link(
gettext("Next"),
class: "button button-secondary button-sm float-right mt-3",
to: address_token_transfers_path(
@conn,
:index,
@address.hash,
@token.contract_address_hash,
@next_page_params
)
) %>
<% end %>
</div>
</div>
</section>
</section>

@ -19,7 +19,8 @@
<%= link(
gettext("Tokens"),
class: "nav-link",
to: address_token_path(@conn, :index, @address.hash)
to: address_token_path(@conn, :index, @address.hash),
"data-test": "tokens_tab_link"
) %>
</li>
<li class="nav-item">

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
<path fill="#7DD79F" fill-rule="evenodd" d="M15 16H1a1 1 0 0 1-1-1v-4a3 3 0 0 1 3-3h1V3a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v5h1a3 3 0 0 1 3 3v4a1 1 0 0 1-1 1zM10 4a2 2 0 1 0-4 0v3a2 2 0 1 0 4 0V4zm4 8c0-1.105-1.075-2-2.4-2h-.379c-.549.61-1.336 1-2.221 1H7c-.885 0-1.672-.39-2.221-1H4.4c-1.325 0-2.4.895-2.4 2v2h12v-2z"/>
</svg>

After

Width:  |  Height:  |  Size: 410 B

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
<path fill="#7DD79F" fill-rule="evenodd" d="M15 9h-1.277c-.347.595-.985 1-1.723 1a1.994 1.994 0 0 1-1.723-1H1a1 1 0 0 1 0-2h9.277c.347-.595.985-1 1.723-1 .738 0 1.376.405 1.723 1H15a1 1 0 0 1 0 2zm0-6H5.723C5.376 3.595 4.738 4 4 4a1.994 1.994 0 0 1-1.723-1H1a1 1 0 0 1 0-2h1.277C2.624.405 3.262 0 4 0c.738 0 1.376.405 1.723 1H15a1 1 0 0 1 0 2zM1 13h1.277c.347-.595.985-1 1.723-1 .738 0 1.376.405 1.723 1H15a1 1 0 0 1 0 2H5.723c-.347.595-.985 1-1.723 1a1.994 1.994 0 0 1-1.723-1H1a1 1 0 0 1 0-2z"/>
</svg>

After

Width:  |  Height:  |  Size: 593 B

@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="24">
<path fill="#5d6165" fill-rule="evenodd" d="M21.998 17.936c0 .006-.004.011-.004.017.009.185-.013.374-.105.547a.892.892 0 0 1-.816.485c-.019.002-.036.011-.055.011l-9.467 4.887a1.11 1.11 0 0 1-.519.111A.113.113 0 0 1 11 24c-.011 0-.021-.006-.032-.006-.052 0-.104-.009-.156-.014-.018-.004-.038-.001-.056-.005a.984.984 0 0 1-.308-.092l-9.425-4.888c-.008 0-.015.005-.023.005-.03 0-.055-.013-.084-.015a.895.895 0 0 1-.805-.485 1.038 1.038 0 0 1-.105-.547c-.002-.01-.006-.019-.006-.03v-.046c.001-.031-.005-.061 0-.092V6.188c-.005-.031.001-.061 0-.092v-.019l.002-.013c0-.043.004-.086.01-.128a.788.788 0 0 1 .114-.459.98.98 0 0 1 .407-.336.928.928 0 0 1 .18-.079c.038-.01.074-.017.112-.024.053-.01.103-.03.157-.034L10.448.117c.156-.081.323-.103.49-.105C10.96.011 10.978 0 11 0s.04.011.062.012c.167.002.334.024.489.104l9.537 4.902c.179.021.343.09.483.199a.613.613 0 0 1 .058.042c.042.037.08.073.116.116.042.044.095.078.129.131.072.111.098.23.109.349A.826.826 0 0 1 22 6.16v11.652c.005.031-.001.062 0 .093v.018l-.002.013zM2 16.159l1.633-1.025c.44-.276 1.002-.112 1.256.366s.103 1.09-.337 1.366l-1.565.983L10 21.486v-8.645l-1.633 1.025c-.44.276-1.002.112-1.256-.366s-.103-1.09.337-1.366l1.573-.988L2 7.522v8.637zM12 2.492V4a1 1 0 0 1-2 0V2.503L3.226 6 10 9.497V8a1 1 0 0 1 2 0v1.508L18.825 6 12 2.492zm8 5.05l-7.017 3.606 1.569.986c.44.276.591.888.337 1.366-.254.478-.816.642-1.256.366L12 12.841v8.656l7.037-3.633-1.589-.998c-.44-.276-.591-.888-.337-1.366.254-.478.816-.642 1.256-.366L20 16.159V7.542z"/>
<svg viewBox="0 0 14 16" xmlns="http://www.w3.org/2000/svg" width="14" height="16">
<path fill="#7DD79F" fill-rule="evenodd" d="M14 11c0 .026-.013.049-.015.074.004.382-.159.757-.481.962l-5.897 3.743-.004.002-.099.063a.983.983 0 0 1-.299.115c-.01.002-.019.009-.03.011-.013.002-.027.001-.04.003-.046.006-.087.027-.135.027-.048 0-.089-.021-.135-.027-.013-.002-.027-.001-.04-.003-.011-.002-.02-.009-.03-.011a.983.983 0 0 1-.299-.115l-.099-.063-.004-.002-5.897-3.743c-.322-.205-.485-.58-.481-.962C.013 11.049 0 11.026 0 11V5a.98.98 0 0 1 .085-.398c.066-.256.184-.494.411-.638L6.393.221l.004-.002.114-.073c.022-.013.046-.013.068-.025A.924.924 0 0 1 6.825.03.86.86 0 0 1 7 .017a.86.86 0 0 1 .175.013c.087.017.166.05.246.091.022.012.046.012.068.025l.114.073.004.002 5.897 3.743c.227.144.345.382.411.638A.98.98 0 0 1 14 5v6zm-6 2.01l4-2.539V6.99L8 9.529v3.481zm-6-2.539l4 2.539V9.529L2 6.99v3.481zm5-8.116L2.834 5 7 7.645 11.166 5 7 2.355z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 945 B

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
<path fill="#7DD79F" fill-rule="evenodd" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm5.906-7h-1.951c-.098 1.559-.408 2.985-.888 4.131A5.996 5.996 0 0 0 13.906 9zM8 14.013c.988 0 1.803-2.171 1.961-5.013H6.039c.158 2.842.973 5.013 1.961 5.013zm-3.067-.882c-.48-1.146-.79-2.572-.888-4.131H2.094a5.996 5.996 0 0 0 2.839 4.131zM2.094 7h1.951c.098-1.559.408-2.985.888-4.131A5.996 5.996 0 0 0 2.094 7zM8 1.987c-.988 0-1.803 2.171-1.961 5.013h3.922C9.803 4.158 8.988 1.987 8 1.987zm3.067.882c.48 1.146.79 2.572.888 4.131h1.951a5.996 5.996 0 0 0-2.839-4.131z"/>
</svg>

After

Width:  |  Height:  |  Size: 643 B

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 17" xmlns="http://www.w3.org/2000/svg" width="16" height="17">
<path fill="#7DD79F" fill-rule="evenodd" d="M15.713 15.727a.982.982 0 0 1-1.388 0l-2.289-2.29C10.773 14.403 9.213 15 7.5 15A7.5 7.5 0 1 1 15 7.5c0 1.719-.602 3.284-1.575 4.55l2.288 2.288a.983.983 0 0 1 0 1.389zM7.5 2a5.5 5.5 0 1 0 0 11 5.5 5.5 0 1 0 0-11z"/>
</svg>

After

Width:  |  Height:  |  Size: 354 B

@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" width="16" height="16">
<path fill="#89EDDA" fill-rule="evenodd" d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zM8 2a6 6 0 1 0 0 12A6 6 0 0 0 8 2zm2 5H9v4a1 1 0 0 1-2 0V7H6a1 1 0 0 1 0-2h4a1 1 0 0 1 0 2z"/>
</svg>

After

Width:  |  Height:  |  Size: 269 B

@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
<path fill="#BAC4CB" fill-rule="evenodd" d="M23 19H12.427l3.284 3.284a1.01 1.01 0 0 1-1.427 1.427l-4.995-4.995A1.01 1.01 0 0 1 8.994 18c0-.259.098-.519.295-.716l4.995-4.995a1.01 1.01 0 0 1 1.427 1.427L12.427 17H23a1 1 0 0 1 0 2zM14.711 6.716l-4.995 4.995a1.01 1.01 0 0 1-1.427-1.427L11.573 7H1a1 1 0 0 1 0-2h10.573L8.289 1.716A1.01 1.01 0 0 1 9.716.289l4.995 4.995c.197.197.295.457.295.716 0 .259-.098.519-.295.716z"/>
<svg viewBox="0 0 16 14" xmlns="http://www.w3.org/2000/svg" width="16" height="14">
<path fill="#7DD79F" fill-rule="evenodd" d="M15 8H7a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2zm0-6H7a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2zM3 14H1a1 1 0 0 1 0-2h2a1 1 0 0 1 0 2zm0-6H1a1 1 0 0 1 0-2h2a1 1 0 0 1 0 2zm0-6H1a1 1 0 0 1 0-2h2a1 1 0 0 1 0 2zm4 10h8a1 1 0 0 1 0 2H7a1 1 0 0 1 0-2z"/>
</svg>

Before

Width:  |  Height:  |  Size: 494 B

After

Width:  |  Height:  |  Size: 368 B

@ -10,21 +10,33 @@
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<%= link to: block_path(@conn, :index), class: "nav-link topnav-nav-link" do %>
<span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_block_icon.html" %>
</span>
<%= gettext("Blocks") %>
<% end %>
</li>
<li class="nav-item">
<%= link to: transaction_path(@conn, :index), class: "nav-link topnav-nav-link" do %>
<span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_transaction_icon.html" %>
</span>
<%= gettext("Transactions") %>
<% end %>
</li>
<li class="nav-item">
<%= link to: address_path(@conn, :index), class: "nav-link topnav-nav-link" do %>
<span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_accounts_icon.html" %>
</span>
<%= gettext("Accounts") %>
<% end %>
</li>
<li class="nav-item">
<%= link to: api_docs_path(@conn, :index), class: "nav-link topnav-nav-link" do %>
<span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_api_icon.html" %>
</span>
<%= gettext("API") %>
<% end %>
</li>
@ -33,7 +45,9 @@
<div class="input-group">
<%= search_input f, :q, class: 'form-control mr-auto', placeholder: gettext("Search by address, transaction hash, or block number"), "aria-describedby": "search-icon", "aria-label": gettext("Search"), "data-test": "search_input" %>
<div class="input-group-append">
<button class="input-group-text" id="search-icon"><i class="fas fa-search"></i></button>
<button class="input-group-text" id="search-icon">
<%= render BlockScoutWeb.IconsView, "_search_icon.html" %>
</button>
</div>
</div>
<button class="btn btn-outline-success my-2 my-sm-0 sr-only" type="submit"><%= gettext "Search" %></button>
@ -41,7 +55,15 @@
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<%= gettext("Mainnet") %>
<!-- ICON FOR MAINNET -->
<span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_network_icon.html" %>
</span>
<!-- ICON FOR TESTNET -->
<!-- <span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_test_network_icon.html" %>
</span> -->
<%= gettext("POA Core") %>
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="https://sokol.blockscout.com/"><%= gettext("POA Sokol") %></a>

@ -9,17 +9,25 @@
<%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @transfer.transaction_hash %>
</p>
<span>
<%= render BlockScoutWeb.AddressView,
"_link.html",
address: @transfer.from_address,
contract: BlockScoutWeb.AddressView.contract?(@transfer.from_address) %>
<%= link to: address_token_transfers_path(@conn, :index, @transfer.from_address, @token.contract_address_hash), "data-test": "address_hash_link" do %>
<%= render(
BlockScoutWeb.AddressView,
"_responsive_hash.html",
address: @transfer.from_address,
contract: BlockScoutWeb.AddressView.contract?(@transfer.from_address)
) %>
<% end %>
&rarr;
<%= render BlockScoutWeb.AddressView,
"_link.html",
address: @transfer.to_address,
contract: BlockScoutWeb.AddressView.contract?(@transfer.to_address) %>
<%= link to: address_token_transfers_path(@conn, :index, @transfer.to_address, @token.contract_address_hash), "data-test": "address_hash_link" do %>
<%= render(
BlockScoutWeb.AddressView,
"_responsive_hash.html",
address: @transfer.to_address,
contract: BlockScoutWeb.AddressView.contract?(@transfer.to_address)
) %>
<% end %>
</span>
<span>

@ -71,7 +71,7 @@
<%= if Enum.any?(@transfers) do %>
<%= for transfer <- @transfers do %>
<%= render("_token_transfer.html", token: @token, transfer: transfer) %>
<%= render("_token_transfer.html", conn: @conn, token: @token, transfer: transfer) %>
<% end %>
<% else %>
<div class="tile tile-muted text-center">

@ -0,0 +1,5 @@
defmodule BlockScoutWeb.AddressTokenTransferView do
use BlockScoutWeb, :view
alias BlockScoutWeb.AddressView
end

@ -8,7 +8,7 @@ msgstr ""
#: lib/block_scout_web/templates/block/index.html.eex:10
#: lib/block_scout_web/templates/chain/show.html.eex:56
#: lib/block_scout_web/templates/layout/_topnav.html.eex:13
#: lib/block_scout_web/templates/layout/_topnav.html.eex:16
msgid "Blocks"
msgstr ""
@ -43,16 +43,18 @@ msgstr ""
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:59
#: lib/block_scout_web/templates/address_token/index.html.eex:11
#: lib/block_scout_web/templates/address_token/index.html.eex:73
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:11
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:74
#: lib/block_scout_web/templates/address_transaction/index.html.eex:13
#: lib/block_scout_web/templates/address_transaction/index.html.eex:59
#: lib/block_scout_web/templates/address_transaction/index.html.eex:62
#: lib/block_scout_web/templates/address_transaction/index.html.eex:147
#: lib/block_scout_web/templates/address_transaction/index.html.eex:60
#: lib/block_scout_web/templates/address_transaction/index.html.eex:63
#: lib/block_scout_web/templates/address_transaction/index.html.eex:148
#: lib/block_scout_web/templates/block_transaction/index.html.eex:13
#: lib/block_scout_web/templates/block_transaction/index.html.eex:23
#: lib/block_scout_web/templates/block_transaction/index.html.eex:26
#: lib/block_scout_web/templates/block_transaction/index.html.eex:36
#: lib/block_scout_web/templates/chain/show.html.eex:73
#: lib/block_scout_web/templates/layout/_topnav.html.eex:18
#: lib/block_scout_web/templates/layout/_topnav.html.eex:24
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:46
#: lib/block_scout_web/templates/transaction/index.html.eex:56
msgid "Transactions"
@ -144,7 +146,7 @@ msgid "Address"
msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:131
#: lib/block_scout_web/templates/address_transaction/index.html.eex:134
#: lib/block_scout_web/templates/address_transaction/index.html.eex:135
#: lib/block_scout_web/views/address_internal_transaction_view.ex:10
#: lib/block_scout_web/views/address_transaction_view.ex:10
msgid "From"
@ -160,7 +162,7 @@ msgid "Success"
msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:120
#: lib/block_scout_web/templates/address_transaction/index.html.eex:123
#: lib/block_scout_web/templates/address_transaction/index.html.eex:124
#: lib/block_scout_web/views/address_internal_transaction_view.ex:9
#: lib/block_scout_web/views/address_transaction_view.ex:9
msgid "To"
@ -319,8 +321,10 @@ msgstr ""
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:69
#: lib/block_scout_web/templates/address_token/index.html.eex:26
#: lib/block_scout_web/templates/address_token/index.html.eex:83
#: lib/block_scout_web/templates/address_transaction/index.html.eex:27
#: lib/block_scout_web/templates/address_transaction/index.html.eex:72
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:27
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:84
#: lib/block_scout_web/templates/address_transaction/index.html.eex:28
#: lib/block_scout_web/templates/address_transaction/index.html.eex:73
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:20
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:38
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:49
@ -332,7 +336,7 @@ msgstr ""
msgid "Internal Transactions"
msgstr ""
#: lib/block_scout_web/templates/layout/_topnav.html.eex:34
#: lib/block_scout_web/templates/layout/_topnav.html.eex:46
msgid "Search by address, transaction hash, or block number"
msgstr ""
@ -367,7 +371,7 @@ msgid "Wei"
msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:114
#: lib/block_scout_web/templates/address_transaction/index.html.eex:117
#: lib/block_scout_web/templates/address_transaction/index.html.eex:118
#: lib/block_scout_web/views/address_internal_transaction_view.ex:11
#: lib/block_scout_web/views/address_transaction_view.ex:11
msgid "All"
@ -456,8 +460,12 @@ msgstr ""
#: lib/block_scout_web/templates/address_token/index.html.eex:69
#: lib/block_scout_web/templates/address_token/index.html.eex:78
#: lib/block_scout_web/templates/address_token/index.html.eex:109
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:70
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:79
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:112
#: lib/block_scout_web/templates/address_transaction/index.html.eex:20
#: lib/block_scout_web/templates/address_transaction/index.html.eex:67
#: lib/block_scout_web/templates/address_transaction/index.html.eex:68
msgid "Tokens"
msgstr ""
@ -502,7 +510,7 @@ msgid "There are no Transactions"
msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:158
#: lib/block_scout_web/templates/address_transaction/index.html.eex:161
#: lib/block_scout_web/templates/address_transaction/index.html.eex:162
#: lib/block_scout_web/templates/block/index.html.eex:20
#: lib/block_scout_web/templates/block_transaction/index.html.eex:50
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:78
@ -529,8 +537,10 @@ msgstr ""
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:77
#: lib/block_scout_web/templates/address_token/index.html.eex:38
#: lib/block_scout_web/templates/address_token/index.html.eex:91
#: lib/block_scout_web/templates/address_transaction/index.html.eex:38
#: lib/block_scout_web/templates/address_transaction/index.html.eex:81
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:39
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:92
#: lib/block_scout_web/templates/address_transaction/index.html.eex:39
#: lib/block_scout_web/templates/address_transaction/index.html.eex:82
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:122
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:141
msgid "Code"
@ -561,17 +571,13 @@ msgid "Contract Creation"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:44
msgid "Mainnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:48
#: lib/block_scout_web/templates/layout/_topnav.html.eex:66
#: lib/block_scout_web/templates/layout/_topnav.html.eex:70
msgid "POA Core"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:47
#: lib/block_scout_web/templates/layout/_topnav.html.eex:69
msgid "POA Sokol"
msgstr ""
@ -611,7 +617,7 @@ msgid "Wallet addresses"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:107
#: lib/block_scout_web/templates/address_transaction/index.html.eex:108
#: lib/block_scout_web/templates/transaction/index.html.eex:53
msgid "Connection Lost, click to load newer transactions"
msgstr ""
@ -644,7 +650,7 @@ msgid "There are no internal transactions for this address."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:155
#: lib/block_scout_web/templates/address_transaction/index.html.eex:156
#: lib/block_scout_web/templates/block_transaction/index.html.eex:44
msgid "There are no transactions for this address."
msgstr ""
@ -673,7 +679,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:21
#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:36
#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:44
#: lib/block_scout_web/templates/transaction/_tile.html.eex:30
msgid "Block #%{number}"
msgstr ""
@ -706,8 +712,11 @@ msgstr ""
#: lib/block_scout_web/templates/address_token/index.html.eex:50
#: lib/block_scout_web/templates/address_token/index.html.eex:58
#: lib/block_scout_web/templates/address_token/index.html.eex:99
#: lib/block_scout_web/templates/address_transaction/index.html.eex:49
#: lib/block_scout_web/templates/address_transaction/index.html.eex:90
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:51
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:59
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:100
#: lib/block_scout_web/templates/address_transaction/index.html.eex:50
#: lib/block_scout_web/templates/address_transaction/index.html.eex:91
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:27
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:55
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:26
@ -787,7 +796,7 @@ msgstr[0] ""
msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:102
#: lib/block_scout_web/templates/address_transaction/index.html.eex:103
#: lib/block_scout_web/templates/chain/show.html.eex:69
#: lib/block_scout_web/templates/transaction/index.html.eex:48
msgid "More transactions have come in"
@ -848,7 +857,7 @@ msgid "Total Supply"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:28
#: lib/block_scout_web/templates/layout/_topnav.html.eex:40
msgid "API"
msgstr ""
@ -1080,6 +1089,7 @@ msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:123
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:134
msgid "Next"
msgstr ""
@ -1171,8 +1181,8 @@ msgid "Reset"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:34
#: lib/block_scout_web/templates/layout/_topnav.html.eex:39
#: lib/block_scout_web/templates/layout/_topnav.html.eex:46
#: lib/block_scout_web/templates/layout/_topnav.html.eex:53
msgid "Search"
msgstr ""
@ -1239,7 +1249,7 @@ msgid "Verify & publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:23
#: lib/block_scout_web/templates/layout/_topnav.html.eex:32
msgid "Accounts"
msgstr ""
@ -1257,3 +1267,8 @@ msgstr ""
#: lib/block_scout_web/templates/address/index.html.eex:8
msgid "total addresses with a balance"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:128
msgid "There are no token transfers for this address."
msgstr ""

@ -20,7 +20,7 @@ msgstr "Block"
#: lib/block_scout_web/templates/block/index.html.eex:10
#: lib/block_scout_web/templates/chain/show.html.eex:56
#: lib/block_scout_web/templates/layout/_topnav.html.eex:13
#: lib/block_scout_web/templates/layout/_topnav.html.eex:16
msgid "Blocks"
msgstr "Blocks"
@ -55,16 +55,18 @@ msgstr "BlockScout"
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:59
#: lib/block_scout_web/templates/address_token/index.html.eex:11
#: lib/block_scout_web/templates/address_token/index.html.eex:73
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:11
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:74
#: lib/block_scout_web/templates/address_transaction/index.html.eex:13
#: lib/block_scout_web/templates/address_transaction/index.html.eex:59
#: lib/block_scout_web/templates/address_transaction/index.html.eex:62
#: lib/block_scout_web/templates/address_transaction/index.html.eex:147
#: lib/block_scout_web/templates/address_transaction/index.html.eex:60
#: lib/block_scout_web/templates/address_transaction/index.html.eex:63
#: lib/block_scout_web/templates/address_transaction/index.html.eex:148
#: lib/block_scout_web/templates/block_transaction/index.html.eex:13
#: lib/block_scout_web/templates/block_transaction/index.html.eex:23
#: lib/block_scout_web/templates/block_transaction/index.html.eex:26
#: lib/block_scout_web/templates/block_transaction/index.html.eex:36
#: lib/block_scout_web/templates/chain/show.html.eex:73
#: lib/block_scout_web/templates/layout/_topnav.html.eex:18
#: lib/block_scout_web/templates/layout/_topnav.html.eex:24
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:46
#: lib/block_scout_web/templates/transaction/index.html.eex:56
msgid "Transactions"
@ -156,7 +158,7 @@ msgid "Address"
msgstr "Address"
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:131
#: lib/block_scout_web/templates/address_transaction/index.html.eex:134
#: lib/block_scout_web/templates/address_transaction/index.html.eex:135
#: lib/block_scout_web/views/address_internal_transaction_view.ex:10
#: lib/block_scout_web/views/address_transaction_view.ex:10
msgid "From"
@ -172,7 +174,7 @@ msgid "Success"
msgstr "Success"
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:120
#: lib/block_scout_web/templates/address_transaction/index.html.eex:123
#: lib/block_scout_web/templates/address_transaction/index.html.eex:124
#: lib/block_scout_web/views/address_internal_transaction_view.ex:9
#: lib/block_scout_web/views/address_transaction_view.ex:9
msgid "To"
@ -331,8 +333,10 @@ msgstr ""
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:69
#: lib/block_scout_web/templates/address_token/index.html.eex:26
#: lib/block_scout_web/templates/address_token/index.html.eex:83
#: lib/block_scout_web/templates/address_transaction/index.html.eex:27
#: lib/block_scout_web/templates/address_transaction/index.html.eex:72
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:27
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:84
#: lib/block_scout_web/templates/address_transaction/index.html.eex:28
#: lib/block_scout_web/templates/address_transaction/index.html.eex:73
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:20
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:38
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:49
@ -344,7 +348,7 @@ msgstr ""
msgid "Internal Transactions"
msgstr ""
#: lib/block_scout_web/templates/layout/_topnav.html.eex:34
#: lib/block_scout_web/templates/layout/_topnav.html.eex:46
msgid "Search by address, transaction hash, or block number"
msgstr ""
@ -379,7 +383,7 @@ msgid "Wei"
msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:114
#: lib/block_scout_web/templates/address_transaction/index.html.eex:117
#: lib/block_scout_web/templates/address_transaction/index.html.eex:118
#: lib/block_scout_web/views/address_internal_transaction_view.ex:11
#: lib/block_scout_web/views/address_transaction_view.ex:11
msgid "All"
@ -468,8 +472,12 @@ msgstr ""
#: lib/block_scout_web/templates/address_token/index.html.eex:69
#: lib/block_scout_web/templates/address_token/index.html.eex:78
#: lib/block_scout_web/templates/address_token/index.html.eex:109
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:70
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:79
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:112
#: lib/block_scout_web/templates/address_transaction/index.html.eex:20
#: lib/block_scout_web/templates/address_transaction/index.html.eex:67
#: lib/block_scout_web/templates/address_transaction/index.html.eex:68
msgid "Tokens"
msgstr ""
@ -514,7 +522,7 @@ msgid "There are no Transactions"
msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:158
#: lib/block_scout_web/templates/address_transaction/index.html.eex:161
#: lib/block_scout_web/templates/address_transaction/index.html.eex:162
#: lib/block_scout_web/templates/block/index.html.eex:20
#: lib/block_scout_web/templates/block_transaction/index.html.eex:50
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:78
@ -541,8 +549,10 @@ msgstr ""
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:77
#: lib/block_scout_web/templates/address_token/index.html.eex:38
#: lib/block_scout_web/templates/address_token/index.html.eex:91
#: lib/block_scout_web/templates/address_transaction/index.html.eex:38
#: lib/block_scout_web/templates/address_transaction/index.html.eex:81
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:39
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:92
#: lib/block_scout_web/templates/address_transaction/index.html.eex:39
#: lib/block_scout_web/templates/address_transaction/index.html.eex:82
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:122
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:141
msgid "Code"
@ -573,17 +583,13 @@ msgid "Contract Creation"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:44
msgid "Mainnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:48
#: lib/block_scout_web/templates/layout/_topnav.html.eex:66
#: lib/block_scout_web/templates/layout/_topnav.html.eex:70
msgid "POA Core"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:47
#: lib/block_scout_web/templates/layout/_topnav.html.eex:69
msgid "POA Sokol"
msgstr ""
@ -623,7 +629,7 @@ msgid "Wallet addresses"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:107
#: lib/block_scout_web/templates/address_transaction/index.html.eex:108
#: lib/block_scout_web/templates/transaction/index.html.eex:53
msgid "Connection Lost, click to load newer transactions"
msgstr ""
@ -656,7 +662,7 @@ msgid "There are no internal transactions for this address."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:155
#: lib/block_scout_web/templates/address_transaction/index.html.eex:156
#: lib/block_scout_web/templates/block_transaction/index.html.eex:44
msgid "There are no transactions for this address."
msgstr ""
@ -685,7 +691,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:21
#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:36
#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:44
#: lib/block_scout_web/templates/transaction/_tile.html.eex:30
msgid "Block #%{number}"
msgstr ""
@ -718,8 +724,11 @@ msgstr ""
#: lib/block_scout_web/templates/address_token/index.html.eex:50
#: lib/block_scout_web/templates/address_token/index.html.eex:58
#: lib/block_scout_web/templates/address_token/index.html.eex:99
#: lib/block_scout_web/templates/address_transaction/index.html.eex:49
#: lib/block_scout_web/templates/address_transaction/index.html.eex:90
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:51
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:59
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:100
#: lib/block_scout_web/templates/address_transaction/index.html.eex:50
#: lib/block_scout_web/templates/address_transaction/index.html.eex:91
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:27
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:55
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:26
@ -799,7 +808,7 @@ msgstr[0] ""
msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:102
#: lib/block_scout_web/templates/address_transaction/index.html.eex:103
#: lib/block_scout_web/templates/chain/show.html.eex:69
#: lib/block_scout_web/templates/transaction/index.html.eex:48
msgid "More transactions have come in"
@ -860,7 +869,7 @@ msgid "Total Supply"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:28
#: lib/block_scout_web/templates/layout/_topnav.html.eex:40
msgid "API"
msgstr ""
@ -1092,6 +1101,7 @@ msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:123
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:134
msgid "Next"
msgstr ""
@ -1183,8 +1193,8 @@ msgid "Reset"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:34
#: lib/block_scout_web/templates/layout/_topnav.html.eex:39
#: lib/block_scout_web/templates/layout/_topnav.html.eex:46
#: lib/block_scout_web/templates/layout/_topnav.html.eex:53
msgid "Search"
msgstr ""
@ -1251,7 +1261,7 @@ msgid "Verify & publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:23
#: lib/block_scout_web/templates/layout/_topnav.html.eex:32
msgid "Accounts"
msgstr ""
@ -1269,3 +1279,8 @@ msgstr ""
#: lib/block_scout_web/templates/address/index.html.eex:8
msgid "total addresses with a balance"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:128
msgid "There are no token transfers for this address."
msgstr ""

@ -0,0 +1,114 @@
defmodule BlockScoutWeb.AddressTokenTransferControllerTest do
use BlockScoutWeb.ConnCase
import BlockScoutWeb.Router.Helpers, only: [address_token_transfers_path: 4]
alias Explorer.Chain.{Address, Token}
describe "GET index/2" do
test "with invalid address hash", %{conn: conn} do
token_hash = "0xc8982771dd50285389c352c175ada74d074427c7"
conn = get(conn, address_token_transfers_path(conn, :index, "invalid_address", token_hash))
assert html_response(conn, 422)
end
test "with invalid token hash", %{conn: conn} do
address_hash = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b"
conn = get(conn, address_token_transfers_path(conn, :index, address_hash, "invalid_address"))
assert html_response(conn, 422)
end
test "with an address that doesn't exist in our database", %{conn: conn} do
address_hash = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b"
%Token{contract_address_hash: token_hash} = insert(:token)
conn = get(conn, address_token_transfers_path(conn, :index, address_hash, token_hash))
assert html_response(conn, 404)
end
test "with an token that doesn't exist in our database", %{conn: conn} do
%Address{hash: address_hash} = insert(:address)
token_hash = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b"
conn = get(conn, address_token_transfers_path(conn, :index, address_hash, token_hash))
assert html_response(conn, 404)
end
test "without token transfers for a token", %{conn: conn} do
%Address{hash: address_hash} = insert(:address)
%Token{contract_address_hash: token_hash} = insert(:token)
conn = get(conn, address_token_transfers_path(conn, :index, address_hash, token_hash))
assert html_response(conn, 200)
assert conn.assigns.transactions == []
end
test "returns the transactions that have token transfers for the given address and token", %{conn: conn} do
address = insert(:address)
token = insert(:token)
transaction =
:transaction
|> insert()
|> with_block()
insert(
:token_transfer,
to_address: address,
transaction: transaction,
token_contract_address: token.contract_address
)
conn = get(conn, address_token_transfers_path(conn, :index, address.hash, token.contract_address_hash))
transaction_hashes = Enum.map(conn.assigns.transactions, & &1.hash)
assert html_response(conn, 200)
assert transaction_hashes == [transaction.hash]
end
test "returns next page of results based on last seen transactions", %{conn: conn} do
address = insert(:address)
token = insert(:token)
second_page_transactions =
1..50
|> Enum.map(fn index ->
block = insert(:block, number: 1000 - index)
transaction =
:transaction
|> insert()
|> with_block(block)
insert(
:token_transfer,
to_address: address,
transaction: transaction,
token_contract_address: token.contract_address
)
transaction
end)
|> Enum.map(& &1.hash)
transaction =
:transaction
|> insert()
|> with_block(insert(:block, number: 1002))
conn =
get(conn, address_token_transfers_path(conn, :index, address.hash, token.contract_address_hash), %{
"block_number" => Integer.to_string(transaction.block_number),
"index" => Integer.to_string(transaction.index)
})
actual_transactions = Enum.map(conn.assigns.transactions, & &1.hash)
assert second_page_transactions == actual_transactions
end
end
end

@ -3,7 +3,7 @@ defmodule BlockScoutWeb.AddressPage do
use Wallaby.DSL
import Wallaby.Query, only: [css: 1, css: 2]
alias Explorer.Chain.{Address, InternalTransaction, Hash, Transaction}
alias Explorer.Chain.{Address, InternalTransaction, Hash, Transaction, Token}
def apply_filter(session, direction) do
session
@ -27,6 +27,14 @@ defmodule BlockScoutWeb.AddressPage do
click(session, css("[data-test='internal_transactions_tab_link']"))
end
def click_tokens(session) do
click(session, css("[data-test='tokens_tab_link']"))
end
def click_token_transfers(session, %Token{contract_address_hash: contract_address_hash}) do
click(session, css("[data-test='token_transfers_#{contract_address_hash}']"))
end
def contract_creation(%InternalTransaction{created_contract_address_hash: hash}) do
css("[data-address-hash='#{hash}']", text: to_string(hash))
end

@ -422,4 +422,42 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
|> assert_has(AddressPage.token_transfers(transaction, count: 3))
end
end
describe "viewing token transfers from a specific token" do
test "list token transfers related to the address", %{
addresses: addresses,
block: block,
session: session
} do
lincoln = addresses.lincoln
taft = addresses.taft
contract_address = insert(:contract_address)
token = insert(:token, contract_address: contract_address)
transaction =
:transaction
|> insert(from_address: lincoln, to_address: contract_address)
|> with_block(block)
insert(
:token_transfer,
from_address: lincoln,
to_address: taft,
transaction: transaction,
token_contract_address: contract_address
)
insert(:token_balance, address: lincoln, token_contract_address_hash: contract_address.hash)
session
|> AddressPage.visit_page(lincoln)
|> AddressPage.click_tokens()
|> AddressPage.click_token_transfers(token)
|> assert_has(AddressPage.token_transfers(transaction, count: 1))
|> assert_has(AddressPage.token_transfer(transaction, lincoln, count: 1))
|> assert_has(AddressPage.token_transfer(transaction, taft, count: 1))
|> refute_has(AddressPage.token_transfers_expansion(transaction))
end
end
end

@ -222,6 +222,27 @@ defmodule Explorer.Chain do
|> Enum.slice(0..paging_options.page_size)
end
@doc """
Finds all `t:Explorer.Chain.Transaction.t/0`s given the address_hash and the token contract
address hash.
## Options
* `:paging_options` - a `t:Explorer.PagingOptions.t/0` used to specify the `:page_size` and
`:key` (in the form of `%{"inserted_at" => inserted_at}`). Results will be the transactions
older than the `index` that are passed.
"""
@spec address_to_transactions_with_token_tranfers(Hash.t(), Hash.t(), [paging_options]) :: [Transaction.t()]
def address_to_transactions_with_token_tranfers(address_hash, token_hash, options \\ []) do
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
address_hash
|> Transaction.transactions_with_token_transfers(token_hash)
|> Transaction.preload_token_transfers(address_hash)
|> handle_paging_options(paging_options)
|> Repo.all()
end
@doc """
The average time it took to mine/validate the last <= 100 `t:Explorer.Chain.Block.t/0`
"""

@ -3,7 +3,7 @@ defmodule Explorer.Chain.Transaction do
use Explorer.Schema
import Ecto.Query, only: [from: 2, preload: 3, where: 3]
import Ecto.Query, only: [from: 2, preload: 3, where: 3, subquery: 1]
alias Ecto.Changeset
@ -16,6 +16,7 @@ defmodule Explorer.Chain.Transaction do
InternalTransaction,
Log,
TokenTransfer,
Transaction,
Wei
}
@ -503,4 +504,30 @@ defmodule Explorer.Chain.Transaction do
changeset
end
end
@doc """
Builds an `Ecto.Query` to fetch transactions with token transfers from the give address hash.
The results will be ordered by block number and index DESC.
"""
def transactions_with_token_transfers(address_hash, token_hash) do
query = transactions_with_token_transfers_query(address_hash, token_hash)
from(
t in subquery(query),
order_by: [desc: t.block_number, desc: t.index],
preload: [:from_address, :to_address, :created_contract_address, :block]
)
end
defp transactions_with_token_transfers_query(address_hash, token_hash) do
from(
t in Transaction,
inner_join: tt in TokenTransfer,
on: t.hash == tt.transaction_hash,
where: tt.token_contract_address_hash == ^token_hash,
where: tt.from_address_hash == ^address_hash or tt.to_address_hash == ^address_hash,
distinct: :hash
)
end
end

@ -37,4 +37,144 @@ defmodule Explorer.Chain.TransactionTest do
assert %Changeset{valid?: true} = Transaction.changeset(%Transaction{}, changeset_params)
end
end
describe "transactions_with_token_transfers/2" do
test "returns the transaction when there is token transfer from the given address" do
address = insert(:address)
token = insert(:token)
transaction =
:transaction
|> insert()
|> with_block()
insert(
:token_transfer,
from_address: address,
transaction: transaction,
token_contract_address: token.contract_address
)
result =
address.hash
|> Transaction.transactions_with_token_transfers(token.contract_address_hash)
|> Repo.all()
|> Enum.map(& &1.hash)
assert result == [transaction.hash]
end
test "returns the transaction when there is token transfer to the given address" do
address = insert(:address)
token = insert(:token)
transaction =
:transaction
|> insert()
|> with_block()
insert(
:token_transfer,
to_address: address,
transaction: transaction,
token_contract_address: token.contract_address
)
result =
address.hash
|> Transaction.transactions_with_token_transfers(token.contract_address_hash)
|> Repo.all()
|> Enum.map(& &1.hash)
assert result == [transaction.hash]
end
test "returns only transactions that have token transfers from the given token hash" do
address = insert(:address)
token = insert(:token)
transaction =
:transaction
|> insert()
|> with_block()
:transaction
|> insert()
|> with_block()
insert(
:token_transfer,
to_address: address,
transaction: transaction,
token_contract_address: token.contract_address
)
insert(
:token_transfer,
to_address: address,
transaction: transaction,
token_contract_address: insert(:token).contract_address
)
result =
address.hash
|> Transaction.transactions_with_token_transfers(token.contract_address_hash)
|> Repo.all()
|> Enum.map(& &1.hash)
assert result == [transaction.hash]
end
test "order the results DESC by block_number" do
address = insert(:address)
token = insert(:token)
transaction_a =
:transaction
|> insert()
|> with_block(insert(:block, number: 1000))
transaction_b =
:transaction
|> insert()
|> with_block(insert(:block, number: 1002))
transaction_c =
:transaction
|> insert()
|> with_block(insert(:block, number: 1003))
insert(
:token_transfer,
amount: 2,
to_address: address,
token_contract_address: token.contract_address,
transaction: transaction_a
)
insert(
:token_transfer,
amount: 1,
to_address: address,
token_contract_address: token.contract_address,
transaction: transaction_b
)
insert(
:token_transfer,
amount: 1,
to_address: address,
token_contract_address: token.contract_address,
transaction: transaction_c
)
result =
address.hash
|> Transaction.transactions_with_token_transfers(token.contract_address_hash)
|> Repo.all()
|> Enum.map(& &1.block_number)
assert result == [transaction_c.block_number, transaction_b.block_number, transaction_a.block_number]
end
end
end

@ -2716,4 +2716,80 @@ defmodule Explorer.ChainTest do
assert Chain.count_token_holders_from_token_hash(contract_address_hash) == 0
end
end
describe "address_to_transactions_with_token_tranfers/2" do
test "paginates transactions by the block number" do
address = insert(:address)
token = insert(:token)
first_page =
:transaction
|> insert()
|> with_block(insert(:block, number: 1000))
second_page =
:transaction
|> insert()
|> with_block(insert(:block, number: 999))
insert(
:token_transfer,
to_address: address,
transaction: first_page,
token_contract_address: token.contract_address
)
insert(
:token_transfer,
to_address: address,
transaction: second_page,
token_contract_address: token.contract_address
)
paging_options = %PagingOptions{
key: {first_page.block_number, first_page.index},
page_size: 2
}
result =
address.hash
|> Chain.address_to_transactions_with_token_tranfers(token.contract_address_hash, paging_options: paging_options)
|> Enum.map(& &1.hash)
assert result == [second_page.hash]
end
test "doesn't duplicate the transaction when there are multiple transfers for it" do
address = insert(:address)
token = insert(:token)
transaction =
:transaction
|> insert()
|> with_block()
insert(
:token_transfer,
amount: 2,
to_address: address,
token_contract_address: token.contract_address,
transaction: transaction
)
insert(
:token_transfer,
amount: 1,
to_address: address,
token_contract_address: token.contract_address,
transaction: transaction
)
result =
address.hash
|> Chain.address_to_transactions_with_token_tranfers(token.contract_address_hash)
|> Enum.map(& &1.hash)
assert result == [transaction.hash]
end
end
end

Loading…
Cancel
Save