Merge branch 'master' into more-transaction-controllers-improvements

pull/2249/head
Victor Baranov 5 years ago committed by GitHub
commit 257ff98b3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      CHANGELOG.md
  2. 20
      README.md
  3. 6
      apps/block_scout_web/assets/css/_typography.scss
  4. 11
      apps/block_scout_web/assets/css/components/_network-selector.scss
  5. 5
      apps/block_scout_web/assets/css/components/_tile.scss
  6. 60
      apps/block_scout_web/assets/css/components/_token-balance-dropdown.scss
  7. 7
      apps/block_scout_web/assets/css/components/_transaction.scss
  8. 9
      apps/block_scout_web/assets/css/theme/_neutral_variables.scss
  9. 9
      apps/block_scout_web/assets/css/theme/_poa_variables.scss
  10. 2
      apps/block_scout_web/assets/js/lib/currency.js
  11. 11
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex
  12. 1
      apps/block_scout_web/lib/block_scout_web/controllers/page_not_found_controller.ex
  13. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/holder_controller.ex
  14. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/inventory_controller.ex
  15. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/read_contract_controller.ex
  16. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex
  17. 31
      apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex
  18. 4
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex
  19. 13
      apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex
  20. 6
      apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex
  21. 2
      apps/block_scout_web/lib/block_scout_web/templates/api_docs/eth_rpc.html.eex
  22. 2
      apps/block_scout_web/lib/block_scout_web/templates/api_docs/index.html.eex
  23. 2
      apps/block_scout_web/lib/block_scout_web/templates/layout/_network_selector.html.eex
  24. 13
      apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/index.html.eex
  25. 4
      apps/block_scout_web/lib/block_scout_web/templates/transaction/_token_transfer.html.eex
  26. 2
      apps/block_scout_web/lib/block_scout_web/views/api_docs_view.ex
  27. 72
      apps/block_scout_web/priv/gettext/default.pot
  28. 79
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  29. 11
      apps/block_scout_web/test/block_scout_web/controllers/page_not_found_controller_test.exs
  30. 4
      apps/explorer/config/config.exs
  31. 11
      apps/explorer/lib/explorer/chain.ex
  32. 3
      apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex
  33. 22
      apps/explorer/lib/explorer/chain/smart_contract.ex
  34. 105
      apps/explorer/lib/explorer/chain/transaction.ex
  35. 11
      apps/explorer/lib/explorer/counters/average_block_time.ex
  36. 3
      apps/explorer/lib/explorer/smart_contract/publisher.ex
  37. 12
      apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex
  38. 1
      apps/explorer/mix.exs
  39. 132
      apps/explorer/priv/repo/migrations/20190619154943_reduce_transaction_status_constraint.exs
  40. 11
      apps/explorer/priv/repo/migrations/20190625085852_add_additional_contract_fields.exs
  41. 61
      apps/explorer/test/explorer/chain/import/runner/internal_transactions_test.exs
  42. 2
      apps/explorer/test/explorer/chain/import_test.exs
  43. 11
      apps/explorer/test/explorer/chain_test.exs
  44. 31
      apps/explorer/test/explorer/counters/average_block_time_test.exs
  45. 2
      apps/indexer/lib/indexer/fetcher/token.ex
  46. 5
      apps/indexer/lib/indexer/fetcher/token_balance.ex
  47. 2
      apps/indexer/lib/indexer/fetcher/token_updater.ex
  48. 35
      apps/indexer/test/indexer/fetcher/token_balance_test.exs
  49. 1
      docs/_sidebar.md
  50. 2
      docs/env-variables.md
  51. 9
      docs/index.html
  52. 20
      docs/projects.md

@ -6,10 +6,18 @@
- [#2151](https://github.com/poanetwork/blockscout/pull/2151) - hide dropdown menu then other networks list is empty
- [#2191](https://github.com/poanetwork/blockscout/pull/2191) - allow to configure token metadata update interval
- [#2146](https://github.com/poanetwork/blockscout/pull/2146) - feat: add eth_getLogs rpc endpoint
- [#2216](https://github.com/poanetwork/blockscout/pull/2216) - Improve token's controllers by avoiding unnecessary preloads
- [#2235](https://github.com/poanetwork/blockscout/pull/2235) - save and show additional validation fields to smart contract
- [#2190](https://github.com/poanetwork/blockscout/pull/2190) - show all token transfers
- [#2193](https://github.com/poanetwork/blockscout/pull/2193) - feat: add BLOCKSCOUT_HOST, and use it in API docs
- [#2266](https://github.com/poanetwork/blockscout/pull/2266) - allow excluding uncles from average block time calculation
### Fixes
- [#2284](https://github.com/poanetwork/blockscout/pull/2284) - add 404 status for not existing pages
- [#2244](https://github.com/poanetwork/blockscout/pull/2244) - fix internal transactions failing to be indexed because of constraint
- [#2281](https://github.com/poanetwork/blockscout/pull/2281) - typo issues, dropdown issues
- [#2278](https://github.com/poanetwork/blockscout/pull/2278) - increase threshold for scientific notation
- [#2275](https://github.com/poanetwork/blockscout/pull/2275) - Description for networks selector
- [#2263](https://github.com/poanetwork/blockscout/pull/2263) - added an ability to close network selector on outside click
- [#2257](https://github.com/poanetwork/blockscout/pull/2257) - 'download csv' button added to different tabs
- [#2242](https://github.com/poanetwork/blockscout/pull/2242) - added styles for 'download csv' button
@ -52,12 +60,16 @@
- [#2173](https://github.com/poanetwork/blockscout/pull/2173) - handle correctly empty transactions
- [#2174](https://github.com/poanetwork/blockscout/pull/2174) - fix reward channel joining
- [#2186](https://github.com/poanetwork/blockscout/pull/2186) - fix net version test
- [#2198](https://github.com/poanetwork/blockscout/pull/2198) - reduce transaction status and error constraint
- [#2167](https://github.com/poanetwork/blockscout/pull/2167) - feat: document eth rpc api mimicking endpoints
- [#2225](https://github.com/poanetwork/blockscout/pull/2225) - fix metadata decoding in Solidity 0.5.9 smart contract verification
- [#2204](https://github.com/poanetwork/blockscout/pull/2204) - fix large contract verification
- [#2247](https://github.com/poanetwork/blockscout/pull/2247) - hide logs search if there are no logs
- [#2248](https://github.com/poanetwork/blockscout/pull/2248) - sort block after query execution for average block time
- [#2249](https://github.com/poanetwork/blockscout/pull/2249) - More transaction controllers improvements
- [#2270](https://github.com/poanetwork/blockscout/pull/2270) - Remove duplicate params in `Indexer.Fetcher.TokenBalance`
- [#2268](https://github.com/poanetwork/blockscout/pull/2268) - remove not existing assigns in html code
- [#2276](https://github.com/poanetwork/blockscout/pull/2276) - remove port in docs
### Chore
- [#2127](https://github.com/poanetwork/blockscout/pull/2127) - use previouse chromedriver version

@ -26,6 +26,25 @@ BlockScout is an Elixir application that allows users to search transactions, vi
Currently available full-featured block explorers (Etherscan, Etherchain, Blockchair) are closed systems which are not independently verifiable. As Ethereum sidechains continue to proliferate in both private and public settings, transparent, open-source tools are needed to analyze and validate transactions.
## Supported Projects
| **Hosted Mainnets** | **Hosted Testnets** | **Additional Chains using BlockScout** |
|--------------------------------------------------------|-------------------------------------------------------|----------------------------------------------------|
| [Aerum](https://blockscout.com/aerum/mainnet) | [Goerli Testnet](https://blockscout.com/eth/goerli) | [ARTIS](https://explorer.sigma1.artis.network) |
| [Callisto](https://blockscout.com/callisto/mainnet) | [Kovan Testnet](https://blockscout.com/eth/kovan) | [Ether-1](https://blocks.ether1.wattpool.net/) |
| [Ethereum Classic](https://blockscout.com/etc/mainnet) | [POA Sokol Testnet](https://blockscout.com/poa/sokol) | [Fuse Network](https://explorer.fuse.io/) |
| [Ethereum Mainnet](https://blockscout.com/eth/mainnet) | [Rinkeby Testnet](https://blockscout.com/eth/rinkeby) | [Oasis Labs](https://blockexplorer.oasiscloud.io/) |
| [POA Core Network](https://blockscout.com/poa/core) | [Ropsten Testnet](https://blockscout.com/eth/ropsten) | [Petrichor](https://explorer.petrachor.com/) |
| [RSK](https://blockscout.com/rsk/mainnet) | | [PIRL](http://pirl.es/) |
| [xDai Chain](https://blockscout.com/poa/dai) | | [SafeChain](https://explorer.safechain.io) |
| | | [SpringChain](https://explorer.springrole.com/) |
| | | [Kotti Testnet](https://kottiexplorer.ethernode.io/) |
| | | [Loom](http://plasma-blockexplorer.dappchains.com/) |
| | | [Tenda](https://tenda.network) |
Current BlockScout versions for hosted projects are available [on the forum](https://forum.poa.network/t/deployed-instances-on-blockscout-com/1938).
## Getting Started
See the [project documentation](https://poanetwork.github.io/blockscout) for instructions:
@ -35,6 +54,7 @@ See the [project documentation](https://poanetwork.github.io/blockscout) for ins
- [ENV variables](https://poanetwork.github.io/blockscout/#/env-variables)
- [Configuration options](https://poanetwork.github.io/blockscout/#/dev-env)
## Acknowledgements
We would like to thank the [EthPrize foundation](http://ethprize.io/) for their funding support.

@ -73,8 +73,12 @@ textarea.form-control {
}
.contract-address {
border-bottom: 1px dashed currentColor;
display: inline-block;
text-decoration: underline;
text-decoration-style: dashed;
&:hover {
text-decoration-style: none;
}
}
.text {

@ -1,6 +1,7 @@
$network-selector-overlay-background: $modal-overlay-color !default;
$network-selector-close-color: $primary !default;
$network-selector-horizontal-padding: 28px;
$network-selector-horizontal-mobile-padding: 14px;
$btn-network-selector-load-more-background: #fff !default;
$btn-network-selector-load-more-color: $primary !default;
$network-selector-search-input-color: #a3a9b5 !default;
@ -143,7 +144,10 @@ $network-selector-item-icon-dimensions: 30px !default;
border-bottom: 1px solid $base-border-color;
display: flex;
flex-shrink: 0;
margin: 0 $network-selector-horizontal-padding;
margin: 0 $network-selector-horizontal-mobile-padding;
@media (min-width: 375px) {
margin: 0 $network-selector-horizontal-padding;
}
}
.network-selector-tab {
@ -277,7 +281,10 @@ $network-selector-item-icon-dimensions: 30px !default;
flex-shrink: 1;
min-height: 100px;
overflow: auto;
padding: 0 $network-selector-horizontal-padding;
padding: 0 $network-selector-horizontal-mobile-padding;
@media (min-width: 375px) {
padding: 0 $network-selector-horizontal-padding;
}
}
.network-selector-load-more-container {

@ -174,6 +174,11 @@ $tile-body-a-color: #5959d8 !default;
.tile-body {
a {
color: $tile-body-a-color;
&:hover {
span {
text-decoration: underline;
}
}
}
}

@ -1,21 +1,61 @@
.token-balance-dropdown {
min-width: 14.375rem;
min-width: 10rem;
margin-top: 1rem;
background-color: $gray-100;
box-shadow: 0 2px 3px 2px $gray-200;
border: none;
background-color: #fff;
&.dropdown-menu {
border-radius: 4px !important;
box-shadow: 0 0.5rem 1rem rgba(202, 199, 226, .3) !important;
border: 1px solid #e2e5ec !important;
}
.dropdown-items {
overflow-y: auto;
max-height: 18.5rem;
.dropdown-item:hover {
color: $white;
.dropdown-item {
&:hover {
color: $secondary;
}
.row:nth-child(2) {
p {
font-size: 12px;
opacity: .65;
}
}
}
}
&:after,
&:before {
.dropdown-header {
padding: 1.1rem 20px .9rem 20px;
font-size: 14px;
font-weight: 400;
}
.dropdown-search-field {
height: 50px;
border: 1px solid #f5f6fa;
background-color: #f5f6fa;
outline: none !important;
font-size: 14px;
color: #828ba0;
font-weight: 300;
padding-left: 52px;
&::placeholder {
color: rgba(#828ba0, .5);
}
}
.dropdown-search-icon {
left: 20px;
top: 50%;
margin-top: -8.5px;
path {
fill: #828ba0;
}
}
&:after {
bottom: 100%;
right: 14%;
border: solid transparent;
@ -26,8 +66,6 @@
}
&:before {
border-bottom-color: $gray-100;
border-width: 0.5rem;
margin-left: -0.5rem;
display: none;
}
}

@ -27,19 +27,20 @@
margin-top: 30px;
}
.download-all-transactions-link {
display: inline-flex;
align-items: center;
text-decoration: none;
svg {
position: relative;
margin-left: 2px;
top: -3px;
left: 3px;
path {
fill: $primary;
}
}
&:hover {
span {
text-decoration: underline;
}
text-decoration: underline;
}
}
}

@ -64,4 +64,11 @@ $card-tab-active: $primary;
// Badges
$badge-neutral-color: $primary;
$badge-neutral-background-color: rgba($primary, .1);
$api-text-monospace-color: $primary;
$api-text-monospace-color: $primary;
// Tokens dropdown
.token-balance-dropdown[aria-labelledby="dropdown-tokens"] {
.dropdown-items .dropdown-item:hover {
color: $primary !important;
}
}

@ -64,4 +64,11 @@ $card-tab-active: $primary;
// Badges
$badge-neutral-color: $primary;
$badge-neutral-background-color: rgba($primary, .1);
$api-text-monospace-color: $primary;
$api-text-monospace-color: $primary;
// Tokens dropdown
.token-balance-dropdown[aria-labelledby="dropdown-tokens"] {
.dropdown-items .dropdown-item:hover {
color: $primary !important;
}
}

@ -18,7 +18,7 @@ function formatCurrencyValue (value, symbol) {
if (value < 0.000001) return `${window.localized['Less than']} ${symbol}0.000001`
if (value < 1) return `${symbol}${numeral(value).format('0.000000')}`
if (value < 100000) return `${symbol}${numeral(value).format('0,0.00')}`
if (value > 1000000000) return `${symbol}${numeral(value).format('0.000e+0')}`
if (value > 1000000000000) return `${symbol}${numeral(value).format('0.000e+0')}`
return `${symbol}${numeral(value).format('0,0')}`
}

@ -25,17 +25,10 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
%{
"address_id" => address_hash_string,
"smart_contract" => smart_contract,
"external_libraries" => external_libraries,
"evm_version" => evm_version,
"optimization" => optimization
"external_libraries" => external_libraries
}
) do
smart_sontact_with_evm_version =
smart_contract
|> Map.put("evm_version", evm_version["evm_version"])
|> Map.put("optimization_runs", parse_optimization_runs(optimization))
case Publisher.publish(address_hash_string, smart_sontact_with_evm_version, external_libraries) do
case Publisher.publish(address_hash_string, smart_contract, external_libraries) do
{:ok, _smart_contract} ->
redirect(conn, to: address_contract_path(conn, :index, address_hash_string))

@ -3,6 +3,7 @@ defmodule BlockScoutWeb.PageNotFoundController do
def index(conn, _params) do
conn
|> put_status(:not_found)
|> render("index.html")
end
end

@ -44,7 +44,7 @@ defmodule BlockScoutWeb.Tokens.HolderController do
def index(conn, %{"token_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token} <- Chain.token_from_address_hash(address_hash) do
{:ok, token} <- Chain.token_from_address_hash(address_hash, [{:contract_address, :smart_contract}]) do
render(
conn,
"index.html",

@ -61,7 +61,7 @@ defmodule BlockScoutWeb.Tokens.InventoryController do
def index(conn, %{"token_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token} <- Chain.token_from_address_hash(address_hash) do
{:ok, token} <- Chain.token_from_address_hash(address_hash, [{:contract_address, :smart_contract}]) do
render(
conn,
"index.html",

@ -5,7 +5,7 @@ defmodule BlockScoutWeb.Tokens.ReadContractController do
def index(conn, %{"token_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token} <- Chain.token_from_address_hash(address_hash) do
{:ok, token} <- Chain.token_from_address_hash(address_hash, [{:contract_address, :smart_contract}]) do
render(
conn,
"index.html",

@ -45,7 +45,7 @@ defmodule BlockScoutWeb.Tokens.TransferController do
def index(conn, %{"token_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token} <- Chain.token_from_address_hash(address_hash) do
{:ok, token} <- Chain.token_from_address_hash(address_hash, [{:contract_address, :smart_contract}]) do
render(
conn,
"index.html",

@ -30,10 +30,39 @@
<dt class="col-sm-4 col-md-2 text-muted"><%= gettext "Compiler version" %></dt>
<dd class="col-sm-8 col-md-10"><%= @address.smart_contract.compiler_version %></dd>
</dl>
<%= if @address.smart_contract.evm_version do %>
<dl class="row">
<dt class="col-sm-4 col-md-2 text-muted"><%= gettext "EVM Version" %></dt>
<dd class="col-sm-8 col-md-10"><%= @address.smart_contract.evm_version %></dd>
</dl>
<% end %>
<dl class="row">
<dt class="col-sm-4 col-md-2 text-muted"><%= gettext "Optimization enabled" %></dt>
<dd class="col-sm-8 col-md-10"><%= format_optimization_text(@address.smart_contract.optimization) %></dd>
</dl>
<%= if @address.smart_contract.optimization && @address.smart_contract.optimization_runs do %>
<dl class="row">
<dt class="col-sm-4 col-md-2 text-muted"><%= gettext "Optimization runs" %></dt>
<dd class="col-sm-8 col-md-10"><%= @address.smart_contract.optimization_runs %></dd>
</dl>
<% end %>
<%= if @address.smart_contract.constructor_arguments do %>
<dl class="row">
<dt class="col-sm-4 col-md-2 text-muted"><%= gettext "Constructor arguments" %></dt>
<dd class="col-sm-8 col-md-10"><%= @address.smart_contract.constructor_arguments %></dd>
</dl>
<% end %>
<%= if @address.smart_contract.external_libraries do %>
<section>
<div class="d-flex justify-content-between align-items-baseline">
<h3><%= gettext "External libraries" %></h3>
</div>
<div class="tile tile-muted mb-4">
<pre class="pre-wrap pre-scrollable"><code class="nohighlight"><%= format_smart_contract_abi(@address.smart_contract.abi) %></code>
</pre>
</div>
</section>
<% end %>
</div>
<hr/>
<section>
@ -64,7 +93,7 @@
<% end %>
<section>
<%= case contract_creation_code do %>
<% {:selfdestructed, transaction_init} -> %>
<% {:selfdestructed, transaction_init} -> %>
<div class="d-flex justify-content-between align-items-baseline">
<h3><%= gettext "Contract Creation Code" %></h3>
<button type="button" class="button button-secondary button-sm" id="button" data-clipboard-text="<%= transaction_init %>" aria-label="copy contract creation code">

@ -44,7 +44,7 @@
<div class="smart-contract-form-group-inner-wrapper">
<%= label :evm_version, :evm_version, gettext("EVM Version") %>
<div class="center-column">
<%= select :evm_version, :evm_version, @evm_versions, class: "form-control border-rounded", selected: "petersburg", "aria-describedby": "evm-version-help-block" %>
<%= select f, :evm_version, @evm_versions, class: "form-control border-rounded", selected: "petersburg", "aria-describedby": "evm-version-help-block" %>
</div>
<div class="smart-contract-form-group-tooltip">The EVM version the contract is written for. If the bytecode does not match the version, we try to verify using the latest EVM version. <a href="https://forum.poa.network/t/smart-contract-verification-evm-version-details/2318" target="_blank">EVM version details</a>.</div>
</div>
@ -76,7 +76,7 @@
<div class="smart-contract-form-group-inner-wrapper">
<%= label f, :name, gettext("Optimization runs") %>
<div class="center-column">
<%= text_input :optimization, :runs, value: 200, class: "form-control border-rounded", "aria-describedby": "optimization-runs-help-block", "data-test": "optimization-runs" %>
<%= text_input f, :optimization_runs, value: 200, class: "form-control border-rounded", "aria-describedby": "optimization-runs-help-block", "data-test": "optimization-runs" %>
</div>
<div class="smart-contract-form-group-tooltip"></div>
</div>

@ -67,17 +67,8 @@
</div>
<div data-items></div>
<!--<div class="transaction-bottom-panel">
<div class="download-all-transactions">
Download <a class="download-all-transactions-link" href=<%= address_transaction_path(@conn, :token_transfers_csv, %{"address_id" => to_string(@address.hash)}) %>><%= gettext("CSV") %></span>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="16">
<path fill="#333333" fill-rule="evenodd" d="M13 16H1c-.999 0-1-1-1-1V1s-.004-1 1-1h6l7 7v8s-.032 1-1 1zm-1-8c0-.99-1-1-1-1H8s-1 .001-1-1V3c0-.999-1-1-1-1H2v12h10V8z"/>
</svg>
</a>
</div>-->
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>
<!--</div>-->
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>
</div>
</div>

@ -18,8 +18,10 @@
<div class="dropdown-menu dropdown-menu-right token-balance-dropdown p-0" aria-labelledby="dropdown-tokens">
<div data-dropdown-items class="dropdown-items">
<div class="m-3 position-relative">
<i class="fas fa-search position-absolute dropdown-search-icon"></i>
<div class="position-relative">
<svg class="position-absolute dropdown-search-icon" viewBox="0 0 16 17" xmlns="http://www.w3.org/2000/svg" width="16" height="17" class="dropdown-search-icon position-absolute">
<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"></path>
</svg>
<%= text_input(
:token_search,
:name,

@ -2,7 +2,7 @@
<div class="card">
<div class="card-body">
<h1 class="card-title margin-bottom-sm"><%= gettext("ETH RPC API Documentation") %></h2>
<p class="api-text-monospace" data-endpoint-url="<%= BlockScoutWeb.Endpoint.url() %>/api/eth_rpc">[ <%= gettext "Base URL:" %> <%= url() %>/api/eth_rpc ]</p>
<p class="api-text-monospace" data-endpoint-url="<%= blockscout_url() %>/api/eth_rpc">[ <%= gettext "Base URL:" %> <%= blockscout_url() %>/api/eth_rpc ]</p>
<p class="card-subtitle margin-bottom-0">
<%= gettext "This API is provided to support some rpc methods in the exact format specified for ethereum nodes, which can be found " %>

@ -2,7 +2,7 @@
<div class="card">
<div class="card-body">
<h1 class="card-title margin-bottom-sm"><%= gettext("API Documentation") %></h2>
<p class="api-text-monospace" data-endpoint-url="<%= BlockScoutWeb.Endpoint.url() %>/api">[ <%= gettext "Base URL:" %> <%= url() %>/api ]</p>
<p class="api-text-monospace" data-endpoint-url="<%= blockscout_url() %>/api">[ <%= gettext "Base URL:" %> <%= blockscout_url() %>/api ]</p>
<p class="card-subtitle margin-bottom-0"><%= gettext "This API is provided for developers transitioning their applications from Etherscan to BlockScout. It supports GET and POST requests." %></p>
</div>
<div class="api-anchors-list">

@ -9,7 +9,7 @@
</div>
<div class="network-selector-text-container">
<h1 class="network-selector-title"><%= gettext("Change Network") %></h1>
<p class="network-selector-text"><%= gettext("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.") %></p>
<p class="network-selector-text"><%= gettext("Use the search box to find a hosted network, or select from the list of available networks below.") %></p>
</div>
<form class="network-selector-search-container">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="17">

@ -27,17 +27,8 @@
</div>
<div data-items></div>
<!--<div class="transaction-bottom-panel">
<div class="download-all-transactions">
Download <a class="download-all-transactions-link" href=<%= address_transaction_path(@conn, :token_transfers_csv, %{"address_id" => to_string(@address.hash)}) %>><%= gettext("CSV") %></span>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="16">
<path fill="#333333" fill-rule="evenodd" d="M13 16H1c-.999 0-1-1-1-1V1s-.004-1 1-1h6l7 7v8s-.032 1-1 1zm-1-8c0-.99-1-1-1-1H8s-1 .001-1-1V3c0-.999-1-1-1-1H2v12h10V8z"/>
</svg>
</a>
</div>-->
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>
<!--</div>-->
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>
</div>
</div>

@ -1,5 +1,5 @@
<div class="text-nowrap row mt-3 mt-sm-0" data-test="token_transfer">
<span class="col-12 col-md-5">
<span class="col-xs-12 col-lg-5">
<%= if from_or_to_address?(@token_transfer, @address) do %>
<%= if @token_transfer.from_address_hash == @address.hash do %>
<span data-test="transaction_type" class="text-danger">
@ -19,7 +19,7 @@
<%= @token_transfer |> BlockScoutWeb.AddressView.address_partial_selector(:to, @address, true) |> BlockScoutWeb.RenderHelpers.render_partial() %>
</span>
</span>
<span class="col-12 col-md-7 ml-3 ml-sm-0">
<span class="col-xs-12 col-lg-4 ml-3 ml-sm-0">
<%= token_transfer_amount(@token_transfer) %>
<%= link(token_symbol(@token_transfer.token), to: token_path(BlockScoutWeb.Endpoint, :show, @token_transfer.token.contract_address_hash)) %>
</span>

@ -34,7 +34,7 @@ defmodule BlockScoutWeb.APIDocsView do
end)
end
defp url do
def blockscout_url do
if System.get_env("BLOCKSCOUT_HOST") do
"http://" <> System.get_env("BLOCKSCOUT_HOST")
else

@ -258,7 +258,7 @@ msgid "Connection Lost, click to load newer validations"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:53
#: lib/block_scout_web/templates/address_contract/index.html.eex:82
msgid "Contract ABI"
msgstr ""
@ -296,7 +296,7 @@ msgid "Contract name:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:41
#: lib/block_scout_web/templates/address_contract/index.html.eex:70
msgid "Contract source code"
msgstr ""
@ -577,7 +577,7 @@ msgid "OUT"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:34
#: lib/block_scout_web/templates/address_contract/index.html.eex:40
msgid "Optimization enabled"
msgstr ""
@ -674,7 +674,7 @@ msgstr ""
#, elixir-format
#:
#: lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex:28
#: lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex:30
msgid "Search tokens"
msgstr ""
@ -1405,17 +1405,17 @@ msgid "Support"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:55
#: lib/block_scout_web/templates/address_contract/index.html.eex:84
msgid "Copy ABI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:71
#: lib/block_scout_web/templates/address_contract/index.html.eex:100
msgid "Copy Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:43
#: lib/block_scout_web/templates/address_contract/index.html.eex:72
msgid "Copy Source Code"
msgstr ""
@ -1487,6 +1487,7 @@ msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:35
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:45
msgid "EVM Version"
msgstr ""
@ -1517,6 +1518,7 @@ msgid "Decompiler version"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:45
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:77
msgid "Optimization runs"
msgstr ""
@ -1583,27 +1585,27 @@ msgid "Block Details"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:83
#: lib/block_scout_web/templates/address_contract/index.html.eex:112
msgid "Contract Byte Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:69
#: lib/block_scout_web/templates/address_contract/index.html.eex:98
msgid "Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:75
#: lib/block_scout_web/templates/address_contract/index.html.eex:104
msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:85
#: lib/block_scout_web/templates/address_contract/index.html.eex:114
msgid "Copy Contract Byte Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:76
#: lib/block_scout_web/templates/address_contract/index.html.eex:105
msgid "Displaying the init data provided of the creating transaction."
msgstr ""
@ -1740,50 +1742,58 @@ msgstr ""
msgid "here."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:26
#: lib/block_scout_web/templates/address_transaction/index.html.eex:72
msgid "CSV"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:11
msgid "Change Network"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:24
msgid "Favorites"
#: lib/block_scout_web/templates/address_contract/index.html.eex:51
msgid "Constructor arguments"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:12
msgid "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore."
#: lib/block_scout_web/views/transaction_view.ex:44
msgid "ERC-20 "
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:22
msgid "Mainnet"
#: lib/block_scout_web/views/transaction_view.ex:45
msgid "ERC-721 "
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:18
msgid "Search network"
#: lib/block_scout_web/templates/address_contract/index.html.eex:58
msgid "External libraries"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:23
msgid "Testnet"
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:24
msgid "Favorites"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:73
#: lib/block_scout_web/templates/address_token/index.html.eex:26
#: lib/block_scout_web/templates/address_transaction/index.html.eex:72
#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:33
msgid "CSV"
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:22
msgid "Mainnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:44
msgid "ERC-20 "
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:18
msgid "Search network"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:45
msgid "ERC-721 "
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:23
msgid "Testnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:12
msgid "Use the search box to find a hosted network, or select from the list of available networks below."
msgstr ""

@ -112,7 +112,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:27
#: lib/block_scout_web/templates/address_transaction/index.html.eex:23
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:20
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:21
#: lib/block_scout_web/views/address_internal_transaction_view.ex:8
#: lib/block_scout_web/views/address_transaction_view.ex:8
msgid "All"
@ -258,7 +258,7 @@ msgid "Connection Lost, click to load newer validations"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:53
#: lib/block_scout_web/templates/address_contract/index.html.eex:82
msgid "Contract ABI"
msgstr ""
@ -296,7 +296,7 @@ msgid "Contract name:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:41
#: lib/block_scout_web/templates/address_contract/index.html.eex:70
msgid "Contract source code"
msgstr ""
@ -577,7 +577,7 @@ msgid "OUT"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:34
#: lib/block_scout_web/templates/address_contract/index.html.eex:40
msgid "Optimization enabled"
msgstr ""
@ -734,7 +734,7 @@ msgid "There are no token transfers for this address."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:19
#: lib/block_scout_web/templates/address_token/index.html.eex:18
msgid "There are no tokens for this address."
msgstr ""
@ -812,7 +812,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:8
#: lib/block_scout_web/templates/address_token/index.html.eex:9
#: lib/block_scout_web/templates/address_token/index.html.eex:8
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:9
#: lib/block_scout_web/views/address_view.ex:304
msgid "Tokens"
@ -1209,7 +1209,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:34
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:61
#: lib/block_scout_web/templates/address_logs/index.html.eex:21
#: lib/block_scout_web/templates/address_token/index.html.eex:14
#: lib/block_scout_web/templates/address_token/index.html.eex:13
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:20
#: lib/block_scout_web/templates/address_transaction/index.html.eex:59
#: lib/block_scout_web/templates/address_validation/index.html.eex:22
@ -1405,17 +1405,17 @@ msgid "Support"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:55
#: lib/block_scout_web/templates/address_contract/index.html.eex:84
msgid "Copy ABI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:71
#: lib/block_scout_web/templates/address_contract/index.html.eex:100
msgid "Copy Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:43
#: lib/block_scout_web/templates/address_contract/index.html.eex:72
msgid "Copy Source Code"
msgstr ""
@ -1487,6 +1487,7 @@ msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:35
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:45
msgid "EVM Version"
msgstr ""
@ -1517,6 +1518,7 @@ msgid "Decompiler version"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:45
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:77
msgid "Optimization runs"
msgstr ""
@ -1583,27 +1585,27 @@ msgid "Block Details"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:83
#: lib/block_scout_web/templates/address_contract/index.html.eex:112
msgid "Contract Byte Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:69
#: lib/block_scout_web/templates/address_contract/index.html.eex:98
msgid "Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:75
#: lib/block_scout_web/templates/address_contract/index.html.eex:104
msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:85
#: lib/block_scout_web/templates/address_contract/index.html.eex:114
msgid "Copy Contract Byte Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:76
#: lib/block_scout_web/templates/address_contract/index.html.eex:105
msgid "Displaying the init data provided of the creating transaction."
msgstr ""
@ -1715,7 +1717,7 @@ msgstr ""
msgid "However, in general, the"
msgstr ""
#, elixir-format, fuzzy
#, elixir-format
#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:27
msgid "There is no decompilded contracts for this address."
msgstr ""
@ -1741,46 +1743,57 @@ msgid "here."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:10
#: lib/block_scout_web/templates/address_token/index.html.eex:26
#: lib/block_scout_web/templates/address_transaction/index.html.eex:72
msgid "CSV"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:11
msgid "Change Network"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:23
msgid "Favorites"
#: lib/block_scout_web/templates/address_contract/index.html.eex:51
msgid "Constructor arguments"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:11
msgid "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore."
#: lib/block_scout_web/views/transaction_view.ex:44
msgid "ERC-20 "
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:21
msgid "Mainnet"
#: lib/block_scout_web/views/transaction_view.ex:45
msgid "ERC-721 "
msgstr ""
#, elixir-format, fuzzy
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:17
msgid "Search network"
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:58
msgid "External libraries"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:24
msgid "Favorites"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:22
msgid "Testnet"
msgid "Mainnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:74
msgid "CSV"
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:18
msgid "Search network"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:44
msgid "ERC-20 "
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:23
msgid "Testnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:45
msgid "ERC-721 "
#: lib/block_scout_web/templates/layout/_network_selector.html.eex:12
msgid "Use the search box to find a hosted network, or select from the list of available networks below."
msgstr ""

@ -0,0 +1,11 @@
defmodule BlockScoutWeb.PageNotFoundControllerTest do
use BlockScoutWeb.ConnCase
describe "GET index/2" do
test "returns 404 status", %{conn: conn} do
conn = get(conn, "/wrong", %{})
assert html_response(conn, 404)
end
end
end

@ -12,7 +12,9 @@ config :explorer,
token_functions_reader_max_retries: 3,
allowed_evm_versions:
System.get_env("ALLOWED_EVM_VERSIONS") ||
"homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg"
"homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg",
include_uncles_in_average_block_time:
if(System.get_env("UNCLES_IN_AVERAGE_BLOCK_TIME") == "false", do: false, else: true)
config :explorer, Explorer.Counters.AverageBlockTime, enabled: true

@ -2670,14 +2670,19 @@ defmodule Explorer.Chain do
@doc """
Fetches a `t:Token.t/0` by an address hash.
Optionally accepts a list of bindings to preload, just like `Ecto.Query.preload/3`
"""
@spec token_from_address_hash(Hash.Address.t()) :: {:ok, Token.t()} | {:error, :not_found}
def token_from_address_hash(%Hash{byte_count: unquote(Hash.Address.byte_count())} = hash) do
@spec token_from_address_hash(Hash.Address.t(), [Macro.t()]) :: {:ok, Token.t()} | {:error, :not_found}
def token_from_address_hash(
%Hash{byte_count: unquote(Hash.Address.byte_count())} = hash,
preloads \\ []
) do
query =
from(
token in Token,
where: token.contract_address_hash == ^hash,
preload: [{:contract_address, :smart_contract}]
preload: ^preloads
)
case Repo.one(query) do

@ -171,8 +171,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
),
status:
fragment(
"COALESCE(?, CASE WHEN (SELECT it.error FROM internal_transactions AS it WHERE it.transaction_hash = ? ORDER BY it.index ASC LIMIT 1) IS NULL THEN ? ELSE ? END)",
t.status,
"CASE WHEN (SELECT it.error FROM internal_transactions AS it WHERE it.transaction_hash = ? ORDER BY it.index ASC LIMIT 1) IS NULL THEN ? ELSE ? END",
t.hash,
type(^:ok, t.status),
type(^:error, t.status)

@ -198,6 +198,9 @@ defmodule Explorer.Chain.SmartContract do
compiler_version: String.t(),
optimization: boolean,
contract_source_code: String.t(),
constructor_arguments: String.t() | nil,
evm_version: String.t() | nil,
optimization_runs: non_neg_integer() | nil,
abi: [function_description]
}
@ -207,6 +210,9 @@ defmodule Explorer.Chain.SmartContract do
field(:optimization, :boolean)
field(:contract_source_code, :string)
field(:constructor_arguments, :string)
field(:evm_version, :string)
field(:optimization_runs, :integer)
field(:external_libraries, :map)
field(:abi, {:array, :map})
has_many(
@ -239,7 +245,10 @@ defmodule Explorer.Chain.SmartContract do
:contract_source_code,
:address_hash,
:abi,
:constructor_arguments
:constructor_arguments,
:evm_version,
:optimization_runs,
:external_libraries
])
|> validate_required([:name, :compiler_version, :optimization, :contract_source_code, :abi, :address_hash])
|> unique_constraint(:address_hash)
@ -248,7 +257,16 @@ defmodule Explorer.Chain.SmartContract do
def invalid_contract_changeset(%__MODULE__{} = smart_contract, attrs, error) do
smart_contract
|> cast(attrs, [:name, :compiler_version, :optimization, :contract_source_code, :address_hash])
|> cast(attrs, [
:name,
:compiler_version,
:optimization,
:contract_source_code,
:address_hash,
:evm_version,
:optimization_runs,
:constructor_arguments
])
|> validate_required([:name, :compiler_version, :optimization, :address_hash])
|> add_error(:contract_source_code, error_message(error))
end

@ -247,7 +247,7 @@ defmodule Explorer.Chain.Transaction do
end
@doc """
A pending transaction has neither `block_hash` nor an `index`
A pending transaction does not have a `block_hash`
iex> changeset = Explorer.Chain.Transaction.changeset(
...> %Transaction{},
@ -267,42 +267,6 @@ defmodule Explorer.Chain.Transaction do
iex> changeset.valid?
true
A pending transaction (which is indicated by not having a `block_hash`) can't have `block_number`,
`cumulative_gas_used`, `gas_used`, or `index`.
iex> changeset = Explorer.Chain.Transaction.changeset(
...> %Transaction{},
...> %{
...> from_address_hash: "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
...> block_number: 34,
...> cumulative_gas_used: 0,
...> gas: 4700000,
...> gas_price: 100000000000,
...> gas_used: 4600000,
...> hash: "0x3a3eb134e6792ce9403ea4188e5e79693de9e4c94e499db132be086400da79e6",
...> index: 0,
...> input: "0x6060604052341561000f57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102db8061005e6000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100a05780638da5cb5b146100c9578063fdacd5761461011e575b600080fd5b341561007257600080fd5b61009e600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610141565b005b34156100ab57600080fd5b6100b3610224565b6040518082815260200191505060405180910390f35b34156100d457600080fd5b6100dc61022a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561012957600080fd5b61013f600480803590602001909190505061024f565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415610220578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b151561020b57600080fd5b6102c65a03f1151561021c57600080fd5b5050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102ac57806001819055505b505600a165627a7a72305820a9c628775efbfbc17477a472413c01ee9b33881f550c59d21bee9928835c854b0029",
...> nonce: 0,
...> r: 0xAD3733DF250C87556335FFE46C23E34DBAFFDE93097EF92F52C88632A40F0C75,
...> s: 0x72caddc0371451a58de2ca6ab64e0f586ccdb9465ff54e1c82564940e89291e3,
...> status: :ok,
...> v: 0x8d,
...> value: 0
...> }
...> )
iex> changeset.valid?
false
iex> Keyword.get_values(changeset.errors, :block_number)
[{"can't be set when the transaction is pending", []}]
iex> Keyword.get_values(changeset.errors, :cumulative_gas_used)
[{"can't be set when the transaction is pending", []}]
iex> Keyword.get_values(changeset.errors, :gas_used)
[{"can't be set when the transaction is pending", []}]
iex> Keyword.get_values(changeset.errors, :index)
[{"can't be set when the transaction is pending", []}]
iex> Keyword.get_values(changeset.errors, :status)
[{"can't be set when the transaction is pending", []}]
A collated transaction MUST have an `index` so its position in the `block` is known and the `cumulative_gas_used` ane
`gas_used` to know its fees.
@ -359,35 +323,6 @@ defmodule Explorer.Chain.Transaction do
iex> changeset.valid?
true
Once the `internal_transactions_indexed_at` is set, both pre- and post-Byzantium transactions will be able to know
their status, so if `internal_transaction_indexed_at` is set, `status` is required.
iex> changeset = Explorer.Chain.Transaction.changeset(
...> %Transaction{},
...> %{
...> block_hash: "0xe52d77084cab13a4e724162bcd8c6028e5ecfaa04d091ee476e96b9958ed6b47",
...> block_number: 34,
...> cumulative_gas_used: 0,
...> from_address_hash: "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
...> gas: 4700000,
...> gas_price: 100000000000,
...> gas_used: 4600000,
...> hash: "0x3a3eb134e6792ce9403ea4188e5e79693de9e4c94e499db132be086400da79e6",
...> index: 0,
...> input: "0x6060604052341561000f57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102db8061005e6000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100a05780638da5cb5b146100c9578063fdacd5761461011e575b600080fd5b341561007257600080fd5b61009e600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610141565b005b34156100ab57600080fd5b6100b3610224565b6040518082815260200191505060405180910390f35b34156100d457600080fd5b6100dc61022a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561012957600080fd5b61013f600480803590602001909190505061024f565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415610220578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b151561020b57600080fd5b6102c65a03f1151561021c57600080fd5b5050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102ac57806001819055505b505600a165627a7a72305820a9c628775efbfbc17477a472413c01ee9b33881f550c59d21bee9928835c854b0029",
...> internal_transactions_indexed_at: DateTime.utc_now(),
...> nonce: 0,
...> r: 0xAD3733DF250C87556335FFE46C23E34DBAFFDE93097EF92F52C88632A40F0C75,
...> s: 0x72caddc0371451a58de2ca6ab64e0f586ccdb9465ff54e1c82564940e89291e3,
...> v: 0x8d,
...> value: 0
...> }
...> )
iex> changeset.valid?
false
iex> Keyword.get_values(changeset.errors, :status)
[{"can't be blank when the internal transactions have been fetched", []}]
The `error` can only be set with a specific error message when `status` is `:error`
iex> changeset = Explorer.Chain.Transaction.changeset(
@ -445,10 +380,9 @@ defmodule Explorer.Chain.Transaction do
transaction
|> cast(attrs, @required_attrs ++ @optional_attrs)
|> validate_required(@required_attrs)
|> validate_collated_or_pending()
|> validate_collated()
|> validate_error()
|> validate_status()
|> check_pending()
|> check_collated()
|> check_error()
|> check_status()
@ -592,29 +526,17 @@ defmodule Explorer.Chain.Transaction do
{collated_field, :"collated_#{collated_field}}"}
end)
@pending_fields_with_check @collated_fields
@pending_fields_with_validation @collated_fields ++ ~w(internal_transaction_indexed_at status)a
@pending_message "can't be set when the transaction is pending"
@pending_field_to_check Enum.into(@pending_fields_with_check, %{}, fn pending_field ->
{pending_field, :"pending_#{pending_field}}"}
end)
defp check_collated(%Changeset{} = changeset) do
check_constraints(changeset, @collated_field_to_check, @collated_message)
end
defp check_pending(%Changeset{} = changeset) do
check_constraints(changeset, @pending_field_to_check, @pending_message)
end
@error_message "can't be set when status is not :error"
defp check_error(%Changeset{} = changeset) do
check_constraint(changeset, :error, message: @error_message, name: :error)
changeset
end
@status_message "can't be blank when the internal transactions have been fetched"
@status_message "can't be set when the block_hash is unknown"
defp check_status(%Changeset{} = changeset) do
check_constraint(changeset, :status, message: @status_message, name: :status)
@ -632,22 +554,10 @@ defmodule Explorer.Chain.Transaction do
end)
end
defp validate_collated_or_pending(%Changeset{} = changeset) do
defp validate_collated(%Changeset{} = changeset) do
case Changeset.get_field(changeset, :block_hash) do
nil -> validate_collated_or_pending(changeset, @pending_fields_with_validation, &validate_pending/2)
%Hash{} -> validate_collated_or_pending(changeset, @collated_fields, &validate_collated/2)
end
end
defp validate_collated_or_pending(%Changeset{} = changeset, fields, field_validator)
when is_list(fields) and is_function(field_validator, 2) do
Enum.reduce(fields, changeset, field_validator)
end
defp validate_pending(field, %Changeset{} = changeset) when is_atom(field) do
case Changeset.get_field(changeset, field) do
%Hash{} -> Enum.reduce(@collated_fields, changeset, &validate_collated/2)
nil -> changeset
_ -> Changeset.add_error(changeset, field, @pending_message)
end
end
@ -667,9 +577,8 @@ defmodule Explorer.Chain.Transaction do
end
defp validate_status(%Changeset{} = changeset) do
# all other errors on status are handled by validate_pending
if Changeset.get_field(changeset, :internal_transactions_indexed_at) != nil and
Changeset.get_field(changeset, :status) == nil do
if Changeset.get_field(changeset, :block_hash) == nil and
Changeset.get_field(changeset, :status) != nil do
Changeset.add_error(changeset, :status, @status_message)
else
changeset

@ -70,8 +70,17 @@ defmodule Explorer.Counters.AverageBlockTime do
select: {block.number, block.timestamp}
)
query =
if Application.get_env(:explorer, :include_uncles_in_average_block_time) do
timestamps_query
else
from(block in timestamps_query,
where: block.consensus == true
)
end
timestamps =
timestamps_query
query
|> Repo.all()
|> Enum.sort_by(fn {_, timestamp} -> timestamp end, &>=/2)
|> Enum.map(fn {number, timestamp} ->

@ -68,9 +68,12 @@ defmodule Explorer.SmartContract.Publisher do
address_hash: address_hash,
name: params["name"],
compiler_version: params["compiler_version"],
evm_version: params["evm_version"],
optimization_runs: params["optimization_runs"],
optimization: params["optimization"],
contract_source_code: params["contract_source_code"],
constructor_arguments: clean_constructor_arguments,
external_libaries: params["external_libraries"],
abi: abi
}
end

@ -70,7 +70,7 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
compiler_version = Keyword.fetch!(params, :compiler_version)
code = Keyword.fetch!(params, :code)
optimize = Keyword.fetch!(params, :optimize)
optimization_runs = params |> Keyword.get(:optimization_runs, 200) |> Integer.to_string()
optimization_runs = optimization_runs(params)
evm_version = Keyword.get(params, :evm_version, List.last(allowed_evm_versions()))
external_libs = Keyword.get(params, :external_libs, %{})
@ -163,6 +163,16 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do
defp optimize_value(true), do: "1"
defp optimize_value("true"), do: "1"
defp optimization_runs(params) do
value = params |> Keyword.get(:optimization_runs, "200")
if is_binary(value) do
value
else
"#{value}"
end
end
defp create_source_file(source) do
{:ok, path} = Briefly.create()

@ -92,7 +92,6 @@ defmodule Explorer.Mixfile do
{:math, "~> 0.3.0"},
{:mock, "~> 0.3.0", only: [:test], runtime: false},
{:mox, "~> 0.4", only: [:test]},
{:nimble_csv, "~> 0.6.0"},
{:poison, "~> 3.1"},
{:nimble_csv, "~> 0.6.0"},
{:postgrex, ">= 0.0.0"},

@ -0,0 +1,132 @@
defmodule Explorer.Repo.Migrations.ReduceTransactionStatusConstraint do
use Ecto.Migration
def up do
drop(
constraint(
:transactions,
:status
)
)
create(
constraint(
:transactions,
:status,
# NOTE: all checks on status are lifted except those regarding block_hash
# This is because of block invalidation, that causes transactions to be
# refetched while previous internal transactions still exist
check: """
(block_hash IS NULL AND status IS NULL) OR
(block_hash IS NOT NULL) OR
(status = 0 and error = 'dropped/replaced')
"""
)
)
drop(
constraint(
:transactions,
:error
)
)
create(
constraint(
:transactions,
:error,
# NOTE: all checks on error are lifted except when status is not 0, for
# the same reasons as above
check: """
(status = 0) OR
(status != 0 AND error IS NULL)
"""
)
)
end
def down do
drop(
constraint(
:transactions,
:status
)
)
create(
constraint(
:transactions,
:status,
# 0 - NULL
# 1 - NOT NULL
#
# | block_hash | internal_transactions_indexed_at | status | OK | description
# |------------|----------------------------------|--------|----|------------
# | 0 | 0 | 0 | 1 | pending
# | 0 | 0 | 1 | 0 | pending with status
# | 0 | 1 | 0 | 0 | pending with internal transactions
# | 0 | 1 | 1 | 0 | pending with internal transactions and status
# | 1 | 0 | 0 | 1 | pre-byzantium collated transaction without internal transactions
# | 1 | 0 | 1 | 1 | post-byzantium collated transaction without internal transactions
# | 1 | 1 | 0 | 0 | pre-byzantium collated transaction with internal transaction without status
# | 1 | 1 | 1 | 1 | pre- or post-byzantium collated transaction with internal transactions and status
#
# [Karnaugh map](https://en.wikipedia.org/wiki/Karnaugh_map)
# b \ is | 00 | 01 | 11 | 10 |
# -------|----|----|----|----|
# 0 | 1 | 0 | 0 | 0 |
# 1 | 1 | 1 | 1 | 0 |
#
# Simplification: ¬i·¬s + b·¬i + b·s
check: """
(internal_transactions_indexed_at IS NULL AND status IS NULL) OR
(block_hash IS NOT NULL AND internal_transactions_indexed_at IS NULL) OR
(block_hash IS NOT NULL AND status IS NOT NULL) OR
(status = 0 and error = 'dropped/replaced')
"""
)
)
drop(
constraint(
:transactions,
:error
)
)
create(
constraint(
:transactions,
:error,
# | status | internal_transactions_indexed_at | error | OK | description
# |--------|----------------------------------|----------|------------|------------
# | NULL | NULL | NULL | TRUE | pending or pre-byzantium collated
# | NULL | NULL | NOT NULL | FALSE | error cannot be known before internal transactions are indexed
# | NULL | NOT NULL | NULL | DON'T CARE | handled by `status` check
# | NULL | NOT NULL | NOT NULL | FALSE | error cannot be set unless status is known to be error (`0`)
# | 0 | NULL | NULL | TRUE | post-byzantium before internal transactions indexed
# | 0 | NULL | NOT NULL | FALSE | error cannot be set unless internal transactions are indexed
# | 0 | NOT NULL | NULL | FALSE | error MUST be set when status is error
# | 0 | NOT NULL | NOT NULL | TRUE | error is set when status is error
# | 1 | NULL | NULL | TRUE | post-byzantium before internal transactions indexed
# | 1 | NULL | NOT NULL | FALSE | error cannot be set when status is ok
# | 1 | NOT NULL | NULL | TRUE | error is not set when status is ok
# | 1 | NOT NULL | NOT NULL | FALSE | error cannot be set when status is ok
#
# Karnaugh map
# s \ ie | NULL, NULL | NULL, NOT NULL | NOT NULL, NOT NULL | NOT NULL, NULL |
# -------|------------|----------------|--------------------|----------------|
# NULL | TRUE | FALSE | FALSE | DON'T CARE |
# 0 | TRUE | FALSE | TRUE | FALSE |
# 1 | TRUE | FALSE | FALSE | TRUE |
#
check: """
(internal_transactions_indexed_at IS NULL AND error IS NULL) OR
(status = 0 AND internal_transactions_indexed_at IS NOT NULL AND error IS NOT NULL) OR
(status != 0 AND internal_transactions_indexed_at IS NOT NULL AND error IS NULL) OR
(status = 0 and error = 'dropped/replaced')
"""
)
)
end
end

@ -0,0 +1,11 @@
defmodule Explorer.Repo.Migrations.AddAdditionalContractFields do
use Ecto.Migration
def change do
alter table(:smart_contracts) do
add(:optimization_runs, :integer, null: true)
add(:evm_version, :string, null: true)
add(:external_libraries, :jsonb, null: true)
end
end
end

@ -0,0 +1,61 @@
defmodule Explorer.Chain.Import.Runner.InternalTransactionsTest do
use Explorer.DataCase
alias Ecto.Multi
alias Explorer.Chain.{Data, Wei, Transaction}
alias Explorer.Chain.Import.Runner.InternalTransactions
describe "run/1" do
test "transaction's status becomes :error when its internal_transaction has an error" do
transaction = insert(:transaction) |> with_block(status: :ok)
assert :ok == transaction.status
index = 0
error = "Reverted"
internal_transaction_changes = make_internal_transaction_changes(transaction.hash, index, error)
assert {:ok, _} = run_internal_transactions([internal_transaction_changes])
assert :error == Repo.get(Transaction, transaction.hash).status
end
end
defp run_internal_transactions(changes_list) when is_list(changes_list) do
Multi.new()
|> InternalTransactions.run(changes_list, %{
timeout: :infinity,
timestamps: %{inserted_at: DateTime.utc_now(), updated_at: DateTime.utc_now()}
})
|> Repo.transaction()
end
defp make_internal_transaction_changes(transaction_hash, index, error) do
%{
from_address_hash: insert(:address).hash,
to_address_hash: insert(:address).hash,
call_type: :call,
gas: 22234,
gas_used:
if is_nil(error) do
18920
else
nil
end,
input: %Data{bytes: <<1>>},
output:
if is_nil(error) do
%Data{bytes: <<2>>}
else
nil
end,
index: index,
trace_address: [],
transaction_hash: transaction_hash,
type: :call,
value: Wei.from(Decimal.new(1), :wei),
error: error
}
end
end

@ -573,7 +573,7 @@ defmodule Explorer.Chain.ImportTest do
transaction =
:transaction
|> insert(error: nil, internal_transactions_indexed_at: nil, status: nil, from_address: from_address, status: 0)
|> insert(error: nil, internal_transactions_indexed_at: nil, status: nil, from_address: from_address)
|> with_block(block, status: :error)
internal_transacton =

@ -3433,6 +3433,17 @@ defmodule Explorer.ChainTest do
token = build(:token)
assert {:error, :not_found} = Chain.token_from_address_hash(token.contract_address.hash)
end
test "with contract_address' smart_contract preloaded" do
smart_contract = build(:smart_contract)
address = insert(:address, smart_contract: smart_contract)
token = insert(:token, contract_address: address)
assert {:ok, result} =
Chain.token_from_address_hash(token.contract_address_hash, [{:contract_address, :smart_contract}])
assert smart_contract = result.contract_address.smart_contract
end
end
test "stream_uncataloged_token_contract_address_hashes/2 reduces with given reducer and accumulator" do

@ -11,6 +11,8 @@ defmodule Explorer.Counters.AverageBlockTimeTest do
start_supervised!(AverageBlockTime)
Application.put_env(:explorer, AverageBlockTime, enabled: true)
Application.put_env(:explorer, :include_uncles_in_average_block_time, true)
on_exit(fn ->
Application.put_env(:explorer, AverageBlockTime, enabled: false)
end)
@ -43,6 +45,35 @@ defmodule Explorer.Counters.AverageBlockTimeTest do
assert AverageBlockTime.average_block_time() == Timex.Duration.parse!("PT3S")
end
test "excludes uncles if include_uncles_in_average_block_time is set to false" do
block_number = 99_999_999
Application.put_env(:explorer, :include_uncles_in_average_block_time, false)
first_timestamp = Timex.now()
insert(:block, number: block_number, consensus: true, timestamp: Timex.shift(first_timestamp, seconds: 3))
insert(:block, number: block_number, consensus: false, timestamp: Timex.shift(first_timestamp, seconds: 4))
insert(:block, number: block_number + 1, consensus: true, timestamp: Timex.shift(first_timestamp, seconds: 5))
AverageBlockTime.refresh()
assert AverageBlockTime.average_block_time() == Timex.Duration.parse!("PT2S")
end
test "excludes uncles if include_uncles_in_average_block_time is set to true" do
block_number = 99_999_999
first_timestamp = Timex.now()
insert(:block, number: block_number, consensus: true, timestamp: Timex.shift(first_timestamp, seconds: 3))
insert(:block, number: block_number, consensus: false, timestamp: Timex.shift(first_timestamp, seconds: 4))
insert(:block, number: block_number + 1, consensus: true, timestamp: Timex.shift(first_timestamp, seconds: 5))
AverageBlockTime.refresh()
assert AverageBlockTime.average_block_time() == Timex.Duration.parse!("PT1S")
end
test "when there are no uncles sorts by block number" do
block_number = 99_999_999

@ -52,7 +52,7 @@ defmodule Indexer.Fetcher.Token do
@impl BufferedTask
@decorate trace(name: "fetch", resource: "Indexer.Fetcher.Token.run/2", service: :indexer, tracer: Tracer)
def run([token_contract_address], _json_rpc_named_arguments) do
case Chain.token_from_address_hash(token_contract_address) do
case Chain.token_from_address_hash(token_contract_address, [{:contract_address, :smart_contract}]) do
{:ok, %Token{} = token} ->
catalog_token(token)
end

@ -93,7 +93,10 @@ defmodule Indexer.Fetcher.TokenBalance do
end
def fetch_from_blockchain(params_list) do
retryable_params_list = Enum.filter(params_list, &(&1.retries_count <= @max_retries))
retryable_params_list =
params_list
|> Enum.filter(&(&1.retries_count <= @max_retries))
|> Enum.uniq_by(&Map.take(&1, [:token_contract_address_hash, :address_hash, :block_number]))
Logger.metadata(count: Enum.count(retryable_params_list))

@ -37,7 +37,7 @@ defmodule Indexer.Fetcher.TokenUpdater do
@doc false
def update_metadata(token_addresses) when is_list(token_addresses) do
Enum.each(token_addresses, fn address ->
case Chain.token_from_address_hash(address) do
case Chain.token_from_address_hash(address, [{:contract_address, :smart_contract}]) do
{:ok, %Token{cataloged: true} = token} ->
update_metadata(token)
end

@ -111,6 +111,41 @@ defmodule Indexer.Fetcher.TokenBalanceTest do
assert TokenBalance.run(token_balances, nil) == :ok
end
test "fetches duplicate params only once" do
%Address.TokenBalance{
address_hash: %Hash{bytes: address_hash_bytes} = address_hash,
token_contract_address_hash: %Hash{bytes: token_contract_address_hash_bytes},
block_number: block_number
} = insert(:token_balance, value_fetched_at: nil, value: nil)
expect(
EthereumJSONRPC.Mox,
:json_rpc,
fn [%{id: id, method: "eth_call", params: [%{data: _, to: _}, _]}], _options ->
{:ok,
[
%{
id: id,
jsonrpc: "2.0",
result: "0x00000000000000000000000000000000000000000000d3c21bcecceda1000000"
}
]}
end
)
assert TokenBalance.run(
[
{address_hash_bytes, token_contract_address_hash_bytes, block_number, 0},
{address_hash_bytes, token_contract_address_hash_bytes, block_number, 0}
],
nil
) == :ok
assert 1 =
from(tb in Address.TokenBalance, where: tb.address_hash == ^address_hash)
|> Explorer.Repo.aggregate(:count, :id)
end
end
describe "import_token_balances/1" do

@ -3,7 +3,6 @@
- About BlockScout
- [About](about.md)
- [Projects Using BlockScout](projects.md)
- [Umbrella Project Organization](umbrella.md)
- Installation & Configuration

@ -1,6 +1,6 @@
# BlockScout Env Variables
Below is a table outlining the environment variables utilized by BlockScout.
Below is a table outlining the environment variables utilized by BlockScout.
| Variable | Required | Description | Default | Version |

@ -6,12 +6,10 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple.css">
<link rel="stylesheet" href="//unpkg.com/docsify/themes/buble.css">
<style>
:root {
/* Reduce the font size */
--base-font-size: 16px;
nav.app-nav li ul {
min-width: 100px;
}
</style>
@ -32,7 +30,6 @@
</script>
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/docsify-themeable@0"></script>
<script src="https://unpkg.com/docsify-plugin-flexible-alerts"></script>
</body>
</html>

@ -1,20 +0,0 @@
<!-- projects.md -->
### Supported Projects
| **Hosted Mainnets** | **Hosted Testnets** | **Additional Chains using BlockScout** |
|--------------------------------------------------------|-------------------------------------------------------|----------------------------------------------------|
| [Aerum](https://blockscout.com/aerum/mainnet) | [Goerli Testnet](https://blockscout.com/eth/goerli) | [ARTIS](https://explorer.sigma1.artis.network) |
| [Callisto](https://blockscout.com/callisto/mainnet) | [Kovan Testnet](https://blockscout.com/eth/kovan) | [Ether-1](https://blocks.ether1.wattpool.net/) |
| [Ethereum Classic](https://blockscout.com/etc/mainnet) | [POA Sokol Testnet](https://blockscout.com/poa/sokol) | [Fuse Network](https://explorer.fuse.io/) |
| [Ethereum Mainnet](https://blockscout.com/eth/mainnet) | [Rinkeby Testnet](https://blockscout.com/eth/rinkeby) | [Oasis Labs](https://blockexplorer.oasiscloud.io/) |
| [POA Core Network](https://blockscout.com/poa/core) | [Ropsten Testnet](https://blockscout.com/eth/ropsten) | [Petrichor](https://explorer.petrachor.com/) |
| [RSK](https://blockscout.com/rsk/mainnet) | | [PIRL](http://pirl.es/) |
| [xDai Chain](https://blockscout.com/poa/dai) | | [SafeChain](https://explorer.safechain.io) |
| | | [SpringChain](https://explorer.springrole.com/) |
| | | [Kotti Testnet](https://kottiexplorer.ethernode.io/) |
| | | [Loom](http://plasma-blockexplorer.dappchains.com/) |
| | | [Tenda](https://tenda.network) |
Current BlockScout versions for hosted projects are available [on the forum](https://forum.poa.network/t/deployed-instances-on-blockscout-com/1938).
Loading…
Cancel
Save